Add bytecode for SERIF action.
[ttfautohint.git] / src / tabytecode.c
blob51d2e83d6a11be39bdd3df888549a5114ef10211
1 /* tabytecode.c */
3 /* written 2011 by Werner Lemberg <wl@gnu.org> */
5 #include "ta.h"
6 #include "tabytecode.h"
9 /* a simple macro to emit bytecode instructions */
10 #define BCI(code) *(bufp++) = (code)
12 /* we increase the stack depth by this amount */
13 #define ADDITIONAL_STACK_ELEMENTS 20
16 /* #define DEBUGGING */
19 #ifdef TA_DEBUG
20 int _ta_debug = 1;
21 int _ta_debug_disable_horz_hints;
22 int _ta_debug_disable_vert_hints;
23 int _ta_debug_disable_blue_hints;
24 void* _ta_debug_hints;
25 #endif
28 typedef struct Hints_Record_ {
29 FT_UInt size;
30 FT_UInt num_actions;
31 FT_Byte* buf;
32 FT_UInt buf_len;
33 } Hints_Record;
35 typedef struct Recorder_ {
36 FONT* font;
37 Hints_Record hints_record;
38 /* see explanations in `TA_sfnt_build_glyph_segments' */
39 FT_UInt* wrap_around_segments;
40 } Recorder;
43 static FT_Error
44 TA_sfnt_compute_global_hints(SFNT* sfnt,
45 FONT* font)
47 FT_Error error;
48 FT_Face face = sfnt->face;
49 FT_UInt enc;
50 FT_UInt idx;
52 static const FT_Encoding latin_encs[] =
54 FT_ENCODING_UNICODE,
55 FT_ENCODING_APPLE_ROMAN,
56 FT_ENCODING_ADOBE_STANDARD,
57 FT_ENCODING_ADOBE_LATIN_1,
59 FT_ENCODING_NONE /* end of list */
63 error = ta_loader_init(font->loader);
64 if (error)
65 return error;
67 /* try to select a latin charmap */
68 for (enc = 0; latin_encs[enc] != FT_ENCODING_NONE; enc++)
70 error = FT_Select_Charmap(face, latin_encs[enc]);
71 if (!error)
72 break;
75 /* load latin glyph `a' to trigger all initializations */
76 idx = FT_Get_Char_Index(face, 'a');
77 error = ta_loader_load_glyph(font->loader, face, idx, 0);
79 return error;
83 static FT_Error
84 TA_table_build_cvt(FT_Byte** cvt,
85 FT_ULong* cvt_len,
86 SFNT* sfnt,
87 FONT* font)
89 TA_LatinAxis haxis;
90 TA_LatinAxis vaxis;
92 FT_UInt i;
93 FT_UInt buf_len;
94 FT_UInt len;
95 FT_Byte* buf;
96 FT_Byte* buf_p;
98 FT_Error error;
101 error = TA_sfnt_compute_global_hints(sfnt, font);
102 if (error)
103 return error;
105 /* XXX check validity of pointers */
106 haxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[0];
107 vaxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[1];
109 buf_len = 2 * (2
110 + haxis->width_count
111 + vaxis->width_count
112 + 2 * vaxis->blue_count);
114 /* buffer length must be a multiple of four */
115 len = (buf_len + 3) & ~3;
116 buf = (FT_Byte*)malloc(len);
117 if (!buf)
118 return FT_Err_Out_Of_Memory;
120 /* pad end of buffer with zeros */
121 buf[len - 1] = 0x00;
122 buf[len - 2] = 0x00;
123 buf[len - 3] = 0x00;
125 buf_p = buf;
127 if (haxis->width_count > 0)
129 *(buf_p++) = HIGH(haxis->widths[0].org);
130 *(buf_p++) = LOW(haxis->widths[0].org);
132 else
134 *(buf_p++) = 0;
135 *(buf_p++) = 50;
137 if (vaxis->width_count > 0)
139 *(buf_p++) = HIGH(vaxis->widths[0].org);
140 *(buf_p++) = LOW(vaxis->widths[0].org);
142 else
144 *(buf_p++) = 0;
145 *(buf_p++) = 50;
148 for (i = 0; i < haxis->width_count; i++)
150 if (haxis->widths[i].org > 0xFFFF)
151 goto Err;
152 *(buf_p++) = HIGH(haxis->widths[i].org);
153 *(buf_p++) = LOW(haxis->widths[i].org);
156 for (i = 0; i < vaxis->width_count; i++)
158 if (vaxis->widths[i].org > 0xFFFF)
159 goto Err;
160 *(buf_p++) = HIGH(vaxis->widths[i].org);
161 *(buf_p++) = LOW(vaxis->widths[i].org);
164 for (i = 0; i < vaxis->blue_count; i++)
166 if (vaxis->blues[i].ref.org > 0xFFFF)
167 goto Err;
168 *(buf_p++) = HIGH(vaxis->blues[i].ref.org);
169 *(buf_p++) = LOW(vaxis->blues[i].ref.org);
172 for (i = 0; i < vaxis->blue_count; i++)
174 if (vaxis->blues[i].shoot.org > 0xFFFF)
175 goto Err;
176 *(buf_p++) = HIGH(vaxis->blues[i].shoot.org);
177 *(buf_p++) = LOW(vaxis->blues[i].shoot.org);
180 #if 0
181 TA_LOG(("--------------------------------------------------\n"));
182 TA_LOG(("glyph %d:\n", idx));
183 ta_glyph_hints_dump_edges(_ta_debug_hints);
184 ta_glyph_hints_dump_segments(_ta_debug_hints);
185 ta_glyph_hints_dump_points(_ta_debug_hints);
186 #endif
188 *cvt = buf;
189 *cvt_len = buf_len;
191 return FT_Err_Ok;
193 Err:
194 free(buf);
195 return TA_Err_Hinter_Overflow;
199 FT_Error
200 TA_sfnt_build_cvt_table(SFNT* sfnt,
201 FONT* font)
203 FT_Error error;
205 FT_Byte* cvt_buf;
206 FT_ULong cvt_len;
209 error = TA_sfnt_add_table_info(sfnt);
210 if (error)
211 return error;
213 error = TA_table_build_cvt(&cvt_buf, &cvt_len, sfnt, font);
214 if (error)
215 return error;
217 /* in case of success, `cvt_buf' gets linked */
218 /* and is eventually freed in `TA_font_unload' */
219 error = TA_font_add_table(font,
220 &sfnt->table_infos[sfnt->num_table_infos - 1],
221 TTAG_cvt, cvt_len, cvt_buf);
222 if (error)
224 free(cvt_buf);
225 return error;
228 return FT_Err_Ok;
232 /* the horizontal and vertical standard widths */
233 #define CVT_HORZ_STANDARD_WIDTH_OFFSET(font) 0
234 #define CVT_VERT_STANDARD_WIDTH_OFFSET(font) \
235 CVT_HORZ_STANDARD_WIDTH_OFFSET(font) + 1
237 /* the horizontal stem widths */
238 #define CVT_HORZ_WIDTHS_OFFSET(font) \
239 CVT_VERT_STANDARD_WIDTH_OFFSET(font) + 1
240 #define CVT_HORZ_WIDTHS_SIZE(font) \
241 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[0].width_count
243 /* the vertical stem widths */
244 #define CVT_VERT_WIDTHS_OFFSET(font) \
245 CVT_HORZ_WIDTHS_OFFSET(font) + CVT_HORZ_WIDTHS_SIZE(font)
246 #define CVT_VERT_WIDTHS_SIZE(font) \
247 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[1].width_count
249 /* the number of blue zones */
250 #define CVT_BLUES_SIZE(font) \
251 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[1].blue_count
253 /* the blue zone values for flat and round edges */
254 #define CVT_BLUE_REFS_OFFSET(font) \
255 CVT_VERT_WIDTHS_OFFSET(font) + CVT_VERT_WIDTHS_SIZE(font)
256 #define CVT_BLUE_SHOOTS_OFFSET(font) \
257 CVT_BLUE_REFS_OFFSET(font) + CVT_BLUES_SIZE(font)
260 /* symbolic names for storage area locations */
262 #define sal_i 0
263 #define sal_j sal_i + 1
264 #define sal_k sal_j + 1
265 #define sal_temp1 sal_k + 1
266 #define sal_temp2 sal_temp1 + 1
267 #define sal_temp3 sal_temp2 + 1
268 #define sal_limit sal_temp3 + 1
269 #define sal_func sal_limit +1
270 #define sal_num_segments sal_func + 1
271 #define sal_scale sal_num_segments + 1
272 #define sal_0x10000 sal_scale + 1
273 #define sal_is_extra_light sal_0x10000 + 1
274 #define sal_anchor sal_is_extra_light + 1
275 #define sal_point_min sal_anchor + 1
276 #define sal_point_max sal_point_min + 1
277 #define sal_segment_offset sal_point_max + 1 /* must be last */
280 /* we need the following macro */
281 /* so that `func_name' doesn't get replaced with its #defined value */
282 /* (as defined in `tabytecode.h') */
284 #define FPGM(func_name) fpgm_ ## func_name
287 /* in the comments below, the top of the stack (`s:') */
288 /* is the rightmost element; the stack is shown */
289 /* after the instruction on the same line has been executed */
291 /* we use two sets of points in the twilight zone (zp0): */
292 /* one set to hold the unhinted segment positions, */
293 /* and another one to track the positions as changed by the hinting -- */
294 /* this is necessary since all points in zp0 */
295 /* have (0,0) as the original coordinates, */
296 /* making e.g. `MD_orig' return useless results */
300 * bci_round
302 * Round a 26.6 number. Contrary to the ROUND bytecode instruction, no
303 * engine specific corrections are applied.
305 * in: val
306 * out: ROUND(val)
309 unsigned char FPGM(bci_round) [] = {
311 PUSHB_1,
312 bci_round,
313 FDEF,
315 DUP,
316 ABS,
317 PUSHB_1,
319 ADD,
320 FLOOR,
321 SWAP,
322 PUSHB_1,
326 NEG,
327 EIF,
329 ENDF,
335 * bci_compute_stem_width
337 * This is the equivalent to the following code from function
338 * `ta_latin_compute_stem_width':
340 * dist = ABS(width)
342 * if (stem_is_serif
343 * && dist < 3*64)
344 * || is_extra_light:
345 * return width
346 * else if base_is_round:
347 * if dist < 80
348 * dist = 64
349 * else if dist < 56:
350 * dist = 56
352 * delta = ABS(dist - std_width)
354 * if delta < 40:
355 * dist = std_width
356 * if dist < 48
357 * dist = 48
358 * goto End
360 * if dist < 3*64:
361 * delta = dist
362 * dist = FLOOR(dist)
363 * delta = delta - dist
365 * if delta < 10:
366 * dist = dist + delta
367 * else if delta < 32:
368 * dist = dist + 10
369 * else if delta < 54:
370 * dist = dist + 54
371 * else
372 * dist = dist + delta
373 * else
374 * dist = ROUND(dist)
376 * End:
377 * if width < 0:
378 * dist = -dist
379 * return dist
382 * in: width
383 * stem_is_serif
384 * base_is_round
385 * out: new_width
386 * sal: sal_is_extra_light
387 * CVT: std_width
390 unsigned char FPGM(bci_compute_stem_width_a) [] = {
392 PUSHB_1,
393 bci_compute_stem_width,
394 FDEF,
396 DUP,
397 ABS, /* s: base_is_round stem_is_serif width dist */
399 DUP,
400 PUSHB_1,
401 3*64,
402 LT, /* dist < 3*64 */
404 PUSHB_1,
406 MINDEX, /* s: base_is_round width dist (dist<3*64) stem_is_serif */
407 AND, /* stem_is_serif && dist < 3*64 */
409 PUSHB_1,
410 sal_is_extra_light,
412 OR, /* (stem_is_serif && dist < 3*64) || is_extra_light */
414 IF, /* s: base_is_round width dist */
415 POP,
416 SWAP,
417 POP, /* s: width */
419 ELSE,
420 ROLL, /* s: width dist base_is_round */
421 IF, /* s: width dist */
422 DUP,
423 PUSHB_1,
425 LT, /* dist < 80 */
426 IF, /* s: width dist */
427 POP,
428 PUSHB_1,
429 64, /* dist = 64 */
430 EIF,
432 ELSE,
433 DUP,
434 PUSHB_1,
436 LT, /* dist < 56 */
437 IF, /* s: width dist */
438 POP,
439 PUSHB_1,
440 56, /* dist = 56 */
441 EIF,
442 EIF,
444 DUP, /* s: width dist dist */
445 PUSHB_1,
449 /* %c, index of std_width */
451 unsigned char FPGM(bci_compute_stem_width_b) [] = {
453 RCVT,
454 SUB,
455 ABS, /* s: width dist delta */
457 PUSHB_1,
459 LT, /* delta < 40 */
460 IF, /* s: width dist */
461 POP,
462 PUSHB_1,
466 /* %c, index of std_width */
468 unsigned char FPGM(bci_compute_stem_width_c) [] = {
470 RCVT, /* dist = std_width */
471 DUP,
472 PUSHB_1,
474 LT, /* dist < 48 */
476 POP,
477 PUSHB_1,
478 48, /* dist = 48 */
479 EIF,
481 ELSE,
482 DUP, /* s: width dist dist */
483 PUSHB_1,
484 3*64,
485 LT, /* dist < 3*64 */
487 DUP, /* s: width delta dist */
488 FLOOR, /* dist = FLOOR(dist) */
489 DUP, /* s: width delta dist dist */
490 ROLL,
491 ROLL, /* s: width dist delta dist */
492 SUB, /* delta = delta - dist */
494 DUP, /* s: width dist delta delta */
495 PUSHB_1,
497 LT, /* delta < 10 */
498 IF, /* s: width dist delta */
499 ADD, /* dist = dist + delta */
501 ELSE,
502 DUP,
503 PUSHB_1,
505 LT, /* delta < 32 */
507 POP,
508 PUSHB_1,
510 ADD, /* dist = dist + 10 */
512 ELSE,
513 DUP,
514 PUSHB_1,
516 LT, /* delta < 54 */
518 POP,
519 PUSHB_1,
521 ADD, /* dist = dist + 54 */
523 ELSE,
524 ADD, /* dist = dist + delta */
526 EIF,
527 EIF,
528 EIF,
530 ELSE,
531 PUSHB_1,
532 bci_round,
533 CALL, /* dist = round(dist) */
535 EIF,
536 EIF,
538 SWAP, /* s: dist width */
539 PUSHB_1,
541 LT, /* width < 0 */
543 NEG, /* dist = -dist */
544 EIF,
545 EIF,
546 EIF,
548 ENDF,
554 * bci_loop
556 * Take a range and a function number and apply the function to all
557 * elements of the range.
559 * in: func_num
560 * end
561 * start
563 * uses: sal_i (counter initialized with `start')
564 * sal_limit (`end')
565 * sal_func (`func_num')
568 unsigned char FPGM(bci_loop) [] = {
570 PUSHB_1,
571 bci_loop,
572 FDEF,
574 PUSHB_1,
575 sal_func,
576 SWAP,
577 WS, /* sal_func = func_num */
578 PUSHB_1,
579 sal_limit,
580 SWAP,
581 WS, /* sal_limit = end */
582 PUSHB_1,
583 sal_i,
584 SWAP,
585 WS, /* sal_i = start */
587 /* start_loop: */
588 PUSHB_1,
589 sal_i,
591 PUSHB_1,
592 sal_limit,
594 LTEQ, /* start <= end */
596 PUSHB_1,
597 sal_func,
599 CALL,
600 PUSHB_3,
601 sal_i,
603 sal_i,
605 ADD, /* start = start + 1 */
608 PUSHB_1,
610 NEG,
611 JMPR, /* goto start_loop */
612 EIF,
614 ENDF,
620 * bci_cvt_rescale
622 * Rescale CVT value by a given factor.
624 * uses: sal_i (CVT index)
625 * sal_scale (scale in 16.16 format)
628 unsigned char FPGM(bci_cvt_rescale) [] = {
630 PUSHB_1,
631 bci_cvt_rescale,
632 FDEF,
634 PUSHB_1,
635 sal_i,
637 DUP,
638 RCVT,
639 PUSHB_1,
640 sal_scale,
642 MUL, /* CVT * scale * 2^10 */
643 PUSHB_1,
644 sal_0x10000,
646 DIV, /* CVT * scale */
648 WCVTP,
650 ENDF,
656 * bci_blue_round
658 * Round a blue ref value and adjust its corresponding shoot value.
660 * uses: sal_i (CVT index)
664 unsigned char FPGM(bci_blue_round_a) [] = {
666 PUSHB_1,
667 bci_blue_round,
668 FDEF,
670 PUSHB_1,
671 sal_i,
673 DUP,
674 RCVT, /* s: ref_idx ref */
676 DUP,
677 PUSHB_1,
678 bci_round,
679 CALL,
680 SWAP, /* s: ref_idx round(ref) ref */
682 PUSHB_2,
686 /* %c, blue_count */
688 unsigned char FPGM(bci_blue_round_b) [] = {
691 CINDEX,
692 ADD, /* s: ref_idx round(ref) ref shoot_idx */
693 DUP,
694 RCVT, /* s: ref_idx round(ref) ref shoot_idx shoot */
696 ROLL, /* s: ref_idx round(ref) shoot_idx shoot ref */
697 SWAP,
698 SUB, /* s: ref_idx round(ref) shoot_idx dist */
699 DUP,
700 ABS, /* s: ref_idx round(ref) shoot_idx dist delta */
702 DUP,
703 PUSHB_1,
705 LT, /* delta < 32 */
707 POP,
708 PUSHB_1,
709 0, /* delta = 0 */
711 ELSE,
712 PUSHB_1,
714 LT, /* delta < 48 */
716 PUSHB_1,
717 32, /* delta = 32 */
719 ELSE,
720 PUSHB_1,
721 64, /* delta = 64 */
722 EIF,
723 EIF,
725 SWAP, /* s: ref_idx round(ref) shoot_idx delta dist */
726 PUSHB_1,
728 LT, /* dist < 0 */
730 NEG, /* delta = -delta */
731 EIF,
733 PUSHB_1,
735 CINDEX,
736 ADD, /* s: ref_idx round(ref) shoot_idx (round(ref) + delta) */
738 WCVTP,
739 WCVTP,
741 ENDF,
747 * bci_get_point_extrema
749 * An auxiliary function for `bci_create_segment'.
751 * in: point-1
752 * out: point
754 * sal: sal_point_min
755 * sal_point_max
758 unsigned char FPGM(bci_get_point_extrema) [] = {
760 PUSHB_1,
761 bci_get_point_extrema,
762 FDEF,
764 PUSHB_1,
766 ADD, /* s: point */
767 DUP,
768 DUP,
770 /* check whether `point' is a new minimum */
771 PUSHB_1,
772 sal_point_min,
773 RS, /* s: point point point point_min */
774 MD_orig,
775 /* if distance is negative, we have a new minimum */
776 PUSHB_1,
779 IF, /* s: point point */
780 DUP,
781 PUSHB_1,
782 sal_point_min,
783 SWAP,
785 EIF,
787 /* check whether `point' is a new maximum */
788 PUSHB_1,
789 sal_point_max,
790 RS, /* s: point point point_max */
791 MD_orig,
792 /* if distance is positive, we have a new maximum */
793 PUSHB_1,
796 IF, /* s: point */
797 DUP,
798 PUSHB_1,
799 sal_point_max,
800 SWAP,
802 EIF, /* s: point */
804 ENDF,
810 * bci_create_segment
812 * Store start and end point of a segment in the storage area,
813 * then construct two points in the twilight zone to represent it:
814 * an original one (which stays unmodified) and a hinted one,
815 * initialized with the original value.
817 * This function is used by `bci_create_segment_points'.
819 * in: start
820 * end
821 * [last (if wrap-around segment)]
822 * [first (if wrap-around segment)]
824 * uses: bci_get_point_extrema
826 * sal: sal_i (start of current segment)
827 * sal_j (current original twilight point)
828 * sal_k (current hinted twilight point)
829 * sal_point_min
830 * sal_point_max
833 unsigned char FPGM(bci_create_segment) [] = {
835 PUSHB_1,
836 bci_create_segment,
837 FDEF,
839 PUSHB_1,
840 sal_i,
842 PUSHB_1,
844 CINDEX,
845 WS, /* sal[sal_i] = start */
847 /* increase `sal_i'; together with the outer loop, this makes sal_i += 2 */
848 PUSHB_3,
849 sal_i,
851 sal_i,
853 ADD, /* sal_i = sal_i + 1 */
856 /* initialize inner loop(s) */
857 PUSHB_2,
858 sal_point_min,
860 CINDEX,
861 WS, /* sal_point_min = start */
862 PUSHB_2,
863 sal_point_max,
865 CINDEX,
866 WS, /* sal_point_max = start */
868 PUSHB_1,
870 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
872 SWAP,
873 DUP,
874 PUSHB_1,
876 CINDEX, /* s: start end end start */
877 LT, /* start > end */
879 /* we have a wrap-around segment with two more arguments */
880 /* to give the last and first point of the contour, respectively; */
881 /* our job is to store a segment `start'-`last', */
882 /* and to get extrema for the two segments */
883 /* `start'-`last' and `first'-`end' */
885 /* s: first last start end */
886 PUSHB_1,
887 sal_i,
889 PUSHB_1,
891 CINDEX,
892 WS, /* sal[sal_i] = last */
894 ROLL,
895 ROLL, /* s: first end last start */
896 DUP,
897 ROLL,
898 SWAP, /* s: first end start last start */
899 SUB, /* s: first end start loop_count */
901 PUSHB_1,
902 bci_get_point_extrema,
903 LOOPCALL,
904 /* clean up stack */
905 POP,
907 SWAP, /* s: end first */
908 PUSHB_1,
910 SUB,
911 DUP,
912 ROLL, /* s: (first - 1) (first - 1) end */
913 SWAP,
914 SUB, /* s: (first - 1) loop_count */
916 PUSHB_1,
917 bci_get_point_extrema,
918 LOOPCALL,
919 /* clean up stack */
920 POP,
922 ELSE, /* s: start end */
923 PUSHB_1,
924 sal_i,
926 PUSHB_1,
928 CINDEX,
929 WS, /* sal[sal_i] = end */
931 PUSHB_1,
933 CINDEX,
934 SUB, /* s: start loop_count */
936 PUSHB_1,
937 bci_get_point_extrema,
938 LOOPCALL,
939 /* clean up stack */
940 POP,
941 EIF,
943 /* the twilight point representing a segment */
944 /* is in the middle between the minimum and maximum */
945 PUSHB_1,
946 sal_point_max,
948 PUSHB_1,
949 sal_point_min,
951 MD_orig,
952 PUSHB_1,
953 2*64,
954 DIV, /* s: delta */
956 PUSHB_4,
957 sal_j,
960 sal_point_min,
962 MDAP_noround, /* set rp0 and rp1 to `sal_point_min' */
963 SZP1, /* set zp1 to twilight zone 0 */
964 SZP2, /* set zp2 to twilight zone 0 */
967 DUP, /* s: delta point[sal_j] point[sal_j] */
968 ALIGNRP, /* align `point[sal_j]' with `sal_point_min' */
969 PUSHB_1,
971 CINDEX, /* s: delta point[sal_j] delta */
972 SHPIX, /* shift `point[sal_j]' by `delta' */
974 PUSHB_1,
975 sal_k,
977 DUP, /* s: delta point[sal_k] point[sal_k] */
978 ALIGNRP, /* align `point[sal_k]' with `sal_point_min' */
979 SWAP,
980 SHPIX, /* shift `point[sal_k]' by `delta' */
982 PUSHB_6,
983 sal_k,
985 sal_k,
986 sal_j,
988 sal_j,
990 ADD, /* original_twilight_point = original_twilight_point + 1 */
993 ADD, /* hinted_twilight_point = hinted_twilight_point + 1 */
996 ENDF,
1002 * bci_create_segments
1004 * Set up segments by defining point ranges which defines them
1005 * and computing twilight points to represent them.
1007 * in: num_segments (N)
1008 * segment_start_0
1009 * segment_end_0
1010 * [contour_last 0 (if wrap-around segment)]
1011 * [contour_first 0 (if wrap-around segment)]
1012 * segment_start_1
1013 * segment_end_1
1014 * [contour_last 0 (if wrap-around segment)]
1015 * [contour_first 0 (if wrap-around segment)]
1016 * ...
1017 * segment_start_(N-1)
1018 * segment_end_(N-1)
1019 * [contour_last (N-1) (if wrap-around segment)]
1020 * [contour_first (N-1) (if wrap-around segment)]
1022 * uses: bci_create_segment
1024 * sal: sal_i (start of current segment)
1025 * sal_j (current original twilight point)
1026 * sal_k (current hinted twilight point)
1027 * sal_num_segments
1030 unsigned char FPGM(bci_create_segments) [] = {
1032 PUSHB_1,
1033 bci_create_segments,
1034 FDEF,
1036 /* all our measurements are taken along the y axis */
1037 SVTCA_y,
1039 PUSHB_1,
1040 sal_num_segments,
1041 SWAP,
1042 WS, /* sal_num_segments = num_segments */
1044 PUSHB_7,
1045 sal_segment_offset,
1046 sal_segment_offset,
1047 sal_num_segments,
1049 sal_k,
1051 sal_j,
1052 sal_num_segments,
1054 WS, /* sal_j = num_segments (offset for original points) */
1055 WS, /* sal_k = 0 (offset for hinted points) */
1058 DUP,
1059 ADD,
1060 ADD,
1061 PUSHB_1,
1063 SUB, /* s: sal_segment_offset (sal_segment_offset + 2*num_segments - 1) */
1065 /* `bci_create_segment_point' also increases the loop counter by 1; */
1066 /* this effectively means we have a loop step of 2 */
1067 PUSHB_2,
1068 bci_create_segment,
1069 bci_loop,
1070 CALL,
1072 ENDF,
1076 unsigned char FPGM(bci_handle_segment) [] = {
1078 PUSHB_1,
1079 bci_handle_segment,
1080 FDEF,
1082 POP, /* XXX segment */
1084 ENDF,
1090 * bci_align_segment
1092 * Align all points in a segment to the twilight point in rp0.
1093 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
1095 * in: segment_index
1098 unsigned char FPGM(bci_align_segment) [] = {
1100 PUSHB_1,
1101 bci_align_segment,
1102 FDEF,
1104 /* we need the values of `sal_segment_offset + 2*segment_index' */
1105 /* and `sal_segment_offset + 2*segment_index + 1' */
1106 DUP,
1107 ADD,
1108 PUSHB_1,
1109 sal_segment_offset,
1110 ADD,
1111 DUP,
1113 SWAP,
1114 PUSHB_1,
1116 ADD,
1117 RS, /* s: first last */
1119 /* start_loop: */
1120 PUSHB_1,
1122 CINDEX, /* s: first last first */
1123 PUSHB_1,
1125 CINDEX, /* s: first last first last */
1126 LTEQ, /* first <= end */
1127 IF, /* s: first last */
1128 SWAP,
1129 DUP, /* s: last first first */
1130 ALIGNRP, /* align point with index `first' with rp0 */
1132 PUSHB_1,
1134 ADD, /* first = first + 1 */
1135 SWAP, /* s: first last */
1137 PUSHB_1,
1139 NEG,
1140 JMPR, /* goto start_loop */
1142 ELSE,
1143 POP,
1144 POP,
1145 EIF,
1147 ENDF,
1151 unsigned char FPGM(bci_handle_segments) [] = {
1153 PUSHB_1,
1154 bci_handle_segments,
1155 FDEF,
1157 POP, /* XXX first segment */
1159 PUSHB_1,
1160 bci_handle_segment,
1161 LOOPCALL,
1163 ENDF,
1169 * bci_align_segments
1171 * Align segments to the twilight point in rp0.
1172 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
1174 * in: first_segment
1175 * loop_counter (N)
1176 * segment_1
1177 * segment_2
1178 * ...
1179 * segment_N
1181 * uses: handle_segment
1185 unsigned char FPGM(bci_align_segments) [] = {
1187 PUSHB_1,
1188 bci_align_segments,
1189 FDEF,
1191 PUSHB_1,
1192 bci_align_segment,
1193 CALL,
1195 PUSHB_1,
1196 bci_align_segment,
1197 LOOPCALL,
1199 ENDF,
1205 * bci_action_adjust_bound
1207 * Handle the ADJUST_BOUND action to align an edge of a stem if the other
1208 * edge of the stem has already been moved, then moving it again if
1209 * necessary to stay bound.
1211 * in: edge2_is_serif
1212 * edge_is_round
1213 * edge_point (in twilight zone)
1214 * edge2_point (in twilight zone)
1215 * edge[-1] (in twilight zone)
1216 * ... stuff for bci_align_segments (edge) ...
1219 unsigned char FPGM(bci_action_adjust_bound) [] = {
1221 PUSHB_1,
1222 bci_action_adjust_bound,
1223 FDEF,
1225 PUSHB_1,
1227 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1229 PUSHB_1,
1231 CINDEX,
1232 PUSHB_1,
1233 sal_num_segments,
1235 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig */
1236 PUSHB_1,
1238 CINDEX,
1239 PUSHB_1,
1240 sal_num_segments,
1242 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig edge_orig */
1243 MD_cur, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1245 PUSHB_1,
1246 bci_compute_stem_width,
1247 CALL,
1248 NEG, /* s: edge[-1] edge2 edge -cur_len */
1250 ROLL, /* s: edge[-1] edge -cur_len edge2 */
1251 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1252 SWAP,
1253 DUP,
1254 DUP, /* s: edge[-1] -cur_len edge edge edge */
1255 ALIGNRP, /* align `edge' with `edge2' */
1256 ROLL,
1257 SHPIX, /* shift `edge' by -cur_len */
1259 SWAP, /* s: edge edge[-1] */
1260 DUP,
1261 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1262 GC_cur,
1263 PUSHB_1,
1265 CINDEX,
1266 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1267 GT, /* edge_pos < edge[-1]_pos */
1269 DUP,
1270 ALIGNRP, /* align `edge' to `edge[-1]' */
1271 EIF,
1273 MDAP_noround, /* set rp0 and rp1 to `edge' */
1275 PUSHB_2,
1276 bci_align_segments,
1278 SZP1, /* set zp1 to normal zone 1 */
1279 CALL,
1281 ENDF,
1287 * bci_action_stem_bound
1289 * Handle the STEM action to align two edges of a stem, then moving one
1290 * edge again if necessary to stay bound.
1292 * The code after computing `cur_len' to shift `edge' and `edge2'
1293 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1295 * if cur_len < 96:
1296 * if cur_len < = 64:
1297 * u_off = 32
1298 * d_off = 32
1299 * else:
1300 * u_off = 38
1301 * d_off = 26
1303 * org_center = edge_orig + org_len / 2
1304 * cur_pos1 = ROUND(org_center)
1306 * delta1 = ABS(org_center - (cur_pos1 - u_off))
1307 * delta2 = ABS(org_center - (cur_pos1 + d_off))
1308 * if (delta1 < delta2):
1309 * cur_pos1 = cur_pos1 - u_off
1310 * else:
1311 * cur_pos1 = cur_pos1 + d_off
1313 * edge = cur_pos1 - cur_len / 2
1315 * else:
1316 * org_pos = anchor + (edge_orig - anchor_orig)
1317 * org_center = edge_orig + org_len / 2
1319 * cur_pos1 = ROUND(org_pos)
1320 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
1321 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
1322 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
1324 * if (delta1 < delta2):
1325 * edge = cur_pos1
1326 * else:
1327 * edge = cur_pos2
1329 * edge2 = edge + cur_len
1331 * in: edge2_is_serif
1332 * edge_is_round
1333 * edge_point (in twilight zone)
1334 * edge2_point (in twilight zone)
1335 * edge[-1] (in twilight zone)
1336 * ... stuff for bci_align_segments (edge) ...
1337 * ... stuff for bci_align_segments (edge2)...
1339 * sal: sal_anchor
1340 * sal_temp1
1341 * sal_temp2
1342 * sal_temp3
1343 * sal_num_segments
1346 #undef sal_u_off
1347 #define sal_u_off sal_temp1
1348 #undef sal_d_off
1349 #define sal_d_off sal_temp2
1350 #undef sal_org_len
1351 #define sal_org_len sal_temp3
1352 #undef sal_edge2
1353 #define sal_edge2 sal_temp3
1355 unsigned char FPGM(bci_action_stem_bound) [] = {
1357 PUSHB_1,
1358 bci_action_stem_bound,
1359 FDEF,
1361 PUSHB_1,
1363 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1365 PUSHB_1,
1367 CINDEX,
1368 PUSHB_1,
1369 sal_num_segments,
1371 ADD,
1372 PUSHB_1,
1374 CINDEX,
1375 DUP,
1376 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1377 PUSHB_1,
1378 sal_num_segments,
1380 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig edge_orig */
1382 MD_cur, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1383 DUP,
1384 PUSHB_1,
1385 sal_org_len,
1386 SWAP,
1389 PUSHB_1,
1390 bci_compute_stem_width,
1391 CALL, /* s: edge[-1] edge2 edge cur_len */
1393 DUP,
1394 PUSHB_1,
1396 LT, /* cur_len < 96 */
1398 DUP,
1399 PUSHB_1,
1401 LTEQ, /* cur_len <= 64 */
1403 PUSHB_4,
1404 sal_u_off,
1406 sal_d_off,
1409 ELSE,
1410 PUSHB_4,
1411 sal_u_off,
1413 sal_d_off,
1415 EIF,
1419 SWAP, /* s: edge[-1] edge2 cur_len edge */
1420 DUP,
1421 PUSHB_1,
1422 sal_num_segments,
1424 ADD, /* s: edge[-1] edge2 cur_len edge edge_orig */
1426 GC_cur,
1427 PUSHB_1,
1428 sal_org_len,
1430 PUSHB_1,
1431 2*64,
1432 DIV,
1433 ADD, /* s: edge[-1] edge2 cur_len edge org_center */
1435 DUP,
1436 PUSHB_1,
1437 bci_round,
1438 CALL, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 */
1440 DUP,
1441 ROLL,
1442 ROLL,
1443 SUB, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) */
1445 DUP,
1446 PUSHB_1,
1447 sal_u_off,
1449 ADD,
1450 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
1452 SWAP,
1453 PUSHB_1,
1454 sal_d_off,
1456 SUB,
1457 ABS, /* s: edge[-1] edge2 cur_len edge cur_pos1 delta1 delta2 */
1459 LT, /* delta1 < delta2 */
1461 PUSHB_1,
1462 sal_u_off,
1464 SUB, /* cur_pos1 = cur_pos1 - u_off */
1466 ELSE,
1467 PUSHB_1,
1468 sal_d_off,
1470 ADD, /* cur_pos1 = cur_pos1 + d_off */
1471 EIF, /* s: edge[-1] edge2 cur_len edge cur_pos1 */
1473 PUSHB_1,
1475 CINDEX,
1476 PUSHB_1,
1477 2*64,
1478 DIV,
1479 SUB, /* arg = cur_pos1 - cur_len/2 */
1481 SWAP, /* s: edge[-1] edge2 cur_len arg edge */
1482 DUP,
1483 DUP,
1484 PUSHB_1,
1486 MINDEX,
1487 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1488 GC_cur,
1489 SUB,
1490 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1492 ELSE,
1493 SWAP, /* s: edge[-1] edge2 cur_len edge */
1494 DUP,
1495 PUSHB_1,
1496 sal_num_segments,
1498 ADD, /* s: edge[-1] edge2 cur_len edge edge_orig */
1500 GC_cur,
1501 PUSHB_1,
1502 sal_org_len,
1504 PUSHB_1,
1505 2*64,
1506 DIV,
1507 ADD, /* s: edge[-1] edge2 cur_len edge org_center */
1509 PUSHB_1,
1510 sal_anchor,
1512 GC_cur, /* s: edge[-1] edge2 cur_len edge org_center anchor_pos */
1513 PUSHB_1,
1515 CINDEX,
1516 PUSHB_1,
1517 sal_num_segments,
1519 ADD,
1520 PUSHB_1,
1521 sal_anchor,
1523 PUSHB_1,
1524 sal_num_segments,
1526 ADD,
1527 MD_cur,
1528 ADD, /* s: edge[-1] edge2 cur_len edge org_center org_pos */
1530 DUP,
1531 PUSHB_1,
1532 bci_round,
1533 CALL, /* cur_pos1 = ROUND(org_pos) */
1534 SWAP,
1535 PUSHB_1,
1536 sal_org_len,
1538 ADD,
1539 PUSHB_1,
1540 bci_round,
1541 CALL,
1542 PUSHB_1,
1544 CINDEX,
1545 SUB, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 cur_pos2 */
1547 PUSHB_1,
1549 CINDEX,
1550 PUSHB_1,
1551 2*64,
1552 DIV,
1553 PUSHB_1,
1555 MINDEX,
1556 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
1558 DUP,
1559 PUSHB_1,
1561 CINDEX,
1562 ADD,
1563 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
1564 SWAP,
1565 PUSHB_1,
1567 CINDEX,
1568 ADD,
1569 ABS, /* s: ... edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
1570 LT, /* delta1 < delta2 */
1572 POP, /* arg = cur_pos1 */
1573 ELSE,
1574 SWAP,
1575 POP, /* arg = cur_pos2 */
1576 EIF, /* s: edge[-1] edge2 cur_len edge arg */
1577 SWAP,
1578 DUP,
1579 DUP,
1580 PUSHB_1,
1582 MINDEX,
1583 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1584 GC_cur,
1585 SUB,
1586 SHPIX, /* edge = arg */
1587 EIF, /* s: edge[-1] edge2 cur_len edge */
1589 ROLL, /* s: edge[-1] cur_len edge edge2 */
1590 DUP,
1591 DUP,
1592 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1593 PUSHB_1,
1594 sal_edge2,
1595 SWAP,
1596 WS, /* s: edge[-1] cur_len edge edge2 */
1597 ROLL,
1598 SHPIX, /* edge2 = edge + cur_len */
1600 SWAP, /* s: edge edge[-1] */
1601 DUP,
1602 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1603 GC_cur,
1604 PUSHB_1,
1606 CINDEX,
1607 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1608 GT, /* edge_pos < edge[-1]_pos */
1610 DUP,
1611 ALIGNRP, /* align `edge' to `edge[-1]' */
1612 EIF,
1614 MDAP_noround, /* set rp0 and rp1 to `edge' */
1616 PUSHB_2,
1617 bci_align_segments,
1619 SZP1, /* set zp1 to normal zone 1 */
1620 CALL,
1622 PUSHB_1,
1623 sal_edge2,
1625 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1627 PUSHB_1,
1628 bci_align_segments,
1629 CALL,
1631 ENDF,
1637 * bci_action_link
1639 * Handle the LINK action to link an edge to another one.
1641 * in: stem_is_serif
1642 * base_is_round
1643 * base_point (in twilight zone)
1644 * stem_point (in twilight zone)
1645 * ... stuff for bci_align_segments (base) ...
1648 unsigned char FPGM(bci_action_link) [] = {
1650 PUSHB_1,
1651 bci_action_link,
1652 FDEF,
1654 PUSHB_1,
1656 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1658 PUSHB_1,
1660 CINDEX,
1661 PUSHB_1,
1662 sal_num_segments,
1664 ADD,
1665 PUSHB_1,
1667 MINDEX,
1668 DUP,
1669 MDAP_noround, /* set rp0 and rp1 to `base_point' (for ALIGNRP below) */
1670 PUSHB_1,
1671 sal_num_segments,
1673 ADD, /* s: stem is_round is_serif stem_orig base_orig */
1675 MD_cur, /* s: stem is_round is_serif dist_orig */
1677 PUSHB_1,
1678 bci_compute_stem_width,
1679 CALL, /* s: stem new_dist */
1681 SWAP,
1682 DUP,
1683 ALIGNRP, /* align `stem_point' with `base_point' */
1684 DUP,
1685 MDAP_noround, /* set rp0 and rp1 to `stem_point' */
1686 SWAP,
1687 SHPIX, /* stem_point = base_point + new_dist */
1689 PUSHB_2,
1690 bci_align_segments,
1692 SZP1, /* set zp1 to normal zone 1 */
1693 CALL,
1695 ENDF,
1701 * bci_action_anchor
1703 * Handle the ANCHOR action to align two edges
1704 * and to set the edge anchor.
1706 * The code after computing `cur_len' to shift `edge' and `edge2'
1707 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1709 * if cur_len < 96:
1710 * if cur_len < = 64:
1711 * u_off = 32
1712 * d_off = 32
1713 * else:
1714 * u_off = 38
1715 * d_off = 26
1717 * org_center = edge_orig + org_len / 2
1718 * cur_pos1 = ROUND(org_center)
1720 * error1 = ABS(org_center - (cur_pos1 - u_off))
1721 * error2 = ABS(org_center - (cur_pos1 + d_off))
1722 * if (error1 < error2):
1723 * cur_pos1 = cur_pos1 - u_off
1724 * else:
1725 * cur_pos1 = cur_pos1 + d_off
1727 * edge = cur_pos1 - cur_len / 2
1728 * edge2 = edge + cur_len
1730 * else:
1731 * edge = ROUND(edge_orig)
1733 * in: edge2_is_serif
1734 * edge_is_round
1735 * edge_point (in twilight zone)
1736 * edge2_point (in twilight zone)
1737 * ... stuff for bci_align_segments (edge) ...
1739 * sal: sal_anchor
1740 * sal_temp1
1741 * sal_temp2
1742 * sal_temp3
1745 #undef sal_u_off
1746 #define sal_u_off sal_temp1
1747 #undef sal_d_off
1748 #define sal_d_off sal_temp2
1749 #undef sal_org_len
1750 #define sal_org_len sal_temp3
1752 unsigned char FPGM(bci_action_anchor) [] = {
1754 PUSHB_1,
1755 bci_action_anchor,
1756 FDEF,
1758 /* store anchor point number in `sal_anchor' */
1759 PUSHB_2,
1760 sal_anchor,
1762 CINDEX,
1763 WS, /* sal_anchor = edge_point */
1765 PUSHB_1,
1767 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1769 PUSHB_1,
1771 CINDEX,
1772 PUSHB_1,
1773 sal_num_segments,
1775 ADD,
1776 PUSHB_1,
1778 CINDEX,
1779 DUP,
1780 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1781 PUSHB_1,
1782 sal_num_segments,
1784 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
1786 MD_cur, /* s: edge2 edge is_round is_serif org_len */
1787 DUP,
1788 PUSHB_1,
1789 sal_org_len,
1790 SWAP,
1793 PUSHB_1,
1794 bci_compute_stem_width,
1795 CALL, /* s: edge2 edge cur_len */
1797 DUP,
1798 PUSHB_1,
1800 LT, /* cur_len < 96 */
1802 DUP,
1803 PUSHB_1,
1805 LTEQ, /* cur_len <= 64 */
1807 PUSHB_4,
1808 sal_u_off,
1810 sal_d_off,
1813 ELSE,
1814 PUSHB_4,
1815 sal_u_off,
1817 sal_d_off,
1819 EIF,
1823 SWAP, /* s: edge2 cur_len edge */
1824 DUP,
1825 PUSHB_1,
1826 sal_num_segments,
1828 ADD, /* s: edge2 cur_len edge edge_orig */
1830 GC_cur,
1831 PUSHB_1,
1832 sal_org_len,
1834 PUSHB_1,
1835 2*64,
1836 DIV,
1837 ADD, /* s: edge2 cur_len edge org_center */
1839 DUP,
1840 PUSHB_1,
1841 bci_round,
1842 CALL, /* s: edge2 cur_len edge org_center cur_pos1 */
1844 DUP,
1845 ROLL,
1846 ROLL,
1847 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
1849 DUP,
1850 PUSHB_1,
1851 sal_u_off,
1853 ADD,
1854 ABS, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) error1 */
1856 SWAP,
1857 PUSHB_1,
1858 sal_d_off,
1860 SUB,
1861 ABS, /* s: edge2 cur_len edge cur_pos1 error1 error2 */
1863 LT, /* error1 < error2 */
1865 PUSHB_1,
1866 sal_u_off,
1868 SUB, /* cur_pos1 = cur_pos1 - u_off */
1870 ELSE,
1871 PUSHB_1,
1872 sal_d_off,
1874 ADD, /* cur_pos1 = cur_pos1 + d_off */
1875 EIF, /* s: edge2 cur_len edge cur_pos1 */
1877 PUSHB_1,
1879 CINDEX,
1880 PUSHB_1,
1881 2*64,
1882 DIV,
1883 SUB, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) */
1885 PUSHB_1,
1887 CINDEX, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) edge */
1888 GC_cur,
1889 SUB,
1890 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1892 SWAP, /* s: cur_len edge2 */
1893 DUP,
1894 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1895 SWAP,
1896 SHPIX, /* edge2 = edge1 + cur_len */
1898 ELSE,
1899 POP, /* s: edge2 edge */
1900 DUP,
1901 PUSHB_1,
1902 sal_num_segments,
1904 ADD, /* s: edge2 edge edge_orig */
1906 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
1907 DUP,
1908 ALIGNRP, /* align `edge' with `edge_orig' */
1909 MDAP_round, /* round `edge' */
1911 /* clean up stack */
1912 POP,
1913 EIF,
1915 PUSHB_2,
1916 bci_align_segments,
1918 SZP1, /* set zp1 to normal zone 1 */
1919 CALL,
1921 ENDF,
1927 * bci_action_blue_anchor
1929 * Handle the BLUE_ANCHOR action to align an edge with a blue zone
1930 * and to set the edge anchor.
1932 * in: anchor_point (in twilight zone)
1933 * blue_cvt_idx
1934 * edge_point (in twilight zone)
1935 * ... stuff for bci_align_segments (edge) ...
1937 * sal: sal_anchor
1940 unsigned char FPGM(bci_action_blue_anchor) [] = {
1942 PUSHB_1,
1943 bci_action_blue_anchor,
1944 FDEF,
1946 /* store anchor point number in `sal_anchor' */
1947 PUSHB_1,
1948 sal_anchor,
1949 SWAP,
1952 PUSHB_1,
1954 SZP0, /* set zp0 to twilight zone 0 */
1956 /* move `edge_point' to `blue_cvt_idx' position */
1957 MIAP_noround, /* this also sets rp0 */
1959 PUSHB_2,
1960 bci_align_segments,
1962 SZP1, /* set zp1 to normal zone 1 */
1963 CALL,
1965 ENDF,
1971 * bci_action_adjust
1973 * Handle the ADJUST action to align an edge of a stem if the other edge
1974 * of the stem has already been moved.
1976 * in: edge2_is_serif
1977 * edge_is_round
1978 * edge_point (in twilight zone)
1979 * edge2_point (in twilight zone)
1980 * ... stuff for bci_align_segments (edge) ...
1983 unsigned char FPGM(bci_action_adjust) [] = {
1985 PUSHB_1,
1986 bci_action_adjust,
1987 FDEF,
1989 PUSHB_1,
1991 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1993 PUSHB_1,
1995 CINDEX,
1996 PUSHB_1,
1997 sal_num_segments,
1999 ADD, /* s: edge2 edge is_round is_serif edge2_orig */
2000 PUSHB_1,
2002 CINDEX,
2003 PUSHB_1,
2004 sal_num_segments,
2006 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
2007 MD_cur, /* s: edge2 edge is_round is_serif org_len */
2009 PUSHB_1,
2010 bci_compute_stem_width,
2011 CALL,
2012 NEG, /* s: edge2 edge -cur_len */
2014 ROLL,
2015 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2016 SWAP,
2017 DUP,
2018 DUP, /* s: -cur_len edge edge edge */
2019 ALIGNRP, /* align `edge' with `edge2' */
2020 ROLL,
2021 SHPIX, /* shift `edge' by -cur_len */
2023 MDAP_noround, /* set rp0 and rp1 to `edge' */
2025 PUSHB_2,
2026 bci_align_segments,
2028 SZP1, /* set zp1 to normal zone 1 */
2029 CALL,
2031 ENDF,
2037 * bci_action_stem
2039 * Handle the STEM action to align two edges of a stem.
2041 * The code after computing `cur_len' to shift `edge' and `edge2'
2042 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
2044 * if cur_len < 96:
2045 * if cur_len < = 64:
2046 * u_off = 32
2047 * d_off = 32
2048 * else:
2049 * u_off = 38
2050 * d_off = 26
2052 * org_center = edge_orig + org_len / 2
2053 * cur_pos1 = ROUND(org_center)
2055 * delta1 = ABS(org_center - (cur_pos1 - u_off))
2056 * delta2 = ABS(org_center - (cur_pos1 + d_off))
2057 * if (delta1 < delta2):
2058 * cur_pos1 = cur_pos1 - u_off
2059 * else:
2060 * cur_pos1 = cur_pos1 + d_off
2062 * edge = cur_pos1 - cur_len / 2
2064 * else:
2065 * org_pos = anchor + (edge_orig - anchor_orig)
2066 * org_center = edge_orig + org_len / 2
2068 * cur_pos1 = ROUND(org_pos)
2069 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
2070 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
2071 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
2073 * if (delta1 < delta2):
2074 * edge = cur_pos1
2075 * else:
2076 * edge = cur_pos2
2078 * edge2 = edge + cur_len
2080 * in: edge2_is_serif
2081 * edge_is_round
2082 * edge_point (in twilight zone)
2083 * edge2_point (in twilight zone)
2084 * ... stuff for bci_align_segments (edge) ...
2085 * ... stuff for bci_align_segments (edge2)...
2087 * sal: sal_anchor
2088 * sal_temp1
2089 * sal_temp2
2090 * sal_temp3
2091 * sal_num_segments
2094 #undef sal_u_off
2095 #define sal_u_off sal_temp1
2096 #undef sal_d_off
2097 #define sal_d_off sal_temp2
2098 #undef sal_org_len
2099 #define sal_org_len sal_temp3
2100 #undef sal_edge2
2101 #define sal_edge2 sal_temp3
2103 unsigned char FPGM(bci_action_stem) [] = {
2105 PUSHB_1,
2106 bci_action_stem,
2107 FDEF,
2109 PUSHB_1,
2111 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2113 PUSHB_1,
2115 CINDEX,
2116 PUSHB_1,
2117 sal_num_segments,
2119 ADD,
2120 PUSHB_1,
2122 CINDEX,
2123 DUP,
2124 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
2125 PUSHB_1,
2126 sal_num_segments,
2128 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
2130 MD_cur, /* s: edge2 edge is_round is_serif org_len */
2131 DUP,
2132 PUSHB_1,
2133 sal_org_len,
2134 SWAP,
2137 PUSHB_1,
2138 bci_compute_stem_width,
2139 CALL, /* s: edge2 edge cur_len */
2141 DUP,
2142 PUSHB_1,
2144 LT, /* cur_len < 96 */
2146 DUP,
2147 PUSHB_1,
2149 LTEQ, /* cur_len <= 64 */
2151 PUSHB_4,
2152 sal_u_off,
2154 sal_d_off,
2157 ELSE,
2158 PUSHB_4,
2159 sal_u_off,
2161 sal_d_off,
2163 EIF,
2167 SWAP, /* s: edge2 cur_len edge */
2168 DUP,
2169 PUSHB_1,
2170 sal_num_segments,
2172 ADD, /* s: edge2 cur_len edge edge_orig */
2174 GC_cur,
2175 PUSHB_1,
2176 sal_org_len,
2178 PUSHB_1,
2179 2*64,
2180 DIV,
2181 ADD, /* s: edge2 cur_len edge org_center */
2183 DUP,
2184 PUSHB_1,
2185 bci_round,
2186 CALL, /* s: edge2 cur_len edge org_center cur_pos1 */
2188 DUP,
2189 ROLL,
2190 ROLL,
2191 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
2193 DUP,
2194 PUSHB_1,
2195 sal_u_off,
2197 ADD,
2198 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
2200 SWAP,
2201 PUSHB_1,
2202 sal_d_off,
2204 SUB,
2205 ABS, /* s: edge2 cur_len edge cur_pos1 delta1 delta2 */
2207 LT, /* delta1 < delta2 */
2209 PUSHB_1,
2210 sal_u_off,
2212 SUB, /* cur_pos1 = cur_pos1 - u_off */
2214 ELSE,
2215 PUSHB_1,
2216 sal_d_off,
2218 ADD, /* cur_pos1 = cur_pos1 + d_off */
2219 EIF, /* s: edge2 cur_len edge cur_pos1 */
2221 PUSHB_1,
2223 CINDEX,
2224 PUSHB_1,
2225 2*64,
2226 DIV,
2227 SUB, /* arg = cur_pos1 - cur_len/2 */
2229 SWAP, /* s: edge2 cur_len arg edge */
2230 DUP,
2231 PUSHB_1,
2233 MINDEX,
2234 SWAP, /* s: edge2 cur_len edge arg edge */
2235 GC_cur,
2236 SUB,
2237 SHPIX, /* edge = cur_pos1 - cur_len/2 */
2239 ELSE,
2240 SWAP, /* s: edge2 cur_len edge */
2241 DUP,
2242 PUSHB_1,
2243 sal_num_segments,
2245 ADD, /* s: edge2 cur_len edge edge_orig */
2247 GC_cur,
2248 PUSHB_1,
2249 sal_org_len,
2251 PUSHB_1,
2252 2*64,
2253 DIV,
2254 ADD, /* s: edge2 cur_len edge org_center */
2256 PUSHB_1,
2257 sal_anchor,
2259 GC_cur, /* s: edge2 cur_len edge org_center anchor_pos */
2260 PUSHB_1,
2262 CINDEX,
2263 PUSHB_1,
2264 sal_num_segments,
2266 ADD,
2267 PUSHB_1,
2268 sal_anchor,
2270 PUSHB_1,
2271 sal_num_segments,
2273 ADD,
2274 MD_cur,
2275 ADD, /* s: edge2 cur_len edge org_center org_pos */
2277 DUP,
2278 PUSHB_1,
2279 bci_round,
2280 CALL, /* cur_pos1 = ROUND(org_pos) */
2281 SWAP,
2282 PUSHB_1,
2283 sal_org_len,
2285 ADD,
2286 PUSHB_1,
2287 bci_round,
2288 CALL,
2289 PUSHB_1,
2291 CINDEX,
2292 SUB, /* s: edge2 cur_len edge org_center cur_pos1 cur_pos2 */
2294 PUSHB_1,
2296 CINDEX,
2297 PUSHB_1,
2298 2*64,
2299 DIV,
2300 PUSHB_1,
2302 MINDEX,
2303 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
2305 DUP,
2306 PUSHB_1,
2308 CINDEX,
2309 ADD,
2310 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
2311 SWAP,
2312 PUSHB_1,
2314 CINDEX,
2315 ADD,
2316 ABS, /* s: edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
2317 LT, /* delta1 < delta2 */
2319 POP, /* arg = cur_pos1 */
2320 ELSE,
2321 SWAP,
2322 POP, /* arg = cur_pos2 */
2323 EIF, /* s: edge2 cur_len edge arg */
2324 SWAP,
2325 DUP,
2326 PUSHB_1,
2328 MINDEX,
2329 SWAP, /* s: edge2 cur_len edge arg edge */
2330 GC_cur,
2331 SUB,
2332 SHPIX, /* edge = arg */
2333 EIF, /* s: edge2 cur_len */
2335 SWAP, /* s: cur_len edge2 */
2336 DUP,
2337 DUP,
2338 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2339 PUSHB_1,
2340 sal_edge2,
2341 SWAP,
2342 WS, /* s: cur_len edge2 */
2343 SWAP,
2344 SHPIX, /* edge2 = edge + cur_len */
2346 PUSHB_2,
2347 bci_align_segments,
2349 SZP1, /* set zp1 to normal zone 1 */
2350 CALL,
2352 PUSHB_1,
2353 sal_edge2,
2355 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2357 PUSHB_1,
2358 bci_align_segments,
2359 CALL,
2360 ENDF,
2366 * bci_action_blue
2368 * Handle the BLUE action to align an edge with a blue zone.
2370 * in: blue_cvt_idx
2371 * edge_point (in twilight zone)
2372 * ... stuff for bci_align_segments (edge) ...
2375 unsigned char FPGM(bci_action_blue) [] = {
2377 PUSHB_1,
2378 bci_action_blue,
2379 FDEF,
2381 PUSHB_1,
2383 SZP0, /* set zp0 to twilight zone 0 */
2385 /* move `edge_point' to `blue_cvt_idx' position */
2386 MIAP_noround, /* this also sets rp0 */
2388 PUSHB_2,
2389 bci_align_segments,
2391 SZP1, /* set zp1 to normal zone 1 */
2392 CALL,
2394 ENDF,
2400 * bci_action_serif
2402 * Handle the SERIF action to align a serif with its base.
2404 * in: serif_point (in twilight zone)
2405 * base_point (in twilight zone)
2406 * ... stuff for bci_align_segments (serif) ...
2409 unsigned char FPGM(bci_action_serif) [] = {
2411 PUSHB_1,
2412 bci_action_serif,
2413 FDEF,
2415 PUSHB_1,
2417 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2419 DUP,
2420 DUP,
2421 PUSHB_1,
2423 MINDEX, /* s: serif serif serif base */
2424 PUSHB_1,
2426 CINDEX,
2427 PUSHB_1,
2428 sal_num_segments,
2430 ADD, /* s: serif serif serif base serif_orig */
2431 SWAP,
2432 DUP,
2433 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2434 PUSHB_1,
2435 sal_num_segments,
2437 ADD, /* s: serif serif serif serif_orig base_orig */
2438 MD_cur,
2439 SWAP,
2440 ALIGNRP, /* align `serif_point' with `base_point' */
2441 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2443 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2445 PUSHB_2,
2446 bci_align_segments,
2448 SZP1, /* set zp1 to normal zone 1 */
2449 CALL,
2451 ENDF,
2457 * bci_action_serif_lower_bound
2459 * Handle the SERIF action to align a serif with its base, then moving it
2460 * again to stay within a lower bound.
2462 * in: serif_point (in twilight zone)
2463 * base_point (in twilight zone)
2464 * edge[-1] (in twilight zone)
2465 * ... stuff for bci_align_segments (serif) ...
2468 unsigned char FPGM(bci_action_serif_lower_bound) [] = {
2470 PUSHB_1,
2471 bci_action_serif_lower_bound,
2472 FDEF,
2474 PUSHB_1,
2476 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2478 DUP,
2479 DUP,
2480 PUSHB_1,
2482 MINDEX, /* s: edge[-1] serif serif serif base */
2483 PUSHB_1,
2485 CINDEX,
2486 PUSHB_1,
2487 sal_num_segments,
2489 ADD, /* s: edge[-1] serif serif serif base serif_orig */
2490 SWAP,
2491 DUP,
2492 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2493 PUSHB_1,
2494 sal_num_segments,
2496 ADD, /* s: edge[-1] serif serif serif serif_orig base_orig */
2497 MD_cur,
2498 SWAP,
2499 ALIGNRP, /* align `serif_point' with `base_point' */
2500 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2502 SWAP, /* s: serif edge[-1] */
2503 DUP,
2504 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2505 GC_cur,
2506 PUSHB_1,
2508 CINDEX,
2509 GC_cur, /* s: serif edge[-1]_pos serif_pos */
2510 GT, /* serif_pos < edge[-1]_pos */
2512 DUP,
2513 ALIGNRP, /* align `serif' to `edge[-1]' */
2514 EIF,
2516 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2518 PUSHB_2,
2519 bci_align_segments,
2521 SZP1, /* set zp1 to normal zone 1 */
2522 CALL,
2524 ENDF,
2530 * bci_action_serif_upper_bound
2532 * Handle the SERIF action to align a serif with its base, then moving it
2533 * again to stay within a upper bound.
2535 * in: serif_point (in twilight zone)
2536 * base_point (in twilight zone)
2537 * edge[1] (in twilight zone)
2538 * ... stuff for bci_align_segments (serif) ...
2541 unsigned char FPGM(bci_action_serif_upper_bound) [] = {
2543 PUSHB_1,
2544 bci_action_serif_upper_bound,
2545 FDEF,
2547 PUSHB_1,
2549 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2551 DUP,
2552 DUP,
2553 PUSHB_1,
2555 MINDEX, /* s: edge[1] serif serif serif base */
2556 PUSHB_1,
2558 CINDEX,
2559 PUSHB_1,
2560 sal_num_segments,
2562 ADD, /* s: edge[1] serif serif serif base serif_orig */
2563 SWAP,
2564 DUP,
2565 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2566 PUSHB_1,
2567 sal_num_segments,
2569 ADD, /* s: edge[1] serif serif serif serif_orig base_orig */
2570 MD_cur,
2571 SWAP,
2572 ALIGNRP, /* align `serif_point' with `base_point' */
2573 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2575 SWAP, /* s: serif edge[1] */
2576 DUP,
2577 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2578 GC_cur,
2579 PUSHB_1,
2581 CINDEX,
2582 GC_cur, /* s: serif edge[1]_pos serif_pos */
2583 LT, /* serif_pos > edge[1]_pos */
2585 DUP,
2586 ALIGNRP, /* align `serif' to `edge[1]' */
2587 EIF,
2589 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2591 PUSHB_2,
2592 bci_align_segments,
2594 SZP1, /* set zp1 to normal zone 1 */
2595 CALL,
2597 ENDF,
2603 * bci_action_serif_lower_upper_bound
2605 * Handle the SERIF action to align a serif with its base, then moving it
2606 * again to stay within a lower and upper bound.
2608 * in: serif_point (in twilight zone)
2609 * base_point (in twilight zone)
2610 * edge[-1] (in twilight zone)
2611 * edge[1] (in twilight zone)
2612 * ... stuff for bci_align_segments (serif) ...
2615 unsigned char FPGM(bci_action_serif_lower_upper_bound) [] = {
2617 PUSHB_1,
2618 bci_action_serif_lower_upper_bound,
2619 FDEF,
2621 PUSHB_1,
2623 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2625 DUP,
2626 DUP,
2627 PUSHB_1,
2629 MINDEX, /* s: edge[1] edge[-1] serif serif serif base */
2630 PUSHB_1,
2632 CINDEX,
2633 PUSHB_1,
2634 sal_num_segments,
2636 ADD, /* s: edge[1] edge[-1] serif serif serif base serif_orig */
2637 SWAP,
2638 DUP,
2639 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2640 PUSHB_1,
2641 sal_num_segments,
2643 ADD, /* s: edge[1] edge[-1] serif serif serif serif_orig base_orig */
2644 MD_cur,
2645 SWAP,
2646 ALIGNRP, /* align `serif_point' with `base_point' */
2647 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2649 SWAP, /* s: edge[1] serif edge[-1] */
2650 DUP,
2651 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2652 GC_cur,
2653 PUSHB_1,
2655 CINDEX,
2656 GC_cur, /* s: edge[1] serif edge[-1]_pos serif_pos */
2657 GT, /* serif_pos < edge[-1]_pos */
2659 DUP,
2660 ALIGNRP, /* align `serif' to `edge[-1]' */
2661 EIF,
2663 SWAP, /* s: serif edge[1] */
2664 DUP,
2665 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2666 GC_cur,
2667 PUSHB_1,
2669 CINDEX,
2670 GC_cur, /* s: serif edge[1]_pos serif_pos */
2671 LT, /* serif_pos > edge[1]_pos */
2673 DUP,
2674 ALIGNRP, /* align `serif' to `edge[1]' */
2675 EIF,
2677 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2679 PUSHB_2,
2680 bci_align_segments,
2682 SZP1, /* set zp1 to normal zone 1 */
2683 CALL,
2685 ENDF,
2689 unsigned char FPGM(bci_action_serif_anchor) [] = {
2691 PUSHB_1,
2692 bci_action_serif_anchor,
2693 FDEF,
2695 PUSHB_1,
2696 bci_handle_segments,
2697 CALL,
2699 /* XXX */
2701 ENDF,
2705 unsigned char FPGM(bci_action_serif_anchor_lower_bound) [] = {
2707 PUSHB_1,
2708 bci_action_serif_anchor_lower_bound,
2709 FDEF,
2711 PUSHB_1,
2712 bci_handle_segments,
2713 CALL,
2715 /* XXX */
2717 ENDF,
2721 unsigned char FPGM(bci_action_serif_anchor_upper_bound) [] = {
2723 PUSHB_1,
2724 bci_action_serif_anchor_upper_bound,
2725 FDEF,
2727 PUSHB_1,
2728 bci_handle_segments,
2729 CALL,
2731 /* XXX */
2733 ENDF,
2737 unsigned char FPGM(bci_action_serif_anchor_lower_upper_bound) [] = {
2739 PUSHB_1,
2740 bci_action_serif_anchor_lower_upper_bound,
2741 FDEF,
2743 PUSHB_1,
2744 bci_handle_segments,
2745 CALL,
2747 /* XXX */
2749 ENDF,
2753 unsigned char FPGM(bci_action_serif_link1) [] = {
2755 PUSHB_1,
2756 bci_action_serif_link1,
2757 FDEF,
2759 PUSHB_1,
2760 bci_handle_segments,
2761 CALL,
2763 /* XXX */
2765 ENDF,
2769 unsigned char FPGM(bci_action_serif_link1_lower_bound) [] = {
2771 PUSHB_1,
2772 bci_action_serif_link1_lower_bound,
2773 FDEF,
2775 PUSHB_1,
2776 bci_handle_segments,
2777 CALL,
2779 /* XXX */
2781 ENDF,
2785 unsigned char FPGM(bci_action_serif_link1_upper_bound) [] = {
2787 PUSHB_1,
2788 bci_action_serif_link1_upper_bound,
2789 FDEF,
2791 PUSHB_1,
2792 bci_handle_segments,
2793 CALL,
2795 /* XXX */
2797 ENDF,
2801 unsigned char FPGM(bci_action_serif_link1_lower_upper_bound) [] = {
2803 PUSHB_1,
2804 bci_action_serif_link1_lower_upper_bound,
2805 FDEF,
2807 PUSHB_1,
2808 bci_handle_segments,
2809 CALL,
2811 /* XXX */
2813 ENDF,
2817 unsigned char FPGM(bci_action_serif_link2) [] = {
2819 PUSHB_1,
2820 bci_action_serif_link2,
2821 FDEF,
2823 PUSHB_1,
2824 bci_handle_segments,
2825 CALL,
2827 /* XXX */
2829 ENDF,
2833 unsigned char FPGM(bci_action_serif_link2_lower_bound) [] = {
2835 PUSHB_1,
2836 bci_action_serif_link2_lower_bound,
2837 FDEF,
2839 PUSHB_1,
2840 bci_handle_segments,
2841 CALL,
2843 /* XXX */
2845 ENDF,
2849 unsigned char FPGM(bci_action_serif_link2_upper_bound) [] = {
2851 PUSHB_1,
2852 bci_action_serif_link2_upper_bound,
2853 FDEF,
2855 PUSHB_1,
2856 bci_handle_segments,
2857 CALL,
2859 /* XXX */
2861 ENDF,
2865 unsigned char FPGM(bci_action_serif_link2_lower_upper_bound) [] = {
2867 PUSHB_1,
2868 bci_action_serif_link2_lower_upper_bound,
2869 FDEF,
2871 PUSHB_1,
2872 bci_handle_segments,
2873 CALL,
2875 /* XXX */
2877 ENDF,
2883 * bci_handle_action
2885 * Execute function.
2887 * in: function_index
2890 unsigned char FPGM(bci_handle_action) [] = {
2892 PUSHB_1,
2893 bci_handle_action,
2894 FDEF,
2896 CALL,
2898 ENDF,
2904 * bci_hint_glyph
2906 * This is the top-level glyph hinting function
2907 * which parses the arguments on the stack and calls subroutines.
2909 * in: num_actions (M)
2910 * action_0_func_idx
2911 * ... data ...
2912 * action_1_func_idx
2913 * ... data ...
2914 * ...
2915 * action_M_func_idx
2916 * ... data ...
2918 * uses: bci_handle_action
2919 * bci_action_adjust_bound
2920 * bci_action_stem_bound
2922 * bci_action_link
2923 * bci_action_anchor
2924 * bci_action_blue_anchor
2925 * bci_action_adjust
2926 * bci_action_stem
2928 * bci_action_blue
2929 * bci_action_serif
2930 * bci_action_serif_anchor
2931 * bci_action_serif_link1
2932 * bci_action_serif_link2
2935 unsigned char FPGM(bci_hint_glyph) [] = {
2937 PUSHB_1,
2938 bci_hint_glyph,
2939 FDEF,
2941 PUSHB_1,
2942 bci_handle_action,
2943 LOOPCALL,
2945 ENDF,
2950 #define COPY_FPGM(func_name) \
2951 memcpy(buf_p, fpgm_ ## func_name, \
2952 sizeof (fpgm_ ## func_name)); \
2953 buf_p += sizeof (fpgm_ ## func_name) \
2955 static FT_Error
2956 TA_table_build_fpgm(FT_Byte** fpgm,
2957 FT_ULong* fpgm_len,
2958 FONT* font)
2960 FT_UInt buf_len;
2961 FT_UInt len;
2962 FT_Byte* buf;
2963 FT_Byte* buf_p;
2966 buf_len = sizeof (FPGM(bci_round))
2967 + sizeof (FPGM(bci_compute_stem_width_a))
2969 + sizeof (FPGM(bci_compute_stem_width_b))
2971 + sizeof (FPGM(bci_compute_stem_width_c))
2972 + sizeof (FPGM(bci_loop))
2973 + sizeof (FPGM(bci_cvt_rescale))
2974 + sizeof (FPGM(bci_blue_round_a))
2976 + sizeof (FPGM(bci_blue_round_b))
2977 + sizeof (FPGM(bci_get_point_extrema))
2978 + sizeof (FPGM(bci_create_segment))
2979 + sizeof (FPGM(bci_create_segments))
2980 + sizeof (FPGM(bci_handle_segment))
2981 + sizeof (FPGM(bci_align_segment))
2982 + sizeof (FPGM(bci_handle_segments))
2983 + sizeof (FPGM(bci_align_segments))
2984 + sizeof (FPGM(bci_action_adjust_bound))
2985 + sizeof (FPGM(bci_action_stem_bound))
2986 + sizeof (FPGM(bci_action_link))
2987 + sizeof (FPGM(bci_action_anchor))
2988 + sizeof (FPGM(bci_action_blue_anchor))
2989 + sizeof (FPGM(bci_action_adjust))
2990 + sizeof (FPGM(bci_action_stem))
2991 + sizeof (FPGM(bci_action_blue))
2992 + sizeof (FPGM(bci_action_serif))
2993 + sizeof (FPGM(bci_action_serif_lower_bound))
2994 + sizeof (FPGM(bci_action_serif_upper_bound))
2995 + sizeof (FPGM(bci_action_serif_lower_upper_bound))
2996 + sizeof (FPGM(bci_action_serif_anchor))
2997 + sizeof (FPGM(bci_action_serif_anchor_lower_bound))
2998 + sizeof (FPGM(bci_action_serif_anchor_upper_bound))
2999 + sizeof (FPGM(bci_action_serif_anchor_lower_upper_bound))
3000 + sizeof (FPGM(bci_action_serif_link1))
3001 + sizeof (FPGM(bci_action_serif_link1_lower_bound))
3002 + sizeof (FPGM(bci_action_serif_link1_upper_bound))
3003 + sizeof (FPGM(bci_action_serif_link1_lower_upper_bound))
3004 + sizeof (FPGM(bci_action_serif_link2))
3005 + sizeof (FPGM(bci_action_serif_link2_lower_bound))
3006 + sizeof (FPGM(bci_action_serif_link2_upper_bound))
3007 + sizeof (FPGM(bci_action_serif_link2_lower_upper_bound))
3008 + sizeof (FPGM(bci_handle_action))
3009 + sizeof (FPGM(bci_hint_glyph));
3010 /* buffer length must be a multiple of four */
3011 len = (buf_len + 3) & ~3;
3012 buf = (FT_Byte*)malloc(len);
3013 if (!buf)
3014 return FT_Err_Out_Of_Memory;
3016 /* pad end of buffer with zeros */
3017 buf[len - 1] = 0x00;
3018 buf[len - 2] = 0x00;
3019 buf[len - 3] = 0x00;
3021 /* copy font program into buffer and fill in the missing variables */
3022 buf_p = buf;
3024 COPY_FPGM(bci_round);
3025 COPY_FPGM(bci_compute_stem_width_a);
3026 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3027 COPY_FPGM(bci_compute_stem_width_b);
3028 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3029 COPY_FPGM(bci_compute_stem_width_c);
3030 COPY_FPGM(bci_loop);
3031 COPY_FPGM(bci_cvt_rescale);
3032 COPY_FPGM(bci_blue_round_a);
3033 *(buf_p++) = (unsigned char)CVT_BLUES_SIZE(font);
3034 COPY_FPGM(bci_blue_round_b);
3035 COPY_FPGM(bci_get_point_extrema);
3036 COPY_FPGM(bci_create_segment);
3037 COPY_FPGM(bci_create_segments);
3038 COPY_FPGM(bci_handle_segment);
3039 COPY_FPGM(bci_align_segment);
3040 COPY_FPGM(bci_handle_segments);
3041 COPY_FPGM(bci_align_segments);
3042 COPY_FPGM(bci_action_adjust_bound);
3043 COPY_FPGM(bci_action_stem_bound);
3044 COPY_FPGM(bci_action_link);
3045 COPY_FPGM(bci_action_anchor);
3046 COPY_FPGM(bci_action_blue_anchor);
3047 COPY_FPGM(bci_action_adjust);
3048 COPY_FPGM(bci_action_stem);
3049 COPY_FPGM(bci_action_blue);
3050 COPY_FPGM(bci_action_serif);
3051 COPY_FPGM(bci_action_serif_lower_bound);
3052 COPY_FPGM(bci_action_serif_upper_bound);
3053 COPY_FPGM(bci_action_serif_lower_upper_bound);
3054 COPY_FPGM(bci_action_serif_anchor);
3055 COPY_FPGM(bci_action_serif_anchor_lower_bound);
3056 COPY_FPGM(bci_action_serif_anchor_upper_bound);
3057 COPY_FPGM(bci_action_serif_anchor_lower_upper_bound);
3058 COPY_FPGM(bci_action_serif_link1);
3059 COPY_FPGM(bci_action_serif_link1_lower_bound);
3060 COPY_FPGM(bci_action_serif_link1_upper_bound);
3061 COPY_FPGM(bci_action_serif_link1_lower_upper_bound);
3062 COPY_FPGM(bci_action_serif_link2);
3063 COPY_FPGM(bci_action_serif_link2_lower_bound);
3064 COPY_FPGM(bci_action_serif_link2_upper_bound);
3065 COPY_FPGM(bci_action_serif_link2_lower_upper_bound);
3066 COPY_FPGM(bci_handle_action);
3067 COPY_FPGM(bci_hint_glyph);
3069 *fpgm = buf;
3070 *fpgm_len = buf_len;
3072 return FT_Err_Ok;
3076 FT_Error
3077 TA_sfnt_build_fpgm_table(SFNT* sfnt,
3078 FONT* font)
3080 FT_Error error;
3082 FT_Byte* fpgm_buf;
3083 FT_ULong fpgm_len;
3086 error = TA_sfnt_add_table_info(sfnt);
3087 if (error)
3088 return error;
3090 error = TA_table_build_fpgm(&fpgm_buf, &fpgm_len, font);
3091 if (error)
3092 return error;
3094 /* in case of success, `fpgm_buf' gets linked */
3095 /* and is eventually freed in `TA_font_unload' */
3096 error = TA_font_add_table(font,
3097 &sfnt->table_infos[sfnt->num_table_infos - 1],
3098 TTAG_fpgm, fpgm_len, fpgm_buf);
3099 if (error)
3101 free(fpgm_buf);
3102 return error;
3105 return FT_Err_Ok;
3109 /* the `prep' instructions */
3111 #define PREP(snippet_name) prep_ ## snippet_name
3113 /* we often need 0x10000 which can't be pushed directly onto the stack, */
3114 /* thus we provide it in the storage area */
3116 unsigned char PREP(store_0x10000) [] = {
3118 PUSHB_1,
3119 sal_0x10000,
3120 PUSHW_2,
3121 0x08, /* 0x800 */
3122 0x00,
3123 0x08, /* 0x800 */
3124 0x00,
3125 MUL, /* 0x10000 */
3130 unsigned char PREP(align_top_a) [] = {
3132 /* optimize the alignment of the top of small letters to the pixel grid */
3134 PUSHB_1,
3138 /* %c, index of alignment blue zone */
3140 unsigned char PREP(align_top_b) [] = {
3142 RCVT,
3143 DUP,
3144 DUP,
3145 PUSHB_1,
3147 ADD,
3148 FLOOR, /* fitted = FLOOR(scaled + 40) */
3149 DUP, /* s: scaled scaled fitted fitted */
3150 ROLL,
3151 NEQ,
3152 IF, /* s: scaled fitted */
3153 PUSHB_1,
3154 sal_0x10000,
3156 MUL, /* scaled in 16.16 format */
3157 SWAP,
3158 DIV, /* (fitted / scaled) in 16.16 format */
3160 PUSHB_1,
3161 sal_scale,
3162 SWAP,
3167 unsigned char PREP(loop_cvt_a) [] = {
3169 /* loop over vertical CVT entries */
3170 PUSHB_4,
3174 /* %c, first vertical index */
3175 /* %c, last vertical index */
3177 unsigned char PREP(loop_cvt_b) [] = {
3179 bci_cvt_rescale,
3180 bci_loop,
3181 CALL,
3183 /* loop over blue refs */
3184 PUSHB_4,
3188 /* %c, first blue ref index */
3189 /* %c, last blue ref index */
3191 unsigned char PREP(loop_cvt_c) [] = {
3193 bci_cvt_rescale,
3194 bci_loop,
3195 CALL,
3197 /* loop over blue shoots */
3198 PUSHB_4,
3202 /* %c, first blue shoot index */
3203 /* %c, last blue shoot index */
3205 unsigned char PREP(loop_cvt_d) [] = {
3207 bci_cvt_rescale,
3208 bci_loop,
3209 CALL,
3210 EIF,
3214 unsigned char PREP(compute_extra_light_a) [] = {
3216 /* compute (vertical) `extra_light' flag */
3217 PUSHB_3,
3218 sal_is_extra_light,
3223 /* %c, index of vertical standard_width */
3225 unsigned char PREP(compute_extra_light_b) [] = {
3227 RCVT,
3228 GT, /* standard_width < 40 */
3233 unsigned char PREP(round_blues_a) [] = {
3235 /* use discrete values for blue zone widths */
3236 PUSHB_4,
3240 /* %c, first blue ref index */
3241 /* %c, last blue ref index */
3243 unsigned char PREP(round_blues_b) [] = {
3245 bci_blue_round,
3246 bci_loop,
3247 CALL
3251 /* XXX talatin.c: 1671 */
3252 /* XXX talatin.c: 1708 */
3253 /* XXX talatin.c: 2182 */
3256 #define COPY_PREP(snippet_name) \
3257 memcpy(buf_p, prep_ ## snippet_name, \
3258 sizeof (prep_ ## snippet_name)); \
3259 buf_p += sizeof (prep_ ## snippet_name);
3261 static FT_Error
3262 TA_table_build_prep(FT_Byte** prep,
3263 FT_ULong* prep_len,
3264 FONT* font)
3266 TA_LatinAxis vaxis;
3267 TA_LatinBlue blue_adjustment;
3268 FT_UInt i;
3270 FT_UInt buf_len;
3271 FT_UInt len;
3272 FT_Byte* buf;
3273 FT_Byte* buf_p;
3276 vaxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[1];
3277 blue_adjustment = NULL;
3279 for (i = 0; i < vaxis->blue_count; i++)
3281 if (vaxis->blues[i].flags & TA_LATIN_BLUE_ADJUSTMENT)
3283 blue_adjustment = &vaxis->blues[i];
3284 break;
3288 buf_len = sizeof (PREP(store_0x10000));
3290 if (blue_adjustment)
3291 buf_len += sizeof (PREP(align_top_a))
3293 + sizeof (PREP(align_top_b))
3294 + sizeof (PREP(loop_cvt_a))
3296 + sizeof (PREP(loop_cvt_b))
3298 + sizeof (PREP(loop_cvt_c))
3300 + sizeof (PREP(loop_cvt_d));
3302 buf_len += sizeof (PREP(compute_extra_light_a))
3304 + sizeof (PREP(compute_extra_light_b));
3306 if (CVT_BLUES_SIZE(font))
3307 buf_len += sizeof (PREP(round_blues_a))
3309 + sizeof (PREP(round_blues_b));
3311 /* buffer length must be a multiple of four */
3312 len = (buf_len + 3) & ~3;
3313 buf = (FT_Byte*)malloc(len);
3314 if (!buf)
3315 return FT_Err_Out_Of_Memory;
3317 /* pad end of buffer with zeros */
3318 buf[len - 1] = 0x00;
3319 buf[len - 2] = 0x00;
3320 buf[len - 3] = 0x00;
3322 /* copy cvt program into buffer and fill in the missing variables */
3323 buf_p = buf;
3325 COPY_PREP(store_0x10000);
3327 if (blue_adjustment)
3329 COPY_PREP(align_top_a);
3330 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
3331 + blue_adjustment - vaxis->blues);
3332 COPY_PREP(align_top_b);
3334 COPY_PREP(loop_cvt_a);
3335 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3336 *(buf_p++) = (unsigned char)(CVT_VERT_WIDTHS_OFFSET(font)
3337 + CVT_VERT_WIDTHS_SIZE(font) - 1);
3338 COPY_PREP(loop_cvt_b);
3339 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
3340 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
3341 + CVT_BLUES_SIZE(font) - 1);
3342 COPY_PREP(loop_cvt_c);
3343 *(buf_p++) = (unsigned char)CVT_BLUE_SHOOTS_OFFSET(font);
3344 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
3345 + CVT_BLUES_SIZE(font) - 1);
3346 COPY_PREP(loop_cvt_d);
3349 COPY_PREP(compute_extra_light_a);
3350 *(buf_p++) = (unsigned char)CVT_VERT_STANDARD_WIDTH_OFFSET(font);
3351 COPY_PREP(compute_extra_light_b);
3353 if (CVT_BLUES_SIZE(font))
3355 COPY_PREP(round_blues_a);
3356 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
3357 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
3358 + CVT_BLUES_SIZE(font) - 1);
3359 COPY_PREP(round_blues_b);
3362 *prep = buf;
3363 *prep_len = buf_len;
3365 return FT_Err_Ok;
3369 FT_Error
3370 TA_sfnt_build_prep_table(SFNT* sfnt,
3371 FONT* font)
3373 FT_Error error;
3375 FT_Byte* prep_buf;
3376 FT_ULong prep_len;
3379 error = TA_sfnt_add_table_info(sfnt);
3380 if (error)
3381 return error;
3383 error = TA_table_build_prep(&prep_buf, &prep_len, font);
3384 if (error)
3385 return error;
3387 /* in case of success, `prep_buf' gets linked */
3388 /* and is eventually freed in `TA_font_unload' */
3389 error = TA_font_add_table(font,
3390 &sfnt->table_infos[sfnt->num_table_infos - 1],
3391 TTAG_prep, prep_len, prep_buf);
3392 if (error)
3394 free(prep_buf);
3395 return error;
3398 return FT_Err_Ok;
3402 /* we store the segments in the storage area; */
3403 /* each segment record consists of the first and last point */
3405 static FT_Byte*
3406 TA_sfnt_build_glyph_segments(SFNT* sfnt,
3407 Recorder* recorder,
3408 FT_Byte* bufp)
3410 FONT* font = recorder->font;
3411 TA_GlyphHints hints = &font->loader->hints;
3412 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
3413 TA_Point points = hints->points;
3414 TA_Segment segments = axis->segments;
3415 TA_Segment seg;
3416 TA_Segment seg_limit;
3418 FT_Outline outline = font->loader->gloader->base.outline;
3420 FT_UInt* args;
3421 FT_UInt* arg;
3422 FT_UInt num_args;
3423 FT_UInt nargs;
3424 FT_UInt num_segments;
3426 FT_UInt* wrap_around_segment;
3427 FT_UInt num_wrap_around_segments;
3429 FT_Bool need_words = 0;
3431 FT_Int n;
3432 FT_UInt i, j;
3433 FT_UInt num_storage;
3434 FT_UInt num_stack_elements;
3435 FT_UInt num_twilight_points;
3438 seg_limit = segments + axis->num_segments;
3439 num_segments = axis->num_segments;
3441 /* some segments can `wrap around' */
3442 /* a contour's start point like 24-25-26-0-1-2 */
3443 /* (there can be at most one such segment per contour); */
3444 /* we thus append additional records to split them into 24-26 and 0-2 */
3445 wrap_around_segment = recorder->wrap_around_segments;
3446 for (seg = segments; seg < seg_limit; seg++)
3447 if (seg->first > seg->last)
3449 /* the stored data is used later for edge linking */
3450 *(wrap_around_segment++) = seg - segments;
3453 num_wrap_around_segments = wrap_around_segment
3454 - recorder->wrap_around_segments;
3455 num_segments += num_wrap_around_segments;
3457 /* wrap-around segments are pushed with four arguments */
3458 num_args = 2 * num_segments + 2 * num_wrap_around_segments + 2;
3460 /* collect all arguments temporarily in an array (in reverse order) */
3461 /* so that we can easily split into chunks of 255 args */
3462 /* as needed by NPUSHB and NPUSHW, respectively */
3463 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
3464 if (!args)
3465 return NULL;
3467 arg = args + num_args - 1;
3469 if (num_segments > 0xFF)
3470 need_words = 1;
3472 *(arg--) = bci_create_segments;
3473 *(arg--) = num_segments;
3475 for (seg = segments; seg < seg_limit; seg++)
3477 FT_UInt first = seg->first - points;
3478 FT_UInt last = seg->last - points;
3481 *(arg--) = first;
3482 *(arg--) = last;
3484 /* we push the last and first contour point */
3485 /* as a third and fourth argument in wrap-around segments */
3486 if (first > last)
3488 for (n = 0; n < outline.n_contours; n++)
3490 FT_UInt end = (FT_UInt)outline.contours[n];
3493 if (first <= end)
3495 *(arg--) = end;
3496 if (end > 0xFF)
3497 need_words = 1;
3499 if (n == 0)
3500 *(arg--) = 0;
3501 else
3502 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
3503 break;
3508 if (last > 0xFF)
3509 need_words = 1;
3512 /* emit the second part of wrap-around segments as separate segments */
3513 /* so that edges can easily link to them */
3514 for (seg = segments; seg < seg_limit; seg++)
3516 FT_UInt first = seg->first - points;
3517 FT_UInt last = seg->last - points;
3520 if (first > last)
3522 for (n = 0; n < outline.n_contours; n++)
3524 if (first <= (FT_UInt)outline.contours[n])
3526 if (n == 0)
3527 *(arg--) = 0;
3528 else
3529 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
3530 break;
3534 *(arg--) = last;
3537 /* with most fonts it is very rare */
3538 /* that any of the pushed arguments is larger than 0xFF, */
3539 /* thus we refrain from further optimizing this case */
3541 arg = args;
3543 if (need_words)
3545 for (i = 0; i < num_args; i += 255)
3547 nargs = (num_args - i > 255) ? 255 : num_args - i;
3549 BCI(NPUSHW);
3550 BCI(nargs);
3551 for (j = 0; j < nargs; j++)
3553 BCI(HIGH(*arg));
3554 BCI(LOW(*arg));
3555 arg++;
3559 else
3561 for (i = 0; i < num_args; i += 255)
3563 nargs = (num_args - i > 255) ? 255 : num_args - i;
3565 BCI(NPUSHB);
3566 BCI(nargs);
3567 for (j = 0; j < nargs; j++)
3569 BCI(*arg);
3570 arg++;
3575 BCI(CALL);
3577 num_storage = sal_segment_offset + num_segments * 2;
3578 if (num_storage > sfnt->max_storage)
3579 sfnt->max_storage = num_storage;
3581 num_twilight_points = num_segments * 2;
3582 if (num_twilight_points > sfnt->max_twilight_points)
3583 sfnt->max_twilight_points = num_twilight_points;
3585 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
3586 if (num_stack_elements > sfnt->max_stack_elements)
3587 sfnt->max_stack_elements = num_stack_elements;
3589 free(args);
3591 return bufp;
3595 static FT_Bool
3596 TA_hints_record_is_different(Hints_Record* hints_records,
3597 FT_UInt num_hints_records,
3598 FT_Byte* start,
3599 FT_Byte* end)
3601 Hints_Record last_hints_record;
3604 if (!hints_records)
3605 return 1;
3607 /* we only need to compare with the last hints record */
3608 last_hints_record = hints_records[num_hints_records - 1];
3610 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
3611 return 1;
3613 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
3614 return 1;
3616 return 0;
3620 static FT_Error
3621 TA_add_hints_record(Hints_Record** hints_records,
3622 FT_UInt* num_hints_records,
3623 FT_Byte* start,
3624 Hints_Record hints_record)
3626 Hints_Record* hints_records_new;
3627 FT_UInt buf_len;
3628 /* at this point, `hints_record.buf' still points into `ins_buf' */
3629 FT_Byte* end = hints_record.buf;
3632 buf_len = (FT_UInt)(end - start);
3634 /* now fill the structure completely */
3635 hints_record.buf_len = buf_len;
3636 hints_record.buf = (FT_Byte*)malloc(buf_len);
3637 if (!hints_record.buf)
3638 return FT_Err_Out_Of_Memory;
3640 memcpy(hints_record.buf, start, buf_len);
3642 (*num_hints_records)++;
3643 hints_records_new =
3644 (Hints_Record*)realloc(*hints_records, *num_hints_records
3645 * sizeof (Hints_Record));
3646 if (!hints_records_new)
3648 free(hints_record.buf);
3649 (*num_hints_records)--;
3650 return FT_Err_Out_Of_Memory;
3652 else
3653 *hints_records = hints_records_new;
3655 (*hints_records)[*num_hints_records - 1] = hints_record;
3657 return FT_Err_Ok;
3661 static FT_Byte*
3662 TA_sfnt_emit_hints_record(SFNT* sfnt,
3663 Hints_Record* hints_record,
3664 FT_Byte* bufp)
3666 FT_Byte* p;
3667 FT_Byte* endp;
3668 FT_Bool need_words = 0;
3670 FT_UInt i, j;
3671 FT_UInt num_arguments;
3672 FT_UInt num_args;
3673 FT_UInt num_stack_elements;
3676 /* check whether any argument is larger than 0xFF */
3677 endp = hints_record->buf + hints_record->buf_len;
3678 for (p = hints_record->buf; p < endp; p += 2)
3679 if (*p)
3680 need_words = 1;
3682 /* with most fonts it is very rare */
3683 /* that any of the pushed arguments is larger than 0xFF, */
3684 /* thus we refrain from further optimizing this case */
3686 num_arguments = hints_record->buf_len / 2;
3687 p = endp - 2;
3689 if (need_words)
3691 for (i = 0; i < num_arguments; i += 255)
3693 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
3695 BCI(NPUSHW);
3696 BCI(num_args);
3697 for (j = 0; j < num_args; j++)
3699 BCI(*p);
3700 BCI(*(p + 1));
3701 p -= 2;
3705 else
3707 /* we only need the lower bytes */
3708 p++;
3710 for (i = 0; i < num_arguments; i += 255)
3712 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
3714 BCI(NPUSHB);
3715 BCI(num_args);
3716 for (j = 0; j < num_args; j++)
3718 BCI(*p);
3719 p -= 2;
3724 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_arguments;
3725 if (num_stack_elements > sfnt->max_stack_elements)
3726 sfnt->max_stack_elements = sfnt->max_stack_elements;
3728 return bufp;
3732 static FT_Byte*
3733 TA_sfnt_emit_hints_records(SFNT* sfnt,
3734 Hints_Record* hints_records,
3735 FT_UInt num_hints_records,
3736 FT_Byte* bufp)
3738 FT_UInt i;
3739 Hints_Record* hints_record;
3742 hints_record = hints_records;
3744 for (i = 0; i < num_hints_records - 1; i++)
3746 BCI(MPPEM);
3747 if (hints_record->size > 0xFF)
3749 BCI(PUSHW_1);
3750 BCI(HIGH((hints_record + 1)->size));
3751 BCI(LOW((hints_record + 1)->size));
3753 else
3755 BCI(PUSHB_1);
3756 BCI((hints_record + 1)->size);
3758 BCI(LT);
3759 BCI(IF);
3760 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
3761 BCI(ELSE);
3763 hints_record++;
3766 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
3768 for (i = 0; i < num_hints_records - 1; i++)
3769 BCI(EIF);
3771 BCI(PUSHB_1);
3772 BCI(bci_hint_glyph);
3773 BCI(CALL);
3775 return bufp;
3779 static void
3780 TA_free_hints_records(Hints_Record* hints_records,
3781 FT_UInt num_hints_records)
3783 FT_UInt i;
3786 for (i = 0; i < num_hints_records; i++)
3787 free(hints_records[i].buf);
3789 free(hints_records);
3793 static FT_Byte*
3794 TA_hints_recorder_handle_segments(FT_Byte* bufp,
3795 TA_AxisHints axis,
3796 TA_Edge edge,
3797 FT_UInt* wraps)
3799 TA_Segment segments = axis->segments;
3800 TA_Segment seg;
3801 FT_UInt seg_idx;
3802 FT_UInt num_segs = 0;
3803 FT_UInt* wrap;
3806 seg_idx = edge->first - segments;
3808 /* we store everything as 16bit numbers */
3809 *(bufp++) = HIGH(seg_idx);
3810 *(bufp++) = LOW(seg_idx);
3812 /* wrap-around segments are stored as two segments */
3813 if (edge->first->first > edge->first->last)
3814 num_segs++;
3816 seg = edge->first->edge_next;
3817 while (seg != edge->first)
3819 num_segs++;
3821 if (seg->first > seg->last)
3822 num_segs++;
3824 seg = seg->edge_next;
3827 *(bufp++) = HIGH(num_segs);
3828 *(bufp++) = LOW(num_segs);
3830 if (edge->first->first > edge->first->last)
3832 /* emit second part of wrap-around segment; */
3833 /* the bytecode positions such segments after `normal' ones */
3834 wrap = wraps;
3835 for (;;)
3837 if (seg_idx == *wrap)
3838 break;
3839 wrap++;
3842 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
3843 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
3846 seg = edge->first->edge_next;
3847 while (seg != edge->first)
3849 seg_idx = seg - segments;
3851 *(bufp++) = HIGH(seg_idx);
3852 *(bufp++) = LOW(seg_idx);
3854 if (seg->first > seg->last)
3856 wrap = wraps;
3857 for (;;)
3859 if (seg_idx == *wrap)
3860 break;
3861 wrap++;
3864 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
3865 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
3868 seg = seg->edge_next;
3871 return bufp;
3875 static void
3876 TA_hints_recorder(TA_Action action,
3877 TA_GlyphHints hints,
3878 TA_Dimension dim,
3879 TA_Edge arg1,
3880 TA_Edge arg2,
3881 TA_Edge arg3,
3882 TA_Edge lower_bound,
3883 TA_Edge upper_bound)
3885 TA_AxisHints axis = &hints->axis[dim];
3886 TA_Segment segments = axis->segments;
3888 Recorder* recorder = (Recorder*)hints->user;
3889 FONT* font = recorder->font;
3890 FT_UInt* wraps = recorder->wrap_around_segments;
3891 FT_Byte* p = recorder->hints_record.buf;
3893 FT_Byte bound_offset = 0;
3896 if (dim == TA_DIMENSION_HORZ)
3897 return;
3899 /* we ignore the BOUND action since we signal this information */
3900 /* with the `bound_offset' parameter */
3901 if (action == ta_bound)
3902 return;
3904 if (lower_bound)
3905 bound_offset += 1;
3906 if (upper_bound)
3907 bound_offset += 2;
3909 /* this reflects the order in the TA_Action enumeration */
3910 *(p++) = 0;
3911 *(p++) = (FT_Byte)action + bound_offset + ACTION_OFFSET;
3913 switch (action)
3915 case ta_link:
3917 TA_Edge base_edge = arg1;
3918 TA_Edge stem_edge = arg2;
3921 *(p++) = 0;
3922 *(p++) = stem_edge->flags & TA_EDGE_SERIF;
3923 *(p++) = 0;
3924 *(p++) = base_edge->flags & TA_EDGE_ROUND;
3925 *(p++) = HIGH(base_edge->first - segments);
3926 *(p++) = LOW(base_edge->first - segments);
3927 *(p++) = HIGH(stem_edge->first - segments);
3928 *(p++) = LOW(stem_edge->first - segments);
3930 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
3932 break;
3934 case ta_anchor:
3936 TA_Edge edge = arg1;
3937 TA_Edge edge2 = arg2;
3940 *(p++) = 0;
3941 *(p++) = edge2->flags & TA_EDGE_SERIF;
3942 *(p++) = 0;
3943 *(p++) = edge->flags & TA_EDGE_ROUND;
3944 *(p++) = HIGH(edge->first - segments);
3945 *(p++) = LOW(edge->first - segments);
3946 *(p++) = HIGH(edge2->first - segments);
3947 *(p++) = LOW(edge2->first - segments);
3949 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3951 break;
3953 case ta_adjust:
3955 TA_Edge edge = arg1;
3956 TA_Edge edge2 = arg2;
3957 TA_Edge edge_minus_one = lower_bound;
3960 *(p++) = 0;
3961 *(p++) = edge2->flags & TA_EDGE_SERIF;
3962 *(p++) = 0;
3963 *(p++) = edge->flags & TA_EDGE_ROUND;
3964 *(p++) = HIGH(edge->first - segments);
3965 *(p++) = LOW(edge->first - segments);
3966 *(p++) = HIGH(edge2->first - segments);
3967 *(p++) = LOW(edge2->first - segments);
3969 if (edge_minus_one)
3971 *(p++) = HIGH(edge_minus_one->first - segments);
3972 *(p++) = LOW(edge_minus_one->first - segments);
3975 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3977 break;
3979 case ta_blue_anchor:
3981 TA_Edge edge = arg1;
3982 TA_Edge blue = arg2;
3985 *(p++) = HIGH(blue->first - segments);
3986 *(p++) = LOW(blue->first - segments);
3988 if (edge->best_blue_is_shoot)
3990 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3991 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3993 else
3995 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3996 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3999 *(p++) = HIGH(edge->first - segments);
4000 *(p++) = LOW(edge->first - segments);
4002 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4004 break;
4006 case ta_stem:
4008 TA_Edge edge = arg1;
4009 TA_Edge edge2 = arg2;
4010 TA_Edge edge_minus_one = lower_bound;
4013 *(p++) = 0;
4014 *(p++) = edge2->flags & TA_EDGE_SERIF;
4015 *(p++) = 0;
4016 *(p++) = edge->flags & TA_EDGE_ROUND;
4017 *(p++) = HIGH(edge->first - segments);
4018 *(p++) = LOW(edge->first - segments);
4019 *(p++) = HIGH(edge2->first - segments);
4020 *(p++) = LOW(edge2->first - segments);
4022 if (edge_minus_one)
4024 *(p++) = HIGH(edge_minus_one->first - segments);
4025 *(p++) = LOW(edge_minus_one->first - segments);
4028 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4029 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
4031 break;
4033 case ta_blue:
4035 TA_Edge edge = arg1;
4038 if (edge->best_blue_is_shoot)
4040 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4041 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4043 else
4045 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4046 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4049 *(p++) = HIGH(edge->first - segments);
4050 *(p++) = LOW(edge->first - segments);
4052 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4054 break;
4056 case ta_serif:
4058 TA_Edge base = arg1->serif;
4059 TA_Edge serif = arg1;
4062 *(p++) = HIGH(serif->first - segments);
4063 *(p++) = LOW(serif->first - segments);
4064 *(p++) = HIGH(base->first - segments);
4065 *(p++) = LOW(base->first - segments);
4067 if (lower_bound)
4069 *(p++) = HIGH(lower_bound->first - segments);
4070 *(p++) = LOW(lower_bound->first - segments);
4072 if (upper_bound)
4074 *(p++) = HIGH(upper_bound->first - segments);
4075 *(p++) = LOW(upper_bound->first - segments);
4078 p = TA_hints_recorder_handle_segments(p, axis, serif, wraps);
4080 break;
4082 case ta_serif_anchor:
4083 p = TA_hints_recorder_handle_segments(p, axis, arg1, wraps);
4084 break;
4086 case ta_serif_link1:
4087 p = TA_hints_recorder_handle_segments(p, axis, arg1, wraps);
4088 break;
4090 case ta_serif_link2:
4091 p = TA_hints_recorder_handle_segments(p, axis, arg1, wraps);
4092 break;
4094 default:
4095 /* there are more cases in the enumeration */
4096 /* which are handled with the `bound_offset' parameter */
4097 break;
4100 recorder->hints_record.num_actions++;
4101 recorder->hints_record.buf = p;
4105 static FT_Error
4106 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
4107 FONT* font,
4108 FT_Long idx)
4110 FT_Face face = sfnt->face;
4111 FT_Error error;
4113 FT_Byte* ins_buf;
4114 FT_UInt ins_len;
4115 FT_Byte* bufp;
4117 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
4118 glyf_Data* data = (glyf_Data*)glyf_table->data;
4119 GLYPH* glyph = &data->glyphs[idx];
4121 TA_GlyphHints hints;
4123 FT_UInt num_hints_records;
4124 Hints_Record* hints_records;
4126 Recorder recorder;
4128 FT_UInt size;
4131 if (idx < 0)
4132 return FT_Err_Invalid_Argument;
4134 /* computing the segments is resolution independent, */
4135 /* thus the pixel size in this call is arbitrary */
4136 error = FT_Set_Pixel_Sizes(face, 20, 20);
4137 if (error)
4138 return error;
4140 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
4141 error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx, 0);
4142 if (error)
4143 return error;
4145 /* do nothing if we have an empty glyph */
4146 if (!face->glyph->outline.n_contours)
4147 return FT_Err_Ok;
4149 /* do nothing if the dummy hinter has been used */
4150 if (font->loader->metrics->clazz == &ta_dummy_script_class)
4151 return FT_Err_Ok;
4153 hints = &font->loader->hints;
4155 /* we allocate a buffer which is certainly large enough */
4156 /* to hold all of the created bytecode instructions; */
4157 /* later on it gets reallocated to its real size */
4158 ins_len = hints->num_points * 1000;
4159 ins_buf = (FT_Byte*)malloc(ins_len);
4160 if (!ins_buf)
4161 return FT_Err_Out_Of_Memory;
4163 /* initialize array with an invalid bytecode */
4164 /* so that we can easily find the array length at reallocation time */
4165 memset(ins_buf, INS_A0, ins_len);
4167 recorder.font = font;
4168 recorder.wrap_around_segments =
4169 (FT_UInt*)malloc(face->glyph->outline.n_contours * sizeof (FT_UInt));
4171 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf);
4173 /* now we loop over a large range of pixel sizes */
4174 /* to find hints records which get pushed onto the bytecode stack */
4175 num_hints_records = 0;
4176 hints_records = NULL;
4178 #ifdef DEBUGGING
4179 printf("glyph %ld\n", idx);
4180 #endif
4182 /* we temporarily use `ins_buf' to record the current glyph hints, */
4183 /* leaving two bytes at the beginning so that the number of actions */
4184 /* can be inserted later on */
4185 ta_loader_register_hints_recorder(font->loader,
4186 TA_hints_recorder,
4187 (void *)&recorder);
4189 for (size = 8; size <= 1000; size++)
4191 /* rewind buffer pointer for recorder */
4192 recorder.hints_record.buf = bufp + 2;
4193 recorder.hints_record.num_actions = 0;
4194 recorder.hints_record.size = size;
4196 error = FT_Set_Pixel_Sizes(face, size, size);
4197 if (error)
4198 goto Err;
4200 /* calling `ta_loader_load_glyph' uses the */
4201 /* `TA_hints_recorder' function as a callback, */
4202 /* modifying `hints_record' */
4203 error = ta_loader_load_glyph(font->loader, face, idx, 0);
4204 if (error)
4205 goto Err;
4207 /* store the number of actions in `ins_buf' */
4208 *bufp = HIGH(recorder.hints_record.num_actions);
4209 *(bufp + 1) = LOW(recorder.hints_record.num_actions);
4211 if (TA_hints_record_is_different(hints_records,
4212 num_hints_records,
4213 bufp, recorder.hints_record.buf))
4215 #ifdef DEBUGGING
4217 FT_Byte* p;
4220 printf(" %d:\n", size);
4221 for (p = bufp; p < recorder.hints_record.buf; p += 2)
4222 printf(" %2d", *p * 256 + *(p + 1));
4223 printf("\n");
4225 #endif
4227 error = TA_add_hints_record(&hints_records,
4228 &num_hints_records,
4229 bufp, recorder.hints_record);
4230 if (error)
4231 goto Err;
4235 if (num_hints_records == 1 && !hints_records[0].num_actions)
4237 /* don't emit anything if we only have a single empty record */
4238 ins_len = 0;
4240 else
4242 FT_Byte* p = bufp;
4245 /* otherwise, clear the temporarily used part of `ins_buf' */
4246 while (*p != INS_A0)
4247 *(p++) = INS_A0;
4249 bufp = TA_sfnt_emit_hints_records(sfnt,
4250 hints_records, num_hints_records,
4251 bufp);
4253 /* we are done, so reallocate the instruction array to its real size */
4254 if (*bufp == INS_A0)
4256 /* search backwards */
4257 while (*bufp == INS_A0)
4258 bufp--;
4259 bufp++;
4261 else
4263 /* search forwards */
4264 while (*bufp != INS_A0)
4265 bufp++;
4268 ins_len = bufp - ins_buf;
4271 if (ins_len > sfnt->max_instructions)
4272 sfnt->max_instructions = ins_len;
4274 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
4275 glyph->ins_len = ins_len;
4277 TA_free_hints_records(hints_records, num_hints_records);
4278 free(recorder.wrap_around_segments);
4280 return FT_Err_Ok;
4282 Err:
4283 TA_free_hints_records(hints_records, num_hints_records);
4284 free(recorder.wrap_around_segments);
4285 free(ins_buf);
4287 return error;
4291 FT_Error
4292 TA_sfnt_build_glyf_hints(SFNT* sfnt,
4293 FONT* font)
4295 FT_Face face = sfnt->face;
4296 FT_Long idx;
4297 FT_Error error;
4300 for (idx = 0; idx < face->num_glyphs; idx++)
4302 error = TA_sfnt_build_glyph_instructions(sfnt, font, idx);
4303 if (error)
4304 return error;
4307 return FT_Err_Ok;
4310 /* end of tabytecode.c */