Add bytecode for SERIF_LINK1 action.
[ttfautohint.git] / src / tabytecode.c
blob17066e86a56b3a3fdeb924b320d3793cd9cdfdbd
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 if necessary 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 if necessary to stay within an 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 if necessary 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,
2691 * bci_action_serif_anchor
2693 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2694 * anchor.
2696 * in: edge_point (in twilight zone)
2697 * ... stuff for bci_align_segments (edge) ...
2700 unsigned char FPGM(bci_action_serif_anchor) [] = {
2702 PUSHB_1,
2703 bci_action_serif_anchor,
2704 FDEF,
2706 PUSHB_1,
2708 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2710 DUP,
2711 PUSHB_1,
2712 sal_num_segments,
2714 ADD, /* s: edge edge_orig */
2716 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
2717 DUP,
2718 DUP,
2719 ALIGNRP, /* align `edge' with `edge_orig' */
2720 MDAP_round, /* round `edge' */
2722 MDAP_noround, /* set rp0 and rp1 to `edge' */
2724 PUSHB_2,
2725 bci_align_segments,
2727 SZP1, /* set zp1 to normal zone 1 */
2728 CALL,
2730 ENDF,
2736 * bci_action_serif_anchor_lower_bound
2738 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2739 * anchor, then moving it again if necessary to stay within a lower
2740 * bound.
2742 * in: edge_point (in twilight zone)
2743 * edge[-1] (in twilight zone)
2744 * ... stuff for bci_align_segments (edge) ...
2747 unsigned char FPGM(bci_action_serif_anchor_lower_bound) [] = {
2749 PUSHB_1,
2750 bci_action_serif_anchor_lower_bound,
2751 FDEF,
2753 PUSHB_1,
2755 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2757 DUP,
2758 PUSHB_1,
2759 sal_num_segments,
2761 ADD, /* s: edge[-1] edge edge_orig */
2763 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
2764 DUP,
2765 DUP,
2766 ALIGNRP, /* align `edge' with `edge_orig' */
2767 MDAP_round, /* round `edge' */
2769 SWAP, /* s: edge edge[-1] */
2770 DUP,
2771 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2772 GC_cur,
2773 PUSHB_1,
2775 CINDEX,
2776 GC_cur, /* s: edge edge[-1]_pos edge_pos */
2777 GT, /* edge_pos < edge[-1]_pos */
2779 DUP,
2780 ALIGNRP, /* align `edge' to `edge[-1]' */
2781 EIF,
2783 MDAP_noround, /* set rp0 and rp1 to `edge' */
2785 PUSHB_2,
2786 bci_align_segments,
2788 SZP1, /* set zp1 to normal zone 1 */
2789 CALL,
2791 ENDF,
2797 * bci_action_serif_anchor_upper_bound
2799 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2800 * anchor, then moving it again if necessary to stay within an upper
2801 * bound.
2803 * in: edge_point (in twilight zone)
2804 * edge[1] (in twilight zone)
2805 * ... stuff for bci_align_segments (edge) ...
2808 unsigned char FPGM(bci_action_serif_anchor_upper_bound) [] = {
2810 PUSHB_1,
2811 bci_action_serif_anchor_upper_bound,
2812 FDEF,
2814 PUSHB_1,
2816 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2818 DUP,
2819 PUSHB_1,
2820 sal_num_segments,
2822 ADD, /* s: edge[1] edge edge_orig */
2824 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
2825 DUP,
2826 DUP,
2827 ALIGNRP, /* align `edge' with `edge_orig' */
2828 MDAP_round, /* round `edge' */
2830 SWAP, /* s: edge edge[1] */
2831 DUP,
2832 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2833 GC_cur,
2834 PUSHB_1,
2836 CINDEX,
2837 GC_cur, /* s: edge edge[1]_pos edge_pos */
2838 LT, /* edge_pos > edge[1]_pos */
2840 DUP,
2841 ALIGNRP, /* align `edge' to `edge[1]' */
2842 EIF,
2844 MDAP_noround, /* set rp0 and rp1 to `edge' */
2846 PUSHB_2,
2847 bci_align_segments,
2849 SZP1, /* set zp1 to normal zone 1 */
2850 CALL,
2852 ENDF,
2858 * bci_action_serif_anchor_lower_upper_bound
2860 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2861 * anchor, then moving it again if necessary to stay within a lower and
2862 * upper bound.
2864 * in: edge_point (in twilight zone)
2865 * edge[-1] (in twilight zone)
2866 * edge[1] (in twilight zone)
2867 * ... stuff for bci_align_segments (edge) ...
2870 unsigned char FPGM(bci_action_serif_anchor_lower_upper_bound) [] = {
2872 PUSHB_1,
2873 bci_action_serif_anchor_lower_upper_bound,
2874 FDEF,
2876 PUSHB_1,
2878 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2880 DUP,
2881 PUSHB_1,
2882 sal_num_segments,
2884 ADD, /* s: edge[1] edge[-1] edge edge_orig */
2886 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
2887 DUP,
2888 DUP,
2889 ALIGNRP, /* align `edge' with `edge_orig' */
2890 MDAP_round, /* round `edge' */
2892 SWAP, /* s: edge[1] edge edge[-1] */
2893 DUP,
2894 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2895 GC_cur,
2896 PUSHB_1,
2898 CINDEX,
2899 GC_cur, /* s: edge[1] edge edge[-1]_pos edge_pos */
2900 GT, /* edge_pos < edge[-1]_pos */
2902 DUP,
2903 ALIGNRP, /* align `edge' to `edge[-1]' */
2904 EIF,
2906 SWAP, /* s: edge edge[1] */
2907 DUP,
2908 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2909 GC_cur,
2910 PUSHB_1,
2912 CINDEX,
2913 GC_cur, /* s: edge edge[1]_pos edge_pos */
2914 LT, /* edge_pos > edge[1]_pos */
2916 DUP,
2917 ALIGNRP, /* align `edge' to `edge[1]' */
2918 EIF,
2920 MDAP_noround, /* set rp0 and rp1 to `edge' */
2922 PUSHB_2,
2923 bci_align_segments,
2925 SZP1, /* set zp1 to normal zone 1 */
2926 CALL,
2928 ENDF,
2934 * bci_action_serif_link1
2936 * Handle the SERIF_LINK1 action to align a serif, depending on edges
2937 * before and after.
2939 * in: before_point (in twilight zone)
2940 * edge_point (in twilight zone)
2941 * after_point (in twilight zone)
2942 * ... stuff for bci_align_segments (edge) ...
2945 unsigned char FPGM(bci_action_serif_link1) [] = {
2947 PUSHB_1,
2948 bci_action_serif_link1,
2949 FDEF,
2951 PUSHB_1,
2953 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2955 PUSHB_1,
2957 CINDEX,
2958 PUSHB_1,
2959 sal_num_segments,
2961 ADD, /* s: after edge before after_orig */
2962 PUSHB_1,
2964 CINDEX,
2965 PUSHB_1,
2966 sal_num_segments,
2968 ADD, /* s: after edge before after_orig before_orig */
2969 MD_cur,
2970 PUSHB_1,
2972 EQ, /* after_orig_pos == before_orig_pos */
2973 IF, /* s: after edge before */
2974 MDAP_noround, /* set rp0 and rp1 to `before' */
2975 DUP,
2976 ALIGNRP, /* align `edge' with `before' */
2977 SWAP,
2978 POP,
2980 ELSE,
2981 PUSHB_1,
2983 CINDEX,
2984 PUSHB_1,
2985 sal_num_segments,
2987 ADD, /* s: ... after edge before edge_orig */
2988 PUSHB_1,
2990 CINDEX,
2991 PUSHB_1,
2992 sal_num_segments,
2994 ADD, /* s: ... after edge before edge_orig before_orig */
2995 MD_cur, /* a = edge_orig_pos - before_orig_pos */
2996 PUSHW_1,
2997 0x10, /* 64*64 */
2998 0x00,
2999 MUL,
3001 PUSHB_1,
3003 CINDEX, /* s: ... after edge before a*64 after */
3004 PUSHB_1,
3006 CINDEX, /* s: ... after edge before a*64 after before */
3007 MD_cur, /* b = after_pos - before_pos */
3008 MUL, /* s: ... after edge before a*b */
3010 PUSHB_1,
3012 CINDEX,
3013 PUSHB_1,
3014 sal_num_segments,
3016 ADD, /* s: ... after edge before a*b after_orig */
3017 PUSHB_1,
3019 CINDEX,
3020 PUSHB_1,
3021 sal_num_segments,
3023 ADD, /* s: ... after edge before a*b after_orig before_orig */
3024 MD_cur, /* c = after_orig_pos - before_orig_pos */
3025 PUSHW_1,
3026 0x10, /* 64*64 */
3027 0x00,
3028 MUL,
3030 DIV, /* s: after edge before a*b/c */
3032 SWAP,
3033 MDAP_noround, /* set rp0 and rp1 to `before' */
3034 SWAP, /* s: after a*b/c edge */
3035 DUP,
3036 DUP,
3037 ALIGNRP, /* align `edge' with `before' */
3038 ROLL,
3039 SHPIX, /* shift `edge' by `a*b/c' */
3041 SWAP, /* s: edge after */
3042 POP,
3043 EIF,
3045 MDAP_noround, /* set rp0 and rp1 to `edge' */
3047 PUSHB_2,
3048 bci_align_segments,
3050 SZP1, /* set zp1 to normal zone 1 */
3051 CALL,
3053 ENDF,
3059 * bci_action_serif_link1_lower_bound
3061 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3062 * before and after. Additionally, move the serif again if necessary to
3063 * stay within a lower bound.
3065 * in: before_point (in twilight zone)
3066 * edge_point (in twilight zone)
3067 * after_point (in twilight zone)
3068 * edge[-1] (in twilight zone)
3069 * ... stuff for bci_align_segments (edge) ...
3072 unsigned char FPGM(bci_action_serif_link1_lower_bound) [] = {
3074 PUSHB_1,
3075 bci_action_serif_link1_lower_bound,
3076 FDEF,
3078 PUSHB_1,
3080 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3082 PUSHB_1,
3084 CINDEX,
3085 PUSHB_1,
3086 sal_num_segments,
3088 ADD, /* s: edge[-1] after edge before after_orig */
3089 PUSHB_1,
3091 CINDEX,
3092 PUSHB_1,
3093 sal_num_segments,
3095 ADD, /* s: edge[-1] after edge before after_orig before_orig */
3096 MD_cur,
3097 PUSHB_1,
3099 EQ, /* after_orig_pos == before_orig_pos */
3100 IF, /* s: edge[-1] after edge before */
3101 MDAP_noround, /* set rp0 and rp1 to `before' */
3102 DUP,
3103 ALIGNRP, /* align `edge' with `before' */
3104 SWAP,
3105 POP,
3107 ELSE,
3108 PUSHB_1,
3110 CINDEX,
3111 PUSHB_1,
3112 sal_num_segments,
3114 ADD, /* s: ... after edge before edge_orig */
3115 PUSHB_1,
3117 CINDEX,
3118 PUSHB_1,
3119 sal_num_segments,
3121 ADD, /* s: ... after edge before edge_orig before_orig */
3122 MD_cur, /* a = edge_orig_pos - before_orig_pos */
3123 PUSHW_1,
3124 0x10, /* 64*64 */
3125 0x00,
3126 MUL,
3128 PUSHB_1,
3130 CINDEX, /* s: ... after edge before a*64 after */
3131 PUSHB_1,
3133 CINDEX, /* s: ... after edge before a*64 after before */
3134 MD_cur, /* b = after_pos - before_pos */
3135 MUL, /* s: ... after edge before a*b */
3137 PUSHB_1,
3139 CINDEX,
3140 PUSHB_1,
3141 sal_num_segments,
3143 ADD, /* s: ... after edge before a*b after_orig */
3144 PUSHB_1,
3146 CINDEX,
3147 PUSHB_1,
3148 sal_num_segments,
3150 ADD, /* s: ... after edge before a*b after_orig before_orig */
3151 MD_cur, /* c = after_orig_pos - before_orig_pos */
3152 PUSHW_1,
3153 0x10, /* 64*64 */
3154 0x00,
3155 MUL,
3157 DIV, /* s: edge[-1] after edge before a*b/c */
3159 SWAP,
3160 MDAP_noround, /* set rp0 and rp1 to `before' */
3161 SWAP, /* s: edge[-1] after a*b/c edge */
3162 DUP,
3163 DUP,
3164 ALIGNRP, /* align `edge' with `before' */
3165 ROLL,
3166 SHPIX, /* shift `edge' by `a*b/c' */
3168 SWAP, /* s: edge[-1] edge after */
3169 POP,
3170 EIF,
3172 SWAP, /* s: edge edge[-1] */
3173 DUP,
3174 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3175 GC_cur,
3176 PUSHB_1,
3178 CINDEX,
3179 GC_cur, /* s: edge edge[-1]_pos edge_pos */
3180 GT, /* edge_pos < edge[-1]_pos */
3182 DUP,
3183 ALIGNRP, /* align `edge' to `edge[-1]' */
3184 EIF,
3186 MDAP_noround, /* set rp0 and rp1 to `edge' */
3188 PUSHB_2,
3189 bci_align_segments,
3191 SZP1, /* set zp1 to normal zone 1 */
3192 CALL,
3193 ENDF,
3199 * bci_action_serif_link1_upper_bound
3201 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3202 * before and after. Additionally, move the serif again if necessary to
3203 * stay within an upper bound.
3205 * in: before_point (in twilight zone)
3206 * edge_point (in twilight zone)
3207 * after_point (in twilight zone)
3208 * edge[1] (in twilight zone)
3209 * ... stuff for bci_align_segments (edge) ...
3212 unsigned char FPGM(bci_action_serif_link1_upper_bound) [] = {
3214 PUSHB_1,
3215 bci_action_serif_link1_upper_bound,
3216 FDEF,
3218 PUSHB_1,
3220 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3222 PUSHB_1,
3224 CINDEX,
3225 PUSHB_1,
3226 sal_num_segments,
3228 ADD, /* s: edge[1] after edge before after_orig */
3229 PUSHB_1,
3231 CINDEX,
3232 PUSHB_1,
3233 sal_num_segments,
3235 ADD, /* s: edge[1] after edge before after_orig before_orig */
3236 MD_cur,
3237 PUSHB_1,
3239 EQ, /* after_orig_pos == before_orig_pos */
3240 IF, /* s: edge[1] after edge before */
3241 MDAP_noround, /* set rp0 and rp1 to `before' */
3242 DUP,
3243 ALIGNRP, /* align `edge' with `before' */
3244 SWAP,
3245 POP,
3247 ELSE,
3248 PUSHB_1,
3250 CINDEX,
3251 PUSHB_1,
3252 sal_num_segments,
3254 ADD, /* s: ... after edge before edge_orig */
3255 PUSHB_1,
3257 CINDEX,
3258 PUSHB_1,
3259 sal_num_segments,
3261 ADD, /* s: ... after edge before edge_orig before_orig */
3262 MD_cur, /* a = edge_orig_pos - before_orig_pos */
3263 PUSHW_1,
3264 0x10, /* 64*64 */
3265 0x00,
3266 MUL,
3268 PUSHB_1,
3270 CINDEX, /* s: ... after edge before a*64 after */
3271 PUSHB_1,
3273 CINDEX, /* s: ... after edge before a*64 after before */
3274 MD_cur, /* b = after_pos - before_pos */
3275 MUL, /* s: ... after edge before a*b */
3277 PUSHB_1,
3279 CINDEX,
3280 PUSHB_1,
3281 sal_num_segments,
3283 ADD, /* s: ... after edge before a*b after_orig */
3284 PUSHB_1,
3286 CINDEX,
3287 PUSHB_1,
3288 sal_num_segments,
3290 ADD, /* s: ... after edge before a*b after_orig before_orig */
3291 MD_cur, /* c = after_orig_pos - before_orig_pos */
3292 PUSHW_1,
3293 0x10, /* 64*64 */
3294 0x00,
3295 MUL,
3297 DIV, /* s: edge[1] after edge before a*b/c */
3299 SWAP,
3300 MDAP_noround, /* set rp0 and rp1 to `before' */
3301 SWAP, /* s: edge[1] after a*b/c edge */
3302 DUP,
3303 DUP,
3304 ALIGNRP, /* align `edge' with `before' */
3305 ROLL,
3306 SHPIX, /* shift `edge' by `a*b/c' */
3308 SWAP, /* s: edge[1] edge after */
3309 POP,
3310 EIF,
3312 SWAP, /* s: edge edge[1] */
3313 DUP,
3314 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3315 GC_cur,
3316 PUSHB_1,
3318 CINDEX,
3319 GC_cur, /* s: edge edge[1]_pos edge_pos */
3320 LT, /* edge_pos > edge[1]_pos */
3322 DUP,
3323 ALIGNRP, /* align `edge' to `edge[1]' */
3324 EIF,
3326 MDAP_noround, /* set rp0 and rp1 to `edge' */
3328 PUSHB_2,
3329 bci_align_segments,
3331 SZP1, /* set zp1 to normal zone 1 */
3332 CALL,
3334 ENDF,
3340 * bci_action_serif_link1_lower_upper_bound
3342 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3343 * before and after. Additionally, move the serif again if necessary to
3344 * stay within a lower and upper bound.
3346 * in: before_point (in twilight zone)
3347 * edge_point (in twilight zone)
3348 * after_point (in twilight zone)
3349 * edge[-1] (in twilight zone)
3350 * edge[1] (in twilight zone)
3351 * ... stuff for bci_align_segments (edge) ...
3354 unsigned char FPGM(bci_action_serif_link1_lower_upper_bound) [] = {
3356 PUSHB_1,
3357 bci_action_serif_link1_lower_upper_bound,
3358 FDEF,
3360 PUSHB_1,
3362 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3364 PUSHB_1,
3366 CINDEX,
3367 PUSHB_1,
3368 sal_num_segments,
3370 ADD, /* s: edge[1] edge[-1] after edge before after_orig */
3371 PUSHB_1,
3373 CINDEX,
3374 PUSHB_1,
3375 sal_num_segments,
3377 ADD, /* s: edge[1] edge[-1] after edge before after_orig before_orig */
3378 MD_cur,
3379 PUSHB_1,
3381 EQ, /* after_orig_pos == before_orig_pos */
3382 IF, /* s: edge[1] edge[-1] after edge before */
3383 MDAP_noround, /* set rp0 and rp1 to `before' */
3384 DUP,
3385 ALIGNRP, /* align `edge' with `before' */
3386 SWAP,
3387 POP,
3389 ELSE,
3390 PUSHB_1,
3392 CINDEX,
3393 PUSHB_1,
3394 sal_num_segments,
3396 ADD, /* s: ... after edge before edge_orig */
3397 PUSHB_1,
3399 CINDEX,
3400 PUSHB_1,
3401 sal_num_segments,
3403 ADD, /* s: ... after edge before edge_orig before_orig */
3404 MD_cur, /* a = edge_orig_pos - before_orig_pos */
3405 PUSHW_1,
3406 0x10, /* 64*64 */
3407 0x00,
3408 MUL,
3410 PUSHB_1,
3412 CINDEX, /* s: ... after edge before a*64 after */
3413 PUSHB_1,
3415 CINDEX, /* s: ... after edge before a*64 after before */
3416 MD_cur, /* b = after_pos - before_pos */
3417 MUL, /* s: ... after edge before a*b */
3419 PUSHB_1,
3421 CINDEX,
3422 PUSHB_1,
3423 sal_num_segments,
3425 ADD, /* s: ... after edge before a*b after_orig */
3426 PUSHB_1,
3428 CINDEX,
3429 PUSHB_1,
3430 sal_num_segments,
3432 ADD, /* s: ... after edge before a*b after_orig before_orig */
3433 MD_cur, /* c = after_orig_pos - before_orig_pos */
3434 PUSHW_1,
3435 0x10, /* 64*64 */
3436 0x00,
3437 MUL,
3439 DIV, /* s: edge[1] edge[-1] after edge before a*b/c */
3441 SWAP,
3442 MDAP_noround, /* set rp0 and rp1 to `before' */
3443 SWAP, /* s: edge[1] edge[-1] after a*b/c edge */
3444 DUP,
3445 DUP,
3446 ALIGNRP, /* align `edge' with `before' */
3447 ROLL,
3448 SHPIX, /* shift `edge' by `a*b/c' */
3450 SWAP, /* s: edge[1] edge[-1] edge after */
3451 POP,
3452 EIF,
3454 SWAP, /* s: edge[1] edge edge[-1] */
3455 DUP,
3456 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3457 GC_cur,
3458 PUSHB_1,
3460 CINDEX,
3461 GC_cur, /* s: edge[1] edge edge[-1]_pos edge_pos */
3462 GT, /* edge_pos < edge[-1]_pos */
3464 DUP,
3465 ALIGNRP, /* align `edge' to `edge[-1]' */
3466 EIF,
3468 SWAP, /* s: edge edge[1] */
3469 DUP,
3470 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3471 GC_cur,
3472 PUSHB_1,
3474 CINDEX,
3475 GC_cur, /* s: edge edge[1]_pos edge_pos */
3476 LT, /* edge_pos > edge[1]_pos */
3478 DUP,
3479 ALIGNRP, /* align `edge' to `edge[1]' */
3480 EIF,
3482 MDAP_noround, /* set rp0 and rp1 to `edge' */
3484 PUSHB_2,
3485 bci_align_segments,
3487 SZP1, /* set zp1 to normal zone 1 */
3488 CALL,
3490 ENDF,
3494 unsigned char FPGM(bci_action_serif_link2) [] = {
3496 PUSHB_1,
3497 bci_action_serif_link2,
3498 FDEF,
3500 PUSHB_1,
3501 bci_handle_segments,
3502 CALL,
3504 /* XXX */
3506 ENDF,
3510 unsigned char FPGM(bci_action_serif_link2_lower_bound) [] = {
3512 PUSHB_1,
3513 bci_action_serif_link2_lower_bound,
3514 FDEF,
3516 PUSHB_1,
3517 bci_handle_segments,
3518 CALL,
3520 /* XXX */
3522 ENDF,
3526 unsigned char FPGM(bci_action_serif_link2_upper_bound) [] = {
3528 PUSHB_1,
3529 bci_action_serif_link2_upper_bound,
3530 FDEF,
3532 PUSHB_1,
3533 bci_handle_segments,
3534 CALL,
3536 /* XXX */
3538 ENDF,
3542 unsigned char FPGM(bci_action_serif_link2_lower_upper_bound) [] = {
3544 PUSHB_1,
3545 bci_action_serif_link2_lower_upper_bound,
3546 FDEF,
3548 PUSHB_1,
3549 bci_handle_segments,
3550 CALL,
3552 /* XXX */
3554 ENDF,
3560 * bci_handle_action
3562 * Execute function.
3564 * in: function_index
3567 unsigned char FPGM(bci_handle_action) [] = {
3569 PUSHB_1,
3570 bci_handle_action,
3571 FDEF,
3573 CALL,
3575 ENDF,
3581 * bci_hint_glyph
3583 * This is the top-level glyph hinting function
3584 * which parses the arguments on the stack and calls subroutines.
3586 * in: num_actions (M)
3587 * action_0_func_idx
3588 * ... data ...
3589 * action_1_func_idx
3590 * ... data ...
3591 * ...
3592 * action_M_func_idx
3593 * ... data ...
3595 * uses: bci_handle_action
3596 * bci_action_adjust_bound
3597 * bci_action_stem_bound
3599 * bci_action_link
3600 * bci_action_anchor
3601 * bci_action_blue_anchor
3602 * bci_action_adjust
3603 * bci_action_stem
3605 * bci_action_blue
3606 * bci_action_serif
3607 * bci_action_serif_anchor
3608 * bci_action_serif_link1
3609 * bci_action_serif_link2
3612 unsigned char FPGM(bci_hint_glyph) [] = {
3614 PUSHB_1,
3615 bci_hint_glyph,
3616 FDEF,
3618 PUSHB_1,
3619 bci_handle_action,
3620 LOOPCALL,
3622 ENDF,
3627 #define COPY_FPGM(func_name) \
3628 memcpy(buf_p, fpgm_ ## func_name, \
3629 sizeof (fpgm_ ## func_name)); \
3630 buf_p += sizeof (fpgm_ ## func_name) \
3632 static FT_Error
3633 TA_table_build_fpgm(FT_Byte** fpgm,
3634 FT_ULong* fpgm_len,
3635 FONT* font)
3637 FT_UInt buf_len;
3638 FT_UInt len;
3639 FT_Byte* buf;
3640 FT_Byte* buf_p;
3643 buf_len = sizeof (FPGM(bci_round))
3644 + sizeof (FPGM(bci_compute_stem_width_a))
3646 + sizeof (FPGM(bci_compute_stem_width_b))
3648 + sizeof (FPGM(bci_compute_stem_width_c))
3649 + sizeof (FPGM(bci_loop))
3650 + sizeof (FPGM(bci_cvt_rescale))
3651 + sizeof (FPGM(bci_blue_round_a))
3653 + sizeof (FPGM(bci_blue_round_b))
3654 + sizeof (FPGM(bci_get_point_extrema))
3655 + sizeof (FPGM(bci_create_segment))
3656 + sizeof (FPGM(bci_create_segments))
3657 + sizeof (FPGM(bci_handle_segment))
3658 + sizeof (FPGM(bci_align_segment))
3659 + sizeof (FPGM(bci_handle_segments))
3660 + sizeof (FPGM(bci_align_segments))
3661 + sizeof (FPGM(bci_action_adjust_bound))
3662 + sizeof (FPGM(bci_action_stem_bound))
3663 + sizeof (FPGM(bci_action_link))
3664 + sizeof (FPGM(bci_action_anchor))
3665 + sizeof (FPGM(bci_action_blue_anchor))
3666 + sizeof (FPGM(bci_action_adjust))
3667 + sizeof (FPGM(bci_action_stem))
3668 + sizeof (FPGM(bci_action_blue))
3669 + sizeof (FPGM(bci_action_serif))
3670 + sizeof (FPGM(bci_action_serif_lower_bound))
3671 + sizeof (FPGM(bci_action_serif_upper_bound))
3672 + sizeof (FPGM(bci_action_serif_lower_upper_bound))
3673 + sizeof (FPGM(bci_action_serif_anchor))
3674 + sizeof (FPGM(bci_action_serif_anchor_lower_bound))
3675 + sizeof (FPGM(bci_action_serif_anchor_upper_bound))
3676 + sizeof (FPGM(bci_action_serif_anchor_lower_upper_bound))
3677 + sizeof (FPGM(bci_action_serif_link1))
3678 + sizeof (FPGM(bci_action_serif_link1_lower_bound))
3679 + sizeof (FPGM(bci_action_serif_link1_upper_bound))
3680 + sizeof (FPGM(bci_action_serif_link1_lower_upper_bound))
3681 + sizeof (FPGM(bci_action_serif_link2))
3682 + sizeof (FPGM(bci_action_serif_link2_lower_bound))
3683 + sizeof (FPGM(bci_action_serif_link2_upper_bound))
3684 + sizeof (FPGM(bci_action_serif_link2_lower_upper_bound))
3685 + sizeof (FPGM(bci_handle_action))
3686 + sizeof (FPGM(bci_hint_glyph));
3687 /* buffer length must be a multiple of four */
3688 len = (buf_len + 3) & ~3;
3689 buf = (FT_Byte*)malloc(len);
3690 if (!buf)
3691 return FT_Err_Out_Of_Memory;
3693 /* pad end of buffer with zeros */
3694 buf[len - 1] = 0x00;
3695 buf[len - 2] = 0x00;
3696 buf[len - 3] = 0x00;
3698 /* copy font program into buffer and fill in the missing variables */
3699 buf_p = buf;
3701 COPY_FPGM(bci_round);
3702 COPY_FPGM(bci_compute_stem_width_a);
3703 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3704 COPY_FPGM(bci_compute_stem_width_b);
3705 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3706 COPY_FPGM(bci_compute_stem_width_c);
3707 COPY_FPGM(bci_loop);
3708 COPY_FPGM(bci_cvt_rescale);
3709 COPY_FPGM(bci_blue_round_a);
3710 *(buf_p++) = (unsigned char)CVT_BLUES_SIZE(font);
3711 COPY_FPGM(bci_blue_round_b);
3712 COPY_FPGM(bci_get_point_extrema);
3713 COPY_FPGM(bci_create_segment);
3714 COPY_FPGM(bci_create_segments);
3715 COPY_FPGM(bci_handle_segment);
3716 COPY_FPGM(bci_align_segment);
3717 COPY_FPGM(bci_handle_segments);
3718 COPY_FPGM(bci_align_segments);
3719 COPY_FPGM(bci_action_adjust_bound);
3720 COPY_FPGM(bci_action_stem_bound);
3721 COPY_FPGM(bci_action_link);
3722 COPY_FPGM(bci_action_anchor);
3723 COPY_FPGM(bci_action_blue_anchor);
3724 COPY_FPGM(bci_action_adjust);
3725 COPY_FPGM(bci_action_stem);
3726 COPY_FPGM(bci_action_blue);
3727 COPY_FPGM(bci_action_serif);
3728 COPY_FPGM(bci_action_serif_lower_bound);
3729 COPY_FPGM(bci_action_serif_upper_bound);
3730 COPY_FPGM(bci_action_serif_lower_upper_bound);
3731 COPY_FPGM(bci_action_serif_anchor);
3732 COPY_FPGM(bci_action_serif_anchor_lower_bound);
3733 COPY_FPGM(bci_action_serif_anchor_upper_bound);
3734 COPY_FPGM(bci_action_serif_anchor_lower_upper_bound);
3735 COPY_FPGM(bci_action_serif_link1);
3736 COPY_FPGM(bci_action_serif_link1_lower_bound);
3737 COPY_FPGM(bci_action_serif_link1_upper_bound);
3738 COPY_FPGM(bci_action_serif_link1_lower_upper_bound);
3739 COPY_FPGM(bci_action_serif_link2);
3740 COPY_FPGM(bci_action_serif_link2_lower_bound);
3741 COPY_FPGM(bci_action_serif_link2_upper_bound);
3742 COPY_FPGM(bci_action_serif_link2_lower_upper_bound);
3743 COPY_FPGM(bci_handle_action);
3744 COPY_FPGM(bci_hint_glyph);
3746 *fpgm = buf;
3747 *fpgm_len = buf_len;
3749 return FT_Err_Ok;
3753 FT_Error
3754 TA_sfnt_build_fpgm_table(SFNT* sfnt,
3755 FONT* font)
3757 FT_Error error;
3759 FT_Byte* fpgm_buf;
3760 FT_ULong fpgm_len;
3763 error = TA_sfnt_add_table_info(sfnt);
3764 if (error)
3765 return error;
3767 error = TA_table_build_fpgm(&fpgm_buf, &fpgm_len, font);
3768 if (error)
3769 return error;
3771 /* in case of success, `fpgm_buf' gets linked */
3772 /* and is eventually freed in `TA_font_unload' */
3773 error = TA_font_add_table(font,
3774 &sfnt->table_infos[sfnt->num_table_infos - 1],
3775 TTAG_fpgm, fpgm_len, fpgm_buf);
3776 if (error)
3778 free(fpgm_buf);
3779 return error;
3782 return FT_Err_Ok;
3786 /* the `prep' instructions */
3788 #define PREP(snippet_name) prep_ ## snippet_name
3790 /* we often need 0x10000 which can't be pushed directly onto the stack, */
3791 /* thus we provide it in the storage area */
3793 unsigned char PREP(store_0x10000) [] = {
3795 PUSHB_1,
3796 sal_0x10000,
3797 PUSHW_2,
3798 0x08, /* 0x800 */
3799 0x00,
3800 0x08, /* 0x800 */
3801 0x00,
3802 MUL, /* 0x10000 */
3807 unsigned char PREP(align_top_a) [] = {
3809 /* optimize the alignment of the top of small letters to the pixel grid */
3811 PUSHB_1,
3815 /* %c, index of alignment blue zone */
3817 unsigned char PREP(align_top_b) [] = {
3819 RCVT,
3820 DUP,
3821 DUP,
3822 PUSHB_1,
3824 ADD,
3825 FLOOR, /* fitted = FLOOR(scaled + 40) */
3826 DUP, /* s: scaled scaled fitted fitted */
3827 ROLL,
3828 NEQ,
3829 IF, /* s: scaled fitted */
3830 PUSHB_1,
3831 sal_0x10000,
3833 MUL, /* scaled in 16.16 format */
3834 SWAP,
3835 DIV, /* (fitted / scaled) in 16.16 format */
3837 PUSHB_1,
3838 sal_scale,
3839 SWAP,
3844 unsigned char PREP(loop_cvt_a) [] = {
3846 /* loop over vertical CVT entries */
3847 PUSHB_4,
3851 /* %c, first vertical index */
3852 /* %c, last vertical index */
3854 unsigned char PREP(loop_cvt_b) [] = {
3856 bci_cvt_rescale,
3857 bci_loop,
3858 CALL,
3860 /* loop over blue refs */
3861 PUSHB_4,
3865 /* %c, first blue ref index */
3866 /* %c, last blue ref index */
3868 unsigned char PREP(loop_cvt_c) [] = {
3870 bci_cvt_rescale,
3871 bci_loop,
3872 CALL,
3874 /* loop over blue shoots */
3875 PUSHB_4,
3879 /* %c, first blue shoot index */
3880 /* %c, last blue shoot index */
3882 unsigned char PREP(loop_cvt_d) [] = {
3884 bci_cvt_rescale,
3885 bci_loop,
3886 CALL,
3887 EIF,
3891 unsigned char PREP(compute_extra_light_a) [] = {
3893 /* compute (vertical) `extra_light' flag */
3894 PUSHB_3,
3895 sal_is_extra_light,
3900 /* %c, index of vertical standard_width */
3902 unsigned char PREP(compute_extra_light_b) [] = {
3904 RCVT,
3905 GT, /* standard_width < 40 */
3910 unsigned char PREP(round_blues_a) [] = {
3912 /* use discrete values for blue zone widths */
3913 PUSHB_4,
3917 /* %c, first blue ref index */
3918 /* %c, last blue ref index */
3920 unsigned char PREP(round_blues_b) [] = {
3922 bci_blue_round,
3923 bci_loop,
3924 CALL
3928 /* XXX talatin.c: 1671 */
3929 /* XXX talatin.c: 1708 */
3930 /* XXX talatin.c: 2182 */
3933 #define COPY_PREP(snippet_name) \
3934 memcpy(buf_p, prep_ ## snippet_name, \
3935 sizeof (prep_ ## snippet_name)); \
3936 buf_p += sizeof (prep_ ## snippet_name);
3938 static FT_Error
3939 TA_table_build_prep(FT_Byte** prep,
3940 FT_ULong* prep_len,
3941 FONT* font)
3943 TA_LatinAxis vaxis;
3944 TA_LatinBlue blue_adjustment;
3945 FT_UInt i;
3947 FT_UInt buf_len;
3948 FT_UInt len;
3949 FT_Byte* buf;
3950 FT_Byte* buf_p;
3953 vaxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[1];
3954 blue_adjustment = NULL;
3956 for (i = 0; i < vaxis->blue_count; i++)
3958 if (vaxis->blues[i].flags & TA_LATIN_BLUE_ADJUSTMENT)
3960 blue_adjustment = &vaxis->blues[i];
3961 break;
3965 buf_len = sizeof (PREP(store_0x10000));
3967 if (blue_adjustment)
3968 buf_len += sizeof (PREP(align_top_a))
3970 + sizeof (PREP(align_top_b))
3971 + sizeof (PREP(loop_cvt_a))
3973 + sizeof (PREP(loop_cvt_b))
3975 + sizeof (PREP(loop_cvt_c))
3977 + sizeof (PREP(loop_cvt_d));
3979 buf_len += sizeof (PREP(compute_extra_light_a))
3981 + sizeof (PREP(compute_extra_light_b));
3983 if (CVT_BLUES_SIZE(font))
3984 buf_len += sizeof (PREP(round_blues_a))
3986 + sizeof (PREP(round_blues_b));
3988 /* buffer length must be a multiple of four */
3989 len = (buf_len + 3) & ~3;
3990 buf = (FT_Byte*)malloc(len);
3991 if (!buf)
3992 return FT_Err_Out_Of_Memory;
3994 /* pad end of buffer with zeros */
3995 buf[len - 1] = 0x00;
3996 buf[len - 2] = 0x00;
3997 buf[len - 3] = 0x00;
3999 /* copy cvt program into buffer and fill in the missing variables */
4000 buf_p = buf;
4002 COPY_PREP(store_0x10000);
4004 if (blue_adjustment)
4006 COPY_PREP(align_top_a);
4007 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
4008 + blue_adjustment - vaxis->blues);
4009 COPY_PREP(align_top_b);
4011 COPY_PREP(loop_cvt_a);
4012 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
4013 *(buf_p++) = (unsigned char)(CVT_VERT_WIDTHS_OFFSET(font)
4014 + CVT_VERT_WIDTHS_SIZE(font) - 1);
4015 COPY_PREP(loop_cvt_b);
4016 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
4017 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
4018 + CVT_BLUES_SIZE(font) - 1);
4019 COPY_PREP(loop_cvt_c);
4020 *(buf_p++) = (unsigned char)CVT_BLUE_SHOOTS_OFFSET(font);
4021 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
4022 + CVT_BLUES_SIZE(font) - 1);
4023 COPY_PREP(loop_cvt_d);
4026 COPY_PREP(compute_extra_light_a);
4027 *(buf_p++) = (unsigned char)CVT_VERT_STANDARD_WIDTH_OFFSET(font);
4028 COPY_PREP(compute_extra_light_b);
4030 if (CVT_BLUES_SIZE(font))
4032 COPY_PREP(round_blues_a);
4033 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
4034 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
4035 + CVT_BLUES_SIZE(font) - 1);
4036 COPY_PREP(round_blues_b);
4039 *prep = buf;
4040 *prep_len = buf_len;
4042 return FT_Err_Ok;
4046 FT_Error
4047 TA_sfnt_build_prep_table(SFNT* sfnt,
4048 FONT* font)
4050 FT_Error error;
4052 FT_Byte* prep_buf;
4053 FT_ULong prep_len;
4056 error = TA_sfnt_add_table_info(sfnt);
4057 if (error)
4058 return error;
4060 error = TA_table_build_prep(&prep_buf, &prep_len, font);
4061 if (error)
4062 return error;
4064 /* in case of success, `prep_buf' gets linked */
4065 /* and is eventually freed in `TA_font_unload' */
4066 error = TA_font_add_table(font,
4067 &sfnt->table_infos[sfnt->num_table_infos - 1],
4068 TTAG_prep, prep_len, prep_buf);
4069 if (error)
4071 free(prep_buf);
4072 return error;
4075 return FT_Err_Ok;
4079 /* we store the segments in the storage area; */
4080 /* each segment record consists of the first and last point */
4082 static FT_Byte*
4083 TA_sfnt_build_glyph_segments(SFNT* sfnt,
4084 Recorder* recorder,
4085 FT_Byte* bufp)
4087 FONT* font = recorder->font;
4088 TA_GlyphHints hints = &font->loader->hints;
4089 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
4090 TA_Point points = hints->points;
4091 TA_Segment segments = axis->segments;
4092 TA_Segment seg;
4093 TA_Segment seg_limit;
4095 FT_Outline outline = font->loader->gloader->base.outline;
4097 FT_UInt* args;
4098 FT_UInt* arg;
4099 FT_UInt num_args;
4100 FT_UInt nargs;
4101 FT_UInt num_segments;
4103 FT_UInt* wrap_around_segment;
4104 FT_UInt num_wrap_around_segments;
4106 FT_Bool need_words = 0;
4108 FT_Int n;
4109 FT_UInt i, j;
4110 FT_UInt num_storage;
4111 FT_UInt num_stack_elements;
4112 FT_UInt num_twilight_points;
4115 seg_limit = segments + axis->num_segments;
4116 num_segments = axis->num_segments;
4118 /* some segments can `wrap around' */
4119 /* a contour's start point like 24-25-26-0-1-2 */
4120 /* (there can be at most one such segment per contour); */
4121 /* we thus append additional records to split them into 24-26 and 0-2 */
4122 wrap_around_segment = recorder->wrap_around_segments;
4123 for (seg = segments; seg < seg_limit; seg++)
4124 if (seg->first > seg->last)
4126 /* the stored data is used later for edge linking */
4127 *(wrap_around_segment++) = seg - segments;
4130 num_wrap_around_segments = wrap_around_segment
4131 - recorder->wrap_around_segments;
4132 num_segments += num_wrap_around_segments;
4134 /* wrap-around segments are pushed with four arguments */
4135 num_args = 2 * num_segments + 2 * num_wrap_around_segments + 2;
4137 /* collect all arguments temporarily in an array (in reverse order) */
4138 /* so that we can easily split into chunks of 255 args */
4139 /* as needed by NPUSHB and NPUSHW, respectively */
4140 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
4141 if (!args)
4142 return NULL;
4144 arg = args + num_args - 1;
4146 if (num_segments > 0xFF)
4147 need_words = 1;
4149 *(arg--) = bci_create_segments;
4150 *(arg--) = num_segments;
4152 for (seg = segments; seg < seg_limit; seg++)
4154 FT_UInt first = seg->first - points;
4155 FT_UInt last = seg->last - points;
4158 *(arg--) = first;
4159 *(arg--) = last;
4161 /* we push the last and first contour point */
4162 /* as a third and fourth argument in wrap-around segments */
4163 if (first > last)
4165 for (n = 0; n < outline.n_contours; n++)
4167 FT_UInt end = (FT_UInt)outline.contours[n];
4170 if (first <= end)
4172 *(arg--) = end;
4173 if (end > 0xFF)
4174 need_words = 1;
4176 if (n == 0)
4177 *(arg--) = 0;
4178 else
4179 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
4180 break;
4185 if (last > 0xFF)
4186 need_words = 1;
4189 /* emit the second part of wrap-around segments as separate segments */
4190 /* so that edges can easily link to them */
4191 for (seg = segments; seg < seg_limit; seg++)
4193 FT_UInt first = seg->first - points;
4194 FT_UInt last = seg->last - points;
4197 if (first > last)
4199 for (n = 0; n < outline.n_contours; n++)
4201 if (first <= (FT_UInt)outline.contours[n])
4203 if (n == 0)
4204 *(arg--) = 0;
4205 else
4206 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
4207 break;
4211 *(arg--) = last;
4214 /* with most fonts it is very rare */
4215 /* that any of the pushed arguments is larger than 0xFF, */
4216 /* thus we refrain from further optimizing this case */
4218 arg = args;
4220 if (need_words)
4222 for (i = 0; i < num_args; i += 255)
4224 nargs = (num_args - i > 255) ? 255 : num_args - i;
4226 BCI(NPUSHW);
4227 BCI(nargs);
4228 for (j = 0; j < nargs; j++)
4230 BCI(HIGH(*arg));
4231 BCI(LOW(*arg));
4232 arg++;
4236 else
4238 for (i = 0; i < num_args; i += 255)
4240 nargs = (num_args - i > 255) ? 255 : num_args - i;
4242 BCI(NPUSHB);
4243 BCI(nargs);
4244 for (j = 0; j < nargs; j++)
4246 BCI(*arg);
4247 arg++;
4252 BCI(CALL);
4254 num_storage = sal_segment_offset + num_segments * 2;
4255 if (num_storage > sfnt->max_storage)
4256 sfnt->max_storage = num_storage;
4258 num_twilight_points = num_segments * 2;
4259 if (num_twilight_points > sfnt->max_twilight_points)
4260 sfnt->max_twilight_points = num_twilight_points;
4262 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
4263 if (num_stack_elements > sfnt->max_stack_elements)
4264 sfnt->max_stack_elements = num_stack_elements;
4266 free(args);
4268 return bufp;
4272 static FT_Bool
4273 TA_hints_record_is_different(Hints_Record* hints_records,
4274 FT_UInt num_hints_records,
4275 FT_Byte* start,
4276 FT_Byte* end)
4278 Hints_Record last_hints_record;
4281 if (!hints_records)
4282 return 1;
4284 /* we only need to compare with the last hints record */
4285 last_hints_record = hints_records[num_hints_records - 1];
4287 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
4288 return 1;
4290 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
4291 return 1;
4293 return 0;
4297 static FT_Error
4298 TA_add_hints_record(Hints_Record** hints_records,
4299 FT_UInt* num_hints_records,
4300 FT_Byte* start,
4301 Hints_Record hints_record)
4303 Hints_Record* hints_records_new;
4304 FT_UInt buf_len;
4305 /* at this point, `hints_record.buf' still points into `ins_buf' */
4306 FT_Byte* end = hints_record.buf;
4309 buf_len = (FT_UInt)(end - start);
4311 /* now fill the structure completely */
4312 hints_record.buf_len = buf_len;
4313 hints_record.buf = (FT_Byte*)malloc(buf_len);
4314 if (!hints_record.buf)
4315 return FT_Err_Out_Of_Memory;
4317 memcpy(hints_record.buf, start, buf_len);
4319 (*num_hints_records)++;
4320 hints_records_new =
4321 (Hints_Record*)realloc(*hints_records, *num_hints_records
4322 * sizeof (Hints_Record));
4323 if (!hints_records_new)
4325 free(hints_record.buf);
4326 (*num_hints_records)--;
4327 return FT_Err_Out_Of_Memory;
4329 else
4330 *hints_records = hints_records_new;
4332 (*hints_records)[*num_hints_records - 1] = hints_record;
4334 return FT_Err_Ok;
4338 static FT_Byte*
4339 TA_sfnt_emit_hints_record(SFNT* sfnt,
4340 Hints_Record* hints_record,
4341 FT_Byte* bufp)
4343 FT_Byte* p;
4344 FT_Byte* endp;
4345 FT_Bool need_words = 0;
4347 FT_UInt i, j;
4348 FT_UInt num_arguments;
4349 FT_UInt num_args;
4350 FT_UInt num_stack_elements;
4353 /* check whether any argument is larger than 0xFF */
4354 endp = hints_record->buf + hints_record->buf_len;
4355 for (p = hints_record->buf; p < endp; p += 2)
4356 if (*p)
4357 need_words = 1;
4359 /* with most fonts it is very rare */
4360 /* that any of the pushed arguments is larger than 0xFF, */
4361 /* thus we refrain from further optimizing this case */
4363 num_arguments = hints_record->buf_len / 2;
4364 p = endp - 2;
4366 if (need_words)
4368 for (i = 0; i < num_arguments; i += 255)
4370 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
4372 BCI(NPUSHW);
4373 BCI(num_args);
4374 for (j = 0; j < num_args; j++)
4376 BCI(*p);
4377 BCI(*(p + 1));
4378 p -= 2;
4382 else
4384 /* we only need the lower bytes */
4385 p++;
4387 for (i = 0; i < num_arguments; i += 255)
4389 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
4391 BCI(NPUSHB);
4392 BCI(num_args);
4393 for (j = 0; j < num_args; j++)
4395 BCI(*p);
4396 p -= 2;
4401 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_arguments;
4402 if (num_stack_elements > sfnt->max_stack_elements)
4403 sfnt->max_stack_elements = sfnt->max_stack_elements;
4405 return bufp;
4409 static FT_Byte*
4410 TA_sfnt_emit_hints_records(SFNT* sfnt,
4411 Hints_Record* hints_records,
4412 FT_UInt num_hints_records,
4413 FT_Byte* bufp)
4415 FT_UInt i;
4416 Hints_Record* hints_record;
4419 hints_record = hints_records;
4421 for (i = 0; i < num_hints_records - 1; i++)
4423 BCI(MPPEM);
4424 if (hints_record->size > 0xFF)
4426 BCI(PUSHW_1);
4427 BCI(HIGH((hints_record + 1)->size));
4428 BCI(LOW((hints_record + 1)->size));
4430 else
4432 BCI(PUSHB_1);
4433 BCI((hints_record + 1)->size);
4435 BCI(LT);
4436 BCI(IF);
4437 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
4438 BCI(ELSE);
4440 hints_record++;
4443 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
4445 for (i = 0; i < num_hints_records - 1; i++)
4446 BCI(EIF);
4448 BCI(PUSHB_1);
4449 BCI(bci_hint_glyph);
4450 BCI(CALL);
4452 return bufp;
4456 static void
4457 TA_free_hints_records(Hints_Record* hints_records,
4458 FT_UInt num_hints_records)
4460 FT_UInt i;
4463 for (i = 0; i < num_hints_records; i++)
4464 free(hints_records[i].buf);
4466 free(hints_records);
4470 static FT_Byte*
4471 TA_hints_recorder_handle_segments(FT_Byte* bufp,
4472 TA_AxisHints axis,
4473 TA_Edge edge,
4474 FT_UInt* wraps)
4476 TA_Segment segments = axis->segments;
4477 TA_Segment seg;
4478 FT_UInt seg_idx;
4479 FT_UInt num_segs = 0;
4480 FT_UInt* wrap;
4483 seg_idx = edge->first - segments;
4485 /* we store everything as 16bit numbers */
4486 *(bufp++) = HIGH(seg_idx);
4487 *(bufp++) = LOW(seg_idx);
4489 /* wrap-around segments are stored as two segments */
4490 if (edge->first->first > edge->first->last)
4491 num_segs++;
4493 seg = edge->first->edge_next;
4494 while (seg != edge->first)
4496 num_segs++;
4498 if (seg->first > seg->last)
4499 num_segs++;
4501 seg = seg->edge_next;
4504 *(bufp++) = HIGH(num_segs);
4505 *(bufp++) = LOW(num_segs);
4507 if (edge->first->first > edge->first->last)
4509 /* emit second part of wrap-around segment; */
4510 /* the bytecode positions such segments after `normal' ones */
4511 wrap = wraps;
4512 for (;;)
4514 if (seg_idx == *wrap)
4515 break;
4516 wrap++;
4519 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
4520 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
4523 seg = edge->first->edge_next;
4524 while (seg != edge->first)
4526 seg_idx = seg - segments;
4528 *(bufp++) = HIGH(seg_idx);
4529 *(bufp++) = LOW(seg_idx);
4531 if (seg->first > seg->last)
4533 wrap = wraps;
4534 for (;;)
4536 if (seg_idx == *wrap)
4537 break;
4538 wrap++;
4541 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
4542 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
4545 seg = seg->edge_next;
4548 return bufp;
4552 static void
4553 TA_hints_recorder(TA_Action action,
4554 TA_GlyphHints hints,
4555 TA_Dimension dim,
4556 TA_Edge arg1,
4557 TA_Edge arg2,
4558 TA_Edge arg3,
4559 TA_Edge lower_bound,
4560 TA_Edge upper_bound)
4562 TA_AxisHints axis = &hints->axis[dim];
4563 TA_Segment segments = axis->segments;
4565 Recorder* recorder = (Recorder*)hints->user;
4566 FONT* font = recorder->font;
4567 FT_UInt* wraps = recorder->wrap_around_segments;
4568 FT_Byte* p = recorder->hints_record.buf;
4570 FT_Byte bound_offset = 0;
4573 if (dim == TA_DIMENSION_HORZ)
4574 return;
4576 /* we ignore the BOUND action since we signal this information */
4577 /* with the `bound_offset' parameter */
4578 if (action == ta_bound)
4579 return;
4581 if (lower_bound)
4582 bound_offset += 1;
4583 if (upper_bound)
4584 bound_offset += 2;
4586 /* this reflects the order in the TA_Action enumeration */
4587 *(p++) = 0;
4588 *(p++) = (FT_Byte)action + bound_offset + ACTION_OFFSET;
4590 switch (action)
4592 case ta_link:
4594 TA_Edge base_edge = arg1;
4595 TA_Edge stem_edge = arg2;
4598 *(p++) = 0;
4599 *(p++) = stem_edge->flags & TA_EDGE_SERIF;
4600 *(p++) = 0;
4601 *(p++) = base_edge->flags & TA_EDGE_ROUND;
4602 *(p++) = HIGH(base_edge->first - segments);
4603 *(p++) = LOW(base_edge->first - segments);
4604 *(p++) = HIGH(stem_edge->first - segments);
4605 *(p++) = LOW(stem_edge->first - segments);
4607 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
4609 break;
4611 case ta_anchor:
4613 TA_Edge edge = arg1;
4614 TA_Edge edge2 = arg2;
4617 *(p++) = 0;
4618 *(p++) = edge2->flags & TA_EDGE_SERIF;
4619 *(p++) = 0;
4620 *(p++) = edge->flags & TA_EDGE_ROUND;
4621 *(p++) = HIGH(edge->first - segments);
4622 *(p++) = LOW(edge->first - segments);
4623 *(p++) = HIGH(edge2->first - segments);
4624 *(p++) = LOW(edge2->first - segments);
4626 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4628 break;
4630 case ta_adjust:
4632 TA_Edge edge = arg1;
4633 TA_Edge edge2 = arg2;
4634 TA_Edge edge_minus_one = lower_bound;
4637 *(p++) = 0;
4638 *(p++) = edge2->flags & TA_EDGE_SERIF;
4639 *(p++) = 0;
4640 *(p++) = edge->flags & TA_EDGE_ROUND;
4641 *(p++) = HIGH(edge->first - segments);
4642 *(p++) = LOW(edge->first - segments);
4643 *(p++) = HIGH(edge2->first - segments);
4644 *(p++) = LOW(edge2->first - segments);
4646 if (edge_minus_one)
4648 *(p++) = HIGH(edge_minus_one->first - segments);
4649 *(p++) = LOW(edge_minus_one->first - segments);
4652 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4654 break;
4656 case ta_blue_anchor:
4658 TA_Edge edge = arg1;
4659 TA_Edge blue = arg2;
4662 *(p++) = HIGH(blue->first - segments);
4663 *(p++) = LOW(blue->first - segments);
4665 if (edge->best_blue_is_shoot)
4667 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4668 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4670 else
4672 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4673 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4676 *(p++) = HIGH(edge->first - segments);
4677 *(p++) = LOW(edge->first - segments);
4679 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4681 break;
4683 case ta_stem:
4685 TA_Edge edge = arg1;
4686 TA_Edge edge2 = arg2;
4687 TA_Edge edge_minus_one = lower_bound;
4690 *(p++) = 0;
4691 *(p++) = edge2->flags & TA_EDGE_SERIF;
4692 *(p++) = 0;
4693 *(p++) = edge->flags & TA_EDGE_ROUND;
4694 *(p++) = HIGH(edge->first - segments);
4695 *(p++) = LOW(edge->first - segments);
4696 *(p++) = HIGH(edge2->first - segments);
4697 *(p++) = LOW(edge2->first - segments);
4699 if (edge_minus_one)
4701 *(p++) = HIGH(edge_minus_one->first - segments);
4702 *(p++) = LOW(edge_minus_one->first - segments);
4705 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4706 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
4708 break;
4710 case ta_blue:
4712 TA_Edge edge = arg1;
4715 if (edge->best_blue_is_shoot)
4717 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4718 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
4720 else
4722 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4723 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
4726 *(p++) = HIGH(edge->first - segments);
4727 *(p++) = LOW(edge->first - segments);
4729 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4731 break;
4733 case ta_serif:
4735 TA_Edge base = arg1->serif;
4736 TA_Edge serif = arg1;
4739 *(p++) = HIGH(serif->first - segments);
4740 *(p++) = LOW(serif->first - segments);
4741 *(p++) = HIGH(base->first - segments);
4742 *(p++) = LOW(base->first - segments);
4744 if (lower_bound)
4746 *(p++) = HIGH(lower_bound->first - segments);
4747 *(p++) = LOW(lower_bound->first - segments);
4749 if (upper_bound)
4751 *(p++) = HIGH(upper_bound->first - segments);
4752 *(p++) = LOW(upper_bound->first - segments);
4755 p = TA_hints_recorder_handle_segments(p, axis, serif, wraps);
4757 break;
4759 case ta_serif_anchor:
4761 TA_Edge edge = arg1;
4764 *(p++) = HIGH(edge->first - segments);
4765 *(p++) = LOW(edge->first - segments);
4767 if (lower_bound)
4769 *(p++) = HIGH(lower_bound->first - segments);
4770 *(p++) = LOW(lower_bound->first - segments);
4772 if (upper_bound)
4774 *(p++) = HIGH(upper_bound->first - segments);
4775 *(p++) = LOW(upper_bound->first - segments);
4778 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4780 break;
4782 case ta_serif_link1:
4784 TA_Edge edge = arg1;
4785 TA_Edge before = arg2;
4786 TA_Edge after = arg3;
4789 *(p++) = HIGH(before->first - segments);
4790 *(p++) = LOW(before->first - segments);
4791 *(p++) = HIGH(edge->first - segments);
4792 *(p++) = LOW(edge->first - segments);
4793 *(p++) = HIGH(after->first - segments);
4794 *(p++) = LOW(after->first - segments);
4796 if (lower_bound)
4798 *(p++) = HIGH(lower_bound->first - segments);
4799 *(p++) = LOW(lower_bound->first - segments);
4801 if (upper_bound)
4803 *(p++) = HIGH(upper_bound->first - segments);
4804 *(p++) = LOW(upper_bound->first - segments);
4807 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
4809 break;
4811 case ta_serif_link2:
4812 p = TA_hints_recorder_handle_segments(p, axis, arg1, wraps);
4813 break;
4815 default:
4816 /* there are more cases in the enumeration */
4817 /* which are handled with the `bound_offset' parameter */
4818 break;
4821 recorder->hints_record.num_actions++;
4822 recorder->hints_record.buf = p;
4826 static FT_Error
4827 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
4828 FONT* font,
4829 FT_Long idx)
4831 FT_Face face = sfnt->face;
4832 FT_Error error;
4834 FT_Byte* ins_buf;
4835 FT_UInt ins_len;
4836 FT_Byte* bufp;
4838 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
4839 glyf_Data* data = (glyf_Data*)glyf_table->data;
4840 GLYPH* glyph = &data->glyphs[idx];
4842 TA_GlyphHints hints;
4844 FT_UInt num_hints_records;
4845 Hints_Record* hints_records;
4847 Recorder recorder;
4849 FT_UInt size;
4852 if (idx < 0)
4853 return FT_Err_Invalid_Argument;
4855 /* computing the segments is resolution independent, */
4856 /* thus the pixel size in this call is arbitrary */
4857 error = FT_Set_Pixel_Sizes(face, 20, 20);
4858 if (error)
4859 return error;
4861 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
4862 error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx, 0);
4863 if (error)
4864 return error;
4866 /* do nothing if we have an empty glyph */
4867 if (!face->glyph->outline.n_contours)
4868 return FT_Err_Ok;
4870 /* do nothing if the dummy hinter has been used */
4871 if (font->loader->metrics->clazz == &ta_dummy_script_class)
4872 return FT_Err_Ok;
4874 hints = &font->loader->hints;
4876 /* we allocate a buffer which is certainly large enough */
4877 /* to hold all of the created bytecode instructions; */
4878 /* later on it gets reallocated to its real size */
4879 ins_len = hints->num_points * 1000;
4880 ins_buf = (FT_Byte*)malloc(ins_len);
4881 if (!ins_buf)
4882 return FT_Err_Out_Of_Memory;
4884 /* initialize array with an invalid bytecode */
4885 /* so that we can easily find the array length at reallocation time */
4886 memset(ins_buf, INS_A0, ins_len);
4888 recorder.font = font;
4889 recorder.wrap_around_segments =
4890 (FT_UInt*)malloc(face->glyph->outline.n_contours * sizeof (FT_UInt));
4892 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf);
4894 /* now we loop over a large range of pixel sizes */
4895 /* to find hints records which get pushed onto the bytecode stack */
4896 num_hints_records = 0;
4897 hints_records = NULL;
4899 #ifdef DEBUGGING
4900 printf("glyph %ld\n", idx);
4901 #endif
4903 /* we temporarily use `ins_buf' to record the current glyph hints, */
4904 /* leaving two bytes at the beginning so that the number of actions */
4905 /* can be inserted later on */
4906 ta_loader_register_hints_recorder(font->loader,
4907 TA_hints_recorder,
4908 (void *)&recorder);
4910 for (size = 8; size <= 1000; size++)
4912 /* rewind buffer pointer for recorder */
4913 recorder.hints_record.buf = bufp + 2;
4914 recorder.hints_record.num_actions = 0;
4915 recorder.hints_record.size = size;
4917 error = FT_Set_Pixel_Sizes(face, size, size);
4918 if (error)
4919 goto Err;
4921 /* calling `ta_loader_load_glyph' uses the */
4922 /* `TA_hints_recorder' function as a callback, */
4923 /* modifying `hints_record' */
4924 error = ta_loader_load_glyph(font->loader, face, idx, 0);
4925 if (error)
4926 goto Err;
4928 /* store the number of actions in `ins_buf' */
4929 *bufp = HIGH(recorder.hints_record.num_actions);
4930 *(bufp + 1) = LOW(recorder.hints_record.num_actions);
4932 if (TA_hints_record_is_different(hints_records,
4933 num_hints_records,
4934 bufp, recorder.hints_record.buf))
4936 #ifdef DEBUGGING
4938 FT_Byte* p;
4941 printf(" %d:\n", size);
4942 for (p = bufp; p < recorder.hints_record.buf; p += 2)
4943 printf(" %2d", *p * 256 + *(p + 1));
4944 printf("\n");
4946 #endif
4948 error = TA_add_hints_record(&hints_records,
4949 &num_hints_records,
4950 bufp, recorder.hints_record);
4951 if (error)
4952 goto Err;
4956 if (num_hints_records == 1 && !hints_records[0].num_actions)
4958 /* don't emit anything if we only have a single empty record */
4959 ins_len = 0;
4961 else
4963 FT_Byte* p = bufp;
4966 /* otherwise, clear the temporarily used part of `ins_buf' */
4967 while (*p != INS_A0)
4968 *(p++) = INS_A0;
4970 bufp = TA_sfnt_emit_hints_records(sfnt,
4971 hints_records, num_hints_records,
4972 bufp);
4974 /* we are done, so reallocate the instruction array to its real size */
4975 if (*bufp == INS_A0)
4977 /* search backwards */
4978 while (*bufp == INS_A0)
4979 bufp--;
4980 bufp++;
4982 else
4984 /* search forwards */
4985 while (*bufp != INS_A0)
4986 bufp++;
4989 ins_len = bufp - ins_buf;
4992 if (ins_len > sfnt->max_instructions)
4993 sfnt->max_instructions = ins_len;
4995 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
4996 glyph->ins_len = ins_len;
4998 TA_free_hints_records(hints_records, num_hints_records);
4999 free(recorder.wrap_around_segments);
5001 return FT_Err_Ok;
5003 Err:
5004 TA_free_hints_records(hints_records, num_hints_records);
5005 free(recorder.wrap_around_segments);
5006 free(ins_buf);
5008 return error;
5012 FT_Error
5013 TA_sfnt_build_glyf_hints(SFNT* sfnt,
5014 FONT* font)
5016 FT_Face face = sfnt->face;
5017 FT_Long idx;
5018 FT_Error error;
5021 for (idx = 0; idx < face->num_glyphs; idx++)
5023 error = TA_sfnt_build_glyph_instructions(sfnt, font, idx);
5024 if (error)
5025 return error;
5028 return FT_Err_Ok;
5031 /* end of tabytecode.c */