Typo.
[ttfautohint.git] / src / tafpgm.c
blobd756d207d91f6547a6a9275d295997c08cbd20e5
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 /* a convenience shorthand for scaling; see `bci_cvt_rescale' for details */
52 #define DO_SCALE \
53 DUP, /* s: a a */ \
54 PUSHB_1, \
55 cvtl_scale, \
56 RCVT, \
57 MUL, /* delta * 2^10 */ \
58 PUSHB_1, \
59 cvtl_0x10000, \
60 RCVT, \
61 DIV, /* delta */ \
62 ADD /* a + delta */
65 /* in the comments below, the top of the stack (`s:') */
66 /* is the rightmost element; the stack is shown */
67 /* after the instruction on the same line has been executed */
71 * bci_round
73 * Round a 26.6 number. Contrary to the ROUND bytecode instruction, no
74 * engine specific corrections are applied.
76 * in: val
77 * out: ROUND(val)
80 unsigned char FPGM(bci_round) [] = {
82 PUSHB_1,
83 bci_round,
84 FDEF,
86 DUP,
87 ABS,
88 PUSHB_1,
89 32,
90 ADD,
91 FLOOR,
92 SWAP,
93 PUSHB_1,
95 LT,
96 IF,
97 NEG,
98 EIF,
100 ENDF,
106 * bci_compute_stem_width
108 * This is the equivalent to the following code from function
109 * `ta_latin_compute_stem_width':
111 * dist = ABS(width)
113 * if (stem_is_serif
114 * && dist < 3*64)
115 * || is_extra_light:
116 * return width
117 * else if base_is_round:
118 * if dist < 80
119 * dist = 64
120 * else if dist < 56:
121 * dist = 56
123 * delta = ABS(dist - std_width)
125 * if delta < 40:
126 * dist = std_width
127 * if dist < 48
128 * dist = 48
129 * goto End
131 * if dist < 3*64:
132 * delta = dist
133 * dist = FLOOR(dist)
134 * delta = delta - dist
136 * if delta < 10:
137 * dist = dist + delta
138 * else if delta < 32:
139 * dist = dist + 10
140 * else if delta < 54:
141 * dist = dist + 54
142 * else
143 * dist = dist + delta
144 * else
145 * dist = ROUND(dist)
147 * End:
148 * if width < 0:
149 * dist = -dist
150 * return dist
153 * in: width
154 * stem_is_serif
155 * base_is_round
157 * out: new_width
159 * CVT: cvtl_is_extra_light
160 * std_width
163 unsigned char FPGM(bci_compute_stem_width_a) [] = {
165 PUSHB_1,
166 bci_compute_stem_width,
167 FDEF,
169 DUP,
170 ABS, /* s: base_is_round stem_is_serif width dist */
172 DUP,
173 PUSHB_1,
174 3*64,
175 LT, /* dist < 3*64 */
177 PUSHB_1,
179 MINDEX, /* s: base_is_round width dist (dist<3*64) stem_is_serif */
180 AND, /* stem_is_serif && dist < 3*64 */
182 PUSHB_1,
183 cvtl_is_extra_light,
184 RCVT,
185 OR, /* (stem_is_serif && dist < 3*64) || is_extra_light */
187 IF, /* s: base_is_round width dist */
188 POP,
189 SWAP,
190 POP, /* s: width */
192 ELSE,
193 ROLL, /* s: width dist base_is_round */
194 IF, /* s: width dist */
195 DUP,
196 PUSHB_1,
198 LT, /* dist < 80 */
199 IF, /* s: width dist */
200 POP,
201 PUSHB_1,
202 64, /* dist = 64 */
203 EIF,
205 ELSE,
206 DUP,
207 PUSHB_1,
209 LT, /* dist < 56 */
210 IF, /* s: width dist */
211 POP,
212 PUSHB_1,
213 56, /* dist = 56 */
214 EIF,
215 EIF,
217 DUP, /* s: width dist dist */
218 PUSHB_1,
222 /* %c, index of std_width */
224 unsigned char FPGM(bci_compute_stem_width_b) [] = {
226 RCVT,
227 SUB,
228 ABS, /* s: width dist delta */
230 PUSHB_1,
232 LT, /* delta < 40 */
233 IF, /* s: width dist */
234 POP,
235 PUSHB_1,
239 /* %c, index of std_width */
241 unsigned char FPGM(bci_compute_stem_width_c) [] = {
243 RCVT, /* dist = std_width */
244 DUP,
245 PUSHB_1,
247 LT, /* dist < 48 */
249 POP,
250 PUSHB_1,
251 48, /* dist = 48 */
252 EIF,
254 ELSE,
255 DUP, /* s: width dist dist */
256 PUSHB_1,
257 3*64,
258 LT, /* dist < 3*64 */
260 DUP, /* s: width delta dist */
261 FLOOR, /* dist = FLOOR(dist) */
262 DUP, /* s: width delta dist dist */
263 ROLL,
264 ROLL, /* s: width dist delta dist */
265 SUB, /* delta = delta - dist */
267 DUP, /* s: width dist delta delta */
268 PUSHB_1,
270 LT, /* delta < 10 */
271 IF, /* s: width dist delta */
272 ADD, /* dist = dist + delta */
274 ELSE,
275 DUP,
276 PUSHB_1,
278 LT, /* delta < 32 */
280 POP,
281 PUSHB_1,
283 ADD, /* dist = dist + 10 */
285 ELSE,
286 DUP,
287 PUSHB_1,
289 LT, /* delta < 54 */
291 POP,
292 PUSHB_1,
294 ADD, /* dist = dist + 54 */
296 ELSE,
297 ADD, /* dist = dist + delta */
299 EIF,
300 EIF,
301 EIF,
303 ELSE,
304 PUSHB_1,
305 bci_round,
306 CALL, /* dist = round(dist) */
308 EIF,
309 EIF,
311 SWAP, /* s: dist width */
312 PUSHB_1,
314 LT, /* width < 0 */
316 NEG, /* dist = -dist */
317 EIF,
318 EIF,
320 ENDF,
326 * bci_loop
328 * Take a range and a function number and apply the function to all
329 * elements of the range.
331 * in: func_num
332 * end
333 * start
335 * sal: sal_i (counter initialized with `start')
336 * sal_limit (`end')
337 * sal_func (`func_num')
340 unsigned char FPGM(bci_loop) [] = {
342 PUSHB_1,
343 bci_loop,
344 FDEF,
346 PUSHB_1,
347 sal_func,
348 SWAP,
349 WS, /* sal_func = func_num */
350 PUSHB_1,
351 sal_limit,
352 SWAP,
353 WS, /* sal_limit = end */
354 PUSHB_1,
355 sal_i,
356 SWAP,
357 WS, /* sal_i = start */
359 /* start_loop: */
360 PUSHB_1,
361 sal_i,
363 PUSHB_1,
364 sal_limit,
366 LTEQ, /* start <= end */
368 PUSHB_1,
369 sal_func,
371 CALL,
372 PUSHB_3,
373 sal_i,
375 sal_i,
377 ADD, /* start = start + 1 */
380 PUSHB_1,
382 NEG,
383 JMPR, /* goto start_loop */
384 EIF,
386 ENDF,
392 * bci_cvt_rescale
394 * Rescale CVT value by `cvtl_scale' (in 16.16 format).
396 * The scaling factor `cvtl_scale' isn't stored as `b/c' but as `(b-c)/c';
397 * consequently, the calculation `a * b/c' is done as `a + delta' with
398 * `delta = a * (b-c)/c'. This avoids overflow.
400 * sal: sal_i (CVT index)
402 * CVT: cvtl_scale
403 * cvtl_0x10000
406 unsigned char FPGM(bci_cvt_rescale) [] = {
408 PUSHB_1,
409 bci_cvt_rescale,
410 FDEF,
412 PUSHB_1,
413 sal_i,
415 DUP,
416 RCVT,
417 DO_SCALE,
418 WCVTP,
420 ENDF,
426 * bci_blue_round
428 * Round a blue ref value and adjust its corresponding shoot value.
430 * sal: sal_i (CVT index)
434 unsigned char FPGM(bci_blue_round_a) [] = {
436 PUSHB_1,
437 bci_blue_round,
438 FDEF,
440 PUSHB_1,
441 sal_i,
443 DUP,
444 RCVT, /* s: ref_idx ref */
446 DUP,
447 PUSHB_1,
448 bci_round,
449 CALL,
450 SWAP, /* s: ref_idx round(ref) ref */
452 PUSHB_2,
456 /* %c, blue_count */
458 unsigned char FPGM(bci_blue_round_b) [] = {
461 CINDEX,
462 ADD, /* s: ref_idx round(ref) ref shoot_idx */
463 DUP,
464 RCVT, /* s: ref_idx round(ref) ref shoot_idx shoot */
466 ROLL, /* s: ref_idx round(ref) shoot_idx shoot ref */
467 SWAP,
468 SUB, /* s: ref_idx round(ref) shoot_idx dist */
469 DUP,
470 ABS, /* s: ref_idx round(ref) shoot_idx dist delta */
472 DUP,
473 PUSHB_1,
475 LT, /* delta < 32 */
477 POP,
478 PUSHB_1,
479 0, /* delta = 0 */
481 ELSE,
482 PUSHB_1,
484 LT, /* delta < 48 */
486 PUSHB_1,
487 32, /* delta = 32 */
489 ELSE,
490 PUSHB_1,
491 64, /* delta = 64 */
492 EIF,
493 EIF,
495 SWAP, /* s: ref_idx round(ref) shoot_idx delta dist */
496 PUSHB_1,
498 LT, /* dist < 0 */
500 NEG, /* delta = -delta */
501 EIF,
503 PUSHB_1,
505 CINDEX,
506 SWAP,
507 SUB, /* s: ref_idx round(ref) shoot_idx (round(ref) - delta) */
509 WCVTP,
510 WCVTP,
512 ENDF,
518 * bci_get_point_extrema
520 * An auxiliary function for `bci_create_segment'.
522 * in: point-1
523 * out: point
525 * sal: sal_point_min
526 * sal_point_max
529 unsigned char FPGM(bci_get_point_extrema) [] = {
531 PUSHB_1,
532 bci_get_point_extrema,
533 FDEF,
535 PUSHB_1,
537 ADD, /* s: point */
538 DUP,
539 DUP,
541 /* check whether `point' is a new minimum */
542 PUSHB_1,
543 sal_point_min,
544 RS, /* s: point point point point_min */
545 MD_orig,
546 /* if distance is negative, we have a new minimum */
547 PUSHB_1,
550 IF, /* s: point point */
551 DUP,
552 PUSHB_1,
553 sal_point_min,
554 SWAP,
556 EIF,
558 /* check whether `point' is a new maximum */
559 PUSHB_1,
560 sal_point_max,
561 RS, /* s: point point point_max */
562 MD_orig,
563 /* if distance is positive, we have a new maximum */
564 PUSHB_1,
567 IF, /* s: point */
568 DUP,
569 PUSHB_1,
570 sal_point_max,
571 SWAP,
573 EIF, /* s: point */
575 ENDF,
581 * bci_create_segment
583 * Store start and end point of a segment in the storage area,
584 * then construct a point in the twilight zone to represent it.
586 * This function is used by `bci_create_segment_points'.
588 * in: start
589 * end
590 * [last (if wrap-around segment)]
591 * [first (if wrap-around segment)]
593 * uses: bci_get_point_extrema
595 * sal: sal_i (start of current segment)
596 * sal_j (current twilight point)
597 * sal_point_min
598 * sal_point_max
600 * CVT: cvtl_scale
601 * cvtl_0x10000
602 * cvtl_temp
605 unsigned char FPGM(bci_create_segment) [] = {
607 PUSHB_1,
608 bci_create_segment,
609 FDEF,
611 PUSHB_1,
612 sal_i,
614 PUSHB_1,
616 CINDEX,
617 WS, /* sal[sal_i] = start */
619 /* increase `sal_i'; together with the outer loop, this makes sal_i += 2 */
620 PUSHB_3,
621 sal_i,
623 sal_i,
625 ADD, /* sal_i = sal_i + 1 */
628 /* initialize inner loop(s) */
629 PUSHB_2,
630 sal_point_min,
632 CINDEX,
633 WS, /* sal_point_min = start */
634 PUSHB_2,
635 sal_point_max,
637 CINDEX,
638 WS, /* sal_point_max = start */
640 PUSHB_1,
642 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
644 SWAP,
645 DUP,
646 PUSHB_1,
648 CINDEX, /* s: start end end start */
649 LT, /* start > end */
651 /* we have a wrap-around segment with two more arguments */
652 /* to give the last and first point of the contour, respectively; */
653 /* our job is to store a segment `start'-`last', */
654 /* and to get extrema for the two segments */
655 /* `start'-`last' and `first'-`end' */
657 /* s: first last start end */
658 PUSHB_1,
659 sal_i,
661 PUSHB_1,
663 CINDEX,
664 WS, /* sal[sal_i] = last */
666 ROLL,
667 ROLL, /* s: first end last start */
668 DUP,
669 ROLL,
670 SWAP, /* s: first end start last start */
671 SUB, /* s: first end start loop_count */
673 PUSHB_1,
674 bci_get_point_extrema,
675 LOOPCALL,
676 /* clean up stack */
677 POP,
679 SWAP, /* s: end first */
680 PUSHB_1,
682 SUB,
683 DUP,
684 ROLL, /* s: (first - 1) (first - 1) end */
685 SWAP,
686 SUB, /* s: (first - 1) loop_count */
688 PUSHB_1,
689 bci_get_point_extrema,
690 LOOPCALL,
691 /* clean up stack */
692 POP,
694 ELSE, /* s: start end */
695 PUSHB_1,
696 sal_i,
698 PUSHB_1,
700 CINDEX,
701 WS, /* sal[sal_i] = end */
703 PUSHB_1,
705 CINDEX,
706 SUB, /* s: start loop_count */
708 PUSHB_1,
709 bci_get_point_extrema,
710 LOOPCALL,
711 /* clean up stack */
712 POP,
713 EIF,
715 /* the twilight point representing a segment */
716 /* is in the middle between the minimum and maximum */
717 PUSHB_1,
718 sal_point_min,
720 GC_orig,
721 PUSHB_1,
722 sal_point_max,
724 GC_orig,
725 ADD,
726 PUSHB_1,
727 2*64,
728 DIV, /* s: middle_pos */
730 DO_SCALE, /* middle_pos = middle_pos * scale */
732 /* write it to temporary CVT location */
733 PUSHB_2,
734 cvtl_temp,
736 SZP0, /* set zp0 to twilight zone 0 */
737 SWAP,
738 WCVTP,
740 /* create twilight point with index `sal_j' */
741 PUSHB_1,
742 sal_j,
744 PUSHB_1,
745 cvtl_temp,
746 MIAP_noround,
748 PUSHB_3,
749 sal_j,
751 sal_j,
753 ADD, /* twilight_point = twilight_point + 1 */
756 ENDF,
762 * bci_create_segments
764 * Set up segments by defining point ranges which defines them
765 * and computing twilight points to represent them.
767 * in: num_segments (N)
768 * segment_start_0
769 * segment_end_0
770 * [contour_last 0 (if wrap-around segment)]
771 * [contour_first 0 (if wrap-around segment)]
772 * segment_start_1
773 * segment_end_1
774 * [contour_last 0 (if wrap-around segment)]
775 * [contour_first 0 (if wrap-around segment)]
776 * ...
777 * segment_start_(N-1)
778 * segment_end_(N-1)
779 * [contour_last (N-1) (if wrap-around segment)]
780 * [contour_first (N-1) (if wrap-around segment)]
782 * uses: bci_create_segment
784 * sal: sal_i (start of current segment)
785 * sal_j (current twilight point)
788 unsigned char FPGM(bci_create_segments) [] = {
790 PUSHB_1,
791 bci_create_segments,
792 FDEF,
794 /* all our measurements are taken along the y axis */
795 SVTCA_y,
797 DUP,
798 ADD,
799 PUSHB_1,
801 SUB, /* delta = (2*num_segments - 1) */
803 PUSHB_4,
804 sal_segment_offset,
805 sal_segment_offset,
807 sal_j,
809 WS, /* sal_j = 0 (point offset) */
811 ROLL,
812 ADD, /* s: ... sal_segment_offset (sal_segment_offset + delta) */
814 /* `bci_create_segment_point' also increases the loop counter by 1; */
815 /* this effectively means we have a loop step of 2 */
816 PUSHB_2,
817 bci_create_segment,
818 bci_loop,
819 CALL,
821 ENDF,
827 * bci_align_segment
829 * Align all points in a segment to the twilight point in rp0.
830 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
832 * in: segment_index
835 unsigned char FPGM(bci_align_segment) [] = {
837 PUSHB_1,
838 bci_align_segment,
839 FDEF,
841 /* we need the values of `sal_segment_offset + 2*segment_index' */
842 /* and `sal_segment_offset + 2*segment_index + 1' */
843 DUP,
844 ADD,
845 PUSHB_1,
846 sal_segment_offset,
847 ADD,
848 DUP,
850 SWAP,
851 PUSHB_1,
853 ADD,
854 RS, /* s: first last */
856 /* start_loop: */
857 PUSHB_1,
859 CINDEX, /* s: first last first */
860 PUSHB_1,
862 CINDEX, /* s: first last first last */
863 LTEQ, /* first <= end */
864 IF, /* s: first last */
865 SWAP,
866 DUP, /* s: last first first */
867 ALIGNRP, /* align point with index `first' with rp0 */
869 PUSHB_1,
871 ADD, /* first = first + 1 */
872 SWAP, /* s: first last */
874 PUSHB_1,
876 NEG,
877 JMPR, /* goto start_loop */
879 ELSE,
880 POP,
881 POP,
882 EIF,
884 ENDF,
890 * bci_align_segments
892 * Align segments to the twilight point in rp0.
893 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
895 * in: first_segment
896 * loop_counter (N)
897 * segment_1
898 * segment_2
899 * ...
900 * segment_N
902 * uses: handle_segment
906 unsigned char FPGM(bci_align_segments) [] = {
908 PUSHB_1,
909 bci_align_segments,
910 FDEF,
912 PUSHB_1,
913 bci_align_segment,
914 CALL,
916 PUSHB_1,
917 bci_align_segment,
918 LOOPCALL,
920 ENDF,
926 * bci_scale_contour
928 * Scale a contour using two points giving the maximum and minimum
929 * coordinates.
931 * It expects that no point on the contour is touched.
933 * in: min_point
934 * max_point
936 * CVT: cvtl_scale
937 * cvtl_0x10000
940 unsigned char FPGM(bci_scale_contour) [] = {
942 PUSHB_1,
943 bci_scale_contour,
944 FDEF,
946 DUP,
947 DUP,
948 GC_orig,
949 DUP,
950 DO_SCALE, /* min_pos_new = min_pos * scale */
951 SWAP,
952 SUB,
953 SHPIX,
955 /* don't scale a single-point contour twice */
956 SWAP,
957 DUP,
958 ROLL,
959 NEQ,
961 DUP,
962 GC_orig,
963 DUP,
964 DO_SCALE, /* max_pos_new = max_pos * scale */
965 SWAP,
966 SUB,
967 SHPIX,
969 ELSE,
970 POP,
971 EIF,
973 ENDF,
979 * bci_scale_glyph
981 * Scale a glyph using a list of points (two points per contour, giving
982 * the maximum and mininum coordinates).
984 * It expects that no point in the glyph is touched.
986 * in: num_contours (N)
987 * min_point_1
988 * max_point_1
989 * min_point_2
990 * max_point_2
991 * ...
992 * min_point_N
993 * max_point_N
995 * uses: bci_scale_contour
998 unsigned char FPGM(bci_scale_glyph) [] = {
1000 PUSHB_1,
1001 bci_scale_glyph,
1002 FDEF,
1004 SVTCA_y,
1006 PUSHB_1,
1008 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
1010 PUSHB_1,
1011 bci_scale_contour,
1012 LOOPCALL,
1014 PUSHB_1,
1016 SZP2, /* set zp2 to normal zone 1 */
1017 IUP_y,
1019 ENDF,
1025 * bci_shift_contour
1027 * Shift a contour by a given amount.
1029 * It expects that rp1 (pointed to by zp0) is set up properly; zp2 must
1030 * point to the normal zone 1.
1032 * in: contour
1033 * out: contour + 1
1036 unsigned char FPGM(bci_shift_contour) [] = {
1038 PUSHB_1,
1039 bci_shift_contour,
1040 FDEF,
1042 DUP,
1043 SHC_rp1, /* shift `contour' by (rp1_pos - rp1_orig_pos) */
1045 PUSHB_1,
1047 ADD,
1049 ENDF,
1055 * bci_shift_subglyph
1057 * Shift a subglyph. To be more specific, it corrects the already applied
1058 * subglyph offset (if any) from the `glyf' table which needs to be scaled
1059 * also.
1061 * If this function is called, a point `x' in the subglyph has been scaled
1062 * already (during the hinting of the subglyph itself), and `offset' has
1063 * been applied also:
1065 * x -> x * scale + offset (1)
1067 * However, the offset should be applied first, then the scaling:
1069 * x -> (x + offset) * scale (2)
1071 * Our job is now to transform (1) to (2); a simple calculation shows that
1072 * we have to shift all points of the subglyph by
1074 * offset * scale - offset = offset * (scale - 1)
1076 * Note that `cvtl_scale' is equal to the above `scale - 1'.
1078 * in: offset (in FUnits)
1079 * num_contours
1080 * first_contour
1082 * CVT: cvtl_funits_to_pixels
1083 * cvtl_0x10000
1084 * cvtl_scale
1087 unsigned char FPGM(bci_shift_subglyph) [] = {
1089 PUSHB_1,
1090 bci_shift_subglyph,
1091 FDEF,
1093 SVTCA_y,
1095 PUSHB_1,
1096 cvtl_funits_to_pixels,
1097 RCVT, /* scaling factor FUnits -> pixels */
1098 MUL,
1099 PUSHB_1,
1100 cvtl_0x10000,
1101 RCVT,
1102 DIV,
1104 /* the autohinter always rounds offsets */
1105 PUSHB_1,
1106 bci_round,
1107 CALL, /* offset = round(offset) */
1109 PUSHB_1,
1110 cvtl_scale,
1111 RCVT,
1112 MUL,
1113 PUSHB_1,
1114 cvtl_0x10000,
1115 RCVT,
1116 DIV, /* delta = offset * (scale - 1) */
1118 /* and round again */
1119 PUSHB_1,
1120 bci_round,
1121 CALL, /* offset = round(offset) */
1123 PUSHB_1,
1125 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1127 /* we create twilight point 0 as a reference point, */
1128 /* setting the original position to zero (using `cvtl_temp') */
1129 PUSHB_5,
1132 cvtl_temp,
1133 cvtl_temp,
1135 WCVTP,
1136 MIAP_noround, /* rp0 and rp1 now point to twilight point 0 */
1138 SWAP, /* s: first_contour num_contours 0 delta */
1139 SHPIX, /* rp1_pos - rp1_orig_pos = delta */
1141 PUSHB_2,
1142 bci_shift_contour,
1144 SZP2, /* set zp2 to normal zone 1 */
1145 LOOPCALL,
1147 ENDF,
1153 * bci_ip_outer_align_point
1155 * Auxiliary function for `bci_action_ip_before' and
1156 * `bci_action_ip_after'.
1158 * It expects rp0 to contain the edge for alignment, zp0 set to twilight
1159 * zone, and both zp1 and zp2 set to normal zone.
1161 * in: point
1163 * sal: sal_i (edge_orig_pos)
1165 * CVT: cvtl_scale
1166 * cvtl_0x10000
1169 unsigned char FPGM(bci_ip_outer_align_point) [] = {
1171 PUSHB_1,
1172 bci_ip_outer_align_point,
1173 FDEF,
1175 DUP,
1176 ALIGNRP, /* align `point' with `edge' */
1177 DUP,
1178 GC_orig,
1179 DO_SCALE, /* point_orig_pos = point_orig_pos * scale */
1181 PUSHB_1,
1182 sal_i,
1184 SUB, /* s: point (point_orig_pos - edge_orig_pos) */
1185 SHPIX,
1187 ENDF,
1193 * bci_ip_on_align_points
1195 * Auxiliary function for `bci_action_ip_on'.
1197 * in: edge (in twilight zone)
1198 * loop_counter (N)
1199 * point_1
1200 * point_2
1201 * ...
1202 * point_N
1205 unsigned char FPGM(bci_ip_on_align_points) [] = {
1207 PUSHB_1,
1208 bci_ip_on_align_points,
1209 FDEF,
1211 MDAP_noround, /* set rp0 and rp1 to `edge' */
1213 SLOOP,
1214 ALIGNRP,
1216 ENDF,
1222 * bci_ip_between_align_point
1224 * Auxiliary function for `bci_ip_between_align_points'.
1226 * It expects rp0 to contain the edge for alignment, zp0 set to twilight
1227 * zone, and both zp1 and zp2 set to normal zone.
1229 * in: point
1231 * sal: sal_i (edge_orig_pos)
1232 * sal_j (stretch_factor)
1234 * CVT: cvtl_scale
1235 * cvtl_0x10000
1238 unsigned char FPGM(bci_ip_between_align_point) [] = {
1240 PUSHB_1,
1241 bci_ip_between_align_point,
1242 FDEF,
1244 DUP,
1245 ALIGNRP, /* align `point' with `edge' */
1246 DUP,
1247 GC_orig,
1248 DO_SCALE, /* point_orig_pos = point_orig_pos * scale */
1250 PUSHB_1,
1251 sal_i,
1253 SUB, /* s: point (point_orig_pos - edge_orig_pos) */
1254 PUSHB_1,
1255 sal_j,
1257 MUL, /* s: point delta */
1258 SHPIX,
1260 ENDF,
1266 * bci_ip_between_align_points
1268 * Auxiliary function for `bci_action_ip_between'.
1270 * in: after_edge (in twilight zone)
1271 * before_edge (in twilight zone)
1272 * loop_counter (N)
1273 * point_1
1274 * point_2
1275 * ...
1276 * point_N
1278 * sal: sal_i (before_orig_pos)
1279 * sal_j (stretch_factor)
1281 * uses: bci_ip_between_align_point
1284 unsigned char FPGM(bci_ip_between_align_points) [] = {
1286 PUSHB_1,
1287 bci_ip_between_align_points,
1288 FDEF,
1290 PUSHB_2,
1293 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1294 CINDEX,
1295 DUP, /* s: ... before after before before */
1296 MDAP_noround, /* set rp0 and rp1 to `before' */
1297 DUP,
1298 GC_orig, /* s: ... before after before before_orig_pos */
1299 PUSHB_1,
1300 sal_i,
1301 SWAP,
1302 WS, /* sal_i = before_orig_pos */
1303 PUSHB_1,
1305 CINDEX, /* s: ... before after before after */
1306 MD_cur, /* b = after_pos - before_pos */
1307 ROLL,
1308 ROLL,
1309 MD_orig_ZP2_0, /* a = after_orig_pos - before_orig_pos */
1310 DIV, /* s: a/b */
1311 PUSHB_1,
1312 sal_j,
1313 SWAP,
1314 WS, /* sal_j = stretch_factor */
1316 PUSHB_3,
1317 bci_ip_between_align_point,
1320 SZP2, /* set zp2 to normal zone 1 */
1321 SZP1, /* set zp1 to normal zone 1 */
1322 LOOPCALL,
1324 ENDF,
1330 * bci_action_ip_before
1332 * Handle `ip_before' data to align points located before the first edge.
1334 * in: first_edge (in twilight zone)
1335 * loop_counter (N)
1336 * point_1
1337 * point_2
1338 * ...
1339 * point_N
1341 * sal: sal_i (first_edge_orig_pos)
1343 * uses: bci_ip_outer_align_point
1346 unsigned char FPGM(bci_action_ip_before) [] = {
1348 PUSHB_1,
1349 bci_action_ip_before,
1350 FDEF,
1352 PUSHB_1,
1354 SZP2, /* set zp2 to twilight zone 0 */
1356 DUP,
1357 GC_orig,
1358 PUSHB_1,
1359 sal_i,
1360 SWAP,
1361 WS, /* sal_i = first_edge_orig_pos */
1363 PUSHB_3,
1367 SZP2, /* set zp2 to normal zone 1 */
1368 SZP1, /* set zp1 to normal zone 1 */
1369 SZP0, /* set zp0 to twilight zone 0 */
1371 MDAP_noround, /* set rp0 and rp1 to `first_edge' */
1373 PUSHB_1,
1374 bci_ip_outer_align_point,
1375 LOOPCALL,
1377 ENDF,
1383 * bci_action_ip_after
1385 * Handle `ip_after' data to align points located after the last edge.
1387 * in: last_edge (in twilight zone)
1388 * loop_counter (N)
1389 * point_1
1390 * point_2
1391 * ...
1392 * point_N
1394 * sal: sal_i (last_edge_orig_pos)
1396 * uses: bci_ip_outer_align_point
1399 unsigned char FPGM(bci_action_ip_after) [] = {
1401 PUSHB_1,
1402 bci_action_ip_after,
1403 FDEF,
1405 PUSHB_1,
1407 SZP2, /* set zp2 to twilight zone 0 */
1409 DUP,
1410 GC_orig,
1411 PUSHB_1,
1412 sal_i,
1413 SWAP,
1414 WS, /* sal_i = last_edge_orig_pos */
1416 PUSHB_3,
1420 SZP2, /* set zp2 to normal zone 1 */
1421 SZP1, /* set zp1 to normal zone 1 */
1422 SZP0, /* set zp0 to twilight zone 0 */
1424 MDAP_noround, /* set rp0 and rp1 to `last_edge' */
1426 PUSHB_1,
1427 bci_ip_outer_align_point,
1428 LOOPCALL,
1430 ENDF,
1436 * bci_action_ip_on
1438 * Handle `ip_on' data to align points located on an edge coordinate (but
1439 * not part of an edge).
1441 * in: loop_counter (M)
1442 * edge_1 (in twilight zone)
1443 * loop_counter (N_1)
1444 * point_1
1445 * point_2
1446 * ...
1447 * point_N_1
1448 * edge_2 (in twilight zone)
1449 * loop_counter (N_2)
1450 * point_1
1451 * point_2
1452 * ...
1453 * point_N_2
1454 * ...
1455 * edge_M (in twilight zone)
1456 * loop_counter (N_M)
1457 * point_1
1458 * point_2
1459 * ...
1460 * point_N_M
1462 * uses: bci_ip_on_align_points
1465 unsigned char FPGM(bci_action_ip_on) [] = {
1467 PUSHB_1,
1468 bci_action_ip_on,
1469 FDEF,
1471 PUSHB_2,
1474 SZP1, /* set zp1 to normal zone 1 */
1475 SZP0, /* set zp0 to twilight zone 0 */
1477 PUSHB_1,
1478 bci_ip_on_align_points,
1479 LOOPCALL,
1481 ENDF,
1487 * bci_action_ip_between
1489 * Handle `ip_between' data to align points located between two edges.
1491 * in: loop_counter (M)
1492 * before_edge_1 (in twilight zone)
1493 * after_edge_1 (in twilight zone)
1494 * loop_counter (N_1)
1495 * point_1
1496 * point_2
1497 * ...
1498 * point_N_1
1499 * before_edge_2 (in twilight zone)
1500 * after_edge_2 (in twilight zone)
1501 * loop_counter (N_2)
1502 * point_1
1503 * point_2
1504 * ...
1505 * point_N_2
1506 * ...
1507 * before_edge_M (in twilight zone)
1508 * after_edge_M (in twilight zone)
1509 * loop_counter (N_M)
1510 * point_1
1511 * point_2
1512 * ...
1513 * point_N_M
1515 * uses: bci_ip_between_align_points
1518 unsigned char FPGM(bci_action_ip_between) [] = {
1520 PUSHB_1,
1521 bci_action_ip_between,
1522 FDEF,
1524 PUSHB_1,
1525 bci_ip_between_align_points,
1526 LOOPCALL,
1528 ENDF,
1534 * bci_action_adjust_common
1536 * Common code for bci_action_adjust routines.
1539 unsigned char FPGM(bci_action_adjust_common) [] = {
1541 PUSHB_1,
1542 bci_action_adjust_common,
1543 FDEF,
1545 PUSHB_1,
1547 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1549 PUSHB_1,
1551 CINDEX, /* s: [...] edge2 edge is_round is_serif edge2 */
1552 PUSHB_1,
1554 CINDEX, /* s: [...] edge2 edge is_round is_serif edge2 edge */
1555 MD_orig_ZP2_0, /* s: [...] edge2 edge is_round is_serif org_len */
1557 PUSHB_1,
1558 bci_compute_stem_width,
1559 CALL,
1560 NEG, /* s: [...] edge2 edge -cur_len */
1562 ROLL, /* s: [...] edge -cur_len edge2 */
1563 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1564 SWAP,
1565 DUP,
1566 DUP, /* s: [...] -cur_len edge edge edge */
1567 ALIGNRP, /* align `edge' with `edge2' */
1568 ROLL,
1569 SHPIX, /* shift `edge' by -cur_len */
1571 ENDF,
1577 * bci_action_adjust_bound
1579 * Handle the ADJUST_BOUND action to align an edge of a stem if the other
1580 * edge of the stem has already been moved, then moving it again if
1581 * necessary to stay bound.
1583 * in: edge2_is_serif
1584 * edge_is_round
1585 * edge_point (in twilight zone)
1586 * edge2_point (in twilight zone)
1587 * edge[-1] (in twilight zone)
1588 * ... stuff for bci_align_segments (edge) ...
1590 * uses: bci_action_adjust_common
1593 unsigned char FPGM(bci_action_adjust_bound) [] = {
1595 PUSHB_1,
1596 bci_action_adjust_bound,
1597 FDEF,
1599 PUSHB_1,
1600 bci_action_adjust_common,
1601 CALL,
1603 SWAP, /* s: edge edge[-1] */
1604 DUP,
1605 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1606 GC_cur,
1607 PUSHB_1,
1609 CINDEX,
1610 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1611 GT, /* edge_pos < edge[-1]_pos */
1613 DUP,
1614 ALIGNRP, /* align `edge' to `edge[-1]' */
1615 EIF,
1617 MDAP_noround, /* set rp0 and rp1 to `edge' */
1619 PUSHB_2,
1620 bci_align_segments,
1622 SZP1, /* set zp1 to normal zone 1 */
1623 CALL,
1625 ENDF,
1631 * bci_action_adjust
1633 * Handle the ADJUST action to align an edge of a stem if the other edge
1634 * of the stem has already been moved.
1636 * in: edge2_is_serif
1637 * edge_is_round
1638 * edge_point (in twilight zone)
1639 * edge2_point (in twilight zone)
1640 * ... stuff for bci_align_segments (edge) ...
1642 * uses: bci_action_adjust_common
1645 unsigned char FPGM(bci_action_adjust) [] = {
1647 PUSHB_1,
1648 bci_action_adjust,
1649 FDEF,
1651 PUSHB_1,
1652 bci_action_adjust_common,
1653 CALL,
1655 MDAP_noround, /* set rp0 and rp1 to `edge' */
1657 PUSHB_2,
1658 bci_align_segments,
1660 SZP1, /* set zp1 to normal zone 1 */
1661 CALL,
1663 ENDF,
1669 * bci_action_stem_common
1671 * Common code for bci_action_stem routines.
1674 #undef sal_u_off
1675 #define sal_u_off sal_temp1
1676 #undef sal_d_off
1677 #define sal_d_off sal_temp2
1678 #undef sal_org_len
1679 #define sal_org_len sal_temp3
1680 #undef sal_edge2
1681 #define sal_edge2 sal_temp3
1683 unsigned char FPGM(bci_action_stem_common) [] = {
1685 PUSHB_1,
1686 bci_action_stem_common,
1687 FDEF,
1689 PUSHB_1,
1691 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1693 PUSHB_1,
1695 CINDEX,
1696 PUSHB_1,
1698 CINDEX,
1699 DUP, /* s: [...] edge2 edge is_round is_serif edge2 edge edge */
1700 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1702 MD_orig_ZP2_0, /* s: [...] edge2 edge is_round is_serif org_len */
1703 DUP,
1704 PUSHB_1,
1705 sal_org_len,
1706 SWAP,
1709 PUSHB_1,
1710 bci_compute_stem_width,
1711 CALL, /* s: [...] edge2 edge cur_len */
1713 DUP,
1714 PUSHB_1,
1716 LT, /* cur_len < 96 */
1718 DUP,
1719 PUSHB_1,
1721 LTEQ, /* cur_len <= 64 */
1723 PUSHB_4,
1724 sal_u_off,
1726 sal_d_off,
1729 ELSE,
1730 PUSHB_4,
1731 sal_u_off,
1733 sal_d_off,
1735 EIF,
1739 SWAP, /* s: [...] edge2 cur_len edge */
1740 DUP,
1741 PUSHB_1,
1742 sal_anchor,
1744 DUP, /* s: [...] edge2 cur_len edge edge anchor anchor */
1745 ROLL,
1746 SWAP,
1747 MD_orig_ZP2_0,
1748 SWAP,
1749 GC_cur,
1750 ADD, /* s: [...] edge2 cur_len edge org_pos */
1751 PUSHB_1,
1752 sal_org_len,
1754 PUSHB_1,
1755 2*64,
1756 DIV,
1757 ADD, /* s: [...] edge2 cur_len edge org_center */
1759 DUP,
1760 PUSHB_1,
1761 bci_round,
1762 CALL, /* s: [...] edge2 cur_len edge org_center cur_pos1 */
1764 DUP,
1765 ROLL,
1766 ROLL,
1767 SUB, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) */
1769 DUP,
1770 PUSHB_1,
1771 sal_u_off,
1773 ADD,
1774 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
1776 SWAP,
1777 PUSHB_1,
1778 sal_d_off,
1780 SUB,
1781 ABS, /* s: [...] edge2 cur_len edge cur_pos1 delta1 delta2 */
1783 LT, /* delta1 < delta2 */
1785 PUSHB_1,
1786 sal_u_off,
1788 SUB, /* cur_pos1 = cur_pos1 - u_off */
1790 ELSE,
1791 PUSHB_1,
1792 sal_d_off,
1794 ADD, /* cur_pos1 = cur_pos1 + d_off */
1795 EIF, /* s: [...] edge2 cur_len edge cur_pos1 */
1797 PUSHB_1,
1799 CINDEX,
1800 PUSHB_1,
1801 2*64,
1802 DIV,
1803 SUB, /* arg = cur_pos1 - cur_len/2 */
1805 SWAP, /* s: [...] edge2 cur_len arg edge */
1806 DUP,
1807 DUP,
1808 PUSHB_1,
1810 MINDEX,
1811 SWAP, /* s: [...] edge2 cur_len edge edge arg edge */
1812 GC_cur,
1813 SUB,
1814 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1816 ELSE,
1817 SWAP, /* s: [...] edge2 cur_len edge */
1818 PUSHB_1,
1819 sal_anchor,
1821 GC_cur, /* s: [...] edge2 cur_len edge anchor_pos */
1822 PUSHB_1,
1824 CINDEX,
1825 PUSHB_1,
1826 sal_anchor,
1828 MD_orig_ZP2_0,
1829 ADD, /* s: [...] edge2 cur_len edge org_pos */
1831 DUP,
1832 PUSHB_1,
1833 sal_org_len,
1835 PUSHB_1,
1836 2*64,
1837 DIV,
1838 ADD, /* s: [...] edge2 cur_len edge org_pos org_center */
1840 SWAP,
1841 DUP,
1842 PUSHB_1,
1843 bci_round,
1844 CALL, /* cur_pos1 = ROUND(org_pos) */
1845 SWAP,
1846 PUSHB_1,
1847 sal_org_len,
1849 ADD,
1850 PUSHB_1,
1851 bci_round,
1852 CALL,
1853 PUSHB_1,
1855 CINDEX,
1856 SUB, /* s: [...] edge2 cur_len edge org_center cur_pos1 cur_pos2 */
1858 PUSHB_1,
1860 CINDEX,
1861 PUSHB_1,
1862 2*64,
1863 DIV,
1864 PUSHB_1,
1866 MINDEX,
1867 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
1869 DUP,
1870 PUSHB_1,
1872 CINDEX,
1873 ADD,
1874 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
1875 SWAP,
1876 PUSHB_1,
1878 CINDEX,
1879 ADD,
1880 ABS, /* s: ... edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
1881 LT, /* delta1 < delta2 */
1883 POP, /* arg = cur_pos1 */
1885 ELSE,
1886 SWAP,
1887 POP, /* arg = cur_pos2 */
1888 EIF, /* s: [...] edge2 cur_len edge arg */
1889 SWAP,
1890 DUP,
1891 DUP,
1892 PUSHB_1,
1894 MINDEX,
1895 SWAP, /* s: [...] edge2 cur_len edge edge arg edge */
1896 GC_cur,
1897 SUB,
1898 SHPIX, /* edge = arg */
1899 EIF, /* s: [...] edge2 cur_len edge */
1901 ENDF,
1907 * bci_action_stem_bound
1909 * Handle the STEM action to align two edges of a stem, then moving one
1910 * edge again if necessary to stay bound.
1912 * The code after computing `cur_len' to shift `edge' and `edge2'
1913 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1915 * if cur_len < 96:
1916 * if cur_len < = 64:
1917 * u_off = 32
1918 * d_off = 32
1919 * else:
1920 * u_off = 38
1921 * d_off = 26
1923 * org_pos = anchor + (edge_orig - anchor_orig);
1924 * org_center = org_pos + org_len / 2;
1926 * cur_pos1 = ROUND(org_center)
1927 * delta1 = ABS(org_center - (cur_pos1 - u_off))
1928 * delta2 = ABS(org_center - (cur_pos1 + d_off))
1929 * if (delta1 < delta2):
1930 * cur_pos1 = cur_pos1 - u_off
1931 * else:
1932 * cur_pos1 = cur_pos1 + d_off
1934 * edge = cur_pos1 - cur_len / 2
1936 * else:
1937 * org_pos = anchor + (edge_orig - anchor_orig)
1938 * org_center = org_pos + org_len / 2;
1940 * cur_pos1 = ROUND(org_pos)
1941 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
1942 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
1943 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
1945 * if (delta1 < delta2):
1946 * edge = cur_pos1
1947 * else:
1948 * edge = cur_pos2
1950 * edge2 = edge + cur_len
1952 * in: edge2_is_serif
1953 * edge_is_round
1954 * edge_point (in twilight zone)
1955 * edge2_point (in twilight zone)
1956 * edge[-1] (in twilight zone)
1957 * ... stuff for bci_align_segments (edge) ...
1958 * ... stuff for bci_align_segments (edge2)...
1960 * sal: sal_anchor
1961 * sal_temp1
1962 * sal_temp2
1963 * sal_temp3
1965 * uses: bci_action_stem_common
1968 unsigned char FPGM(bci_action_stem_bound) [] = {
1970 PUSHB_1,
1971 bci_action_stem_bound,
1972 FDEF,
1974 PUSHB_1,
1975 bci_action_stem_common,
1976 CALL,
1978 ROLL, /* s: edge[-1] cur_len edge edge2 */
1979 DUP,
1980 DUP,
1981 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1982 PUSHB_1,
1983 sal_edge2,
1984 SWAP,
1985 WS, /* s: edge[-1] cur_len edge edge2 */
1986 ROLL,
1987 SHPIX, /* edge2 = edge + cur_len */
1989 SWAP, /* s: edge edge[-1] */
1990 DUP,
1991 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1992 GC_cur,
1993 PUSHB_1,
1995 CINDEX,
1996 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1997 GT, /* edge_pos < edge[-1]_pos */
1999 DUP,
2000 ALIGNRP, /* align `edge' to `edge[-1]' */
2001 EIF,
2003 MDAP_noround, /* set rp0 and rp1 to `edge' */
2005 PUSHB_2,
2006 bci_align_segments,
2008 SZP1, /* set zp1 to normal zone 1 */
2009 CALL,
2011 PUSHB_1,
2012 sal_edge2,
2014 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2016 PUSHB_1,
2017 bci_align_segments,
2018 CALL,
2020 ENDF,
2026 * bci_action_stem
2028 * Handle the STEM action to align two edges of a stem.
2030 * See `bci_action_stem_bound' for more details.
2032 * in: edge2_is_serif
2033 * edge_is_round
2034 * edge_point (in twilight zone)
2035 * edge2_point (in twilight zone)
2036 * ... stuff for bci_align_segments (edge) ...
2037 * ... stuff for bci_align_segments (edge2)...
2039 * sal: sal_anchor
2040 * sal_temp1
2041 * sal_temp2
2042 * sal_temp3
2044 * uses: bci_action_stem_common
2047 unsigned char FPGM(bci_action_stem) [] = {
2049 PUSHB_1,
2050 bci_action_stem,
2051 FDEF,
2053 PUSHB_1,
2054 bci_action_stem_common,
2055 CALL,
2057 POP,
2058 SWAP, /* s: cur_len edge2 */
2059 DUP,
2060 DUP,
2061 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2062 PUSHB_1,
2063 sal_edge2,
2064 SWAP,
2065 WS, /* s: cur_len edge2 */
2066 SWAP,
2067 SHPIX, /* edge2 = edge + cur_len */
2069 PUSHB_2,
2070 bci_align_segments,
2072 SZP1, /* set zp1 to normal zone 1 */
2073 CALL,
2075 PUSHB_1,
2076 sal_edge2,
2078 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2080 PUSHB_1,
2081 bci_align_segments,
2082 CALL,
2083 ENDF,
2089 * bci_action_link
2091 * Handle the LINK action to link an edge to another one.
2093 * in: stem_is_serif
2094 * base_is_round
2095 * base_point (in twilight zone)
2096 * stem_point (in twilight zone)
2097 * ... stuff for bci_align_segments (base) ...
2100 unsigned char FPGM(bci_action_link) [] = {
2102 PUSHB_1,
2103 bci_action_link,
2104 FDEF,
2106 PUSHB_1,
2108 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2110 PUSHB_1,
2112 CINDEX,
2113 PUSHB_1,
2115 MINDEX,
2116 DUP, /* s: stem is_round is_serif stem base base */
2117 MDAP_noround, /* set rp0 and rp1 to `base_point' (for ALIGNRP below) */
2119 MD_orig_ZP2_0, /* s: stem is_round is_serif dist_orig */
2121 PUSHB_1,
2122 bci_compute_stem_width,
2123 CALL, /* s: stem new_dist */
2125 SWAP,
2126 DUP,
2127 ALIGNRP, /* align `stem_point' with `base_point' */
2128 DUP,
2129 MDAP_noround, /* set rp0 and rp1 to `stem_point' */
2130 SWAP,
2131 SHPIX, /* stem_point = base_point + new_dist */
2133 PUSHB_2,
2134 bci_align_segments,
2136 SZP1, /* set zp1 to normal zone 1 */
2137 CALL,
2139 ENDF,
2145 * bci_action_anchor
2147 * Handle the ANCHOR action to align two edges
2148 * and to set the edge anchor.
2150 * The code after computing `cur_len' to shift `edge' and `edge2'
2151 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
2153 * if cur_len < 96:
2154 * if cur_len < = 64:
2155 * u_off = 32
2156 * d_off = 32
2157 * else:
2158 * u_off = 38
2159 * d_off = 26
2161 * org_center = edge_orig + org_len / 2
2162 * cur_pos1 = ROUND(org_center)
2164 * error1 = ABS(org_center - (cur_pos1 - u_off))
2165 * error2 = ABS(org_center - (cur_pos1 + d_off))
2166 * if (error1 < error2):
2167 * cur_pos1 = cur_pos1 - u_off
2168 * else:
2169 * cur_pos1 = cur_pos1 + d_off
2171 * edge = cur_pos1 - cur_len / 2
2172 * edge2 = edge + cur_len
2174 * else:
2175 * edge = ROUND(edge_orig)
2177 * in: edge2_is_serif
2178 * edge_is_round
2179 * edge_point (in twilight zone)
2180 * edge2_point (in twilight zone)
2181 * ... stuff for bci_align_segments (edge) ...
2183 * sal: sal_anchor
2184 * sal_temp1
2185 * sal_temp2
2186 * sal_temp3
2189 #undef sal_u_off
2190 #define sal_u_off sal_temp1
2191 #undef sal_d_off
2192 #define sal_d_off sal_temp2
2193 #undef sal_org_len
2194 #define sal_org_len sal_temp3
2196 unsigned char FPGM(bci_action_anchor) [] = {
2198 PUSHB_1,
2199 bci_action_anchor,
2200 FDEF,
2202 /* store anchor point number in `sal_anchor' */
2203 PUSHB_2,
2204 sal_anchor,
2206 CINDEX,
2207 WS, /* sal_anchor = edge_point */
2209 PUSHB_1,
2211 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2213 PUSHB_1,
2215 CINDEX,
2216 PUSHB_1,
2218 CINDEX,
2219 DUP, /* s: edge2 edge is_round is_serif edge2 edge edge */
2220 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
2222 MD_orig_ZP2_0, /* s: edge2 edge is_round is_serif org_len */
2223 DUP,
2224 PUSHB_1,
2225 sal_org_len,
2226 SWAP,
2229 PUSHB_1,
2230 bci_compute_stem_width,
2231 CALL, /* s: edge2 edge cur_len */
2233 DUP,
2234 PUSHB_1,
2236 LT, /* cur_len < 96 */
2238 DUP,
2239 PUSHB_1,
2241 LTEQ, /* cur_len <= 64 */
2243 PUSHB_4,
2244 sal_u_off,
2246 sal_d_off,
2249 ELSE,
2250 PUSHB_4,
2251 sal_u_off,
2253 sal_d_off,
2255 EIF,
2259 SWAP, /* s: edge2 cur_len edge */
2260 DUP, /* s: edge2 cur_len edge edge */
2262 GC_orig,
2263 PUSHB_1,
2264 sal_org_len,
2266 PUSHB_1,
2267 2*64,
2268 DIV,
2269 ADD, /* s: edge2 cur_len edge org_center */
2271 DUP,
2272 PUSHB_1,
2273 bci_round,
2274 CALL, /* s: edge2 cur_len edge org_center cur_pos1 */
2276 DUP,
2277 ROLL,
2278 ROLL,
2279 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
2281 DUP,
2282 PUSHB_1,
2283 sal_u_off,
2285 ADD,
2286 ABS, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) error1 */
2288 SWAP,
2289 PUSHB_1,
2290 sal_d_off,
2292 SUB,
2293 ABS, /* s: edge2 cur_len edge cur_pos1 error1 error2 */
2295 LT, /* error1 < error2 */
2297 PUSHB_1,
2298 sal_u_off,
2300 SUB, /* cur_pos1 = cur_pos1 - u_off */
2302 ELSE,
2303 PUSHB_1,
2304 sal_d_off,
2306 ADD, /* cur_pos1 = cur_pos1 + d_off */
2307 EIF, /* s: edge2 cur_len edge cur_pos1 */
2309 PUSHB_1,
2311 CINDEX,
2312 PUSHB_1,
2313 2*64,
2314 DIV,
2315 SUB, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) */
2317 PUSHB_1,
2319 CINDEX, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) edge */
2320 GC_cur,
2321 SUB,
2322 SHPIX, /* edge = cur_pos1 - cur_len/2 */
2324 SWAP, /* s: cur_len edge2 */
2325 DUP,
2326 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2327 SWAP,
2328 SHPIX, /* edge2 = edge1 + cur_len */
2330 ELSE,
2331 POP, /* s: edge2 edge */
2332 DUP,
2333 DUP,
2334 GC_cur,
2335 SWAP,
2336 GC_orig,
2337 PUSHB_1,
2338 bci_round,
2339 CALL, /* s: edge2 edge edge_pos round(edge_orig_pos) */
2340 SWAP,
2341 SUB,
2342 SHPIX, /* edge = round(edge_orig) */
2344 /* clean up stack */
2345 POP,
2346 EIF,
2348 PUSHB_2,
2349 bci_align_segments,
2351 SZP1, /* set zp1 to normal zone 1 */
2352 CALL,
2354 ENDF,
2360 * bci_action_blue_anchor
2362 * Handle the BLUE_ANCHOR action to align an edge with a blue zone
2363 * and to set the edge anchor.
2365 * in: anchor_point (in twilight zone)
2366 * blue_cvt_idx
2367 * edge_point (in twilight zone)
2368 * ... stuff for bci_align_segments (edge) ...
2370 * sal: sal_anchor
2372 * uses: bci_action_blue
2375 unsigned char FPGM(bci_action_blue_anchor) [] = {
2377 PUSHB_1,
2378 bci_action_blue_anchor,
2379 FDEF,
2381 /* store anchor point number in `sal_anchor' */
2382 PUSHB_1,
2383 sal_anchor,
2384 SWAP,
2387 PUSHB_1,
2388 bci_action_blue,
2389 CALL,
2391 ENDF,
2397 * bci_action_blue
2399 * Handle the BLUE action to align an edge with a blue zone.
2401 * in: blue_cvt_idx
2402 * edge_point (in twilight zone)
2403 * ... stuff for bci_align_segments (edge) ...
2406 unsigned char FPGM(bci_action_blue) [] = {
2408 PUSHB_1,
2409 bci_action_blue,
2410 FDEF,
2412 PUSHB_1,
2414 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2416 /* move `edge_point' to `blue_cvt_idx' position; */
2417 /* note that we can't use MIAP since this would modify */
2418 /* the twilight point's original coordinates also */
2419 RCVT,
2420 SWAP,
2421 DUP,
2422 MDAP_noround, /* set rp0 and rp1 to `edge' */
2423 DUP,
2424 GC_cur, /* s: new_pos edge edge_pos */
2425 ROLL,
2426 SWAP,
2427 SUB, /* s: edge (new_pos - edge_pos) */
2428 SHPIX,
2430 PUSHB_2,
2431 bci_align_segments,
2433 SZP1, /* set zp1 to normal zone 1 */
2434 CALL,
2436 ENDF,
2442 * bci_action_serif_common
2444 * Common code for bci_action_serif routines.
2447 unsigned char FPGM(bci_action_serif_common) [] = {
2449 PUSHB_1,
2450 bci_action_serif_common,
2451 FDEF,
2453 PUSHB_1,
2455 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2457 DUP,
2458 DUP,
2459 DUP,
2460 PUSHB_1,
2462 MINDEX, /* s: [...] serif serif serif serif base */
2463 DUP,
2464 MDAP_noround, /* set rp0 and rp1 to `base_point' */
2465 MD_orig_ZP2_0,
2466 SWAP,
2467 ALIGNRP, /* align `serif_point' with `base_point' */
2468 SHPIX, /* serif = base + (serif_orig_pos - base_orig_pos) */
2470 ENDF,
2476 * bci_lower_bound
2478 * Move an edge if necessary to stay within a lower bound.
2480 * in: edge
2481 * bound
2484 unsigned char FPGM(bci_lower_bound) [] = {
2486 PUSHB_1,
2487 bci_lower_bound,
2488 FDEF,
2490 SWAP, /* s: edge bound */
2491 DUP,
2492 MDAP_noround, /* set rp0 and rp1 to `bound' */
2493 GC_cur,
2494 PUSHB_1,
2496 CINDEX,
2497 GC_cur, /* s: edge bound_pos edge_pos */
2498 GT, /* edge_pos < bound_pos */
2500 DUP,
2501 ALIGNRP, /* align `edge' to `bound' */
2502 EIF,
2504 MDAP_noround, /* set rp0 and rp1 to `edge_point' */
2506 PUSHB_2,
2507 bci_align_segments,
2509 SZP1, /* set zp1 to normal zone 1 */
2510 CALL,
2512 ENDF,
2518 * bci_upper_bound
2520 * Move an edge if necessary to stay within an upper bound.
2522 * in: edge
2523 * bound
2526 unsigned char FPGM(bci_upper_bound) [] = {
2528 PUSHB_1,
2529 bci_upper_bound,
2530 FDEF,
2532 SWAP, /* s: edge bound */
2533 DUP,
2534 MDAP_noround, /* set rp0 and rp1 to `bound' */
2535 GC_cur,
2536 PUSHB_1,
2538 CINDEX,
2539 GC_cur, /* s: edge bound_pos edge_pos */
2540 LT, /* edge_pos > bound_pos */
2542 DUP,
2543 ALIGNRP, /* align `edge' to `bound' */
2544 EIF,
2546 MDAP_noround, /* set rp0 and rp1 to `edge_point' */
2548 PUSHB_2,
2549 bci_align_segments,
2551 SZP1, /* set zp1 to normal zone 1 */
2552 CALL,
2554 ENDF,
2560 * bci_lower_upper_bound
2562 * Move an edge if necessary to stay within a lower and lower bound.
2564 * in: edge
2565 * lower
2566 * upper
2569 unsigned char FPGM(bci_lower_upper_bound) [] = {
2571 PUSHB_1,
2572 bci_lower_upper_bound,
2573 FDEF,
2575 SWAP, /* s: upper serif lower */
2576 DUP,
2577 MDAP_noround, /* set rp0 and rp1 to `lower' */
2578 GC_cur,
2579 PUSHB_1,
2581 CINDEX,
2582 GC_cur, /* s: upper serif lower_pos serif_pos */
2583 GT, /* serif_pos < lower_pos */
2585 DUP,
2586 ALIGNRP, /* align `serif' to `lower' */
2587 EIF,
2589 SWAP, /* s: serif upper */
2590 DUP,
2591 MDAP_noround, /* set rp0 and rp1 to `upper' */
2592 GC_cur,
2593 PUSHB_1,
2595 CINDEX,
2596 GC_cur, /* s: serif upper_pos serif_pos */
2597 LT, /* serif_pos > upper_pos */
2599 DUP,
2600 ALIGNRP, /* align `serif' to `upper' */
2601 EIF,
2603 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2605 PUSHB_2,
2606 bci_align_segments,
2608 SZP1, /* set zp1 to normal zone 1 */
2609 CALL,
2611 ENDF,
2617 * bci_action_serif
2619 * Handle the SERIF action to align a serif with its base.
2621 * in: serif_point (in twilight zone)
2622 * base_point (in twilight zone)
2623 * ... stuff for bci_align_segments (serif) ...
2625 * uses: bci_action_serif_common
2628 unsigned char FPGM(bci_action_serif) [] = {
2630 PUSHB_1,
2631 bci_action_serif,
2632 FDEF,
2634 PUSHB_1,
2635 bci_action_serif_common,
2636 CALL,
2638 MDAP_noround, /* set rp0 and rp1 to `serif_point' */
2640 PUSHB_2,
2641 bci_align_segments,
2643 SZP1, /* set zp1 to normal zone 1 */
2644 CALL,
2646 ENDF,
2652 * bci_action_serif_lower_bound
2654 * Handle the SERIF action to align a serif with its base, then moving it
2655 * again if necessary to stay within a lower bound.
2657 * in: serif_point (in twilight zone)
2658 * base_point (in twilight zone)
2659 * edge[-1] (in twilight zone)
2660 * ... stuff for bci_align_segments (serif) ...
2662 * uses: bci_action_serif_common
2663 * bci_lower_bound
2666 unsigned char FPGM(bci_action_serif_lower_bound) [] = {
2668 PUSHB_1,
2669 bci_action_serif_lower_bound,
2670 FDEF,
2672 PUSHB_1,
2673 bci_action_serif_common,
2674 CALL,
2676 PUSHB_1,
2677 bci_lower_bound,
2678 CALL,
2680 ENDF,
2686 * bci_action_serif_upper_bound
2688 * Handle the SERIF action to align a serif with its base, then moving it
2689 * again if necessary to stay within an upper bound.
2691 * in: serif_point (in twilight zone)
2692 * base_point (in twilight zone)
2693 * edge[1] (in twilight zone)
2694 * ... stuff for bci_align_segments (serif) ...
2696 * uses: bci_action_serif_common
2697 * bci_upper_bound
2700 unsigned char FPGM(bci_action_serif_upper_bound) [] = {
2702 PUSHB_1,
2703 bci_action_serif_upper_bound,
2704 FDEF,
2706 PUSHB_1,
2707 bci_action_serif_common,
2708 CALL,
2710 PUSHB_1,
2711 bci_upper_bound,
2712 CALL,
2714 ENDF,
2720 * bci_action_serif_lower_upper_bound
2722 * Handle the SERIF action to align a serif with its base, then moving it
2723 * again if necessary to stay within a lower and upper bound.
2725 * in: serif_point (in twilight zone)
2726 * base_point (in twilight zone)
2727 * edge[-1] (in twilight zone)
2728 * edge[1] (in twilight zone)
2729 * ... stuff for bci_align_segments (serif) ...
2731 * uses: bci_action_serif_common
2732 * bci_lower_upper_bound
2735 unsigned char FPGM(bci_action_serif_lower_upper_bound) [] = {
2737 PUSHB_1,
2738 bci_action_serif_lower_upper_bound,
2739 FDEF,
2741 PUSHB_1,
2743 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2745 PUSHB_1,
2746 bci_action_serif_common,
2747 CALL,
2749 PUSHB_1,
2750 bci_lower_upper_bound,
2751 CALL,
2753 ENDF,
2759 * bci_action_serif_anchor_common
2761 * Common code for bci_action_serif_anchor routines.
2764 unsigned char FPGM(bci_action_serif_anchor_common) [] = {
2766 PUSHB_1,
2767 bci_action_serif_anchor_common,
2768 FDEF,
2770 PUSHB_1,
2772 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2774 DUP,
2775 PUSHB_1,
2776 sal_anchor,
2777 SWAP,
2778 WS, /* sal_anchor = edge_point */
2780 DUP,
2781 DUP,
2782 DUP,
2783 GC_cur,
2784 SWAP,
2785 GC_orig,
2786 PUSHB_1,
2787 bci_round,
2788 CALL, /* s: [...] edge edge edge_pos round(edge_orig_pos) */
2789 SWAP,
2790 SUB,
2791 SHPIX, /* edge = round(edge_orig) */
2793 ENDF,
2799 * bci_action_serif_anchor
2801 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2802 * anchor.
2804 * in: edge_point (in twilight zone)
2805 * ... stuff for bci_align_segments (edge) ...
2807 * uses: bci_action_serif_anchor_common
2810 unsigned char FPGM(bci_action_serif_anchor) [] = {
2812 PUSHB_1,
2813 bci_action_serif_anchor,
2814 FDEF,
2816 PUSHB_1,
2817 bci_action_serif_anchor_common,
2818 CALL,
2820 MDAP_noround, /* set rp0 and rp1 to `edge' */
2822 PUSHB_2,
2823 bci_align_segments,
2825 SZP1, /* set zp1 to normal zone 1 */
2826 CALL,
2828 ENDF,
2834 * bci_action_serif_anchor_lower_bound
2836 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2837 * anchor, then moving it again if necessary to stay within a lower
2838 * bound.
2840 * in: edge_point (in twilight zone)
2841 * edge[-1] (in twilight zone)
2842 * ... stuff for bci_align_segments (edge) ...
2844 * uses: bci_action_serif_anchor_common
2845 * bci_lower_bound
2848 unsigned char FPGM(bci_action_serif_anchor_lower_bound) [] = {
2850 PUSHB_1,
2851 bci_action_serif_anchor_lower_bound,
2852 FDEF,
2854 PUSHB_1,
2855 bci_action_serif_anchor_common,
2856 CALL,
2858 PUSHB_1,
2859 bci_lower_bound,
2860 CALL,
2862 ENDF,
2868 * bci_action_serif_anchor_upper_bound
2870 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2871 * anchor, then moving it again if necessary to stay within an upper
2872 * bound.
2874 * in: edge_point (in twilight zone)
2875 * edge[1] (in twilight zone)
2876 * ... stuff for bci_align_segments (edge) ...
2878 * uses: bci_action_serif_anchor_common
2879 * bci_upper_bound
2882 unsigned char FPGM(bci_action_serif_anchor_upper_bound) [] = {
2884 PUSHB_1,
2885 bci_action_serif_anchor_upper_bound,
2886 FDEF,
2888 PUSHB_1,
2889 bci_action_serif_anchor_common,
2890 CALL,
2892 PUSHB_1,
2893 bci_upper_bound,
2894 CALL,
2896 ENDF,
2902 * bci_action_serif_anchor_lower_upper_bound
2904 * Handle the SERIF_ANCHOR action to align a serif and to set the edge
2905 * anchor, then moving it again if necessary to stay within a lower and
2906 * upper bound.
2908 * in: edge_point (in twilight zone)
2909 * edge[-1] (in twilight zone)
2910 * edge[1] (in twilight zone)
2911 * ... stuff for bci_align_segments (edge) ...
2913 * uses: bci_action_serif_anchor_common
2914 * bci_lower_upper_bound
2917 unsigned char FPGM(bci_action_serif_anchor_lower_upper_bound) [] = {
2919 PUSHB_1,
2920 bci_action_serif_anchor_lower_upper_bound,
2921 FDEF,
2923 PUSHB_1,
2924 bci_action_serif_anchor_common,
2925 CALL,
2927 PUSHB_1,
2928 bci_lower_upper_bound,
2929 CALL,
2931 ENDF,
2937 * bci_action_serif_link1_common
2939 * Common code for bci_action_serif_link1 routines.
2942 unsigned char FPGM(bci_action_serif_link1_common) [] = {
2944 PUSHB_1,
2945 bci_action_serif_link1_common,
2946 FDEF,
2948 PUSHB_1,
2950 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2952 PUSHB_1,
2954 CINDEX, /* s: [...] after edge before after */
2955 PUSHB_1,
2957 CINDEX, /* s: [...] after edge before after before */
2958 MD_orig_ZP2_0,
2959 PUSHB_1,
2961 EQ, /* after_orig_pos == before_orig_pos */
2962 IF, /* s: [...] after edge before */
2963 MDAP_noround, /* set rp0 and rp1 to `before' */
2964 DUP,
2965 ALIGNRP, /* align `edge' with `before' */
2966 SWAP,
2967 POP,
2969 ELSE,
2970 /* we have to execute `a*b/c', with b/c very near to 1: */
2971 /* to avoid overflow while retaining precision, */
2972 /* we transform this to `a + a * (b-c)/c' */
2974 PUSHB_1,
2976 CINDEX, /* s: [...] after edge before edge */
2977 PUSHB_1,
2979 CINDEX, /* s: [...] after edge before edge before */
2980 MD_orig_ZP2_0, /* a = edge_orig_pos - before_orig_pos */
2982 DUP,
2983 PUSHB_1,
2985 CINDEX, /* s: [...] after edge before a a after */
2986 PUSHB_1,
2988 CINDEX, /* s: [...] after edge before a a after before */
2989 MD_orig_ZP2_0, /* c = after_orig_pos - before_orig_pos */
2991 PUSHB_1,
2993 CINDEX, /* s: [...] after edge before a a c after */
2994 PUSHB_1,
2996 CINDEX, /* s: [...] after edge before a a c after before */
2997 MD_cur, /* b = after_pos - before_pos */
2999 PUSHB_1,
3001 CINDEX, /* s: [...] after edge before a a c b c */
3002 SUB, /* b-c */
3004 PUSHB_1,
3005 cvtl_0x10000,
3006 RCVT,
3007 MUL, /* (b-c) in 16.16 format */
3008 SWAP,
3009 DIV, /* s: [...] after edge before a a (b-c)/c */
3011 MUL, /* a * (b-c)/c * 2^10 */
3012 PUSHB_1,
3013 cvtl_0x10000,
3014 RCVT,
3015 DIV, /* a * (b-c)/c */
3016 ADD, /* a*b/c */
3018 SWAP,
3019 MDAP_noround, /* set rp0 and rp1 to `before' */
3020 SWAP, /* s: [...] after a*b/c edge */
3021 DUP,
3022 DUP,
3023 ALIGNRP, /* align `edge' with `before' */
3024 ROLL,
3025 SHPIX, /* shift `edge' by `a*b/c' */
3027 SWAP, /* s: [...] edge after */
3028 POP,
3029 EIF,
3031 ENDF,
3037 * bci_action_serif_link1
3039 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3040 * before and after.
3042 * in: before_point (in twilight zone)
3043 * edge_point (in twilight zone)
3044 * after_point (in twilight zone)
3045 * ... stuff for bci_align_segments (edge) ...
3047 * uses: bci_action_serif_link1_common
3050 unsigned char FPGM(bci_action_serif_link1) [] = {
3052 PUSHB_1,
3053 bci_action_serif_link1,
3054 FDEF,
3056 PUSHB_1,
3057 bci_action_serif_link1_common,
3058 CALL,
3060 MDAP_noround, /* set rp0 and rp1 to `edge' */
3062 PUSHB_2,
3063 bci_align_segments,
3065 SZP1, /* set zp1 to normal zone 1 */
3066 CALL,
3068 ENDF,
3074 * bci_action_serif_link1_lower_bound
3076 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3077 * before and after. Additionally, move the serif again if necessary to
3078 * stay within a lower bound.
3080 * in: before_point (in twilight zone)
3081 * edge_point (in twilight zone)
3082 * after_point (in twilight zone)
3083 * edge[-1] (in twilight zone)
3084 * ... stuff for bci_align_segments (edge) ...
3086 * uses: bci_action_serif_link1_common
3087 * bci_lower_bound
3090 unsigned char FPGM(bci_action_serif_link1_lower_bound) [] = {
3092 PUSHB_1,
3093 bci_action_serif_link1_lower_bound,
3094 FDEF,
3096 PUSHB_1,
3097 bci_action_serif_link1_common,
3098 CALL,
3100 PUSHB_1,
3101 bci_lower_bound,
3102 CALL,
3104 ENDF,
3110 * bci_action_serif_link1_upper_bound
3112 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3113 * before and after. Additionally, move the serif again if necessary to
3114 * stay within an upper bound.
3116 * in: before_point (in twilight zone)
3117 * edge_point (in twilight zone)
3118 * after_point (in twilight zone)
3119 * edge[1] (in twilight zone)
3120 * ... stuff for bci_align_segments (edge) ...
3122 * uses: bci_action_serif_link1_common
3123 * bci_upper_bound
3126 unsigned char FPGM(bci_action_serif_link1_upper_bound) [] = {
3128 PUSHB_1,
3129 bci_action_serif_link1_upper_bound,
3130 FDEF,
3132 PUSHB_1,
3133 bci_action_serif_link1_common,
3134 CALL,
3136 PUSHB_1,
3137 bci_upper_bound,
3138 CALL,
3140 ENDF,
3146 * bci_action_serif_link1_lower_upper_bound
3148 * Handle the SERIF_LINK1 action to align a serif, depending on edges
3149 * before and after. Additionally, move the serif again if necessary to
3150 * stay within a lower and upper bound.
3152 * in: before_point (in twilight zone)
3153 * edge_point (in twilight zone)
3154 * after_point (in twilight zone)
3155 * edge[-1] (in twilight zone)
3156 * edge[1] (in twilight zone)
3157 * ... stuff for bci_align_segments (edge) ...
3159 * uses: bci_action_serif_link1_common
3160 * bci_lower_upper_bound
3163 unsigned char FPGM(bci_action_serif_link1_lower_upper_bound) [] = {
3165 PUSHB_1,
3166 bci_action_serif_link1_lower_upper_bound,
3167 FDEF,
3169 PUSHB_1,
3170 bci_action_serif_link1_common,
3171 CALL,
3173 PUSHB_1,
3174 bci_lower_upper_bound,
3175 CALL,
3177 ENDF,
3183 * bci_action_serif_link2_common
3185 * Common code for bci_action_serif_link2 routines.
3188 unsigned char FPGM(bci_action_serif_link2_common) [] = {
3190 PUSHB_1,
3191 bci_action_serif_link2_common,
3192 FDEF,
3194 PUSHB_1,
3196 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
3198 DUP, /* s: [...] edge edge */
3199 PUSHB_1,
3200 sal_anchor,
3202 DUP, /* s: [...] edge edge anchor anchor */
3203 MDAP_noround, /* set rp0 and rp1 to `sal_anchor' */
3205 MD_orig_ZP2_0,
3206 DUP,
3207 ADD,
3208 PUSHB_1,
3210 ADD,
3211 FLOOR,
3212 PUSHB_1,
3213 2*64,
3214 DIV, /* delta = (edge_orig_pos - anchor_orig_pos + 16) & ~31 */
3216 SWAP,
3217 DUP,
3218 DUP,
3219 ALIGNRP, /* align `edge' with `sal_anchor' */
3220 ROLL,
3221 SHPIX, /* shift `edge' by `delta' */
3223 ENDF,
3229 * bci_action_serif_link2
3231 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3233 * in: edge_point (in twilight zone)
3234 * ... stuff for bci_align_segments (edge) ...
3236 * uses: bci_action_serif_link2_common
3239 unsigned char FPGM(bci_action_serif_link2) [] = {
3241 PUSHB_1,
3242 bci_action_serif_link2,
3243 FDEF,
3245 PUSHB_1,
3246 bci_action_serif_link2_common,
3247 CALL,
3249 MDAP_noround, /* set rp0 and rp1 to `edge' */
3251 PUSHB_2,
3252 bci_align_segments,
3254 SZP1, /* set zp1 to normal zone 1 */
3255 CALL,
3257 ENDF,
3263 * bci_action_serif_link2_lower_bound
3265 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3266 * Additionally, move the serif again if necessary to stay within a lower
3267 * bound.
3269 * in: edge_point (in twilight zone)
3270 * edge[-1] (in twilight zone)
3271 * ... stuff for bci_align_segments (edge) ...
3273 * uses: bci_action_serif_link2_common
3274 * bci_lower_bound
3277 unsigned char FPGM(bci_action_serif_link2_lower_bound) [] = {
3279 PUSHB_1,
3280 bci_action_serif_link2_lower_bound,
3281 FDEF,
3283 PUSHB_1,
3284 bci_action_serif_link2_common,
3285 CALL,
3287 PUSHB_1,
3288 bci_lower_bound,
3289 CALL,
3291 ENDF,
3297 * bci_action_serif_link2_upper_bound
3299 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3300 * Additionally, move the serif again if necessary to stay within an upper
3301 * bound.
3303 * in: edge_point (in twilight zone)
3304 * edge[1] (in twilight zone)
3305 * ... stuff for bci_align_segments (edge) ...
3307 * uses: bci_action_serif_link2_common
3308 * bci_upper_bound
3311 unsigned char FPGM(bci_action_serif_link2_upper_bound) [] = {
3313 PUSHB_1,
3314 bci_action_serif_link2_upper_bound,
3315 FDEF,
3317 PUSHB_1,
3318 bci_action_serif_link2_common,
3319 CALL,
3321 PUSHB_1,
3322 bci_upper_bound,
3323 CALL,
3325 ENDF,
3331 * bci_action_serif_link2_lower_upper_bound
3333 * Handle the SERIF_LINK2 action to align a serif relative to the anchor.
3334 * Additionally, move the serif again if necessary to stay within a lower
3335 * and upper bound.
3337 * in: edge_point (in twilight zone)
3338 * edge[-1] (in twilight zone)
3339 * edge[1] (in twilight zone)
3340 * ... stuff for bci_align_segments (edge) ...
3342 * uses: bci_action_serif_link2_common
3343 * bci_lower_upper_bound
3346 unsigned char FPGM(bci_action_serif_link2_lower_upper_bound) [] = {
3348 PUSHB_1,
3349 bci_action_serif_link2_lower_upper_bound,
3350 FDEF,
3352 PUSHB_1,
3353 bci_action_serif_link2_common,
3354 CALL,
3356 PUSHB_1,
3357 bci_lower_upper_bound,
3358 CALL,
3360 ENDF,
3366 * bci_handle_action
3368 * Execute function.
3370 * in: function_index
3373 unsigned char FPGM(bci_handle_action) [] = {
3375 PUSHB_1,
3376 bci_handle_action,
3377 FDEF,
3379 CALL,
3381 ENDF,
3387 * bci_hint_glyph
3389 * This is the top-level glyph hinting function
3390 * which parses the arguments on the stack and calls subroutines.
3392 * in: num_actions (M)
3393 * action_0_func_idx
3394 * ... data ...
3395 * action_1_func_idx
3396 * ... data ...
3397 * ...
3398 * action_M_func_idx
3399 * ... data ...
3401 * uses: bci_handle_action
3403 * bci_action_ip_before
3404 * bci_action_ip_after
3405 * bci_action_ip_on
3406 * bci_action_ip_between
3408 * bci_action_adjust_bound
3409 * bci_action_stem_bound
3411 * bci_action_link
3412 * bci_action_anchor
3413 * bci_action_blue_anchor
3414 * bci_action_adjust
3415 * bci_action_stem
3416 * bci_action_blue
3418 * bci_action_serif
3419 * bci_action_serif_lower_bound
3420 * bci_action_serif_upper_bound
3421 * bci_action_serif_lower_upper_bound
3423 * bci_action_serif_anchor
3424 * bci_action_serif_anchor_lower_bound
3425 * bci_action_serif_anchor_upper_bound
3426 * bci_action_serif_anchor_lower_upper_bound
3428 * bci_action_serif_link1
3429 * bci_action_serif_link1_lower_bound
3430 * bci_action_serif_link1_upper_bound
3431 * bci_action_serif_link1_lower_upper_bound
3433 * bci_action_serif_link2
3434 * bci_action_serif_link2_lower_bound
3435 * bci_action_serif_link2_upper_bound
3436 * bci_action_serif_link2_lower_upper_bound
3439 unsigned char FPGM(bci_hint_glyph) [] = {
3441 PUSHB_1,
3442 bci_hint_glyph,
3443 FDEF,
3445 PUSHB_1,
3446 bci_handle_action,
3447 LOOPCALL,
3449 PUSHB_1,
3451 SZP2, /* set zp2 to normal zone 1 */
3452 IUP_y,
3454 ENDF,
3459 #define COPY_FPGM(func_name) \
3460 memcpy(buf_p, fpgm_ ## func_name, \
3461 sizeof (fpgm_ ## func_name)); \
3462 buf_p += sizeof (fpgm_ ## func_name) \
3464 static FT_Error
3465 TA_table_build_fpgm(FT_Byte** fpgm,
3466 FT_ULong* fpgm_len,
3467 FONT* font)
3469 FT_UInt buf_len;
3470 FT_UInt len;
3471 FT_Byte* buf;
3472 FT_Byte* buf_p;
3475 buf_len = sizeof (FPGM(bci_round))
3476 + sizeof (FPGM(bci_compute_stem_width_a))
3478 + sizeof (FPGM(bci_compute_stem_width_b))
3480 + sizeof (FPGM(bci_compute_stem_width_c))
3481 + sizeof (FPGM(bci_loop))
3482 + sizeof (FPGM(bci_cvt_rescale))
3483 + sizeof (FPGM(bci_blue_round_a))
3485 + sizeof (FPGM(bci_blue_round_b))
3486 + sizeof (FPGM(bci_get_point_extrema))
3488 + sizeof (FPGM(bci_create_segment))
3489 + sizeof (FPGM(bci_create_segments))
3490 + sizeof (FPGM(bci_align_segment))
3491 + sizeof (FPGM(bci_align_segments))
3493 + sizeof (FPGM(bci_scale_contour))
3494 + sizeof (FPGM(bci_scale_glyph))
3495 + sizeof (FPGM(bci_shift_contour))
3496 + sizeof (FPGM(bci_shift_subglyph))
3498 + sizeof (FPGM(bci_ip_outer_align_point))
3499 + sizeof (FPGM(bci_ip_on_align_points))
3500 + sizeof (FPGM(bci_ip_between_align_point))
3501 + sizeof (FPGM(bci_ip_between_align_points))
3503 + sizeof (FPGM(bci_action_adjust_common))
3504 + sizeof (FPGM(bci_action_stem_common))
3505 + sizeof (FPGM(bci_action_serif_common))
3506 + sizeof (FPGM(bci_action_serif_anchor_common))
3507 + sizeof (FPGM(bci_action_serif_link1_common))
3508 + sizeof (FPGM(bci_action_serif_link2_common))
3510 + sizeof (FPGM(bci_lower_bound))
3511 + sizeof (FPGM(bci_upper_bound))
3512 + sizeof (FPGM(bci_lower_upper_bound))
3514 + sizeof (FPGM(bci_action_ip_before))
3515 + sizeof (FPGM(bci_action_ip_after))
3516 + sizeof (FPGM(bci_action_ip_on))
3517 + sizeof (FPGM(bci_action_ip_between))
3519 + sizeof (FPGM(bci_action_adjust_bound))
3520 + sizeof (FPGM(bci_action_stem_bound))
3521 + sizeof (FPGM(bci_action_link))
3522 + sizeof (FPGM(bci_action_anchor))
3523 + sizeof (FPGM(bci_action_blue_anchor))
3524 + sizeof (FPGM(bci_action_adjust))
3525 + sizeof (FPGM(bci_action_stem))
3526 + sizeof (FPGM(bci_action_blue))
3527 + sizeof (FPGM(bci_action_serif))
3528 + sizeof (FPGM(bci_action_serif_lower_bound))
3529 + sizeof (FPGM(bci_action_serif_upper_bound))
3530 + sizeof (FPGM(bci_action_serif_lower_upper_bound))
3531 + sizeof (FPGM(bci_action_serif_anchor))
3532 + sizeof (FPGM(bci_action_serif_anchor_lower_bound))
3533 + sizeof (FPGM(bci_action_serif_anchor_upper_bound))
3534 + sizeof (FPGM(bci_action_serif_anchor_lower_upper_bound))
3535 + sizeof (FPGM(bci_action_serif_link1))
3536 + sizeof (FPGM(bci_action_serif_link1_lower_bound))
3537 + sizeof (FPGM(bci_action_serif_link1_upper_bound))
3538 + sizeof (FPGM(bci_action_serif_link1_lower_upper_bound))
3539 + sizeof (FPGM(bci_action_serif_link2))
3540 + sizeof (FPGM(bci_action_serif_link2_lower_bound))
3541 + sizeof (FPGM(bci_action_serif_link2_upper_bound))
3542 + sizeof (FPGM(bci_action_serif_link2_lower_upper_bound))
3544 + sizeof (FPGM(bci_handle_action))
3545 + sizeof (FPGM(bci_hint_glyph));
3546 /* buffer length must be a multiple of four */
3547 len = (buf_len + 3) & ~3;
3548 buf = (FT_Byte*)malloc(len);
3549 if (!buf)
3550 return FT_Err_Out_Of_Memory;
3552 /* pad end of buffer with zeros */
3553 buf[len - 1] = 0x00;
3554 buf[len - 2] = 0x00;
3555 buf[len - 3] = 0x00;
3557 /* copy font program into buffer and fill in the missing variables */
3558 buf_p = buf;
3560 COPY_FPGM(bci_round);
3561 COPY_FPGM(bci_compute_stem_width_a);
3562 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3563 COPY_FPGM(bci_compute_stem_width_b);
3564 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
3565 COPY_FPGM(bci_compute_stem_width_c);
3566 COPY_FPGM(bci_loop);
3567 COPY_FPGM(bci_cvt_rescale);
3568 COPY_FPGM(bci_blue_round_a);
3569 *(buf_p++) = (unsigned char)CVT_BLUES_SIZE(font);
3570 COPY_FPGM(bci_blue_round_b);
3571 COPY_FPGM(bci_get_point_extrema);
3573 COPY_FPGM(bci_create_segment);
3574 COPY_FPGM(bci_create_segments);
3575 COPY_FPGM(bci_align_segment);
3576 COPY_FPGM(bci_align_segments);
3578 COPY_FPGM(bci_scale_contour);
3579 COPY_FPGM(bci_scale_glyph);
3580 COPY_FPGM(bci_shift_contour);
3581 COPY_FPGM(bci_shift_subglyph);
3583 COPY_FPGM(bci_ip_outer_align_point);
3584 COPY_FPGM(bci_ip_on_align_points);
3585 COPY_FPGM(bci_ip_between_align_point);
3586 COPY_FPGM(bci_ip_between_align_points);
3588 COPY_FPGM(bci_action_adjust_common);
3589 COPY_FPGM(bci_action_stem_common);
3590 COPY_FPGM(bci_action_serif_common);
3591 COPY_FPGM(bci_action_serif_anchor_common);
3592 COPY_FPGM(bci_action_serif_link1_common);
3593 COPY_FPGM(bci_action_serif_link2_common);
3595 COPY_FPGM(bci_lower_bound);
3596 COPY_FPGM(bci_upper_bound);
3597 COPY_FPGM(bci_lower_upper_bound);
3599 COPY_FPGM(bci_action_ip_before);
3600 COPY_FPGM(bci_action_ip_after);
3601 COPY_FPGM(bci_action_ip_on);
3602 COPY_FPGM(bci_action_ip_between);
3604 COPY_FPGM(bci_action_adjust_bound);
3605 COPY_FPGM(bci_action_stem_bound);
3606 COPY_FPGM(bci_action_link);
3607 COPY_FPGM(bci_action_anchor);
3608 COPY_FPGM(bci_action_blue_anchor);
3609 COPY_FPGM(bci_action_adjust);
3610 COPY_FPGM(bci_action_stem);
3611 COPY_FPGM(bci_action_blue);
3612 COPY_FPGM(bci_action_serif);
3613 COPY_FPGM(bci_action_serif_lower_bound);
3614 COPY_FPGM(bci_action_serif_upper_bound);
3615 COPY_FPGM(bci_action_serif_lower_upper_bound);
3616 COPY_FPGM(bci_action_serif_anchor);
3617 COPY_FPGM(bci_action_serif_anchor_lower_bound);
3618 COPY_FPGM(bci_action_serif_anchor_upper_bound);
3619 COPY_FPGM(bci_action_serif_anchor_lower_upper_bound);
3620 COPY_FPGM(bci_action_serif_link1);
3621 COPY_FPGM(bci_action_serif_link1_lower_bound);
3622 COPY_FPGM(bci_action_serif_link1_upper_bound);
3623 COPY_FPGM(bci_action_serif_link1_lower_upper_bound);
3624 COPY_FPGM(bci_action_serif_link2);
3625 COPY_FPGM(bci_action_serif_link2_lower_bound);
3626 COPY_FPGM(bci_action_serif_link2_upper_bound);
3627 COPY_FPGM(bci_action_serif_link2_lower_upper_bound);
3629 COPY_FPGM(bci_handle_action);
3630 COPY_FPGM(bci_hint_glyph);
3632 *fpgm = buf;
3633 *fpgm_len = buf_len;
3635 return FT_Err_Ok;
3639 FT_Error
3640 TA_sfnt_build_fpgm_table(SFNT* sfnt,
3641 FONT* font)
3643 FT_Error error;
3645 FT_Byte* fpgm_buf;
3646 FT_ULong fpgm_len;
3649 error = TA_sfnt_add_table_info(sfnt);
3650 if (error)
3651 return error;
3653 error = TA_table_build_fpgm(&fpgm_buf, &fpgm_len, font);
3654 if (error)
3655 return error;
3657 if (fpgm_len > sfnt->max_instructions)
3658 sfnt->max_instructions = fpgm_len;
3660 /* in case of success, `fpgm_buf' gets linked */
3661 /* and is eventually freed in `TA_font_unload' */
3662 error = TA_font_add_table(font,
3663 &sfnt->table_infos[sfnt->num_table_infos - 1],
3664 TTAG_fpgm, fpgm_len, fpgm_buf);
3665 if (error)
3667 free(fpgm_buf);
3668 return error;
3671 return FT_Err_Ok;
3674 /* end of tafpgm.c */