Eliminate `sal_num_segments'.
[ttfautohint.git] / src / tafpgm.c
blobb1514d92aa28229773fe6667ed7075c62eacfde3
1 /* tafpgm.c */
3 /*
4 * Copyright (C) 2011 by Werner Lemberg.
6 * This file is part of the ttfautohint library, and may only be used,
7 * modified, and distributed under the terms given in `COPYING'. By
8 * continuing to use, modify, or distribute this file you indicate that you
9 * have read `COPYING' and understand and accept it fully.
11 * The file `COPYING' mentioned in the previous paragraph is distributed
12 * with the ttfautohint library.
16 #include "tabytecode.h"
19 /* we need the following macro */
20 /* so that `func_name' doesn't get replaced with its #defined value */
21 /* (as defined in `tabytecode.h') */
23 #define FPGM(func_name) fpgm_ ## func_name
27 * Due to a bug in FreeType up to and including version 2.4.7,
28 * it is not possible to use MD_orig with twilight points.
29 * We circumvent this by using GC_orig.
31 #define MD_orig_fixed \
32 GC_orig, \
33 SWAP, \
34 GC_orig, \
35 SWAP, \
36 SUB
38 #define MD_orig_ZP2_0 \
39 MD_orig_fixed
41 #define MD_orig_ZP2_1 \
42 PUSHB_1, \
43 0, \
44 SZP2, \
45 MD_orig_fixed, \
46 PUSHB_1, \
47 1, \
48 SZP2
51 /* in the comments below, the top of the stack (`s:') */
52 /* is the rightmost element; the stack is shown */
53 /* after the instruction on the same line has been executed */
57 * bci_round
59 * Round a 26.6 number. Contrary to the ROUND bytecode instruction, no
60 * engine specific corrections are applied.
62 * in: val
63 * out: ROUND(val)
66 unsigned char FPGM(bci_round) [] = {
68 PUSHB_1,
69 bci_round,
70 FDEF,
72 DUP,
73 ABS,
74 PUSHB_1,
75 32,
76 ADD,
77 FLOOR,
78 SWAP,
79 PUSHB_1,
81 LT,
82 IF,
83 NEG,
84 EIF,
86 ENDF,
92 * bci_compute_stem_width
94 * This is the equivalent to the following code from function
95 * `ta_latin_compute_stem_width':
97 * dist = ABS(width)
99 * if (stem_is_serif
100 * && dist < 3*64)
101 * || is_extra_light:
102 * return width
103 * else if base_is_round:
104 * if dist < 80
105 * dist = 64
106 * else if dist < 56:
107 * dist = 56
109 * delta = ABS(dist - std_width)
111 * if delta < 40:
112 * dist = std_width
113 * if dist < 48
114 * dist = 48
115 * goto End
117 * if dist < 3*64:
118 * delta = dist
119 * dist = FLOOR(dist)
120 * delta = delta - dist
122 * if delta < 10:
123 * dist = dist + delta
124 * else if delta < 32:
125 * dist = dist + 10
126 * else if delta < 54:
127 * dist = dist + 54
128 * else
129 * dist = dist + delta
130 * else
131 * dist = ROUND(dist)
133 * End:
134 * if width < 0:
135 * dist = -dist
136 * return dist
139 * in: width
140 * stem_is_serif
141 * base_is_round
143 * out: new_width
145 * CVT: cvtl_is_extra_light
146 * std_width
149 unsigned char FPGM(bci_compute_stem_width_a) [] = {
151 PUSHB_1,
152 bci_compute_stem_width,
153 FDEF,
155 DUP,
156 ABS, /* s: base_is_round stem_is_serif width dist */
158 DUP,
159 PUSHB_1,
160 3*64,
161 LT, /* dist < 3*64 */
163 PUSHB_1,
165 MINDEX, /* s: base_is_round width dist (dist<3*64) stem_is_serif */
166 AND, /* stem_is_serif && dist < 3*64 */
168 PUSHB_1,
169 cvtl_is_extra_light,
170 RCVT,
171 OR, /* (stem_is_serif && dist < 3*64) || is_extra_light */
173 IF, /* s: base_is_round width dist */
174 POP,
175 SWAP,
176 POP, /* s: width */
178 ELSE,
179 ROLL, /* s: width dist base_is_round */
180 IF, /* s: width dist */
181 DUP,
182 PUSHB_1,
184 LT, /* dist < 80 */
185 IF, /* s: width dist */
186 POP,
187 PUSHB_1,
188 64, /* dist = 64 */
189 EIF,
191 ELSE,
192 DUP,
193 PUSHB_1,
195 LT, /* dist < 56 */
196 IF, /* s: width dist */
197 POP,
198 PUSHB_1,
199 56, /* dist = 56 */
200 EIF,
201 EIF,
203 DUP, /* s: width dist dist */
204 PUSHB_1,
208 /* %c, index of std_width */
210 unsigned char FPGM(bci_compute_stem_width_b) [] = {
212 RCVT,
213 SUB,
214 ABS, /* s: width dist delta */
216 PUSHB_1,
218 LT, /* delta < 40 */
219 IF, /* s: width dist */
220 POP,
221 PUSHB_1,
225 /* %c, index of std_width */
227 unsigned char FPGM(bci_compute_stem_width_c) [] = {
229 RCVT, /* dist = std_width */
230 DUP,
231 PUSHB_1,
233 LT, /* dist < 48 */
235 POP,
236 PUSHB_1,
237 48, /* dist = 48 */
238 EIF,
240 ELSE,
241 DUP, /* s: width dist dist */
242 PUSHB_1,
243 3*64,
244 LT, /* dist < 3*64 */
246 DUP, /* s: width delta dist */
247 FLOOR, /* dist = FLOOR(dist) */
248 DUP, /* s: width delta dist dist */
249 ROLL,
250 ROLL, /* s: width dist delta dist */
251 SUB, /* delta = delta - dist */
253 DUP, /* s: width dist delta delta */
254 PUSHB_1,
256 LT, /* delta < 10 */
257 IF, /* s: width dist delta */
258 ADD, /* dist = dist + delta */
260 ELSE,
261 DUP,
262 PUSHB_1,
264 LT, /* delta < 32 */
266 POP,
267 PUSHB_1,
269 ADD, /* dist = dist + 10 */
271 ELSE,
272 DUP,
273 PUSHB_1,
275 LT, /* delta < 54 */
277 POP,
278 PUSHB_1,
280 ADD, /* dist = dist + 54 */
282 ELSE,
283 ADD, /* dist = dist + delta */
285 EIF,
286 EIF,
287 EIF,
289 ELSE,
290 PUSHB_1,
291 bci_round,
292 CALL, /* dist = round(dist) */
294 EIF,
295 EIF,
297 SWAP, /* s: dist width */
298 PUSHB_1,
300 LT, /* width < 0 */
302 NEG, /* dist = -dist */
303 EIF,
304 EIF,
306 ENDF,
312 * bci_loop
314 * Take a range and a function number and apply the function to all
315 * elements of the range.
317 * in: func_num
318 * end
319 * start
321 * sal: sal_i (counter initialized with `start')
322 * sal_limit (`end')
323 * sal_func (`func_num')
326 unsigned char FPGM(bci_loop) [] = {
328 PUSHB_1,
329 bci_loop,
330 FDEF,
332 PUSHB_1,
333 sal_func,
334 SWAP,
335 WS, /* sal_func = func_num */
336 PUSHB_1,
337 sal_limit,
338 SWAP,
339 WS, /* sal_limit = end */
340 PUSHB_1,
341 sal_i,
342 SWAP,
343 WS, /* sal_i = start */
345 /* start_loop: */
346 PUSHB_1,
347 sal_i,
349 PUSHB_1,
350 sal_limit,
352 LTEQ, /* start <= end */
354 PUSHB_1,
355 sal_func,
357 CALL,
358 PUSHB_3,
359 sal_i,
361 sal_i,
363 ADD, /* start = start + 1 */
366 PUSHB_1,
368 NEG,
369 JMPR, /* goto start_loop */
370 EIF,
372 ENDF,
378 * bci_cvt_rescale
380 * Rescale CVT value by `cvtl_scale' (in 16.16 format).
382 * sal: sal_i (CVT index)
384 * CVT: cvtl_scale
385 * cvtl_0x10000
388 unsigned char FPGM(bci_cvt_rescale) [] = {
390 PUSHB_1,
391 bci_cvt_rescale,
392 FDEF,
394 PUSHB_1,
395 sal_i,
397 DUP,
398 RCVT,
399 PUSHB_1,
400 cvtl_scale,
401 RCVT,
402 MUL, /* CVT * scale * 2^10 */
403 PUSHB_1,
404 cvtl_0x10000,
405 RCVT,
406 DIV, /* CVT * scale */
408 WCVTP,
410 ENDF,
416 * bci_blue_round
418 * Round a blue ref value and adjust its corresponding shoot value.
420 * sal: sal_i (CVT index)
424 unsigned char FPGM(bci_blue_round_a) [] = {
426 PUSHB_1,
427 bci_blue_round,
428 FDEF,
430 PUSHB_1,
431 sal_i,
433 DUP,
434 RCVT, /* s: ref_idx ref */
436 DUP,
437 PUSHB_1,
438 bci_round,
439 CALL,
440 SWAP, /* s: ref_idx round(ref) ref */
442 PUSHB_2,
446 /* %c, blue_count */
448 unsigned char FPGM(bci_blue_round_b) [] = {
451 CINDEX,
452 ADD, /* s: ref_idx round(ref) ref shoot_idx */
453 DUP,
454 RCVT, /* s: ref_idx round(ref) ref shoot_idx shoot */
456 ROLL, /* s: ref_idx round(ref) shoot_idx shoot ref */
457 SWAP,
458 SUB, /* s: ref_idx round(ref) shoot_idx dist */
459 DUP,
460 ABS, /* s: ref_idx round(ref) shoot_idx dist delta */
462 DUP,
463 PUSHB_1,
465 LT, /* delta < 32 */
467 POP,
468 PUSHB_1,
469 0, /* delta = 0 */
471 ELSE,
472 PUSHB_1,
474 LT, /* delta < 48 */
476 PUSHB_1,
477 32, /* delta = 32 */
479 ELSE,
480 PUSHB_1,
481 64, /* delta = 64 */
482 EIF,
483 EIF,
485 SWAP, /* s: ref_idx round(ref) shoot_idx delta dist */
486 PUSHB_1,
488 LT, /* dist < 0 */
490 NEG, /* delta = -delta */
491 EIF,
493 PUSHB_1,
495 CINDEX,
496 SWAP,
497 SUB, /* s: ref_idx round(ref) shoot_idx (round(ref) - delta) */
499 WCVTP,
500 WCVTP,
502 ENDF,
508 * bci_get_point_extrema
510 * An auxiliary function for `bci_create_segment'.
512 * in: point-1
513 * out: point
515 * sal: sal_point_min
516 * sal_point_max
519 unsigned char FPGM(bci_get_point_extrema) [] = {
521 PUSHB_1,
522 bci_get_point_extrema,
523 FDEF,
525 PUSHB_1,
527 ADD, /* s: point */
528 DUP,
529 DUP,
531 /* check whether `point' is a new minimum */
532 PUSHB_1,
533 sal_point_min,
534 RS, /* s: point point point point_min */
535 MD_orig,
536 /* if distance is negative, we have a new minimum */
537 PUSHB_1,
540 IF, /* s: point point */
541 DUP,
542 PUSHB_1,
543 sal_point_min,
544 SWAP,
546 EIF,
548 /* check whether `point' is a new maximum */
549 PUSHB_1,
550 sal_point_max,
551 RS, /* s: point point point_max */
552 MD_orig,
553 /* if distance is positive, we have a new maximum */
554 PUSHB_1,
557 IF, /* s: point */
558 DUP,
559 PUSHB_1,
560 sal_point_max,
561 SWAP,
563 EIF, /* s: point */
565 ENDF,
571 * bci_create_segment
573 * Store start and end point of a segment in the storage area,
574 * then construct a point in the twilight zone to represent it.
576 * This function is used by `bci_create_segment_points'.
578 * in: start
579 * end
580 * [last (if wrap-around segment)]
581 * [first (if wrap-around segment)]
583 * uses: bci_get_point_extrema
585 * sal: sal_i (start of current segment)
586 * sal_j (current twilight point)
587 * sal_point_min
588 * sal_point_max
590 * CVT: cvtl_scale
591 * cvtl_0x10000
592 * cvtl_temp
595 unsigned char FPGM(bci_create_segment) [] = {
597 PUSHB_1,
598 bci_create_segment,
599 FDEF,
601 PUSHB_1,
602 sal_i,
604 PUSHB_1,
606 CINDEX,
607 WS, /* sal[sal_i] = start */
609 /* increase `sal_i'; together with the outer loop, this makes sal_i += 2 */
610 PUSHB_3,
611 sal_i,
613 sal_i,
615 ADD, /* sal_i = sal_i + 1 */
618 /* initialize inner loop(s) */
619 PUSHB_2,
620 sal_point_min,
622 CINDEX,
623 WS, /* sal_point_min = start */
624 PUSHB_2,
625 sal_point_max,
627 CINDEX,
628 WS, /* sal_point_max = start */
630 PUSHB_1,
632 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
634 SWAP,
635 DUP,
636 PUSHB_1,
638 CINDEX, /* s: start end end start */
639 LT, /* start > end */
641 /* we have a wrap-around segment with two more arguments */
642 /* to give the last and first point of the contour, respectively; */
643 /* our job is to store a segment `start'-`last', */
644 /* and to get extrema for the two segments */
645 /* `start'-`last' and `first'-`end' */
647 /* s: first last start end */
648 PUSHB_1,
649 sal_i,
651 PUSHB_1,
653 CINDEX,
654 WS, /* sal[sal_i] = last */
656 ROLL,
657 ROLL, /* s: first end last start */
658 DUP,
659 ROLL,
660 SWAP, /* s: first end start last start */
661 SUB, /* s: first end start loop_count */
663 PUSHB_1,
664 bci_get_point_extrema,
665 LOOPCALL,
666 /* clean up stack */
667 POP,
669 SWAP, /* s: end first */
670 PUSHB_1,
672 SUB,
673 DUP,
674 ROLL, /* s: (first - 1) (first - 1) end */
675 SWAP,
676 SUB, /* s: (first - 1) loop_count */
678 PUSHB_1,
679 bci_get_point_extrema,
680 LOOPCALL,
681 /* clean up stack */
682 POP,
684 ELSE, /* s: start end */
685 PUSHB_1,
686 sal_i,
688 PUSHB_1,
690 CINDEX,
691 WS, /* sal[sal_i] = end */
693 PUSHB_1,
695 CINDEX,
696 SUB, /* s: start loop_count */
698 PUSHB_1,
699 bci_get_point_extrema,
700 LOOPCALL,
701 /* clean up stack */
702 POP,
703 EIF,
705 /* the twilight point representing a segment */
706 /* is in the middle between the minimum and maximum */
707 PUSHB_1,
708 sal_point_min,
710 GC_orig,
711 PUSHB_1,
712 sal_point_max,
714 GC_orig,
715 ADD,
716 PUSHB_1,
717 2*64,
718 DIV, /* s: middle_pos */
720 /* now scale it */
721 PUSHB_1,
722 cvtl_scale,
723 RCVT,
724 MUL, /* middle_pos * scale * 2^10 */
725 PUSHB_1,
726 cvtl_0x10000,
727 RCVT,
728 DIV, /* middle_pos = middle_pos * scale */
730 /* write it to temporary CVT location */
731 PUSHB_2,
732 cvtl_temp,
734 SZP0, /* set zp0 to twilight zone 0 */
735 SWAP,
736 WCVTP,
738 /* create twilight point with index `sal_j' */
739 PUSHB_1,
740 sal_j,
742 PUSHB_1,
743 cvtl_temp,
744 MIAP_noround,
746 PUSHB_3,
747 sal_j,
749 sal_j,
751 ADD, /* twilight_point = twilight_point + 1 */
754 ENDF,
760 * bci_create_segments
762 * Set up segments by defining point ranges which defines them
763 * and computing twilight points to represent them.
765 * in: num_segments (N)
766 * segment_start_0
767 * segment_end_0
768 * [contour_last 0 (if wrap-around segment)]
769 * [contour_first 0 (if wrap-around segment)]
770 * segment_start_1
771 * segment_end_1
772 * [contour_last 0 (if wrap-around segment)]
773 * [contour_first 0 (if wrap-around segment)]
774 * ...
775 * segment_start_(N-1)
776 * segment_end_(N-1)
777 * [contour_last (N-1) (if wrap-around segment)]
778 * [contour_first (N-1) (if wrap-around segment)]
780 * uses: bci_create_segment
782 * sal: sal_i (start of current segment)
783 * sal_j (current twilight point)
786 unsigned char FPGM(bci_create_segments) [] = {
788 PUSHB_1,
789 bci_create_segments,
790 FDEF,
792 /* all our measurements are taken along the y axis */
793 SVTCA_y,
795 DUP,
796 ADD,
797 PUSHB_1,
799 SUB, /* delta = (2*num_segments - 1) */
801 PUSHB_4,
802 sal_segment_offset,
803 sal_segment_offset,
805 sal_j,
807 WS, /* sal_j = 0 (point offset) */
809 ROLL,
810 ADD, /* s: ... sal_segment_offset (sal_segment_offset + delta) */
812 /* `bci_create_segment_point' also increases the loop counter by 1; */
813 /* this effectively means we have a loop step of 2 */
814 PUSHB_2,
815 bci_create_segment,
816 bci_loop,
817 CALL,
819 ENDF,
825 * bci_align_segment
827 * Align all points in a segment to the twilight point in rp0.
828 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
830 * in: segment_index
833 unsigned char FPGM(bci_align_segment) [] = {
835 PUSHB_1,
836 bci_align_segment,
837 FDEF,
839 /* we need the values of `sal_segment_offset + 2*segment_index' */
840 /* and `sal_segment_offset + 2*segment_index + 1' */
841 DUP,
842 ADD,
843 PUSHB_1,
844 sal_segment_offset,
845 ADD,
846 DUP,
848 SWAP,
849 PUSHB_1,
851 ADD,
852 RS, /* s: first last */
854 /* start_loop: */
855 PUSHB_1,
857 CINDEX, /* s: first last first */
858 PUSHB_1,
860 CINDEX, /* s: first last first last */
861 LTEQ, /* first <= end */
862 IF, /* s: first last */
863 SWAP,
864 DUP, /* s: last first first */
865 ALIGNRP, /* align point with index `first' with rp0 */
867 PUSHB_1,
869 ADD, /* first = first + 1 */
870 SWAP, /* s: first last */
872 PUSHB_1,
874 NEG,
875 JMPR, /* goto start_loop */
877 ELSE,
878 POP,
879 POP,
880 EIF,
882 ENDF,
888 * bci_align_segments
890 * Align segments to the twilight point in rp0.
891 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
893 * in: first_segment
894 * loop_counter (N)
895 * segment_1
896 * segment_2
897 * ...
898 * segment_N
900 * uses: handle_segment
904 unsigned char FPGM(bci_align_segments) [] = {
906 PUSHB_1,
907 bci_align_segments,
908 FDEF,
910 PUSHB_1,
911 bci_align_segment,
912 CALL,
914 PUSHB_1,
915 bci_align_segment,
916 LOOPCALL,
918 ENDF,
924 * bci_scale_contour
926 * Scale a contour using two points giving the maximum and minimum
927 * coordinates.
929 * It expects that no point on the contour is touched.
931 * in: min_point
932 * max_point
934 * CVT: cvtl_scale
935 * cvtl_0x10000
938 unsigned char FPGM(bci_scale_contour) [] = {
940 PUSHB_1,
941 bci_scale_contour,
942 FDEF,
944 DUP,
945 DUP,
946 GC_orig,
947 DUP,
948 PUSHB_1,
949 cvtl_scale,
950 RCVT,
951 MUL, /* min_pos * scale * 2^10 */
952 PUSHB_1,
953 cvtl_0x10000,
954 RCVT,
955 DIV, /* min_pos_new = min_pos * scale */
956 SWAP,
957 SUB,
958 SHPIX,
960 /* don't scale a single-point contour twice */
961 SWAP,
962 DUP,
963 ROLL,
964 NEQ,
966 DUP,
967 GC_orig,
968 DUP,
969 PUSHB_1,
970 cvtl_scale,
971 RCVT,
972 MUL, /* max_pos * scale * 2^10 */
973 PUSHB_1,
974 cvtl_0x10000,
975 RCVT,
976 DIV, /* max_pos_new = max_pos * scale */
977 SWAP,
978 SUB,
979 SHPIX,
981 ELSE,
982 POP,
983 EIF,
985 ENDF,
991 * bci_scale_glyph
993 * Scale a glyph using a list of points (two points per contour, giving
994 * the maximum and mininum coordinates).
996 * It expects that no point in the glyph is touched.
998 * in: num_contours (N)
999 * min_point_1
1000 * max_point_1
1001 * min_point_2
1002 * max_point_2
1003 * ...
1004 * min_point_N
1005 * max_point_N
1007 * uses: bci_scale_contour
1010 unsigned char FPGM(bci_scale_glyph) [] = {
1012 PUSHB_1,
1013 bci_scale_glyph,
1014 FDEF,
1016 SVTCA_y,
1018 PUSHB_1,
1020 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
1022 PUSHB_1,
1023 bci_scale_contour,
1024 LOOPCALL,
1026 PUSHB_1,
1028 SZP2, /* set zp2 to normal zone 1 */
1029 IUP_y,
1031 ENDF,
1037 * bci_shift_contour
1039 * Shift a contour by a given amount.
1041 * It expects that rp1 (pointed to by zp0) is set up properly; zp2 must
1042 * point to the normal zone 1.
1044 * in: contour
1045 * out: contour + 1
1048 unsigned char FPGM(bci_shift_contour) [] = {
1050 PUSHB_1,
1051 bci_shift_contour,
1052 FDEF,
1054 DUP,
1055 SHC_rp1, /* shift `contour' by (rp1_pos - rp1_orig_pos) */
1057 PUSHB_1,
1059 ADD,
1061 ENDF,
1067 * bci_shift_subglyph
1069 * Shift a subglyph. To be more specific, it corrects the already applied
1070 * subglyph offset (if any) from the `glyf' table which needs to be scaled
1071 * also.
1073 * If this function is called, a point `x' in the subglyph has been scaled
1074 * by `cvtl_scale' already (during the hinting of the subglyph itself), and
1075 * `offset' has been applied also:
1077 * x -> x * scale + offset (1)
1079 * However, the offset should be applied first, then the scaling:
1081 * x -> (x + offset) * scale (2)
1083 * Our job is now to transform (1) to (2); a simple calculation shows that
1084 * we have to shift all points of the subglyph by
1086 * offset * scale - offset = offset * (scale - 1)
1088 * in: offset (in FUnits)
1089 * num_contours
1090 * first_contour
1092 * CVT: cvtl_funits_to_pixels
1093 * cvtl_0x10000
1094 * cvtl_scale
1097 unsigned char FPGM(bci_shift_subglyph) [] = {
1099 PUSHB_1,
1100 bci_shift_subglyph,
1101 FDEF,
1103 SVTCA_y,
1105 PUSHB_1,
1106 cvtl_funits_to_pixels,
1107 RCVT, /* scaling factor FUnits -> pixels */
1108 MUL,
1109 PUSHB_1,
1110 cvtl_0x10000,
1111 RCVT,
1112 DIV,
1114 /* the autohinter always rounds offsets */
1115 PUSHB_1,
1116 bci_round,
1117 CALL, /* offset = round(offset) */
1119 PUSHB_1,
1120 cvtl_scale,
1121 RCVT,
1122 PUSHB_1,
1123 cvtl_0x10000,
1124 RCVT,
1125 SUB, /* scale - 1 (in 16.16 format) */
1126 MUL,
1127 PUSHB_1,
1128 cvtl_0x10000,
1129 RCVT,
1130 DIV, /* delta = offset * (scale - 1) */
1132 /* and round again */
1133 PUSHB_1,
1134 bci_round,
1135 CALL, /* offset = round(offset) */
1137 PUSHB_1,
1139 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1141 /* we create twilight point 0 as a reference point, */
1142 /* setting the original position to zero (using `cvtl_temp') */
1143 PUSHB_5,
1146 cvtl_temp,
1147 cvtl_temp,
1149 WCVTP,
1150 MIAP_noround, /* rp0 and rp1 now point to twilight point 0 */
1152 SWAP, /* s: first_contour num_contours 0 delta */
1153 SHPIX, /* rp1_pos - rp1_orig_pos = delta */
1155 PUSHB_2,
1156 bci_shift_contour,
1158 SZP2, /* set zp2 to normal zone 1 */
1159 LOOPCALL,
1161 ENDF,
1167 * bci_ip_outer_align_point
1169 * Auxiliary function for `bci_action_ip_before' and
1170 * `bci_action_ip_after'.
1172 * It expects rp0 to contain the edge for alignment, zp0 set to twilight
1173 * zone, and both zp1 and zp2 set to normal zone.
1175 * in: point
1177 * sal: sal_i (edge_orig_pos)
1179 * CVT: cvtl_scale
1180 * cvtl_0x10000
1183 unsigned char FPGM(bci_ip_outer_align_point) [] = {
1185 PUSHB_1,
1186 bci_ip_outer_align_point,
1187 FDEF,
1189 DUP,
1190 ALIGNRP, /* align `point' with `edge' */
1191 DUP,
1192 GC_orig,
1193 /* now scale it */
1194 PUSHB_1,
1195 cvtl_scale,
1196 RCVT,
1197 MUL, /* point_orig_pos * scale * 2^10 */
1198 PUSHB_1,
1199 cvtl_0x10000,
1200 RCVT,
1201 DIV, /* point_orig_pos = point_orig_pos * scale */
1203 PUSHB_1,
1204 sal_i,
1206 SUB, /* s: point (point_orig_pos - edge_orig_pos) */
1207 SHPIX,
1209 ENDF,
1215 * bci_ip_on_align_points
1217 * Auxiliary function for `bci_action_ip_on'.
1219 * in: edge (in twilight zone)
1220 * loop_counter (N)
1221 * point_1
1222 * point_2
1223 * ...
1224 * point_N
1227 unsigned char FPGM(bci_ip_on_align_points) [] = {
1229 PUSHB_1,
1230 bci_ip_on_align_points,
1231 FDEF,
1233 MDAP_noround, /* set rp0 and rp1 to `edge' */
1235 SLOOP,
1236 ALIGNRP,
1238 ENDF,
1244 * bci_ip_between_align_point
1246 * Auxiliary function for `bci_ip_between_align_points'.
1248 * It expects rp0 to contain the edge for alignment, zp0 set to twilight
1249 * zone, and both zp1 and zp2 set to normal zone.
1251 * in: point
1253 * sal: sal_i (edge_orig_pos)
1254 * sal_j (stretch_factor)
1256 * CVT: cvtl_scale
1257 * cvtl_0x10000
1260 unsigned char FPGM(bci_ip_between_align_point) [] = {
1262 PUSHB_1,
1263 bci_ip_between_align_point,
1264 FDEF,
1266 DUP,
1267 ALIGNRP, /* align `point' with `edge' */
1268 DUP,
1269 GC_orig,
1270 /* now scale it */
1271 PUSHB_1,
1272 cvtl_scale,
1273 RCVT,
1274 MUL, /* point_orig_pos * scale * 2^10 */
1275 PUSHB_1,
1276 cvtl_0x10000,
1277 RCVT,
1278 DIV, /* point_orig_pos = point_orig_pos * scale */
1280 PUSHB_1,
1281 sal_i,
1283 SUB, /* s: point (point_orig_pos - edge_orig_pos) */
1284 PUSHB_1,
1285 sal_j,
1287 MUL, /* s: point delta */
1288 SHPIX,
1290 ENDF,
1296 * bci_ip_between_align_points
1298 * Auxiliary function for `bci_action_ip_between'.
1300 * in: after_edge (in twilight zone)
1301 * before_edge (in twilight zone)
1302 * loop_counter (N)
1303 * point_1
1304 * point_2
1305 * ...
1306 * point_N
1308 * sal: sal_i (before_orig_pos)
1309 * sal_j (stretch_factor)
1311 * uses: bci_ip_between_align_point
1314 unsigned char FPGM(bci_ip_between_align_points) [] = {
1316 PUSHB_1,
1317 bci_ip_between_align_points,
1318 FDEF,
1320 PUSHB_2,
1323 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1324 CINDEX,
1325 DUP, /* s: ... before after before before */
1326 MDAP_noround, /* set rp0 and rp1 to `before' */
1327 DUP,
1328 GC_orig, /* s: ... before after before before_orig_pos */
1329 PUSHB_1,
1330 sal_i,
1331 SWAP,
1332 WS, /* sal_i = before_orig_pos */
1333 PUSHB_1,
1335 CINDEX, /* s: ... before after before after */
1336 MD_cur, /* b = after_pos - before_pos */
1337 ROLL,
1338 ROLL,
1339 MD_orig_ZP2_0, /* a = after_orig_pos - before_orig_pos */
1340 DIV, /* s: a/b */
1341 PUSHB_1,
1342 sal_j,
1343 SWAP,
1344 WS, /* sal_j = stretch_factor */
1346 PUSHB_3,
1347 bci_ip_between_align_point,
1350 SZP2, /* set zp2 to normal zone 1 */
1351 SZP1, /* set zp1 to normal zone 1 */
1352 LOOPCALL,
1354 ENDF,
1360 * bci_action_ip_before
1362 * Handle `ip_before' data to align points located before the first edge.
1364 * in: first_edge (in twilight zone)
1365 * loop_counter (N)
1366 * point_1
1367 * point_2
1368 * ...
1369 * point_N
1371 * sal: sal_i (first_edge_orig_pos)
1373 * uses: bci_ip_outer_align_point
1376 unsigned char FPGM(bci_action_ip_before) [] = {
1378 PUSHB_1,
1379 bci_action_ip_before,
1380 FDEF,
1382 PUSHB_1,
1384 SZP2, /* set zp2 to twilight zone 0 */
1386 DUP,
1387 GC_orig,
1388 PUSHB_1,
1389 sal_i,
1390 SWAP,
1391 WS, /* sal_i = first_edge_orig_pos */
1393 PUSHB_3,
1397 SZP2, /* set zp2 to normal zone 1 */
1398 SZP1, /* set zp1 to normal zone 1 */
1399 SZP0, /* set zp0 to twilight zone 0 */
1401 MDAP_noround, /* set rp0 and rp1 to `first_edge' */
1403 PUSHB_1,
1404 bci_ip_outer_align_point,
1405 LOOPCALL,
1407 ENDF,
1413 * bci_action_ip_after
1415 * Handle `ip_after' data to align points located after the last edge.
1417 * in: last_edge (in twilight zone)
1418 * loop_counter (N)
1419 * point_1
1420 * point_2
1421 * ...
1422 * point_N
1424 * sal: sal_i (last_edge_orig_pos)
1426 * uses: bci_ip_outer_align_point
1429 unsigned char FPGM(bci_action_ip_after) [] = {
1431 PUSHB_1,
1432 bci_action_ip_after,
1433 FDEF,
1435 PUSHB_1,
1437 SZP2, /* set zp2 to twilight zone 0 */
1439 DUP,
1440 GC_orig,
1441 PUSHB_1,
1442 sal_i,
1443 SWAP,
1444 WS, /* sal_i = last_edge_orig_pos */
1446 PUSHB_3,
1450 SZP2, /* set zp2 to normal zone 1 */
1451 SZP1, /* set zp1 to normal zone 1 */
1452 SZP0, /* set zp0 to twilight zone 0 */
1454 MDAP_noround, /* set rp0 and rp1 to `last_edge' */
1456 PUSHB_1,
1457 bci_ip_outer_align_point,
1458 LOOPCALL,
1460 ENDF,
1466 * bci_action_ip_on
1468 * Handle `ip_on' data to align points located on an edge coordinate (but
1469 * not part of an edge).
1471 * in: loop_counter (M)
1472 * edge_1 (in twilight zone)
1473 * loop_counter (N_1)
1474 * point_1
1475 * point_2
1476 * ...
1477 * point_N_1
1478 * edge_2 (in twilight zone)
1479 * loop_counter (N_2)
1480 * point_1
1481 * point_2
1482 * ...
1483 * point_N_2
1484 * ...
1485 * edge_M (in twilight zone)
1486 * loop_counter (N_M)
1487 * point_1
1488 * point_2
1489 * ...
1490 * point_N_M
1492 * uses: bci_ip_on_align_points
1495 unsigned char FPGM(bci_action_ip_on) [] = {
1497 PUSHB_1,
1498 bci_action_ip_on,
1499 FDEF,
1501 PUSHB_2,
1504 SZP1, /* set zp1 to normal zone 1 */
1505 SZP0, /* set zp0 to twilight zone 0 */
1507 PUSHB_1,
1508 bci_ip_on_align_points,
1509 LOOPCALL,
1511 ENDF,
1517 * bci_action_ip_between
1519 * Handle `ip_between' data to align points located between two edges.
1521 * in: loop_counter (M)
1522 * before_edge_1 (in twilight zone)
1523 * after_edge_1 (in twilight zone)
1524 * loop_counter (N_1)
1525 * point_1
1526 * point_2
1527 * ...
1528 * point_N_1
1529 * before_edge_2 (in twilight zone)
1530 * after_edge_2 (in twilight zone)
1531 * loop_counter (N_2)
1532 * point_1
1533 * point_2
1534 * ...
1535 * point_N_2
1536 * ...
1537 * before_edge_M (in twilight zone)
1538 * after_edge_M (in twilight zone)
1539 * loop_counter (N_M)
1540 * point_1
1541 * point_2
1542 * ...
1543 * point_N_M
1545 * uses: bci_ip_between_align_points
1548 unsigned char FPGM(bci_action_ip_between) [] = {
1550 PUSHB_1,
1551 bci_action_ip_between,
1552 FDEF,
1554 PUSHB_1,
1555 bci_ip_between_align_points,
1556 LOOPCALL,
1558 ENDF,
1564 * bci_action_adjust_bound
1566 * Handle the ADJUST_BOUND action to align an edge of a stem if the other
1567 * edge of the stem has already been moved, then moving it again if
1568 * necessary to stay bound.
1570 * in: edge2_is_serif
1571 * edge_is_round
1572 * edge_point (in twilight zone)
1573 * edge2_point (in twilight zone)
1574 * edge[-1] (in twilight zone)
1575 * ... stuff for bci_align_segments (edge) ...
1578 unsigned char FPGM(bci_action_adjust_bound) [] = {
1580 PUSHB_1,
1581 bci_action_adjust_bound,
1582 FDEF,
1584 PUSHB_1,
1586 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1588 PUSHB_1,
1590 CINDEX, /* s: edge[-1] edge2 edge is_round is_serif edge2 */
1591 PUSHB_1,
1593 CINDEX, /* s: edge[-1] edge2 edge is_round is_serif edge2 edge */
1594 MD_orig_ZP2_0, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1596 PUSHB_1,
1597 bci_compute_stem_width,
1598 CALL,
1599 NEG, /* s: edge[-1] edge2 edge -cur_len */
1601 ROLL, /* s: edge[-1] edge -cur_len edge2 */
1602 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1603 SWAP,
1604 DUP,
1605 DUP, /* s: edge[-1] -cur_len edge edge edge */
1606 ALIGNRP, /* align `edge' with `edge2' */
1607 ROLL,
1608 SHPIX, /* shift `edge' by -cur_len */
1610 SWAP, /* s: edge edge[-1] */
1611 DUP,
1612 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1613 GC_cur,
1614 PUSHB_1,
1616 CINDEX,
1617 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1618 GT, /* edge_pos < edge[-1]_pos */
1620 DUP,
1621 ALIGNRP, /* align `edge' to `edge[-1]' */
1622 EIF,
1624 MDAP_noround, /* set rp0 and rp1 to `edge' */
1626 PUSHB_2,
1627 bci_align_segments,
1629 SZP1, /* set zp1 to normal zone 1 */
1630 CALL,
1632 ENDF,
1638 * bci_action_stem_bound
1640 * Handle the STEM action to align two edges of a stem, then moving one
1641 * edge again if necessary to stay bound.
1643 * The code after computing `cur_len' to shift `edge' and `edge2'
1644 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1646 * if cur_len < 96:
1647 * if cur_len < = 64:
1648 * u_off = 32
1649 * d_off = 32
1650 * else:
1651 * u_off = 38
1652 * d_off = 26
1654 * org_pos = anchor + (edge_orig - anchor_orig);
1655 * org_center = org_pos + org_len / 2;
1657 * cur_pos1 = ROUND(org_center)
1658 * delta1 = ABS(org_center - (cur_pos1 - u_off))
1659 * delta2 = ABS(org_center - (cur_pos1 + d_off))
1660 * if (delta1 < delta2):
1661 * cur_pos1 = cur_pos1 - u_off
1662 * else:
1663 * cur_pos1 = cur_pos1 + d_off
1665 * edge = cur_pos1 - cur_len / 2
1667 * else:
1668 * org_pos = anchor + (edge_orig - anchor_orig)
1669 * org_center = org_pos + org_len / 2;
1671 * cur_pos1 = ROUND(org_pos)
1672 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
1673 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
1674 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
1676 * if (delta1 < delta2):
1677 * edge = cur_pos1
1678 * else:
1679 * edge = cur_pos2
1681 * edge2 = edge + cur_len
1683 * in: edge2_is_serif
1684 * edge_is_round
1685 * edge_point (in twilight zone)
1686 * edge2_point (in twilight zone)
1687 * edge[-1] (in twilight zone)
1688 * ... stuff for bci_align_segments (edge) ...
1689 * ... stuff for bci_align_segments (edge2)...
1691 * sal: sal_anchor
1692 * sal_temp1
1693 * sal_temp2
1694 * sal_temp3
1697 #undef sal_u_off
1698 #define sal_u_off sal_temp1
1699 #undef sal_d_off
1700 #define sal_d_off sal_temp2
1701 #undef sal_org_len
1702 #define sal_org_len sal_temp3
1703 #undef sal_edge2
1704 #define sal_edge2 sal_temp3
1706 unsigned char FPGM(bci_action_stem_bound) [] = {
1708 PUSHB_1,
1709 bci_action_stem_bound,
1710 FDEF,
1712 PUSHB_1,
1714 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1716 PUSHB_1,
1718 CINDEX,
1719 PUSHB_1,
1721 CINDEX,
1722 DUP, /* s: edge[-1] edge2 edge is_round is_serif edge2 edge edge */
1723 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1725 MD_orig_ZP2_0, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1726 DUP,
1727 PUSHB_1,
1728 sal_org_len,
1729 SWAP,
1732 PUSHB_1,
1733 bci_compute_stem_width,
1734 CALL, /* s: edge[-1] edge2 edge cur_len */
1736 DUP,
1737 PUSHB_1,
1739 LT, /* cur_len < 96 */
1741 DUP,
1742 PUSHB_1,
1744 LTEQ, /* cur_len <= 64 */
1746 PUSHB_4,
1747 sal_u_off,
1749 sal_d_off,
1752 ELSE,
1753 PUSHB_4,
1754 sal_u_off,
1756 sal_d_off,
1758 EIF,
1762 SWAP, /* s: edge[-1] edge2 cur_len edge */
1763 DUP,
1764 PUSHB_1,
1765 sal_anchor,
1767 DUP, /* s: edge[-1] edge2 cur_len edge edge anchor anchor */
1768 ROLL,
1769 SWAP,
1770 MD_orig_ZP2_0,
1771 SWAP,
1772 GC_cur,
1773 ADD, /* s: edge[-1] edge2 cur_len edge org_pos */
1774 PUSHB_1,
1775 sal_org_len,
1777 PUSHB_1,
1778 2*64,
1779 DIV,
1780 ADD, /* s: edge[-1] edge2 cur_len edge org_center */
1782 DUP,
1783 PUSHB_1,
1784 bci_round,
1785 CALL, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 */
1787 DUP,
1788 ROLL,
1789 ROLL,
1790 SUB, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) */
1792 DUP,
1793 PUSHB_1,
1794 sal_u_off,
1796 ADD,
1797 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
1799 SWAP,
1800 PUSHB_1,
1801 sal_d_off,
1803 SUB,
1804 ABS, /* s: edge[-1] edge2 cur_len edge cur_pos1 delta1 delta2 */
1806 LT, /* delta1 < delta2 */
1808 PUSHB_1,
1809 sal_u_off,
1811 SUB, /* cur_pos1 = cur_pos1 - u_off */
1813 ELSE,
1814 PUSHB_1,
1815 sal_d_off,
1817 ADD, /* cur_pos1 = cur_pos1 + d_off */
1818 EIF, /* s: edge[-1] edge2 cur_len edge cur_pos1 */
1820 PUSHB_1,
1822 CINDEX,
1823 PUSHB_1,
1824 2*64,
1825 DIV,
1826 SUB, /* arg = cur_pos1 - cur_len/2 */
1828 SWAP, /* s: edge[-1] edge2 cur_len arg edge */
1829 DUP,
1830 DUP,
1831 PUSHB_1,
1833 MINDEX,
1834 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1835 GC_cur,
1836 SUB,
1837 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1839 ELSE,
1840 SWAP, /* s: edge[-1] edge2 cur_len edge */
1841 PUSHB_1,
1842 sal_anchor,
1844 GC_cur, /* s: edge[-1] edge2 cur_len edge anchor_pos */
1845 PUSHB_1,
1847 CINDEX,
1848 PUSHB_1,
1849 sal_anchor,
1851 MD_orig_ZP2_0,
1852 ADD, /* s: edge[-1] edge2 cur_len edge org_pos */
1854 DUP,
1855 PUSHB_1,
1856 sal_org_len,
1858 PUSHB_1,
1859 2*64,
1860 DIV,
1861 ADD, /* s: edge[-1] edge2 cur_len edge org_pos org_center */
1863 SWAP,
1864 DUP,
1865 PUSHB_1,
1866 bci_round,
1867 CALL, /* cur_pos1 = ROUND(org_pos) */
1868 SWAP,
1869 PUSHB_1,
1870 sal_org_len,
1872 ADD,
1873 PUSHB_1,
1874 bci_round,
1875 CALL,
1876 PUSHB_1,
1878 CINDEX,
1879 SUB, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 cur_pos2 */
1881 PUSHB_1,
1883 CINDEX,
1884 PUSHB_1,
1885 2*64,
1886 DIV,
1887 PUSHB_1,
1889 MINDEX,
1890 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
1892 DUP,
1893 PUSHB_1,
1895 CINDEX,
1896 ADD,
1897 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
1898 SWAP,
1899 PUSHB_1,
1901 CINDEX,
1902 ADD,
1903 ABS, /* s: ... edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
1904 LT, /* delta1 < delta2 */
1906 POP, /* arg = cur_pos1 */
1908 ELSE,
1909 SWAP,
1910 POP, /* arg = cur_pos2 */
1911 EIF, /* s: edge[-1] edge2 cur_len edge arg */
1912 SWAP,
1913 DUP,
1914 DUP,
1915 PUSHB_1,
1917 MINDEX,
1918 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1919 GC_cur,
1920 SUB,
1921 SHPIX, /* edge = arg */
1922 EIF, /* s: edge[-1] edge2 cur_len edge */
1924 ROLL, /* s: edge[-1] cur_len edge edge2 */
1925 DUP,
1926 DUP,
1927 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1928 PUSHB_1,
1929 sal_edge2,
1930 SWAP,
1931 WS, /* s: edge[-1] cur_len edge edge2 */
1932 ROLL,
1933 SHPIX, /* edge2 = edge + cur_len */
1935 SWAP, /* s: edge edge[-1] */
1936 DUP,
1937 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1938 GC_cur,
1939 PUSHB_1,
1941 CINDEX,
1942 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1943 GT, /* edge_pos < edge[-1]_pos */
1945 DUP,
1946 ALIGNRP, /* align `edge' to `edge[-1]' */
1947 EIF,
1949 MDAP_noround, /* set rp0 and rp1 to `edge' */
1951 PUSHB_2,
1952 bci_align_segments,
1954 SZP1, /* set zp1 to normal zone 1 */
1955 CALL,
1957 PUSHB_1,
1958 sal_edge2,
1960 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1962 PUSHB_1,
1963 bci_align_segments,
1964 CALL,
1966 ENDF,
1972 * bci_action_link
1974 * Handle the LINK action to link an edge to another one.
1976 * in: stem_is_serif
1977 * base_is_round
1978 * base_point (in twilight zone)
1979 * stem_point (in twilight zone)
1980 * ... stuff for bci_align_segments (base) ...
1983 unsigned char FPGM(bci_action_link) [] = {
1985 PUSHB_1,
1986 bci_action_link,
1987 FDEF,
1989 PUSHB_1,
1991 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1993 PUSHB_1,
1995 CINDEX,
1996 PUSHB_1,
1998 MINDEX,
1999 DUP, /* s: stem is_round is_serif stem base base */
2000 MDAP_noround, /* set rp0 and rp1 to `base_point' (for ALIGNRP below) */
2002 MD_orig_ZP2_0, /* s: stem is_round is_serif dist_orig */
2004 PUSHB_1,
2005 bci_compute_stem_width,
2006 CALL, /* s: stem new_dist */
2008 SWAP,
2009 DUP,
2010 ALIGNRP, /* align `stem_point' with `base_point' */
2011 DUP,
2012 MDAP_noround, /* set rp0 and rp1 to `stem_point' */
2013 SWAP,
2014 SHPIX, /* stem_point = base_point + new_dist */
2016 PUSHB_2,
2017 bci_align_segments,
2019 SZP1, /* set zp1 to normal zone 1 */
2020 CALL,
2022 ENDF,
2028 * bci_action_anchor
2030 * Handle the ANCHOR action to align two edges
2031 * and to set the edge anchor.
2033 * The code after computing `cur_len' to shift `edge' and `edge2'
2034 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
2036 * if cur_len < 96:
2037 * if cur_len < = 64:
2038 * u_off = 32
2039 * d_off = 32
2040 * else:
2041 * u_off = 38
2042 * d_off = 26
2044 * org_center = edge_orig + org_len / 2
2045 * cur_pos1 = ROUND(org_center)
2047 * error1 = ABS(org_center - (cur_pos1 - u_off))
2048 * error2 = ABS(org_center - (cur_pos1 + d_off))
2049 * if (error1 < error2):
2050 * cur_pos1 = cur_pos1 - u_off
2051 * else:
2052 * cur_pos1 = cur_pos1 + d_off
2054 * edge = cur_pos1 - cur_len / 2
2055 * edge2 = edge + cur_len
2057 * else:
2058 * edge = ROUND(edge_orig)
2060 * in: edge2_is_serif
2061 * edge_is_round
2062 * edge_point (in twilight zone)
2063 * edge2_point (in twilight zone)
2064 * ... stuff for bci_align_segments (edge) ...
2066 * sal: sal_anchor
2067 * sal_temp1
2068 * sal_temp2
2069 * sal_temp3
2072 #undef sal_u_off
2073 #define sal_u_off sal_temp1
2074 #undef sal_d_off
2075 #define sal_d_off sal_temp2
2076 #undef sal_org_len
2077 #define sal_org_len sal_temp3
2079 unsigned char FPGM(bci_action_anchor) [] = {
2081 PUSHB_1,
2082 bci_action_anchor,
2083 FDEF,
2085 /* store anchor point number in `sal_anchor' */
2086 PUSHB_2,
2087 sal_anchor,
2089 CINDEX,
2090 WS, /* sal_anchor = edge_point */
2092 PUSHB_1,
2094 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2096 PUSHB_1,
2098 CINDEX,
2099 PUSHB_1,
2101 CINDEX,
2102 DUP, /* s: edge2 edge is_round is_serif edge2 edge edge */
2103 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
2105 MD_orig_ZP2_0, /* s: edge2 edge is_round is_serif org_len */
2106 DUP,
2107 PUSHB_1,
2108 sal_org_len,
2109 SWAP,
2112 PUSHB_1,
2113 bci_compute_stem_width,
2114 CALL, /* s: edge2 edge cur_len */
2116 DUP,
2117 PUSHB_1,
2119 LT, /* cur_len < 96 */
2121 DUP,
2122 PUSHB_1,
2124 LTEQ, /* cur_len <= 64 */
2126 PUSHB_4,
2127 sal_u_off,
2129 sal_d_off,
2132 ELSE,
2133 PUSHB_4,
2134 sal_u_off,
2136 sal_d_off,
2138 EIF,
2142 SWAP, /* s: edge2 cur_len edge */
2143 DUP, /* s: edge2 cur_len edge edge */
2145 GC_orig,
2146 PUSHB_1,
2147 sal_org_len,
2149 PUSHB_1,
2150 2*64,
2151 DIV,
2152 ADD, /* s: edge2 cur_len edge org_center */
2154 DUP,
2155 PUSHB_1,
2156 bci_round,
2157 CALL, /* s: edge2 cur_len edge org_center cur_pos1 */
2159 DUP,
2160 ROLL,
2161 ROLL,
2162 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
2164 DUP,
2165 PUSHB_1,
2166 sal_u_off,
2168 ADD,
2169 ABS, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) error1 */
2171 SWAP,
2172 PUSHB_1,
2173 sal_d_off,
2175 SUB,
2176 ABS, /* s: edge2 cur_len edge cur_pos1 error1 error2 */
2178 LT, /* error1 < error2 */
2180 PUSHB_1,
2181 sal_u_off,
2183 SUB, /* cur_pos1 = cur_pos1 - u_off */
2185 ELSE,
2186 PUSHB_1,
2187 sal_d_off,
2189 ADD, /* cur_pos1 = cur_pos1 + d_off */
2190 EIF, /* s: edge2 cur_len edge cur_pos1 */
2192 PUSHB_1,
2194 CINDEX,
2195 PUSHB_1,
2196 2*64,
2197 DIV,
2198 SUB, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) */
2200 PUSHB_1,
2202 CINDEX, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) edge */
2203 GC_cur,
2204 SUB,
2205 SHPIX, /* edge = cur_pos1 - cur_len/2 */
2207 SWAP, /* s: cur_len edge2 */
2208 DUP,
2209 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2210 SWAP,
2211 SHPIX, /* edge2 = edge1 + cur_len */
2213 ELSE,
2214 POP, /* s: edge2 edge */
2215 DUP,
2216 DUP,
2217 GC_cur,
2218 SWAP,
2219 GC_orig,
2220 PUSHB_1,
2221 bci_round,
2222 CALL, /* s: edge2 edge edge_pos round(edge_orig_pos) */
2223 SWAP,
2224 SUB,
2225 SHPIX, /* edge = round(edge_orig) */
2227 /* clean up stack */
2228 POP,
2229 EIF,
2231 PUSHB_2,
2232 bci_align_segments,
2234 SZP1, /* set zp1 to normal zone 1 */
2235 CALL,
2237 ENDF,
2243 * bci_action_blue_anchor
2245 * Handle the BLUE_ANCHOR action to align an edge with a blue zone
2246 * and to set the edge anchor.
2248 * in: anchor_point (in twilight zone)
2249 * blue_cvt_idx
2250 * edge_point (in twilight zone)
2251 * ... stuff for bci_align_segments (edge) ...
2253 * sal: sal_anchor
2256 unsigned char FPGM(bci_action_blue_anchor) [] = {
2258 PUSHB_1,
2259 bci_action_blue_anchor,
2260 FDEF,
2262 /* store anchor point number in `sal_anchor' */
2263 PUSHB_1,
2264 sal_anchor,
2265 SWAP,
2268 PUSHB_1,
2270 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2272 /* move `edge_point' to `blue_cvt_idx' position; */
2273 /* note that we can't use MIAP since this would modify */
2274 /* the twilight point's original coordinates also */
2275 RCVT,
2276 SWAP,
2277 DUP,
2278 MDAP_noround, /* set rp0 and rp1 to `edge' */
2279 DUP,
2280 GC_cur, /* s: new_pos edge edge_pos */
2281 ROLL,
2282 SWAP,
2283 SUB, /* s: edge (new_pos - edge_pos) */
2284 SHPIX,
2286 PUSHB_2,
2287 bci_align_segments,
2289 SZP1, /* set zp1 to normal zone 1 */
2290 CALL,
2292 ENDF,
2298 * bci_action_adjust
2300 * Handle the ADJUST action to align an edge of a stem if the other edge
2301 * of the stem has already been moved.
2303 * in: edge2_is_serif
2304 * edge_is_round
2305 * edge_point (in twilight zone)
2306 * edge2_point (in twilight zone)
2307 * ... stuff for bci_align_segments (edge) ...
2310 unsigned char FPGM(bci_action_adjust) [] = {
2312 PUSHB_1,
2313 bci_action_adjust,
2314 FDEF,
2316 PUSHB_1,
2318 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2320 PUSHB_1,
2322 CINDEX, /* s: edge2 edge is_round is_serif edge2 */
2323 PUSHB_1,
2325 CINDEX, /* s: edge2 edge is_round is_serif edge2 edge */
2326 MD_orig_ZP2_0, /* s: edge2 edge is_round is_serif org_len */
2328 PUSHB_1,
2329 bci_compute_stem_width,
2330 CALL,
2331 NEG, /* s: edge2 edge -cur_len */
2333 ROLL,
2334 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2335 SWAP,
2336 DUP,
2337 DUP, /* s: -cur_len edge edge edge */
2338 ALIGNRP, /* align `edge' with `edge2' */
2339 ROLL,
2340 SHPIX, /* shift `edge' by -cur_len */
2342 MDAP_noround, /* set rp0 and rp1 to `edge' */
2344 PUSHB_2,
2345 bci_align_segments,
2347 SZP1, /* set zp1 to normal zone 1 */
2348 CALL,
2350 ENDF,
2356 * bci_action_stem
2358 * Handle the STEM action to align two edges of a stem.
2360 * The code after computing `cur_len' to shift `edge' and `edge2'
2361 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
2363 * if cur_len < 96:
2364 * if cur_len < = 64:
2365 * u_off = 32
2366 * d_off = 32
2367 * else:
2368 * u_off = 38
2369 * d_off = 26
2371 * org_pos = anchor + (edge_orig - anchor_orig);
2372 * org_center = org_pos + org_len / 2;
2374 * cur_pos1 = ROUND(org_center)
2375 * delta1 = ABS(org_center - (cur_pos1 - u_off))
2376 * delta2 = ABS(org_center - (cur_pos1 + d_off))
2377 * if (delta1 < delta2):
2378 * cur_pos1 = cur_pos1 - u_off
2379 * else:
2380 * cur_pos1 = cur_pos1 + d_off
2382 * edge = cur_pos1 - cur_len / 2
2384 * else:
2385 * org_pos = anchor + (edge_orig - anchor_orig)
2386 * org_center = org_pos + org_len / 2;
2388 * cur_pos1 = ROUND(org_pos)
2389 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
2390 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
2391 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
2393 * if (delta1 < delta2):
2394 * edge = cur_pos1
2395 * else:
2396 * edge = cur_pos2
2398 * edge2 = edge + cur_len
2400 * in: edge2_is_serif
2401 * edge_is_round
2402 * edge_point (in twilight zone)
2403 * edge2_point (in twilight zone)
2404 * ... stuff for bci_align_segments (edge) ...
2405 * ... stuff for bci_align_segments (edge2)...
2407 * sal: sal_anchor
2408 * sal_temp1
2409 * sal_temp2
2410 * sal_temp3
2413 #undef sal_u_off
2414 #define sal_u_off sal_temp1
2415 #undef sal_d_off
2416 #define sal_d_off sal_temp2
2417 #undef sal_org_len
2418 #define sal_org_len sal_temp3
2419 #undef sal_edge2
2420 #define sal_edge2 sal_temp3
2422 unsigned char FPGM(bci_action_stem) [] = {
2424 PUSHB_1,
2425 bci_action_stem,
2426 FDEF,
2428 PUSHB_1,
2430 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2432 PUSHB_1,
2434 CINDEX,
2435 PUSHB_1,
2437 CINDEX,
2438 DUP, /* s: edge2 edge is_round is_serif edge2 edge edge */
2439 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
2441 MD_orig_ZP2_0, /* s: edge2 edge is_round is_serif org_len */
2442 DUP,
2443 PUSHB_1,
2444 sal_org_len,
2445 SWAP,
2448 PUSHB_1,
2449 bci_compute_stem_width,
2450 CALL, /* s: edge2 edge cur_len */
2452 DUP,
2453 PUSHB_1,
2455 LT, /* cur_len < 96 */
2457 DUP,
2458 PUSHB_1,
2460 LTEQ, /* cur_len <= 64 */
2462 PUSHB_4,
2463 sal_u_off,
2465 sal_d_off,
2468 ELSE,
2469 PUSHB_4,
2470 sal_u_off,
2472 sal_d_off,
2474 EIF,
2478 SWAP, /* s: edge2 cur_len edge */
2479 DUP,
2480 PUSHB_1,
2481 sal_anchor,
2483 DUP, /* s: edge2 cur_len edge edge anchor anchor */
2484 ROLL,
2485 SWAP,
2486 MD_orig_ZP2_0,
2487 SWAP,
2488 GC_cur,
2489 ADD, /* s: edge2 cur_len edge org_pos */
2490 PUSHB_1,
2491 sal_org_len,
2493 PUSHB_1,
2494 2*64,
2495 DIV,
2496 ADD, /* s: edge2 cur_len edge org_center */
2498 DUP,
2499 PUSHB_1,
2500 bci_round,
2501 CALL, /* s: edge2 cur_len edge org_center cur_pos1 */
2503 DUP,
2504 ROLL,
2505 ROLL,
2506 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
2508 DUP,
2509 PUSHB_1,
2510 sal_u_off,
2512 ADD,
2513 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
2515 SWAP,
2516 PUSHB_1,
2517 sal_d_off,
2519 SUB,
2520 ABS, /* s: edge2 cur_len edge cur_pos1 delta1 delta2 */
2522 LT, /* delta1 < delta2 */
2524 PUSHB_1,
2525 sal_u_off,
2527 SUB, /* cur_pos1 = cur_pos1 - u_off */
2529 ELSE,
2530 PUSHB_1,
2531 sal_d_off,
2533 ADD, /* cur_pos1 = cur_pos1 + d_off */
2534 EIF, /* s: edge2 cur_len edge cur_pos1 */
2536 PUSHB_1,
2538 CINDEX,
2539 PUSHB_1,
2540 2*64,
2541 DIV,
2542 SUB, /* arg = cur_pos1 - cur_len/2 */
2544 SWAP, /* s: edge2 cur_len arg edge */
2545 DUP,
2546 PUSHB_1,
2548 MINDEX,
2549 SWAP, /* s: edge2 cur_len edge arg edge */
2550 GC_cur,
2551 SUB,
2552 SHPIX, /* edge = cur_pos1 - cur_len/2 */
2554 ELSE,
2555 SWAP, /* s: edge2 cur_len edge */
2556 PUSHB_1,
2557 sal_anchor,
2559 GC_cur, /* s: edge2 cur_len edge anchor_pos */
2560 PUSHB_1,
2562 CINDEX,
2563 PUSHB_1,
2564 sal_anchor,
2566 MD_orig_ZP2_0,
2567 ADD, /* s: edge2 cur_len edge org_pos */
2569 DUP,
2570 PUSHB_1,
2571 sal_org_len,
2573 PUSHB_1,
2574 2*64,
2575 DIV,
2576 ADD, /* s: edge2 cur_len edge org_pos org_center */
2578 SWAP,
2579 DUP,
2580 PUSHB_1,
2581 bci_round,
2582 CALL, /* cur_pos1 = ROUND(org_pos) */
2583 SWAP,
2584 PUSHB_1,
2585 sal_org_len,
2587 ADD,
2588 PUSHB_1,
2589 bci_round,
2590 CALL,
2591 PUSHB_1,
2593 CINDEX,
2594 SUB, /* s: edge2 cur_len edge org_center cur_pos1 cur_pos2 */
2596 PUSHB_1,
2598 CINDEX,
2599 PUSHB_1,
2600 2*64,
2601 DIV,
2602 PUSHB_1,
2604 MINDEX,
2605 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
2607 DUP,
2608 PUSHB_1,
2610 CINDEX,
2611 ADD,
2612 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
2613 SWAP,
2614 PUSHB_1,
2616 CINDEX,
2617 ADD,
2618 ABS, /* s: edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
2619 LT, /* delta1 < delta2 */
2621 POP, /* arg = cur_pos1 */
2623 ELSE,
2624 SWAP,
2625 POP, /* arg = cur_pos2 */
2626 EIF, /* s: edge2 cur_len edge arg */
2627 SWAP,
2628 DUP,
2629 PUSHB_1,
2631 MINDEX,
2632 SWAP, /* s: edge2 cur_len edge arg edge */
2633 GC_cur,
2634 SUB,
2635 SHPIX, /* edge = arg */
2636 EIF, /* s: edge2 cur_len */
2638 SWAP, /* s: cur_len edge2 */
2639 DUP,
2640 DUP,
2641 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2642 PUSHB_1,
2643 sal_edge2,
2644 SWAP,
2645 WS, /* s: cur_len edge2 */
2646 SWAP,
2647 SHPIX, /* edge2 = edge + cur_len */
2649 PUSHB_2,
2650 bci_align_segments,
2652 SZP1, /* set zp1 to normal zone 1 */
2653 CALL,
2655 PUSHB_1,
2656 sal_edge2,
2658 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2660 PUSHB_1,
2661 bci_align_segments,
2662 CALL,
2663 ENDF,
2669 * bci_action_blue
2671 * Handle the BLUE action to align an edge with a blue zone.
2673 * in: blue_cvt_idx
2674 * edge_point (in twilight zone)
2675 * ... stuff for bci_align_segments (edge) ...
2678 unsigned char FPGM(bci_action_blue) [] = {
2680 PUSHB_1,
2681 bci_action_blue,
2682 FDEF,
2684 PUSHB_1,
2686 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2688 /* move `edge_point' to `blue_cvt_idx' position; */
2689 /* note that we can't use MIAP since this would modify */
2690 /* the twilight point's original coordinates also */
2691 RCVT,
2692 SWAP,
2693 DUP,
2694 MDAP_noround, /* set rp0 and rp1 to `edge' */
2695 DUP,
2696 GC_cur, /* s: new_pos edge edge_pos */
2697 ROLL,
2698 SWAP,
2699 SUB, /* s: edge (new_pos - edge_pos) */
2700 SHPIX,
2702 PUSHB_2,
2703 bci_align_segments,
2705 SZP1, /* set zp1 to normal zone 1 */
2706 CALL,
2708 ENDF,
2714 * bci_action_serif
2716 * Handle the SERIF action to align a serif with its base.
2718 * in: serif_point (in twilight zone)
2719 * base_point (in twilight zone)
2720 * ... stuff for bci_align_segments (serif) ...
2723 unsigned char FPGM(bci_action_serif) [] = {
2725 PUSHB_1,
2726 bci_action_serif,
2727 FDEF,
2729 PUSHB_1,
2731 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2733 DUP,
2734 DUP,
2735 DUP,
2736 PUSHB_1,
2738 MINDEX, /* s: serif serif serif serif base */
2739 DUP,
2740 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2741 MD_orig_ZP2_0,
2742 SWAP,
2743 ALIGNRP, /* align `serif_point' with `base_point' */
2744 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2746 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2748 PUSHB_2,
2749 bci_align_segments,
2751 SZP1, /* set zp1 to normal zone 1 */
2752 CALL,
2754 ENDF,
2760 * bci_action_serif_lower_bound
2762 * Handle the SERIF action to align a serif with its base, then moving it
2763 * again if necessary to stay within a lower bound.
2765 * in: serif_point (in twilight zone)
2766 * base_point (in twilight zone)
2767 * edge[-1] (in twilight zone)
2768 * ... stuff for bci_align_segments (serif) ...
2771 unsigned char FPGM(bci_action_serif_lower_bound) [] = {
2773 PUSHB_1,
2774 bci_action_serif_lower_bound,
2775 FDEF,
2777 PUSHB_1,
2779 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2781 DUP,
2782 DUP,
2783 DUP,
2784 PUSHB_1,
2786 MINDEX, /* s: edge[-1] serif serif serif serif base */
2787 DUP,
2788 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2789 MD_orig_ZP2_0,
2790 SWAP,
2791 ALIGNRP, /* align `serif_point' with `base_point' */
2792 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2794 SWAP, /* s: serif edge[-1] */
2795 DUP,
2796 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2797 GC_cur,
2798 PUSHB_1,
2800 CINDEX,
2801 GC_cur, /* s: serif edge[-1]_pos serif_pos */
2802 GT, /* serif_pos < edge[-1]_pos */
2804 DUP,
2805 ALIGNRP, /* align `serif' to `edge[-1]' */
2806 EIF,
2808 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2810 PUSHB_2,
2811 bci_align_segments,
2813 SZP1, /* set zp1 to normal zone 1 */
2814 CALL,
2816 ENDF,
2822 * bci_action_serif_upper_bound
2824 * Handle the SERIF action to align a serif with its base, then moving it
2825 * again if necessary to stay within an upper bound.
2827 * in: serif_point (in twilight zone)
2828 * base_point (in twilight zone)
2829 * edge[1] (in twilight zone)
2830 * ... stuff for bci_align_segments (serif) ...
2833 unsigned char FPGM(bci_action_serif_upper_bound) [] = {
2835 PUSHB_1,
2836 bci_action_serif_upper_bound,
2837 FDEF,
2839 PUSHB_1,
2841 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2843 DUP,
2844 DUP,
2845 DUP,
2846 PUSHB_1,
2848 MINDEX, /* s: edge[1] serif serif serif serif base */
2849 DUP,
2850 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2851 MD_orig_ZP2_0,
2852 SWAP,
2853 ALIGNRP, /* align `serif_point' with `base_point' */
2854 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2856 SWAP, /* s: serif edge[1] */
2857 DUP,
2858 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2859 GC_cur,
2860 PUSHB_1,
2862 CINDEX,
2863 GC_cur, /* s: serif edge[1]_pos serif_pos */
2864 LT, /* serif_pos > edge[1]_pos */
2866 DUP,
2867 ALIGNRP, /* align `serif' to `edge[1]' */
2868 EIF,
2870 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2872 PUSHB_2,
2873 bci_align_segments,
2875 SZP1, /* set zp1 to normal zone 1 */
2876 CALL,
2878 ENDF,
2884 * bci_action_serif_lower_upper_bound
2886 * Handle the SERIF action to align a serif with its base, then moving it
2887 * again if necessary to stay within a lower and upper bound.
2889 * in: serif_point (in twilight zone)
2890 * base_point (in twilight zone)
2891 * edge[-1] (in twilight zone)
2892 * edge[1] (in twilight zone)
2893 * ... stuff for bci_align_segments (serif) ...
2896 unsigned char FPGM(bci_action_serif_lower_upper_bound) [] = {
2898 PUSHB_1,
2899 bci_action_serif_lower_upper_bound,
2900 FDEF,
2902 PUSHB_1,
2904 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2906 DUP,
2907 DUP,
2908 DUP,
2909 PUSHB_1,
2911 MINDEX, /* s: edge[1] edge[-1] serif serif serif serif base */
2912 DUP,
2913 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2914 MD_orig_ZP2_0,
2915 SWAP,
2916 ALIGNRP, /* align `serif_point' with `base_point' */
2917 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2919 SWAP, /* s: edge[1] serif edge[-1] */
2920 DUP,
2921 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
2922 GC_cur,
2923 PUSHB_1,
2925 CINDEX,
2926 GC_cur, /* s: edge[1] serif edge[-1]_pos serif_pos */
2927 GT, /* serif_pos < edge[-1]_pos */
2929 DUP,
2930 ALIGNRP, /* align `serif' to `edge[-1]' */
2931 EIF,
2933 SWAP, /* s: serif edge[1] */
2934 DUP,
2935 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
2936 GC_cur,
2937 PUSHB_1,
2939 CINDEX,
2940 GC_cur, /* s: serif edge[1]_pos serif_pos */
2941 LT, /* serif_pos > edge[1]_pos */
2943 DUP,
2944 ALIGNRP, /* align `serif' to `edge[1]' */
2945 EIF,
2947 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2949 PUSHB_2,
2950 bci_align_segments,
2952 SZP1, /* set zp1 to normal zone 1 */
2953 CALL,
2955 ENDF,
2961 * bci_action_serif_anchor
2963 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2964 * anchor.
2966 * in: edge_point (in twilight zone)
2967 * ... stuff for bci_align_segments (edge) ...
2970 unsigned char FPGM(bci_action_serif_anchor) [] = {
2972 PUSHB_1,
2973 bci_action_serif_anchor,
2974 FDEF,
2976 PUSHB_1,
2978 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2980 DUP,
2981 PUSHB_1,
2982 sal_anchor,
2983 SWAP,
2984 WS, /* sal_anchor = edge_point */
2986 DUP,
2987 DUP,
2988 DUP,
2989 GC_cur,
2990 SWAP,
2991 GC_orig,
2992 PUSHB_1,
2993 bci_round,
2994 CALL, /* s: edge edge edge_pos round(edge_orig_pos) */
2995 SWAP,
2996 SUB,
2997 SHPIX, /* edge = round(edge_orig) */
2999 MDAP_noround, /* set rp0 and rp1 to `edge' */
3001 PUSHB_2,
3002 bci_align_segments,
3004 SZP1, /* set zp1 to normal zone 1 */
3005 CALL,
3007 ENDF,
3013 * bci_action_serif_anchor_lower_bound
3015 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
3016 * anchor, then moving it again if necessary to stay within a lower
3017 * bound.
3019 * in: edge_point (in twilight zone)
3020 * edge[-1] (in twilight zone)
3021 * ... stuff for bci_align_segments (edge) ...
3024 unsigned char FPGM(bci_action_serif_anchor_lower_bound) [] = {
3026 PUSHB_1,
3027 bci_action_serif_anchor_lower_bound,
3028 FDEF,
3030 PUSHB_1,
3032 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3034 DUP,
3035 PUSHB_1,
3036 sal_anchor,
3037 SWAP,
3038 WS, /* sal_anchor = edge_point */
3040 DUP,
3041 DUP,
3042 DUP,
3043 GC_cur,
3044 SWAP,
3045 GC_orig,
3046 PUSHB_1,
3047 bci_round,
3048 CALL, /* s: edge[-1] edge edge edge_pos round(edge_orig_pos) */
3049 SWAP,
3050 SUB,
3051 SHPIX, /* edge = round(edge_orig) */
3053 SWAP, /* s: edge edge[-1] */
3054 DUP,
3055 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3056 GC_cur,
3057 PUSHB_1,
3059 CINDEX,
3060 GC_cur, /* s: edge edge[-1]_pos edge_pos */
3061 GT, /* edge_pos < edge[-1]_pos */
3063 DUP,
3064 ALIGNRP, /* align `edge' to `edge[-1]' */
3065 EIF,
3067 MDAP_noround, /* set rp0 and rp1 to `edge' */
3069 PUSHB_2,
3070 bci_align_segments,
3072 SZP1, /* set zp1 to normal zone 1 */
3073 CALL,
3075 ENDF,
3081 * bci_action_serif_anchor_upper_bound
3083 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
3084 * anchor, then moving it again if necessary to stay within an upper
3085 * bound.
3087 * in: edge_point (in twilight zone)
3088 * edge[1] (in twilight zone)
3089 * ... stuff for bci_align_segments (edge) ...
3092 unsigned char FPGM(bci_action_serif_anchor_upper_bound) [] = {
3094 PUSHB_1,
3095 bci_action_serif_anchor_upper_bound,
3096 FDEF,
3098 PUSHB_1,
3100 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3102 DUP,
3103 PUSHB_1,
3104 sal_anchor,
3105 SWAP,
3106 WS, /* sal_anchor = edge_point */
3108 DUP,
3109 DUP,
3110 DUP,
3111 GC_cur,
3112 SWAP,
3113 GC_orig,
3114 PUSHB_1,
3115 bci_round,
3116 CALL, /* s: edge[1] edge edge edge_pos round(edge_orig_pos) */
3117 SWAP,
3118 SUB,
3119 SHPIX, /* edge = round(edge_orig) */
3121 SWAP, /* s: edge edge[1] */
3122 DUP,
3123 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3124 GC_cur,
3125 PUSHB_1,
3127 CINDEX,
3128 GC_cur, /* s: edge edge[1]_pos edge_pos */
3129 LT, /* edge_pos > edge[1]_pos */
3131 DUP,
3132 ALIGNRP, /* align `edge' to `edge[1]' */
3133 EIF,
3135 MDAP_noround, /* set rp0 and rp1 to `edge' */
3137 PUSHB_2,
3138 bci_align_segments,
3140 SZP1, /* set zp1 to normal zone 1 */
3141 CALL,
3143 ENDF,
3149 * bci_action_serif_anchor_lower_upper_bound
3151 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
3152 * anchor, then moving it again if necessary to stay within a lower and
3153 * upper bound.
3155 * in: edge_point (in twilight zone)
3156 * edge[-1] (in twilight zone)
3157 * edge[1] (in twilight zone)
3158 * ... stuff for bci_align_segments (edge) ...
3161 unsigned char FPGM(bci_action_serif_anchor_lower_upper_bound) [] = {
3163 PUSHB_1,
3164 bci_action_serif_anchor_lower_upper_bound,
3165 FDEF,
3167 PUSHB_1,
3169 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3171 DUP,
3172 PUSHB_1,
3173 sal_anchor,
3174 SWAP,
3175 WS, /* sal_anchor = edge_point */
3177 DUP,
3178 DUP,
3179 DUP,
3180 GC_cur,
3181 SWAP,
3182 GC_orig,
3183 PUSHB_1,
3184 bci_round,
3185 CALL, /* s: edge[1] edge[-1] edge edge edge_pos round(edge_orig_pos) */
3186 SWAP,
3187 SUB,
3188 SHPIX, /* edge = round(edge_orig) */
3190 SWAP, /* s: edge[1] edge edge[-1] */
3191 DUP,
3192 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3193 GC_cur,
3194 PUSHB_1,
3196 CINDEX,
3197 GC_cur, /* s: edge[1] edge edge[-1]_pos edge_pos */
3198 GT, /* edge_pos < edge[-1]_pos */
3200 DUP,
3201 ALIGNRP, /* align `edge' to `edge[-1]' */
3202 EIF,
3204 SWAP, /* s: edge edge[1] */
3205 DUP,
3206 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3207 GC_cur,
3208 PUSHB_1,
3210 CINDEX,
3211 GC_cur, /* s: edge edge[1]_pos edge_pos */
3212 LT, /* edge_pos > edge[1]_pos */
3214 DUP,
3215 ALIGNRP, /* align `edge' to `edge[1]' */
3216 EIF,
3218 MDAP_noround, /* set rp0 and rp1 to `edge' */
3220 PUSHB_2,
3221 bci_align_segments,
3223 SZP1, /* set zp1 to normal zone 1 */
3224 CALL,
3226 ENDF,
3232 * bci_action_serif_link1
3234 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3235 * before and after.
3237 * in: before_point (in twilight zone)
3238 * edge_point (in twilight zone)
3239 * after_point (in twilight zone)
3240 * ... stuff for bci_align_segments (edge) ...
3243 unsigned char FPGM(bci_action_serif_link1) [] = {
3245 PUSHB_1,
3246 bci_action_serif_link1,
3247 FDEF,
3249 PUSHB_1,
3251 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3253 PUSHB_1,
3255 CINDEX, /* s: after edge before after */
3256 PUSHB_1,
3258 CINDEX, /* s: after edge before after before */
3259 MD_orig_ZP2_0,
3260 PUSHB_1,
3262 EQ, /* after_orig_pos == before_orig_pos */
3263 IF, /* s: after edge before */
3264 MDAP_noround, /* set rp0 and rp1 to `before' */
3265 DUP,
3266 ALIGNRP, /* align `edge' with `before' */
3267 SWAP,
3268 POP,
3270 ELSE,
3271 PUSHB_1,
3273 CINDEX, /* s: ... after edge before edge */
3274 PUSHB_1,
3276 CINDEX, /* s: ... after edge before edge before */
3277 MD_orig_ZP2_0, /* a = edge_orig_pos - before_orig_pos */
3278 PUSHW_1,
3279 0x10, /* 64*64 */
3280 0x00,
3281 MUL,
3283 PUSHB_1,
3285 CINDEX, /* s: ... after edge before a*64 after */
3286 PUSHB_1,
3288 CINDEX, /* s: ... after edge before a*64 after before */
3289 MD_cur, /* b = after_pos - before_pos */
3290 MUL, /* s: ... after edge before a*b */
3292 PUSHB_1,
3294 CINDEX, /* s: ... after edge before a*b after */
3295 PUSHB_1,
3297 CINDEX, /* s: ... after edge before a*b after before */
3298 MD_orig_ZP2_0, /* c = after_orig_pos - before_orig_pos */
3299 PUSHW_1,
3300 0x10, /* 64*64 */
3301 0x00,
3302 MUL,
3304 DIV, /* s: after edge before a*b/c */
3306 SWAP,
3307 MDAP_noround, /* set rp0 and rp1 to `before' */
3308 SWAP, /* s: after a*b/c edge */
3309 DUP,
3310 DUP,
3311 ALIGNRP, /* align `edge' with `before' */
3312 ROLL,
3313 SHPIX, /* shift `edge' by `a*b/c' */
3315 SWAP, /* s: edge after */
3316 POP,
3317 EIF,
3319 MDAP_noround, /* set rp0 and rp1 to `edge' */
3321 PUSHB_2,
3322 bci_align_segments,
3324 SZP1, /* set zp1 to normal zone 1 */
3325 CALL,
3327 ENDF,
3333 * bci_action_serif_link1_lower_bound
3335 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3336 * before and after. Additionally, move the serif again if necessary to
3337 * stay within a lower bound.
3339 * in: before_point (in twilight zone)
3340 * edge_point (in twilight zone)
3341 * after_point (in twilight zone)
3342 * edge[-1] (in twilight zone)
3343 * ... stuff for bci_align_segments (edge) ...
3346 unsigned char FPGM(bci_action_serif_link1_lower_bound) [] = {
3348 PUSHB_1,
3349 bci_action_serif_link1_lower_bound,
3350 FDEF,
3352 PUSHB_1,
3354 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3356 PUSHB_1,
3358 CINDEX, /* s: edge[-1] after edge before after */
3359 PUSHB_1,
3361 CINDEX, /* s: edge[-1] after edge before after before */
3362 MD_orig_ZP2_0,
3363 PUSHB_1,
3365 EQ, /* after_orig_pos == before_orig_pos */
3366 IF, /* s: edge[-1] after edge before */
3367 MDAP_noround, /* set rp0 and rp1 to `before' */
3368 DUP,
3369 ALIGNRP, /* align `edge' with `before' */
3370 SWAP,
3371 POP,
3373 ELSE,
3374 PUSHB_1,
3376 CINDEX, /* s: ... after edge before edge */
3377 PUSHB_1,
3379 CINDEX, /* s: ... after edge before edge before */
3380 MD_orig_ZP2_0, /* a = edge_orig_pos - before_orig_pos */
3381 PUSHW_1,
3382 0x10, /* 64*64 */
3383 0x00,
3384 MUL,
3386 PUSHB_1,
3388 CINDEX, /* s: ... after edge before a*64 after */
3389 PUSHB_1,
3391 CINDEX, /* s: ... after edge before a*64 after before */
3392 MD_cur, /* b = after_pos - before_pos */
3393 MUL, /* s: ... after edge before a*b */
3395 PUSHB_1,
3397 CINDEX, /* s: ... after edge before a*b after */
3398 PUSHB_1,
3400 CINDEX, /* s: ... after edge before a*b after before */
3401 MD_orig_ZP2_0, /* c = after_orig_pos - before_orig_pos */
3402 PUSHW_1,
3403 0x10, /* 64*64 */
3404 0x00,
3405 MUL,
3407 DIV, /* s: edge[-1] after edge before a*b/c */
3409 SWAP,
3410 MDAP_noround, /* set rp0 and rp1 to `before' */
3411 SWAP, /* s: edge[-1] after a*b/c edge */
3412 DUP,
3413 DUP,
3414 ALIGNRP, /* align `edge' with `before' */
3415 ROLL,
3416 SHPIX, /* shift `edge' by `a*b/c' */
3418 SWAP, /* s: edge[-1] edge after */
3419 POP,
3420 EIF,
3422 SWAP, /* s: edge edge[-1] */
3423 DUP,
3424 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3425 GC_cur,
3426 PUSHB_1,
3428 CINDEX,
3429 GC_cur, /* s: edge edge[-1]_pos edge_pos */
3430 GT, /* edge_pos < edge[-1]_pos */
3432 DUP,
3433 ALIGNRP, /* align `edge' to `edge[-1]' */
3434 EIF,
3436 MDAP_noround, /* set rp0 and rp1 to `edge' */
3438 PUSHB_2,
3439 bci_align_segments,
3441 SZP1, /* set zp1 to normal zone 1 */
3442 CALL,
3443 ENDF,
3449 * bci_action_serif_link1_upper_bound
3451 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3452 * before and after. Additionally, move the serif again if necessary to
3453 * stay within an upper bound.
3455 * in: before_point (in twilight zone)
3456 * edge_point (in twilight zone)
3457 * after_point (in twilight zone)
3458 * edge[1] (in twilight zone)
3459 * ... stuff for bci_align_segments (edge) ...
3462 unsigned char FPGM(bci_action_serif_link1_upper_bound) [] = {
3464 PUSHB_1,
3465 bci_action_serif_link1_upper_bound,
3466 FDEF,
3468 PUSHB_1,
3470 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3472 PUSHB_1,
3474 CINDEX, /* s: edge[1] after edge before after */
3475 PUSHB_1,
3477 CINDEX, /* s: edge[1] after edge before after before */
3478 MD_orig_ZP2_0,
3479 PUSHB_1,
3481 EQ, /* after_orig_pos == before_orig_pos */
3482 IF, /* s: edge[1] after edge before */
3483 MDAP_noround, /* set rp0 and rp1 to `before' */
3484 DUP,
3485 ALIGNRP, /* align `edge' with `before' */
3486 SWAP,
3487 POP,
3489 ELSE,
3490 PUSHB_1,
3492 CINDEX, /* s: ... after edge before edge */
3493 PUSHB_1,
3495 CINDEX, /* s: ... after edge before edge before */
3496 MD_orig_ZP2_0, /* a = edge_orig_pos - before_orig_pos */
3497 PUSHW_1,
3498 0x10, /* 64*64 */
3499 0x00,
3500 MUL,
3502 PUSHB_1,
3504 CINDEX, /* s: ... after edge before a*64 after */
3505 PUSHB_1,
3507 CINDEX, /* s: ... after edge before a*64 after before */
3508 MD_cur, /* b = after_pos - before_pos */
3509 MUL, /* s: ... after edge before a*b */
3511 PUSHB_1,
3513 CINDEX, /* s: ... after edge before a*b after */
3514 PUSHB_1,
3516 CINDEX, /* s: ... after edge before a*b after before */
3517 MD_orig_ZP2_0, /* c = after_orig_pos - before_orig_pos */
3518 PUSHW_1,
3519 0x10, /* 64*64 */
3520 0x00,
3521 MUL,
3523 DIV, /* s: edge[1] after edge before a*b/c */
3525 SWAP,
3526 MDAP_noround, /* set rp0 and rp1 to `before' */
3527 SWAP, /* s: edge[1] after a*b/c edge */
3528 DUP,
3529 DUP,
3530 ALIGNRP, /* align `edge' with `before' */
3531 ROLL,
3532 SHPIX, /* shift `edge' by `a*b/c' */
3534 SWAP, /* s: edge[1] edge after */
3535 POP,
3536 EIF,
3538 SWAP, /* s: edge edge[1] */
3539 DUP,
3540 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3541 GC_cur,
3542 PUSHB_1,
3544 CINDEX,
3545 GC_cur, /* s: edge edge[1]_pos edge_pos */
3546 LT, /* edge_pos > edge[1]_pos */
3548 DUP,
3549 ALIGNRP, /* align `edge' to `edge[1]' */
3550 EIF,
3552 MDAP_noround, /* set rp0 and rp1 to `edge' */
3554 PUSHB_2,
3555 bci_align_segments,
3557 SZP1, /* set zp1 to normal zone 1 */
3558 CALL,
3560 ENDF,
3566 * bci_action_serif_link1_lower_upper_bound
3568 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3569 * before and after. Additionally, move the serif again if necessary to
3570 * stay within a lower and upper bound.
3572 * in: before_point (in twilight zone)
3573 * edge_point (in twilight zone)
3574 * after_point (in twilight zone)
3575 * edge[-1] (in twilight zone)
3576 * edge[1] (in twilight zone)
3577 * ... stuff for bci_align_segments (edge) ...
3580 unsigned char FPGM(bci_action_serif_link1_lower_upper_bound) [] = {
3582 PUSHB_1,
3583 bci_action_serif_link1_lower_upper_bound,
3584 FDEF,
3586 PUSHB_1,
3588 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3590 PUSHB_1,
3592 CINDEX, /* s: edge[1] edge[-1] after edge before after */
3593 PUSHB_1,
3595 CINDEX, /* s: edge[1] edge[-1] after edge before after before */
3596 MD_orig_ZP2_0,
3597 PUSHB_1,
3599 EQ, /* after_orig_pos == before_orig_pos */
3600 IF, /* s: edge[1] edge[-1] after edge before */
3601 MDAP_noround, /* set rp0 and rp1 to `before' */
3602 DUP,
3603 ALIGNRP, /* align `edge' with `before' */
3604 SWAP,
3605 POP,
3607 ELSE,
3608 PUSHB_1,
3610 CINDEX, /* s: ... after edge before edge */
3611 PUSHB_1,
3613 CINDEX, /* s: ... after edge before edge before */
3614 MD_orig_ZP2_0, /* a = edge_orig_pos - before_orig_pos */
3615 PUSHW_1,
3616 0x10, /* 64*64 */
3617 0x00,
3618 MUL,
3620 PUSHB_1,
3622 CINDEX, /* s: ... after edge before a*64 after */
3623 PUSHB_1,
3625 CINDEX, /* s: ... after edge before a*64 after before */
3626 MD_cur, /* b = after_pos - before_pos */
3627 MUL, /* s: ... after edge before a*b */
3629 PUSHB_1,
3631 CINDEX, /* s: ... after edge before a*b after */
3632 PUSHB_1,
3634 CINDEX, /* s: ... after edge before a*b after_orig before */
3635 MD_orig_ZP2_0, /* c = after_orig_pos - before_orig_pos */
3636 PUSHW_1,
3637 0x10, /* 64*64 */
3638 0x00,
3639 MUL,
3641 DIV, /* s: edge[1] edge[-1] after edge before a*b/c */
3643 SWAP,
3644 MDAP_noround, /* set rp0 and rp1 to `before' */
3645 SWAP, /* s: edge[1] edge[-1] after a*b/c edge */
3646 DUP,
3647 DUP,
3648 ALIGNRP, /* align `edge' with `before' */
3649 ROLL,
3650 SHPIX, /* shift `edge' by `a*b/c' */
3652 SWAP, /* s: edge[1] edge[-1] edge after */
3653 POP,
3654 EIF,
3656 SWAP, /* s: edge[1] edge edge[-1] */
3657 DUP,
3658 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3659 GC_cur,
3660 PUSHB_1,
3662 CINDEX,
3663 GC_cur, /* s: edge[1] edge edge[-1]_pos edge_pos */
3664 GT, /* edge_pos < edge[-1]_pos */
3666 DUP,
3667 ALIGNRP, /* align `edge' to `edge[-1]' */
3668 EIF,
3670 SWAP, /* s: edge edge[1] */
3671 DUP,
3672 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3673 GC_cur,
3674 PUSHB_1,
3676 CINDEX,
3677 GC_cur, /* s: edge edge[1]_pos edge_pos */
3678 LT, /* edge_pos > edge[1]_pos */
3680 DUP,
3681 ALIGNRP, /* align `edge' to `edge[1]' */
3682 EIF,
3684 MDAP_noround, /* set rp0 and rp1 to `edge' */
3686 PUSHB_2,
3687 bci_align_segments,
3689 SZP1, /* set zp1 to normal zone 1 */
3690 CALL,
3692 ENDF,
3698 * bci_action_serif_link2
3700 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3702 * in: edge_point (in twilight zone)
3703 * ... stuff for bci_align_segments (edge) ...
3706 unsigned char FPGM(bci_action_serif_link2) [] = {
3708 PUSHB_1,
3709 bci_action_serif_link2,
3710 FDEF,
3712 PUSHB_1,
3714 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3716 DUP, /* s: edge edge */
3717 PUSHB_1,
3718 sal_anchor,
3720 DUP, /* s: edge edge anchor anchor */
3721 MDAP_noround, /* set rp0 and rp1 to `sal_anchor' */
3723 MD_orig_ZP2_0,
3724 DUP,
3725 ADD,
3726 PUSHB_1,
3728 ADD,
3729 FLOOR,
3730 PUSHB_1,
3731 2*64,
3732 DIV, /* delta = (edge_orig_pos - anchor_orig_pos + 16) & ~31 */
3734 SWAP,
3735 DUP,
3736 DUP,
3737 ALIGNRP, /* align `edge' with `sal_anchor' */
3738 ROLL,
3739 SHPIX, /* shift `edge' by `delta' */
3741 MDAP_noround, /* set rp0 and rp1 to `edge' */
3743 PUSHB_2,
3744 bci_align_segments,
3746 SZP1, /* set zp1 to normal zone 1 */
3747 CALL,
3749 ENDF,
3755 * bci_action_serif_link2_lower_bound
3757 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3758 * Additionally, move the serif again if necessary to stay within a lower
3759 * bound.
3761 * in: edge_point (in twilight zone)
3762 * edge[-1] (in twilight zone)
3763 * ... stuff for bci_align_segments (edge) ...
3766 unsigned char FPGM(bci_action_serif_link2_lower_bound) [] = {
3768 PUSHB_1,
3769 bci_action_serif_link2_lower_bound,
3770 FDEF,
3772 PUSHB_1,
3774 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3776 DUP, /* s: edge[-1] edge edge */
3777 PUSHB_1,
3778 sal_anchor,
3780 DUP, /* s: edge[-1] edge edge anchor anchor */
3781 MDAP_noround, /* set rp0 and rp1 to `sal_anchor' */
3783 MD_orig_ZP2_0,
3784 DUP,
3785 ADD,
3786 PUSHB_1,
3788 ADD,
3789 FLOOR,
3790 PUSHB_1,
3791 2*64,
3792 DIV, /* delta = (edge_orig_pos - anchor_orig_pos + 16) & ~31 */
3794 SWAP,
3795 DUP,
3796 DUP,
3797 ALIGNRP, /* align `edge' with `sal_anchor' */
3798 ROLL,
3799 SHPIX, /* shift `edge' by `delta' */
3801 SWAP, /* s: edge edge[-1] */
3802 DUP,
3803 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3804 GC_cur,
3805 PUSHB_1,
3807 CINDEX,
3808 GC_cur, /* s: edge edge[-1]_pos edge_pos */
3809 GT, /* edge_pos < edge[-1]_pos */
3811 DUP,
3812 ALIGNRP, /* align `edge' to `edge[-1]' */
3813 EIF,
3815 MDAP_noround, /* set rp0 and rp1 to `edge' */
3817 PUSHB_2,
3818 bci_align_segments,
3820 SZP1, /* set zp1 to normal zone 1 */
3821 CALL,
3823 ENDF,
3829 * bci_action_serif_link2_upper_bound
3831 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3832 * Additionally, move the serif again if necessary to stay within an upper
3833 * bound.
3835 * in: edge_point (in twilight zone)
3836 * edge[1] (in twilight zone)
3837 * ... stuff for bci_align_segments (edge) ...
3840 unsigned char FPGM(bci_action_serif_link2_upper_bound) [] = {
3842 PUSHB_1,
3843 bci_action_serif_link2_upper_bound,
3844 FDEF,
3846 PUSHB_1,
3848 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3850 DUP, /* s: edge[1] edge edge */
3851 PUSHB_1,
3852 sal_anchor,
3854 DUP, /* s: edge[1] edge edge anchor anchor */
3855 MDAP_noround, /* set rp0 and rp1 to `sal_anchor' */
3857 MD_orig_ZP2_0,
3858 DUP,
3859 ADD,
3860 PUSHB_1,
3862 ADD,
3863 FLOOR,
3864 PUSHB_1,
3865 2*64,
3866 DIV, /* delta = (edge_orig_pos - anchor_orig_pos + 16) & ~31 */
3868 SWAP,
3869 DUP,
3870 DUP,
3871 ALIGNRP, /* align `edge' with `sal_anchor' */
3872 ROLL,
3873 SHPIX, /* shift `edge' by `delta' */
3875 SWAP, /* s: edge edge[1] */
3876 DUP,
3877 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3878 GC_cur,
3879 PUSHB_1,
3881 CINDEX,
3882 GC_cur, /* s: edge edge[1]_pos edge_pos */
3883 LT, /* edge_pos > edge[1]_pos */
3885 DUP,
3886 ALIGNRP, /* align `edge' to `edge[1]' */
3887 EIF,
3889 MDAP_noround, /* set rp0 and rp1 to `edge' */
3891 PUSHB_2,
3892 bci_align_segments,
3894 SZP1, /* set zp1 to normal zone 1 */
3895 CALL,
3897 ENDF,
3903 * bci_action_serif_link2_lower_upper_bound
3905 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3906 * Additionally, move the serif again if necessary to stay within a lower
3907 * and upper bound.
3909 * in: edge_point (in twilight zone)
3910 * edge[-1] (in twilight zone)
3911 * edge[1] (in twilight zone)
3912 * ... stuff for bci_align_segments (edge) ...
3915 unsigned char FPGM(bci_action_serif_link2_lower_upper_bound) [] = {
3917 PUSHB_1,
3918 bci_action_serif_link2_lower_upper_bound,
3919 FDEF,
3921 PUSHB_1,
3923 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3925 DUP, /* s: edge[1] edge[-1] edge edge */
3926 PUSHB_1,
3927 sal_anchor,
3929 DUP, /* s: edge[1] edge[-1] edge edge anchor anchor */
3930 MDAP_noround, /* set rp0 and rp1 to `sal_anchor' */
3932 MD_orig_ZP2_0,
3933 DUP,
3934 ADD,
3935 PUSHB_1,
3937 ADD,
3938 FLOOR,
3939 PUSHB_1,
3940 2*64,
3941 DIV, /* delta = (edge_orig_pos - anchor_orig_pos + 16) & ~31 */
3943 SWAP,
3944 DUP,
3945 DUP,
3946 ALIGNRP, /* align `edge' with `sal_anchor' */
3947 ROLL,
3948 SHPIX, /* shift `edge' by `delta' */
3950 SWAP, /* s: edge[1] edge edge[-1] */
3951 DUP,
3952 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
3953 GC_cur,
3954 PUSHB_1,
3956 CINDEX,
3957 GC_cur, /* s: edge[1] edge edge[-1]_pos edge_pos */
3958 GT, /* edge_pos < edge[-1]_pos */
3960 DUP,
3961 ALIGNRP, /* align `edge' to `edge[-1]' */
3962 EIF,
3964 SWAP, /* s: edge edge[1] */
3965 DUP,
3966 MDAP_noround, /* set rp0 and rp1 to `edge[1]' */
3967 GC_cur,
3968 PUSHB_1,
3970 CINDEX,
3971 GC_cur, /* s: edge edge[1]_pos edge_pos */
3972 LT, /* edge_pos > edge[1]_pos */
3974 DUP,
3975 ALIGNRP, /* align `edge' to `edge[1]' */
3976 EIF,
3978 MDAP_noround, /* set rp0 and rp1 to `edge' */
3980 PUSHB_2,
3981 bci_align_segments,
3983 SZP1, /* set zp1 to normal zone 1 */
3984 CALL,
3986 ENDF,
3992 * bci_handle_action
3994 * Execute function.
3996 * in: function_index
3999 unsigned char FPGM(bci_handle_action) [] = {
4001 PUSHB_1,
4002 bci_handle_action,
4003 FDEF,
4005 CALL,
4007 ENDF,
4013 * bci_hint_glyph
4015 * This is the top-level glyph hinting function
4016 * which parses the arguments on the stack and calls subroutines.
4018 * in: num_actions (M)
4019 * action_0_func_idx
4020 * ... data ...
4021 * action_1_func_idx
4022 * ... data ...
4023 * ...
4024 * action_M_func_idx
4025 * ... data ...
4027 * uses: bci_handle_action
4029 * bci_action_ip_before
4030 * bci_action_ip_after
4031 * bci_action_ip_on
4032 * bci_action_ip_between
4034 * bci_action_adjust_bound
4035 * bci_action_stem_bound
4037 * bci_action_link
4038 * bci_action_anchor
4039 * bci_action_blue_anchor
4040 * bci_action_adjust
4041 * bci_action_stem
4042 * bci_action_blue
4044 * bci_action_serif
4045 * bci_action_serif_lower_bound
4046 * bci_action_serif_upper_bound
4047 * bci_action_serif_lower_upper_bound
4049 * bci_action_serif_anchor
4050 * bci_action_serif_anchor_lower_bound
4051 * bci_action_serif_anchor_upper_bound
4052 * bci_action_serif_anchor_lower_upper_bound
4054 * bci_action_serif_link1
4055 * bci_action_serif_link1_lower_bound
4056 * bci_action_serif_link1_upper_bound
4057 * bci_action_serif_link1_lower_upper_bound
4059 * bci_action_serif_link2
4060 * bci_action_serif_link2_lower_bound
4061 * bci_action_serif_link2_upper_bound
4062 * bci_action_serif_link2_lower_upper_bound
4065 unsigned char FPGM(bci_hint_glyph) [] = {
4067 PUSHB_1,
4068 bci_hint_glyph,
4069 FDEF,
4071 PUSHB_1,
4072 bci_handle_action,
4073 LOOPCALL,
4075 PUSHB_1,
4077 SZP2, /* set zp2 to normal zone 1 */
4078 IUP_y,
4080 ENDF,
4085 #define COPY_FPGM(func_name) \
4086 memcpy(buf_p, fpgm_ ## func_name, \
4087 sizeof (fpgm_ ## func_name)); \
4088 buf_p += sizeof (fpgm_ ## func_name) \
4090 static FT_Error
4091 TA_table_build_fpgm(FT_Byte** fpgm,
4092 FT_ULong* fpgm_len,
4093 FONT* font)
4095 FT_UInt buf_len;
4096 FT_UInt len;
4097 FT_Byte* buf;
4098 FT_Byte* buf_p;
4101 buf_len = sizeof (FPGM(bci_round))
4102 + sizeof (FPGM(bci_compute_stem_width_a))
4104 + sizeof (FPGM(bci_compute_stem_width_b))
4106 + sizeof (FPGM(bci_compute_stem_width_c))
4107 + sizeof (FPGM(bci_loop))
4108 + sizeof (FPGM(bci_cvt_rescale))
4109 + sizeof (FPGM(bci_blue_round_a))
4111 + sizeof (FPGM(bci_blue_round_b))
4112 + sizeof (FPGM(bci_get_point_extrema))
4114 + sizeof (FPGM(bci_create_segment))
4115 + sizeof (FPGM(bci_create_segments))
4116 + sizeof (FPGM(bci_align_segment))
4117 + sizeof (FPGM(bci_align_segments))
4119 + sizeof (FPGM(bci_scale_contour))
4120 + sizeof (FPGM(bci_scale_glyph))
4121 + sizeof (FPGM(bci_shift_contour))
4122 + sizeof (FPGM(bci_shift_subglyph))
4124 + sizeof (FPGM(bci_ip_outer_align_point))
4125 + sizeof (FPGM(bci_ip_on_align_points))
4126 + sizeof (FPGM(bci_ip_between_align_point))
4127 + sizeof (FPGM(bci_ip_between_align_points))
4129 + sizeof (FPGM(bci_action_ip_before))
4130 + sizeof (FPGM(bci_action_ip_after))
4131 + sizeof (FPGM(bci_action_ip_on))
4132 + sizeof (FPGM(bci_action_ip_between))
4134 + sizeof (FPGM(bci_action_adjust_bound))
4135 + sizeof (FPGM(bci_action_stem_bound))
4136 + sizeof (FPGM(bci_action_link))
4137 + sizeof (FPGM(bci_action_anchor))
4138 + sizeof (FPGM(bci_action_blue_anchor))
4139 + sizeof (FPGM(bci_action_adjust))
4140 + sizeof (FPGM(bci_action_stem))
4141 + sizeof (FPGM(bci_action_blue))
4142 + sizeof (FPGM(bci_action_serif))
4143 + sizeof (FPGM(bci_action_serif_lower_bound))
4144 + sizeof (FPGM(bci_action_serif_upper_bound))
4145 + sizeof (FPGM(bci_action_serif_lower_upper_bound))
4146 + sizeof (FPGM(bci_action_serif_anchor))
4147 + sizeof (FPGM(bci_action_serif_anchor_lower_bound))
4148 + sizeof (FPGM(bci_action_serif_anchor_upper_bound))
4149 + sizeof (FPGM(bci_action_serif_anchor_lower_upper_bound))
4150 + sizeof (FPGM(bci_action_serif_link1))
4151 + sizeof (FPGM(bci_action_serif_link1_lower_bound))
4152 + sizeof (FPGM(bci_action_serif_link1_upper_bound))
4153 + sizeof (FPGM(bci_action_serif_link1_lower_upper_bound))
4154 + sizeof (FPGM(bci_action_serif_link2))
4155 + sizeof (FPGM(bci_action_serif_link2_lower_bound))
4156 + sizeof (FPGM(bci_action_serif_link2_upper_bound))
4157 + sizeof (FPGM(bci_action_serif_link2_lower_upper_bound))
4159 + sizeof (FPGM(bci_handle_action))
4160 + sizeof (FPGM(bci_hint_glyph));
4161 /* buffer length must be a multiple of four */
4162 len = (buf_len + 3) & ~3;
4163 buf = (FT_Byte*)malloc(len);
4164 if (!buf)
4165 return FT_Err_Out_Of_Memory;
4167 /* pad end of buffer with zeros */
4168 buf[len - 1] = 0x00;
4169 buf[len - 2] = 0x00;
4170 buf[len - 3] = 0x00;
4172 /* copy font program into buffer and fill in the missing variables */
4173 buf_p = buf;
4175 COPY_FPGM(bci_round);
4176 COPY_FPGM(bci_compute_stem_width_a);
4177 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
4178 COPY_FPGM(bci_compute_stem_width_b);
4179 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
4180 COPY_FPGM(bci_compute_stem_width_c);
4181 COPY_FPGM(bci_loop);
4182 COPY_FPGM(bci_cvt_rescale);
4183 COPY_FPGM(bci_blue_round_a);
4184 *(buf_p++) = (unsigned char)CVT_BLUES_SIZE(font);
4185 COPY_FPGM(bci_blue_round_b);
4186 COPY_FPGM(bci_get_point_extrema);
4188 COPY_FPGM(bci_create_segment);
4189 COPY_FPGM(bci_create_segments);
4190 COPY_FPGM(bci_align_segment);
4191 COPY_FPGM(bci_align_segments);
4193 COPY_FPGM(bci_scale_contour);
4194 COPY_FPGM(bci_scale_glyph);
4195 COPY_FPGM(bci_shift_contour);
4196 COPY_FPGM(bci_shift_subglyph);
4198 COPY_FPGM(bci_ip_outer_align_point);
4199 COPY_FPGM(bci_ip_on_align_points);
4200 COPY_FPGM(bci_ip_between_align_point);
4201 COPY_FPGM(bci_ip_between_align_points);
4203 COPY_FPGM(bci_action_ip_before);
4204 COPY_FPGM(bci_action_ip_after);
4205 COPY_FPGM(bci_action_ip_on);
4206 COPY_FPGM(bci_action_ip_between);
4208 COPY_FPGM(bci_action_adjust_bound);
4209 COPY_FPGM(bci_action_stem_bound);
4210 COPY_FPGM(bci_action_link);
4211 COPY_FPGM(bci_action_anchor);
4212 COPY_FPGM(bci_action_blue_anchor);
4213 COPY_FPGM(bci_action_adjust);
4214 COPY_FPGM(bci_action_stem);
4215 COPY_FPGM(bci_action_blue);
4216 COPY_FPGM(bci_action_serif);
4217 COPY_FPGM(bci_action_serif_lower_bound);
4218 COPY_FPGM(bci_action_serif_upper_bound);
4219 COPY_FPGM(bci_action_serif_lower_upper_bound);
4220 COPY_FPGM(bci_action_serif_anchor);
4221 COPY_FPGM(bci_action_serif_anchor_lower_bound);
4222 COPY_FPGM(bci_action_serif_anchor_upper_bound);
4223 COPY_FPGM(bci_action_serif_anchor_lower_upper_bound);
4224 COPY_FPGM(bci_action_serif_link1);
4225 COPY_FPGM(bci_action_serif_link1_lower_bound);
4226 COPY_FPGM(bci_action_serif_link1_upper_bound);
4227 COPY_FPGM(bci_action_serif_link1_lower_upper_bound);
4228 COPY_FPGM(bci_action_serif_link2);
4229 COPY_FPGM(bci_action_serif_link2_lower_bound);
4230 COPY_FPGM(bci_action_serif_link2_upper_bound);
4231 COPY_FPGM(bci_action_serif_link2_lower_upper_bound);
4233 COPY_FPGM(bci_handle_action);
4234 COPY_FPGM(bci_hint_glyph);
4236 *fpgm = buf;
4237 *fpgm_len = buf_len;
4239 return FT_Err_Ok;
4243 FT_Error
4244 TA_sfnt_build_fpgm_table(SFNT* sfnt,
4245 FONT* font)
4247 FT_Error error;
4249 FT_Byte* fpgm_buf;
4250 FT_ULong fpgm_len;
4253 error = TA_sfnt_add_table_info(sfnt);
4254 if (error)
4255 return error;
4257 error = TA_table_build_fpgm(&fpgm_buf, &fpgm_len, font);
4258 if (error)
4259 return error;
4261 if (fpgm_len > sfnt->max_instructions)
4262 sfnt->max_instructions = fpgm_len;
4264 /* in case of success, `fpgm_buf' gets linked */
4265 /* and is eventually freed in `TA_font_unload' */
4266 error = TA_font_add_table(font,
4267 &sfnt->table_infos[sfnt->num_table_infos - 1],
4268 TTAG_fpgm, fpgm_len, fpgm_buf);
4269 if (error)
4271 free(fpgm_buf);
4272 return error;
4275 return FT_Err_Ok;
4278 /* end of tafpgm.c */