Add bytecode for STEM action (part 2).
[ttfautohint.git] / src / tabytecode.c
blob11f4071641930bd373a56f7aedec52570798209d
1 /* tabytecode.c */
3 /* written 2011 by Werner Lemberg <wl@gnu.org> */
5 #include "ta.h"
6 #include "tabytecode.h"
9 /* a simple macro to emit bytecode instructions */
10 #define BCI(code) *(bufp++) = (code)
12 /* we increase the stack depth by this amount */
13 #define ADDITIONAL_STACK_ELEMENTS 20
16 /* #define DEBUGGING */
19 #ifdef TA_DEBUG
20 int _ta_debug = 1;
21 int _ta_debug_disable_horz_hints;
22 int _ta_debug_disable_vert_hints;
23 int _ta_debug_disable_blue_hints;
24 void* _ta_debug_hints;
25 #endif
28 typedef struct Hints_Record_ {
29 FT_UInt size;
30 FT_UInt num_actions;
31 FT_Byte* buf;
32 FT_UInt buf_len;
33 } Hints_Record;
35 typedef struct Recorder_ {
36 FONT* font;
37 Hints_Record hints_record;
38 /* see explanations in `TA_sfnt_build_glyph_segments' */
39 FT_UInt* wrap_around_segments;
40 } Recorder;
43 static FT_Error
44 TA_sfnt_compute_global_hints(SFNT* sfnt,
45 FONT* font)
47 FT_Error error;
48 FT_Face face = sfnt->face;
49 FT_UInt enc;
50 FT_UInt idx;
52 static const FT_Encoding latin_encs[] =
54 FT_ENCODING_UNICODE,
55 FT_ENCODING_APPLE_ROMAN,
56 FT_ENCODING_ADOBE_STANDARD,
57 FT_ENCODING_ADOBE_LATIN_1,
59 FT_ENCODING_NONE /* end of list */
63 error = ta_loader_init(font->loader);
64 if (error)
65 return error;
67 /* try to select a latin charmap */
68 for (enc = 0; latin_encs[enc] != FT_ENCODING_NONE; enc++)
70 error = FT_Select_Charmap(face, latin_encs[enc]);
71 if (!error)
72 break;
75 /* load latin glyph `a' to trigger all initializations */
76 idx = FT_Get_Char_Index(face, 'a');
77 error = ta_loader_load_glyph(font->loader, face, idx, 0);
79 return error;
83 static FT_Error
84 TA_table_build_cvt(FT_Byte** cvt,
85 FT_ULong* cvt_len,
86 SFNT* sfnt,
87 FONT* font)
89 TA_LatinAxis haxis;
90 TA_LatinAxis vaxis;
92 FT_UInt i;
93 FT_UInt buf_len;
94 FT_UInt len;
95 FT_Byte* buf;
96 FT_Byte* buf_p;
98 FT_Error error;
101 error = TA_sfnt_compute_global_hints(sfnt, font);
102 if (error)
103 return error;
105 /* XXX check validity of pointers */
106 haxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[0];
107 vaxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[1];
109 buf_len = 2 * (2
110 + haxis->width_count
111 + vaxis->width_count
112 + 2 * vaxis->blue_count);
114 /* buffer length must be a multiple of four */
115 len = (buf_len + 3) & ~3;
116 buf = (FT_Byte*)malloc(len);
117 if (!buf)
118 return FT_Err_Out_Of_Memory;
120 /* pad end of buffer with zeros */
121 buf[len - 1] = 0x00;
122 buf[len - 2] = 0x00;
123 buf[len - 3] = 0x00;
125 buf_p = buf;
127 if (haxis->width_count > 0)
129 *(buf_p++) = HIGH(haxis->widths[0].org);
130 *(buf_p++) = LOW(haxis->widths[0].org);
132 else
134 *(buf_p++) = 0;
135 *(buf_p++) = 50;
137 if (vaxis->width_count > 0)
139 *(buf_p++) = HIGH(vaxis->widths[0].org);
140 *(buf_p++) = LOW(vaxis->widths[0].org);
142 else
144 *(buf_p++) = 0;
145 *(buf_p++) = 50;
148 for (i = 0; i < haxis->width_count; i++)
150 if (haxis->widths[i].org > 0xFFFF)
151 goto Err;
152 *(buf_p++) = HIGH(haxis->widths[i].org);
153 *(buf_p++) = LOW(haxis->widths[i].org);
156 for (i = 0; i < vaxis->width_count; i++)
158 if (vaxis->widths[i].org > 0xFFFF)
159 goto Err;
160 *(buf_p++) = HIGH(vaxis->widths[i].org);
161 *(buf_p++) = LOW(vaxis->widths[i].org);
164 for (i = 0; i < vaxis->blue_count; i++)
166 if (vaxis->blues[i].ref.org > 0xFFFF)
167 goto Err;
168 *(buf_p++) = HIGH(vaxis->blues[i].ref.org);
169 *(buf_p++) = LOW(vaxis->blues[i].ref.org);
172 for (i = 0; i < vaxis->blue_count; i++)
174 if (vaxis->blues[i].shoot.org > 0xFFFF)
175 goto Err;
176 *(buf_p++) = HIGH(vaxis->blues[i].shoot.org);
177 *(buf_p++) = LOW(vaxis->blues[i].shoot.org);
180 #if 0
181 TA_LOG(("--------------------------------------------------\n"));
182 TA_LOG(("glyph %d:\n", idx));
183 ta_glyph_hints_dump_edges(_ta_debug_hints);
184 ta_glyph_hints_dump_segments(_ta_debug_hints);
185 ta_glyph_hints_dump_points(_ta_debug_hints);
186 #endif
188 *cvt = buf;
189 *cvt_len = buf_len;
191 return FT_Err_Ok;
193 Err:
194 free(buf);
195 return TA_Err_Hinter_Overflow;
199 FT_Error
200 TA_sfnt_build_cvt_table(SFNT* sfnt,
201 FONT* font)
203 FT_Error error;
205 FT_Byte* cvt_buf;
206 FT_ULong cvt_len;
209 error = TA_sfnt_add_table_info(sfnt);
210 if (error)
211 return error;
213 error = TA_table_build_cvt(&cvt_buf, &cvt_len, sfnt, font);
214 if (error)
215 return error;
217 /* in case of success, `cvt_buf' gets linked */
218 /* and is eventually freed in `TA_font_unload' */
219 error = TA_font_add_table(font,
220 &sfnt->table_infos[sfnt->num_table_infos - 1],
221 TTAG_cvt, cvt_len, cvt_buf);
222 if (error)
224 free(cvt_buf);
225 return error;
228 return FT_Err_Ok;
232 /* the horizontal and vertical standard widths */
233 #define CVT_HORZ_STANDARD_WIDTH_OFFSET(font) 0
234 #define CVT_VERT_STANDARD_WIDTH_OFFSET(font) \
235 CVT_HORZ_STANDARD_WIDTH_OFFSET(font) + 1
237 /* the horizontal stem widths */
238 #define CVT_HORZ_WIDTHS_OFFSET(font) \
239 CVT_VERT_STANDARD_WIDTH_OFFSET(font) + 1
240 #define CVT_HORZ_WIDTHS_SIZE(font) \
241 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[0].width_count
243 /* the vertical stem widths */
244 #define CVT_VERT_WIDTHS_OFFSET(font) \
245 CVT_HORZ_WIDTHS_OFFSET(font) + CVT_HORZ_WIDTHS_SIZE(font)
246 #define CVT_VERT_WIDTHS_SIZE(font) \
247 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[1].width_count
249 /* the number of blue zones */
250 #define CVT_BLUES_SIZE(font) \
251 ((TA_LatinMetrics)font->loader->hints.metrics)->axis[1].blue_count
253 /* the blue zone values for flat and round edges */
254 #define CVT_BLUE_REFS_OFFSET(font) \
255 CVT_VERT_WIDTHS_OFFSET(font) + CVT_VERT_WIDTHS_SIZE(font)
256 #define CVT_BLUE_SHOOTS_OFFSET(font) \
257 CVT_BLUE_REFS_OFFSET(font) + CVT_BLUES_SIZE(font)
260 /* symbolic names for storage area locations */
262 #define sal_i 0
263 #define sal_j sal_i + 1
264 #define sal_k sal_j + 1
265 #define sal_temp1 sal_k + 1
266 #define sal_temp2 sal_temp1 + 1
267 #define sal_temp3 sal_temp2 + 1
268 #define sal_limit sal_temp3 + 1
269 #define sal_func sal_limit +1
270 #define sal_num_segments sal_func + 1
271 #define sal_scale sal_num_segments + 1
272 #define sal_0x10000 sal_scale + 1
273 #define sal_is_extra_light sal_0x10000 + 1
274 #define sal_anchor sal_is_extra_light + 1
275 #define sal_point_min sal_anchor + 1
276 #define sal_point_max sal_point_min + 1
277 #define sal_segment_offset sal_point_max + 1 /* must be last */
280 /* we need the following macro */
281 /* so that `func_name' doesn't get replaced with its #defined value */
282 /* (as defined in `tabytecode.h') */
284 #define FPGM(func_name) fpgm_ ## func_name
287 /* in the comments below, the top of the stack (`s:') */
288 /* is the rightmost element; the stack is shown */
289 /* after the instruction on the same line has been executed */
291 /* we use two sets of points in the twilight zone (zp0): */
292 /* one set to hold the unhinted segment positions, */
293 /* and another one to track the positions as changed by the hinting -- */
294 /* this is necessary since all points in zp0 */
295 /* have (0,0) as the original coordinates, */
296 /* making e.g. `MD_orig' return useless results */
300 * bci_compute_stem_width
302 * This is the equivalent to the following code from function
303 * `ta_latin_compute_stem_width':
305 * dist = ABS(width)
307 * if (stem_is_serif
308 * && dist < 3*64)
309 * || is_extra_light:
310 * return width
311 * else if base_is_round:
312 * if dist < 80
313 * dist = 64
314 * else if dist < 56:
315 * dist = 56
317 * delta = ABS(dist - std_width)
319 * if delta < 40:
320 * dist = std_width
321 * if dist < 48
322 * dist = 48
323 * goto End
325 * if dist < 3*64:
326 * delta = dist
327 * dist = FLOOR(dist)
328 * delta = delta - dist
330 * if delta < 10:
331 * dist = dist + delta
332 * else if delta < 32:
333 * dist = dist + 10
334 * else if delta < 54:
335 * dist = dist + 54
336 * else
337 * dist = dist + delta
338 * else
339 * dist = ROUND(dist)
341 * End:
342 * if width < 0:
343 * dist = -dist
344 * return dist
347 * in: width
348 * stem_is_serif
349 * base_is_round
350 * out: new_width
351 * sal: sal_is_extra_light
352 * CVT: std_width
355 unsigned char FPGM(bci_compute_stem_width_a) [] = {
357 PUSHB_1,
358 bci_compute_stem_width,
359 FDEF,
361 DUP,
362 ABS, /* s: base_is_round stem_is_serif width dist */
364 DUP,
365 PUSHB_1,
366 3*64,
367 LT, /* dist < 3*64 */
369 PUSHB_1,
371 MINDEX, /* s: base_is_round width dist (dist<3*64) stem_is_serif */
372 AND, /* stem_is_serif && dist < 3*64 */
374 PUSHB_1,
375 sal_is_extra_light,
377 OR, /* (stem_is_serif && dist < 3*64) || is_extra_light */
379 IF, /* s: base_is_round width dist */
380 POP,
381 SWAP,
382 POP, /* s: width */
384 ELSE,
385 ROLL, /* s: width dist base_is_round */
386 IF, /* s: width dist */
387 DUP,
388 PUSHB_1,
390 LT, /* dist < 80 */
391 IF, /* s: width dist */
392 POP,
393 PUSHB_1,
394 64, /* dist = 64 */
395 EIF,
397 ELSE,
398 DUP,
399 PUSHB_1,
401 LT, /* dist < 56 */
402 IF, /* s: width dist */
403 POP,
404 PUSHB_1,
405 56, /* dist = 56 */
406 EIF,
407 EIF,
409 DUP, /* s: width dist dist */
410 PUSHB_1,
414 /* %c, index of std_width */
416 unsigned char FPGM(bci_compute_stem_width_b) [] = {
418 RCVT,
419 SUB,
420 ABS, /* s: width dist delta */
422 PUSHB_1,
424 LT, /* delta < 40 */
425 IF, /* s: width dist */
426 POP,
427 PUSHB_1,
431 /* %c, index of std_width */
433 unsigned char FPGM(bci_compute_stem_width_c) [] = {
435 RCVT, /* dist = std_width */
436 DUP,
437 PUSHB_1,
439 LT, /* dist < 48 */
441 POP,
442 PUSHB_1,
443 48, /* dist = 48 */
444 EIF,
446 ELSE,
447 DUP, /* s: width dist dist */
448 PUSHB_1,
449 3*64,
450 LT, /* dist < 3*64 */
452 DUP, /* s: width delta dist */
453 FLOOR, /* dist = FLOOR(dist) */
454 DUP, /* s: width delta dist dist */
455 ROLL,
456 ROLL, /* s: width dist delta dist */
457 SUB, /* delta = delta - dist */
459 DUP, /* s: width dist delta delta */
460 PUSHB_1,
462 LT, /* delta < 10 */
463 IF, /* s: width dist delta */
464 ADD, /* dist = dist + delta */
466 ELSE,
467 DUP,
468 PUSHB_1,
470 LT, /* delta < 32 */
472 POP,
473 PUSHB_1,
475 ADD, /* dist = dist + 10 */
477 ELSE,
478 DUP,
479 PUSHB_1,
481 LT, /* delta < 54 */
483 POP,
484 PUSHB_1,
486 ADD, /* dist = dist + 54 */
488 ELSE,
489 ADD, /* dist = dist + delta */
491 EIF,
492 EIF,
493 EIF,
495 ELSE,
496 PUSHB_1,
498 ADD,
499 FLOOR, /* dist = round(dist) */
501 EIF,
502 EIF,
504 SWAP, /* s: dist width */
505 PUSHB_1,
507 LT, /* width < 0 */
509 NEG, /* dist = -dist */
510 EIF,
511 EIF,
512 EIF,
514 ENDF,
520 * bci_loop
522 * Take a range and a function number and apply the function to all
523 * elements of the range.
525 * in: func_num
526 * end
527 * start
529 * uses: sal_i (counter initialized with `start')
530 * sal_limit (`end')
531 * sal_func (`func_num')
534 unsigned char FPGM(bci_loop) [] = {
536 PUSHB_1,
537 bci_loop,
538 FDEF,
540 PUSHB_1,
541 sal_func,
542 SWAP,
543 WS, /* sal_func = func_num */
544 PUSHB_1,
545 sal_limit,
546 SWAP,
547 WS, /* sal_limit = end */
548 PUSHB_1,
549 sal_i,
550 SWAP,
551 WS, /* sal_i = start */
553 /* start_loop: */
554 PUSHB_1,
555 sal_i,
557 PUSHB_1,
558 sal_limit,
560 LTEQ, /* start <= end */
562 PUSHB_1,
563 sal_func,
565 CALL,
566 PUSHB_3,
567 sal_i,
569 sal_i,
571 ADD, /* start = start + 1 */
574 PUSHB_1,
576 NEG,
577 JMPR, /* goto start_loop */
578 EIF,
580 ENDF,
586 * bci_cvt_rescale
588 * Rescale CVT value by a given factor.
590 * uses: sal_i (CVT index)
591 * sal_scale (scale in 16.16 format)
594 unsigned char FPGM(bci_cvt_rescale) [] = {
596 PUSHB_1,
597 bci_cvt_rescale,
598 FDEF,
600 PUSHB_1,
601 sal_i,
603 DUP,
604 RCVT,
605 PUSHB_1,
606 sal_scale,
608 MUL, /* CVT * scale * 2^10 */
609 PUSHB_1,
610 sal_0x10000,
612 DIV, /* CVT * scale */
614 WCVTP,
616 ENDF,
622 * bci_blue_round
624 * Round a blue ref value and adjust its corresponding shoot value.
626 * uses: sal_i (CVT index)
630 unsigned char FPGM(bci_blue_round_a) [] = {
632 PUSHB_1,
633 bci_blue_round,
634 FDEF,
636 PUSHB_1,
637 sal_i,
639 DUP,
640 RCVT, /* s: ref_idx ref */
642 DUP,
643 PUSHB_1,
645 ADD,
646 FLOOR,
647 SWAP, /* s: ref_idx round(ref) ref */
649 PUSHB_2,
653 /* %c, blue_count */
655 unsigned char FPGM(bci_blue_round_b) [] = {
658 CINDEX,
659 ADD, /* s: ref_idx round(ref) ref shoot_idx */
660 DUP,
661 RCVT, /* s: ref_idx round(ref) ref shoot_idx shoot */
663 ROLL, /* s: ref_idx round(ref) shoot_idx shoot ref */
664 SWAP,
665 SUB, /* s: ref_idx round(ref) shoot_idx dist */
666 DUP,
667 ABS, /* s: ref_idx round(ref) shoot_idx dist delta */
669 DUP,
670 PUSHB_1,
672 LT, /* delta < 32 */
674 POP,
675 PUSHB_1,
676 0, /* delta = 0 */
678 ELSE,
679 PUSHB_1,
681 LT, /* delta < 48 */
683 PUSHB_1,
684 32, /* delta = 32 */
686 ELSE,
687 PUSHB_1,
688 64, /* delta = 64 */
689 EIF,
690 EIF,
692 SWAP, /* s: ref_idx round(ref) shoot_idx delta dist */
693 PUSHB_1,
695 LT, /* dist < 0 */
697 NEG, /* delta = -delta */
698 EIF,
700 PUSHB_1,
702 CINDEX,
703 ADD, /* s: ref_idx round(ref) shoot_idx (round(ref) + delta) */
705 WCVTP,
706 WCVTP,
708 ENDF,
714 * bci_get_point_extrema
716 * An auxiliary function for `bci_create_segment'.
718 * in: point-1
719 * out: point
721 * sal: sal_point_min
722 * sal_point_max
725 unsigned char FPGM(bci_get_point_extrema) [] = {
727 PUSHB_1,
728 bci_get_point_extrema,
729 FDEF,
731 PUSHB_1,
733 ADD, /* s: point */
734 DUP,
735 DUP,
737 /* check whether `point' is a new minimum */
738 PUSHB_1,
739 sal_point_min,
740 RS, /* s: point point point point_min */
741 MD_orig,
742 /* if distance is negative, we have a new minimum */
743 PUSHB_1,
746 IF, /* s: point point */
747 DUP,
748 PUSHB_1,
749 sal_point_min,
750 SWAP,
752 EIF,
754 /* check whether `point' is a new maximum */
755 PUSHB_1,
756 sal_point_max,
757 RS, /* s: point point point_max */
758 MD_orig,
759 /* if distance is positive, we have a new maximum */
760 PUSHB_1,
763 IF, /* s: point */
764 DUP,
765 PUSHB_1,
766 sal_point_max,
767 SWAP,
769 EIF, /* s: point */
771 ENDF,
777 * bci_create_segment
779 * Store start and end point of a segment in the storage area,
780 * then construct two points in the twilight zone to represent it:
781 * an original one (which stays unmodified) and a hinted one,
782 * initialized with the original value.
784 * This function is used by `bci_create_segment_points'.
786 * in: start
787 * end
788 * [last (if wrap-around segment)]
789 * [first (if wrap-around segment)]
791 * uses: bci_get_point_extrema
793 * sal: sal_i (start of current segment)
794 * sal_j (current original twilight point)
795 * sal_k (current hinted twilight point)
796 * sal_point_min
797 * sal_point_max
800 unsigned char FPGM(bci_create_segment) [] = {
802 PUSHB_1,
803 bci_create_segment,
804 FDEF,
806 PUSHB_1,
807 sal_i,
809 PUSHB_1,
811 CINDEX,
812 WS, /* sal[sal_i] = start */
814 /* increase `sal_i'; together with the outer loop, this makes sal_i += 2 */
815 PUSHB_3,
816 sal_i,
818 sal_i,
820 ADD, /* sal_i = sal_i + 1 */
823 /* initialize inner loop(s) */
824 PUSHB_2,
825 sal_point_min,
827 CINDEX,
828 WS, /* sal_point_min = start */
829 PUSHB_2,
830 sal_point_max,
832 CINDEX,
833 WS, /* sal_point_max = start */
835 PUSHB_1,
837 SZPS, /* set zp0, zp1, and zp2 to normal zone 1 */
839 SWAP,
840 DUP,
841 PUSHB_1,
843 CINDEX, /* s: start end end start */
844 LT, /* start > end */
846 /* we have a wrap-around segment with two more arguments */
847 /* to give the last and first point of the contour, respectively; */
848 /* our job is to store a segment `start'-`last', */
849 /* and to get extrema for the two segments */
850 /* `start'-`last' and `first'-`end' */
852 /* s: first last start end */
853 PUSHB_1,
854 sal_i,
856 PUSHB_1,
858 CINDEX,
859 WS, /* sal[sal_i] = last */
861 ROLL,
862 ROLL, /* s: first end last start */
863 DUP,
864 ROLL,
865 SWAP, /* s: first end start last start */
866 SUB, /* s: first end start loop_count */
868 PUSHB_1,
869 bci_get_point_extrema,
870 LOOPCALL,
871 /* clean up stack */
872 POP,
874 SWAP, /* s: end first */
875 PUSHB_1,
877 SUB,
878 DUP,
879 ROLL, /* s: (first - 1) (first - 1) end */
880 SWAP,
881 SUB, /* s: (first - 1) loop_count */
883 PUSHB_1,
884 bci_get_point_extrema,
885 LOOPCALL,
886 /* clean up stack */
887 POP,
889 ELSE, /* s: start end */
890 PUSHB_1,
891 sal_i,
893 PUSHB_1,
895 CINDEX,
896 WS, /* sal[sal_i] = end */
898 PUSHB_1,
900 CINDEX,
901 SUB, /* s: start loop_count */
903 PUSHB_1,
904 bci_get_point_extrema,
905 LOOPCALL,
906 /* clean up stack */
907 POP,
908 EIF,
910 /* the twilight point representing a segment */
911 /* is in the middle between the minimum and maximum */
912 PUSHB_1,
913 sal_point_max,
915 PUSHB_1,
916 sal_point_min,
918 MD_orig,
919 PUSHB_1,
920 2*64,
921 DIV, /* s: delta */
923 PUSHB_4,
924 sal_j,
927 sal_point_min,
929 MDAP_noround, /* set rp0 and rp1 to `sal_point_min' */
930 SZP1, /* set zp1 to twilight zone 0 */
931 SZP2, /* set zp2 to twilight zone 0 */
934 DUP, /* s: delta point[sal_j] point[sal_j] */
935 ALIGNRP, /* align `point[sal_j]' with `sal_point_min' */
936 PUSHB_1,
938 CINDEX, /* s: delta point[sal_j] delta */
939 SHPIX, /* shift `point[sal_j]' by `delta' */
941 PUSHB_1,
942 sal_k,
944 DUP, /* s: delta point[sal_k] point[sal_k] */
945 ALIGNRP, /* align `point[sal_k]' with `sal_point_min' */
946 SWAP,
947 SHPIX, /* shift `point[sal_k]' by `delta' */
949 PUSHB_6,
950 sal_k,
952 sal_k,
953 sal_j,
955 sal_j,
957 ADD, /* original_twilight_point = original_twilight_point + 1 */
960 ADD, /* hinted_twilight_point = hinted_twilight_point + 1 */
963 ENDF,
969 * bci_create_segments
971 * Set up segments by defining point ranges which defines them
972 * and computing twilight points to represent them.
974 * in: num_segments (N)
975 * segment_start_0
976 * segment_end_0
977 * [contour_last 0 (if wrap-around segment)]
978 * [contour_first 0 (if wrap-around segment)]
979 * segment_start_1
980 * segment_end_1
981 * [contour_last 0 (if wrap-around segment)]
982 * [contour_first 0 (if wrap-around segment)]
983 * ...
984 * segment_start_(N-1)
985 * segment_end_(N-1)
986 * [contour_last (N-1) (if wrap-around segment)]
987 * [contour_first (N-1) (if wrap-around segment)]
989 * uses: bci_create_segment
991 * sal: sal_i (start of current segment)
992 * sal_j (current original twilight point)
993 * sal_k (current hinted twilight point)
994 * sal_num_segments
997 unsigned char FPGM(bci_create_segments) [] = {
999 PUSHB_1,
1000 bci_create_segments,
1001 FDEF,
1003 /* all our measurements are taken along the y axis */
1004 SVTCA_y,
1006 PUSHB_1,
1007 sal_num_segments,
1008 SWAP,
1009 WS, /* sal_num_segments = num_segments */
1011 PUSHB_7,
1012 sal_segment_offset,
1013 sal_segment_offset,
1014 sal_num_segments,
1016 sal_k,
1018 sal_j,
1019 sal_num_segments,
1021 WS, /* sal_j = num_segments (offset for original points) */
1022 WS, /* sal_k = 0 (offset for hinted points) */
1025 DUP,
1026 ADD,
1027 ADD,
1028 PUSHB_1,
1030 SUB, /* s: sal_segment_offset (sal_segment_offset + 2*num_segments - 1) */
1032 /* `bci_create_segment_point' also increases the loop counter by 1; */
1033 /* this effectively means we have a loop step of 2 */
1034 PUSHB_2,
1035 bci_create_segment,
1036 bci_loop,
1037 CALL,
1039 ENDF,
1043 unsigned char FPGM(bci_handle_segment) [] = {
1045 PUSHB_1,
1046 bci_handle_segment,
1047 FDEF,
1049 POP, /* XXX segment */
1051 ENDF,
1057 * bci_align_segment
1059 * Align all points in a segment to the twilight point in rp0.
1060 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
1062 * in: segment_index
1065 unsigned char FPGM(bci_align_segment) [] = {
1067 PUSHB_1,
1068 bci_align_segment,
1069 FDEF,
1071 /* we need the values of `sal_segment_offset + 2*segment_index' */
1072 /* and `sal_segment_offset + 2*segment_index + 1' */
1073 DUP,
1074 ADD,
1075 PUSHB_1,
1076 sal_segment_offset,
1077 ADD,
1078 DUP,
1080 SWAP,
1081 PUSHB_1,
1083 ADD,
1084 RS, /* s: first last */
1086 /* start_loop: */
1087 PUSHB_1,
1089 CINDEX, /* s: first last first */
1090 PUSHB_1,
1092 CINDEX, /* s: first last first last */
1093 LTEQ, /* first <= end */
1094 IF, /* s: first last */
1095 SWAP,
1096 DUP, /* s: last first first */
1097 ALIGNRP, /* align point with index `first' with rp0 */
1099 PUSHB_1,
1101 ADD, /* first = first + 1 */
1102 SWAP, /* s: first last */
1104 PUSHB_1,
1106 NEG,
1107 JMPR, /* goto start_loop */
1109 ELSE,
1110 POP,
1111 POP,
1112 EIF,
1114 ENDF,
1118 unsigned char FPGM(bci_handle_segments) [] = {
1120 PUSHB_1,
1121 bci_handle_segments,
1122 FDEF,
1124 POP, /* XXX first segment */
1126 PUSHB_1,
1127 bci_handle_segment,
1128 LOOPCALL,
1130 ENDF,
1136 * bci_align_segments
1138 * Align segments to the twilight point in rp0.
1139 * zp0 and zp1 must be set to 0 (twilight) and 1 (normal), respectively.
1141 * in: first_segment
1142 * loop_counter (N)
1143 * segment_1
1144 * segment_2
1145 * ...
1146 * segment_N
1148 * uses: handle_segment
1152 unsigned char FPGM(bci_align_segments) [] = {
1154 PUSHB_1,
1155 bci_align_segments,
1156 FDEF,
1158 PUSHB_1,
1159 bci_align_segment,
1160 CALL,
1162 PUSHB_1,
1163 bci_align_segment,
1164 LOOPCALL,
1166 ENDF,
1172 * bci_action_adjust_bound
1174 * Handle the ADJUST_BOUND action to align an edge of a stem if the other
1175 * edge of the stem has already been moved, then moving it again if
1176 * necessary to stay bound.
1178 * in: edge2_is_serif
1179 * edge_is_round
1180 * edge_point (in twilight zone)
1181 * edge2_point (in twilight zone)
1182 * edge[-1] (in twilight zone)
1183 * ... stuff for bci_align_segments (edge) ...
1186 unsigned char FPGM(bci_action_adjust_bound) [] = {
1188 PUSHB_1,
1189 bci_action_adjust_bound,
1190 FDEF,
1192 PUSHB_1,
1194 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1196 PUSHB_1,
1198 CINDEX,
1199 PUSHB_1,
1200 sal_num_segments,
1202 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig */
1203 PUSHB_1,
1205 CINDEX,
1206 PUSHB_1,
1207 sal_num_segments,
1209 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig edge_orig */
1210 MD_cur, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1212 PUSHB_1,
1213 bci_compute_stem_width,
1214 CALL,
1215 NEG, /* s: edge[-1] edge2 edge -cur_len */
1217 ROLL, /* s: edge[-1] edge -cur_len edge2 */
1218 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1219 SWAP,
1220 DUP,
1221 DUP, /* s: edge[-1] -cur_len edge edge edge */
1222 ALIGNRP, /* align `edge' with `edge2' */
1223 ROLL,
1224 SHPIX, /* shift `edge' by -cur_len */
1226 SWAP, /* s: edge edge[-1] */
1227 DUP,
1228 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1229 GC_cur,
1230 PUSHB_1,
1232 CINDEX,
1233 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1234 GT, /* edge_pos < edge[-1]_pos */
1236 DUP,
1237 ALIGNRP, /* align `edge' to `edge[-1]' */
1238 EIF,
1240 MDAP_noround, /* set rp0 and rp1 to `edge' */
1242 PUSHB_2,
1243 bci_align_segments,
1245 SZP1, /* set zp1 to normal zone 1 */
1246 CALL,
1248 ENDF,
1254 * bci_action_stem_bound
1256 * Handle the STEM action to align two edges of a stem, then moving one
1257 * edge again if necessary to stay bound.
1259 * The code after computing `cur_len' to shift `edge' and `edge2'
1260 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1262 * if cur_len < 96:
1263 * if cur_len < = 64:
1264 * u_off = 32
1265 * d_off = 32
1266 * else:
1267 * u_off = 38
1268 * d_off = 26
1270 * org_center = edge_orig + org_len / 2
1271 * cur_pos1 = ROUND(org_center)
1273 * delta1 = ABS(org_center - (cur_pos1 - u_off))
1274 * delta2 = ABS(org_center - (cur_pos1 + d_off))
1275 * if (delta1 < delta2):
1276 * cur_pos1 = cur_pos1 - u_off
1277 * else:
1278 * cur_pos1 = cur_pos1 + d_off
1280 * edge = cur_pos1 - cur_len / 2
1282 * else:
1283 * org_pos = anchor + (edge_orig - anchor_orig)
1284 * org_center = edge_orig + org_len / 2
1286 * cur_pos1 = ROUND(org_pos)
1287 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
1288 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
1289 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
1291 * if (delta1 < delta2):
1292 * edge = cur_pos1
1293 * else:
1294 * edge = cur_pos2
1296 * edge2 = edge + cur_len
1298 * in: edge2_is_serif
1299 * edge_is_round
1300 * edge_point (in twilight zone)
1301 * edge2_point (in twilight zone)
1302 * edge[-1] (in twilight zone)
1303 * ... stuff for bci_align_segments (edge) ...
1304 * ... stuff for bci_align_segments (edge2)...
1306 * sal: sal_anchor
1307 * sal_temp1
1308 * sal_temp2
1309 * sal_temp3
1310 * sal_num_segments
1313 #undef sal_u_off
1314 #define sal_u_off sal_temp1
1315 #undef sal_d_off
1316 #define sal_d_off sal_temp2
1317 #undef sal_org_len
1318 #define sal_org_len sal_temp3
1319 #undef sal_edge2
1320 #define sal_edge2 sal_temp3
1322 unsigned char FPGM(bci_action_stem_bound) [] = {
1324 PUSHB_1,
1325 bci_action_stem_bound,
1326 FDEF,
1328 PUSHB_1,
1330 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1332 PUSHB_1,
1334 CINDEX,
1335 PUSHB_1,
1336 sal_num_segments,
1338 ADD,
1339 PUSHB_1,
1341 CINDEX,
1342 DUP,
1343 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1344 PUSHB_1,
1345 sal_num_segments,
1347 ADD, /* s: edge[-1] edge2 edge is_round is_serif edge2_orig edge_orig */
1349 MD_cur, /* s: edge[-1] edge2 edge is_round is_serif org_len */
1350 DUP,
1351 PUSHB_1,
1352 sal_org_len,
1353 SWAP,
1356 PUSHB_1,
1357 bci_compute_stem_width,
1358 CALL, /* s: edge[-1] edge2 edge cur_len */
1360 DUP,
1361 PUSHB_1,
1363 LT, /* cur_len < 96 */
1365 DUP,
1366 PUSHB_1,
1368 LTEQ, /* cur_len <= 64 */
1370 PUSHB_4,
1371 sal_u_off,
1373 sal_d_off,
1376 ELSE,
1377 PUSHB_4,
1378 sal_u_off,
1380 sal_d_off,
1382 EIF,
1386 SWAP, /* s: edge[-1] edge2 cur_len edge */
1387 DUP,
1388 PUSHB_1,
1389 sal_num_segments,
1391 ADD, /* s: edge[-1] edge2 cur_len edge edge_orig */
1393 GC_cur,
1394 PUSHB_1,
1395 sal_org_len,
1397 PUSHB_1,
1398 2*64,
1399 DIV,
1400 ADD, /* s: edge[-1] edge2 cur_len edge org_center */
1402 DUP,
1403 PUSHB_1,
1405 ADD,
1406 FLOOR, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 */
1408 DUP,
1409 ROLL,
1410 ROLL,
1411 SUB, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) */
1413 DUP,
1414 PUSHB_1,
1415 sal_u_off,
1417 ADD,
1418 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
1420 SWAP,
1421 PUSHB_1,
1422 sal_d_off,
1424 SUB,
1425 ABS, /* s: edge[-1] edge2 cur_len edge cur_pos1 delta1 delta2 */
1427 LT, /* delta1 < delta2 */
1429 PUSHB_1,
1430 sal_u_off,
1432 SUB, /* cur_pos1 = cur_pos1 - u_off */
1434 ELSE,
1435 PUSHB_1,
1436 sal_d_off,
1438 ADD, /* cur_pos1 = cur_pos1 + d_off */
1439 EIF, /* s: edge[-1] edge2 cur_len edge cur_pos1 */
1441 PUSHB_1,
1443 CINDEX,
1444 PUSHB_1,
1445 2*64,
1446 DIV,
1447 SUB, /* arg = cur_pos1 - cur_len/2 */
1449 SWAP, /* s: edge[-1] edge2 cur_len arg edge */
1450 DUP,
1451 DUP,
1452 PUSHB_1,
1454 MINDEX,
1455 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1456 GC_cur,
1457 SUB,
1458 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1460 ELSE,
1461 SWAP, /* s: edge[-1] edge2 cur_len edge */
1462 DUP,
1463 PUSHB_1,
1464 sal_num_segments,
1466 ADD, /* s: edge[-1] edge2 cur_len edge edge_orig */
1468 GC_cur,
1469 PUSHB_1,
1470 sal_org_len,
1472 PUSHB_1,
1473 2*64,
1474 DIV,
1475 ADD, /* s: edge[-1] edge2 cur_len edge org_center */
1477 PUSHB_1,
1478 sal_anchor,
1480 GC_cur, /* s: edge[-1] edge2 cur_len edge org_center anchor_pos */
1481 PUSHB_1,
1483 CINDEX,
1484 PUSHB_1,
1485 sal_num_segments,
1487 ADD,
1488 PUSHB_1,
1489 sal_anchor,
1491 PUSHB_1,
1492 sal_num_segments,
1494 ADD,
1495 MD_cur,
1496 ADD, /* s: edge[-1] edge2 cur_len edge org_center org_pos */
1498 DUP,
1499 PUSHB_1,
1501 ADD,
1502 FLOOR, /* cur_pos1 = ROUND(org_pos) */
1503 SWAP,
1504 PUSHB_1,
1505 sal_org_len,
1507 ADD,
1508 PUSHB_1,
1510 ADD,
1511 FLOOR,
1512 PUSHB_1,
1514 CINDEX,
1515 SUB, /* s: edge[-1] edge2 cur_len edge org_center cur_pos1 cur_pos2 */
1517 PUSHB_1,
1519 CINDEX,
1520 PUSHB_1,
1521 2*64,
1522 DIV,
1523 PUSHB_1,
1525 MINDEX,
1526 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
1528 DUP,
1529 PUSHB_1,
1531 CINDEX,
1532 ADD,
1533 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
1534 SWAP,
1535 PUSHB_1,
1537 CINDEX,
1538 ADD,
1539 ABS, /* s: ... edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
1540 LT, /* delta1 < delta2 */
1542 POP, /* arg = cur_pos1 */
1543 ELSE,
1544 SWAP,
1545 POP, /* arg = cur_pos2 */
1546 EIF, /* s: edge[-1] edge2 cur_len edge arg */
1547 SWAP,
1548 DUP,
1549 DUP,
1550 PUSHB_1,
1552 MINDEX,
1553 SWAP, /* s: edge[-1] edge2 cur_len edge edge arg edge */
1554 GC_cur,
1555 SUB,
1556 SHPIX, /* edge = arg */
1557 EIF, /* s: edge[-1] edge2 cur_len edge */
1559 ROLL, /* s: edge[-1] cur_len edge edge2 */
1560 DUP,
1561 DUP,
1562 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1563 PUSHB_1,
1564 sal_edge2,
1565 SWAP,
1566 WS, /* s: edge[-1] cur_len edge edge2 */
1567 ROLL,
1568 SHPIX, /* edge2 = edge + cur_len */
1570 SWAP, /* s: edge edge[-1] */
1571 DUP,
1572 MDAP_noround, /* set rp0 and rp1 to `edge[-1]' */
1573 GC_cur,
1574 PUSHB_1,
1576 CINDEX,
1577 GC_cur, /* s: edge edge[-1]_pos edge_pos */
1578 GT, /* edge_pos < edge[-1]_pos */
1580 DUP,
1581 ALIGNRP, /* align `edge' to `edge[-1]' */
1582 EIF,
1584 MDAP_noround, /* set rp0 and rp1 to `edge' */
1586 PUSHB_2,
1587 bci_align_segments,
1589 SZP1, /* set zp1 to normal zone 1 */
1590 CALL,
1592 PUSHB_1,
1593 sal_edge2,
1595 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1597 PUSHB_1,
1598 bci_align_segments,
1599 CALL,
1601 ENDF,
1607 * bci_action_link
1609 * Handle the LINK action to link an edge to another one.
1611 * in: stem_is_serif
1612 * base_is_round
1613 * base_point (in twilight zone)
1614 * stem_point (in twilight zone)
1615 * ... stuff for bci_align_segments (base) ...
1618 unsigned char FPGM(bci_action_link) [] = {
1620 PUSHB_1,
1621 bci_action_link,
1622 FDEF,
1624 PUSHB_1,
1626 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1628 PUSHB_1,
1630 CINDEX,
1631 PUSHB_1,
1632 sal_num_segments,
1634 ADD,
1635 PUSHB_1,
1637 MINDEX,
1638 DUP,
1639 MDAP_noround, /* set rp0 and rp1 to `base_point' (for ALIGNRP below) */
1640 PUSHB_1,
1641 sal_num_segments,
1643 ADD, /* s: stem is_round is_serif stem_orig base_orig */
1645 MD_cur, /* s: stem is_round is_serif dist_orig */
1647 PUSHB_1,
1648 bci_compute_stem_width,
1649 CALL, /* s: stem new_dist */
1651 SWAP,
1652 DUP,
1653 ALIGNRP, /* align `stem_point' with `base_point' */
1654 DUP,
1655 MDAP_noround, /* set rp0 and rp1 to `stem_point' */
1656 SWAP,
1657 SHPIX, /* stem_point = base_point + new_dist */
1659 PUSHB_2,
1660 bci_align_segments,
1662 SZP1, /* set zp1 to normal zone 1 */
1663 CALL,
1665 ENDF,
1671 * bci_action_anchor
1673 * Handle the ANCHOR action to align two edges
1674 * and to set the edge anchor.
1676 * The code after computing `cur_len' to shift `edge' and `edge2'
1677 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
1679 * if cur_len < 96:
1680 * if cur_len < = 64:
1681 * u_off = 32
1682 * d_off = 32
1683 * else:
1684 * u_off = 38
1685 * d_off = 26
1687 * org_center = edge_orig + org_len / 2
1688 * cur_pos1 = ROUND(org_center)
1690 * error1 = ABS(org_center - (cur_pos1 - u_off))
1691 * error2 = ABS(org_center - (cur_pos1 + d_off))
1692 * if (error1 < error2):
1693 * cur_pos1 = cur_pos1 - u_off
1694 * else:
1695 * cur_pos1 = cur_pos1 + d_off
1697 * edge = cur_pos1 - cur_len / 2
1698 * edge2 = edge + cur_len
1700 * else:
1701 * edge = ROUND(edge_orig)
1703 * in: edge2_is_serif
1704 * edge_is_round
1705 * edge_point (in twilight zone)
1706 * edge2_point (in twilight zone)
1707 * ... stuff for bci_align_segments (edge) ...
1709 * sal: sal_anchor
1710 * sal_temp1
1711 * sal_temp2
1712 * sal_temp3
1715 #undef sal_u_off
1716 #define sal_u_off sal_temp1
1717 #undef sal_d_off
1718 #define sal_d_off sal_temp2
1719 #undef sal_org_len
1720 #define sal_org_len sal_temp3
1722 unsigned char FPGM(bci_action_anchor) [] = {
1724 PUSHB_1,
1725 bci_action_anchor,
1726 FDEF,
1728 /* store anchor point number in `sal_anchor' */
1729 PUSHB_2,
1730 sal_anchor,
1732 CINDEX,
1733 WS, /* sal_anchor = edge_point */
1735 PUSHB_1,
1737 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1739 PUSHB_1,
1741 CINDEX,
1742 PUSHB_1,
1743 sal_num_segments,
1745 ADD,
1746 PUSHB_1,
1748 CINDEX,
1749 DUP,
1750 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
1751 PUSHB_1,
1752 sal_num_segments,
1754 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
1756 MD_cur, /* s: edge2 edge is_round is_serif org_len */
1757 DUP,
1758 PUSHB_1,
1759 sal_org_len,
1760 SWAP,
1763 PUSHB_1,
1764 bci_compute_stem_width,
1765 CALL, /* s: edge2 edge cur_len */
1767 DUP,
1768 PUSHB_1,
1770 LT, /* cur_len < 96 */
1772 DUP,
1773 PUSHB_1,
1775 LTEQ, /* cur_len <= 64 */
1777 PUSHB_4,
1778 sal_u_off,
1780 sal_d_off,
1783 ELSE,
1784 PUSHB_4,
1785 sal_u_off,
1787 sal_d_off,
1789 EIF,
1793 SWAP, /* s: edge2 cur_len edge */
1794 DUP,
1795 PUSHB_1,
1796 sal_num_segments,
1798 ADD, /* s: edge2 cur_len edge edge_orig */
1800 GC_cur,
1801 PUSHB_1,
1802 sal_org_len,
1804 PUSHB_1,
1805 2*64,
1806 DIV,
1807 ADD, /* s: edge2 cur_len edge org_center */
1809 DUP,
1810 PUSHB_1,
1812 ADD,
1813 FLOOR, /* s: edge2 cur_len edge org_center cur_pos1 */
1815 DUP,
1816 ROLL,
1817 ROLL,
1818 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
1820 DUP,
1821 PUSHB_1,
1822 sal_u_off,
1824 ADD,
1825 ABS, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) error1 */
1827 SWAP,
1828 PUSHB_1,
1829 sal_d_off,
1831 SUB,
1832 ABS, /* s: edge2 cur_len edge cur_pos1 error1 error2 */
1834 LT, /* error1 < error2 */
1836 PUSHB_1,
1837 sal_u_off,
1839 SUB, /* cur_pos1 = cur_pos1 - u_off */
1841 ELSE,
1842 PUSHB_1,
1843 sal_d_off,
1845 ADD, /* cur_pos1 = cur_pos1 + d_off */
1846 EIF, /* s: edge2 cur_len edge cur_pos1 */
1848 PUSHB_1,
1850 CINDEX,
1851 PUSHB_1,
1852 2*64,
1853 DIV,
1854 SUB, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) */
1856 PUSHB_1,
1858 CINDEX, /* s: edge2 cur_len edge (cur_pos1 - cur_len/2) edge */
1859 GC_cur,
1860 SUB,
1861 SHPIX, /* edge = cur_pos1 - cur_len/2 */
1863 SWAP, /* s: cur_len edge2 */
1864 DUP,
1865 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
1866 SWAP,
1867 SHPIX, /* edge2 = edge1 + cur_len */
1869 ELSE,
1870 POP, /* s: edge2 edge */
1871 DUP,
1872 PUSHB_1,
1873 sal_num_segments,
1875 ADD, /* s: edge2 edge edge_orig */
1877 MDAP_noround, /* set rp0 and rp1 to `edge_orig' */
1878 DUP,
1879 ALIGNRP, /* align `edge' with `edge_orig' */
1880 MDAP_round, /* round `edge' */
1882 /* clean up stack */
1883 POP,
1884 EIF,
1886 PUSHB_2,
1887 bci_align_segments,
1889 SZP1, /* set zp1 to normal zone 1 */
1890 CALL,
1892 ENDF,
1898 * bci_action_blue_anchor
1900 * Handle the BLUE_ANCHOR action to align an edge with a blue zone
1901 * and to set the edge anchor.
1903 * in: anchor_point (in twilight zone)
1904 * blue_cvt_idx
1905 * edge_point (in twilight zone)
1906 * ... stuff for bci_align_segments (edge) ...
1908 * sal: sal_anchor
1911 unsigned char FPGM(bci_action_blue_anchor) [] = {
1913 PUSHB_1,
1914 bci_action_blue_anchor,
1915 FDEF,
1917 /* store anchor point number in `sal_anchor' */
1918 PUSHB_1,
1919 sal_anchor,
1920 SWAP,
1923 PUSHB_1,
1925 SZP0, /* set zp0 to twilight zone 0 */
1927 /* move `edge_point' to `blue_cvt_idx' position */
1928 MIAP_noround, /* this also sets rp0 */
1930 PUSHB_2,
1931 bci_align_segments,
1933 SZP1, /* set zp1 to normal zone 1 */
1934 CALL,
1936 ENDF,
1942 * bci_action_adjust
1944 * Handle the ADJUST action to align an edge of a stem if the other edge
1945 * of the stem has already been moved.
1947 * in: edge2_is_serif
1948 * edge_is_round
1949 * edge_point (in twilight zone)
1950 * edge2_point (in twilight zone)
1951 * ... stuff for bci_align_segments (edge) ...
1954 unsigned char FPGM(bci_action_adjust) [] = {
1956 PUSHB_1,
1957 bci_action_adjust,
1958 FDEF,
1960 PUSHB_1,
1962 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
1964 PUSHB_1,
1966 CINDEX,
1967 PUSHB_1,
1968 sal_num_segments,
1970 ADD, /* s: edge2 edge is_round is_serif edge2_orig */
1971 PUSHB_1,
1973 CINDEX,
1974 PUSHB_1,
1975 sal_num_segments,
1977 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
1978 MD_cur, /* s: edge2 edge is_round is_serif org_len */
1980 PUSHB_1,
1981 bci_compute_stem_width,
1982 CALL,
1983 NEG, /* s: edge2 edge -cur_len */
1985 ROLL,
1986 MDAP_noround, /* set rp0 and rp1 to `edge2' */
1987 SWAP,
1988 DUP,
1989 DUP, /* s: -cur_len edge edge edge */
1990 ALIGNRP, /* align `edge' with `edge2' */
1991 ROLL,
1992 SHPIX, /* shift `edge' by -cur_len */
1994 MDAP_noround, /* set rp0 and rp1 to `edge' */
1996 PUSHB_2,
1997 bci_align_segments,
1999 SZP1, /* set zp1 to normal zone 1 */
2000 CALL,
2002 ENDF,
2008 * bci_action_stem
2010 * Handle the STEM action to align two edges of a stem.
2012 * The code after computing `cur_len' to shift `edge' and `edge2'
2013 * is equivalent to the snippet below (part of `ta_latin_hint_edges'):
2015 * if cur_len < 96:
2016 * if cur_len < = 64:
2017 * u_off = 32
2018 * d_off = 32
2019 * else:
2020 * u_off = 38
2021 * d_off = 26
2023 * org_center = edge_orig + org_len / 2
2024 * cur_pos1 = ROUND(org_center)
2026 * delta1 = ABS(org_center - (cur_pos1 - u_off))
2027 * delta2 = ABS(org_center - (cur_pos1 + d_off))
2028 * if (delta1 < delta2):
2029 * cur_pos1 = cur_pos1 - u_off
2030 * else:
2031 * cur_pos1 = cur_pos1 + d_off
2033 * edge = cur_pos1 - cur_len / 2
2035 * else:
2036 * org_pos = anchor + (edge_orig - anchor_orig)
2037 * org_center = edge_orig + org_len / 2
2039 * cur_pos1 = ROUND(org_pos)
2040 * delta1 = ABS(cur_pos1 + cur_len / 2 - org_center)
2041 * cur_pos2 = ROUND(org_pos + org_len) - cur_len
2042 * delta2 = ABS(cur_pos2 + cur_len / 2 - org_center)
2044 * if (delta1 < delta2):
2045 * edge = cur_pos1
2046 * else:
2047 * edge = cur_pos2
2049 * edge2 = edge + cur_len
2051 * in: edge2_is_serif
2052 * edge_is_round
2053 * edge_point (in twilight zone)
2054 * edge2_point (in twilight zone)
2055 * ... stuff for bci_align_segments (edge) ...
2056 * ... stuff for bci_align_segments (edge2)...
2058 * sal: sal_anchor
2059 * sal_temp1
2060 * sal_temp2
2061 * sal_temp3
2062 * sal_num_segments
2065 #undef sal_u_off
2066 #define sal_u_off sal_temp1
2067 #undef sal_d_off
2068 #define sal_d_off sal_temp2
2069 #undef sal_org_len
2070 #define sal_org_len sal_temp3
2071 #undef sal_edge2
2072 #define sal_edge2 sal_temp3
2074 unsigned char FPGM(bci_action_stem) [] = {
2076 PUSHB_1,
2077 bci_action_stem,
2078 FDEF,
2080 PUSHB_1,
2082 SZPS, /* set zp0, zp1, and zp2 to twilight zone 0 */
2084 PUSHB_1,
2086 CINDEX,
2087 PUSHB_1,
2088 sal_num_segments,
2090 ADD,
2091 PUSHB_1,
2093 CINDEX,
2094 DUP,
2095 MDAP_noround, /* set rp0 and rp1 to `edge_point' (for ALIGNRP below) */
2096 PUSHB_1,
2097 sal_num_segments,
2099 ADD, /* s: edge2 edge is_round is_serif edge2_orig edge_orig */
2101 MD_cur, /* s: edge2 edge is_round is_serif org_len */
2102 DUP,
2103 PUSHB_1,
2104 sal_org_len,
2105 SWAP,
2108 PUSHB_1,
2109 bci_compute_stem_width,
2110 CALL, /* s: edge2 edge cur_len */
2112 DUP,
2113 PUSHB_1,
2115 LT, /* cur_len < 96 */
2117 DUP,
2118 PUSHB_1,
2120 LTEQ, /* cur_len <= 64 */
2122 PUSHB_4,
2123 sal_u_off,
2125 sal_d_off,
2128 ELSE,
2129 PUSHB_4,
2130 sal_u_off,
2132 sal_d_off,
2134 EIF,
2138 SWAP, /* s: edge2 cur_len edge */
2139 DUP,
2140 PUSHB_1,
2141 sal_num_segments,
2143 ADD, /* s: edge2 cur_len edge edge_orig */
2145 GC_cur,
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,
2157 ADD,
2158 FLOOR, /* s: edge2 cur_len edge org_center cur_pos1 */
2160 DUP,
2161 ROLL,
2162 ROLL,
2163 SUB, /* s: edge2 cur_len edge cur_pos1 (org_center - cur_pos1) */
2165 DUP,
2166 PUSHB_1,
2167 sal_u_off,
2169 ADD,
2170 ABS, /* s: ... cur_len edge cur_pos1 (org_center - cur_pos1) delta1 */
2172 SWAP,
2173 PUSHB_1,
2174 sal_d_off,
2176 SUB,
2177 ABS, /* s: edge2 cur_len edge cur_pos1 delta1 delta2 */
2179 LT, /* delta1 < delta2 */
2181 PUSHB_1,
2182 sal_u_off,
2184 SUB, /* cur_pos1 = cur_pos1 - u_off */
2186 ELSE,
2187 PUSHB_1,
2188 sal_d_off,
2190 ADD, /* cur_pos1 = cur_pos1 + d_off */
2191 EIF, /* s: edge2 cur_len edge cur_pos1 */
2193 PUSHB_1,
2195 CINDEX,
2196 PUSHB_1,
2197 2*64,
2198 DIV,
2199 SUB, /* arg = cur_pos1 - cur_len/2 */
2201 SWAP, /* s: edge2 cur_len arg edge */
2202 DUP,
2203 PUSHB_1,
2205 MINDEX,
2206 SWAP, /* s: edge2 cur_len edge arg edge */
2207 GC_cur,
2208 SUB,
2209 SHPIX, /* edge = cur_pos1 - cur_len/2 */
2211 ELSE,
2212 SWAP, /* s: edge2 cur_len edge */
2213 DUP,
2214 PUSHB_1,
2215 sal_num_segments,
2217 ADD, /* s: edge2 cur_len edge edge_orig */
2219 GC_cur,
2220 PUSHB_1,
2221 sal_org_len,
2223 PUSHB_1,
2224 2*64,
2225 DIV,
2226 ADD, /* s: edge2 cur_len edge org_center */
2228 PUSHB_1,
2229 sal_anchor,
2231 GC_cur, /* s: edge2 cur_len edge org_center anchor_pos */
2232 PUSHB_1,
2234 CINDEX,
2235 PUSHB_1,
2236 sal_num_segments,
2238 ADD,
2239 PUSHB_1,
2240 sal_anchor,
2242 PUSHB_1,
2243 sal_num_segments,
2245 ADD,
2246 MD_cur,
2247 ADD, /* s: edge2 cur_len edge org_center org_pos */
2249 DUP,
2250 PUSHB_1,
2252 ADD,
2253 FLOOR, /* cur_pos1 = ROUND(org_pos) */
2254 SWAP,
2255 PUSHB_1,
2256 sal_org_len,
2258 ADD,
2259 PUSHB_1,
2261 ADD,
2262 FLOOR,
2263 PUSHB_1,
2265 CINDEX,
2266 SUB, /* s: edge2 cur_len edge org_center cur_pos1 cur_pos2 */
2268 PUSHB_1,
2270 CINDEX,
2271 PUSHB_1,
2272 2*64,
2273 DIV,
2274 PUSHB_1,
2276 MINDEX,
2277 SUB, /* s: ... cur_len edge cur_pos1 cur_pos2 (cur_len/2 - org_center) */
2279 DUP,
2280 PUSHB_1,
2282 CINDEX,
2283 ADD,
2284 ABS, /* delta1 = ABS(cur_pos1 + cur_len / 2 - org_center) */
2285 SWAP,
2286 PUSHB_1,
2288 CINDEX,
2289 ADD,
2290 ABS, /* s: edge2 cur_len edge cur_pos1 cur_pos2 delta1 delta2 */
2291 LT, /* delta1 < delta2 */
2293 POP, /* arg = cur_pos1 */
2294 ELSE,
2295 SWAP,
2296 POP, /* arg = cur_pos2 */
2297 EIF, /* s: edge2 cur_len edge arg */
2298 SWAP,
2299 DUP,
2300 PUSHB_1,
2302 MINDEX,
2303 SWAP, /* s: edge2 cur_len edge arg edge */
2304 GC_cur,
2305 SUB,
2306 SHPIX, /* edge = arg */
2307 EIF, /* s: edge2 cur_len */
2309 SWAP, /* s: cur_len edge2 */
2310 DUP,
2311 DUP,
2312 ALIGNRP, /* align `edge2' with rp0 (still `edge') */
2313 PUSHB_1,
2314 sal_edge2,
2315 SWAP,
2316 WS, /* s: cur_len edge2 */
2317 SWAP,
2318 SHPIX, /* edge2 = edge + cur_len */
2320 PUSHB_2,
2321 bci_align_segments,
2323 SZP1, /* set zp1 to normal zone 1 */
2324 CALL,
2326 PUSHB_1,
2327 sal_edge2,
2329 MDAP_noround, /* set rp0 and rp1 to `edge2' */
2331 PUSHB_1,
2332 bci_align_segments,
2333 CALL,
2334 ENDF,
2340 * bci_action_blue
2342 * Handle the BLUE action to align an edge with a blue zone.
2344 * in: blue_cvt_idx
2345 * edge_point (in twilight zone)
2346 * ... stuff for bci_align_segments (edge) ...
2349 unsigned char FPGM(bci_action_blue) [] = {
2351 PUSHB_1,
2352 bci_action_blue,
2353 FDEF,
2355 PUSHB_1,
2357 SZP0, /* set zp0 to twilight zone 0 */
2359 /* move `edge_point' to `blue_cvt_idx' position */
2360 MIAP_noround, /* this also sets rp0 */
2362 PUSHB_2,
2363 bci_align_segments,
2365 SZP1, /* set zp1 to normal zone 1 */
2366 CALL,
2368 ENDF,
2372 unsigned char FPGM(bci_action_serif) [] = {
2374 PUSHB_1,
2375 bci_action_serif,
2376 FDEF,
2378 PUSHB_1,
2379 bci_handle_segments,
2380 CALL,
2382 /* XXX */
2384 ENDF,
2388 unsigned char FPGM(bci_action_serif_anchor) [] = {
2390 PUSHB_1,
2391 bci_action_serif_anchor,
2392 FDEF,
2394 PUSHB_1,
2395 bci_handle_segments,
2396 CALL,
2398 /* XXX */
2400 ENDF,
2404 unsigned char FPGM(bci_action_serif_link1) [] = {
2406 PUSHB_1,
2407 bci_action_serif_link1,
2408 FDEF,
2410 PUSHB_1,
2411 bci_handle_segments,
2412 CALL,
2414 /* XXX */
2416 ENDF,
2420 unsigned char FPGM(bci_action_serif_link2) [] = {
2422 PUSHB_1,
2423 bci_action_serif_link2,
2424 FDEF,
2426 PUSHB_1,
2427 bci_handle_segments,
2428 CALL,
2430 /* XXX */
2432 ENDF,
2438 * bci_handle_action
2440 * Execute function.
2442 * in: function_index
2445 unsigned char FPGM(bci_handle_action) [] = {
2447 PUSHB_1,
2448 bci_handle_action,
2449 FDEF,
2451 CALL,
2453 ENDF,
2459 * bci_hint_glyph
2461 * This is the top-level glyph hinting function
2462 * which parses the arguments on the stack and calls subroutines.
2464 * in: num_actions (M)
2465 * action_0_func_idx
2466 * ... data ...
2467 * action_1_func_idx
2468 * ... data ...
2469 * ...
2470 * action_M_func_idx
2471 * ... data ...
2473 * uses: bci_handle_action
2474 * bci_action_adjust_bound
2475 * bci_action_stem_bound
2477 * bci_action_link
2478 * bci_action_anchor
2479 * bci_action_blue_anchor
2480 * bci_action_adjust
2481 * bci_action_stem
2483 * bci_action_blue
2484 * bci_action_serif
2485 * bci_action_serif_anchor
2486 * bci_action_serif_link1
2487 * bci_action_serif_link2
2490 unsigned char FPGM(bci_hint_glyph) [] = {
2492 PUSHB_1,
2493 bci_hint_glyph,
2494 FDEF,
2496 PUSHB_1,
2497 bci_handle_action,
2498 LOOPCALL,
2500 ENDF,
2505 #define COPY_FPGM(func_name) \
2506 memcpy(buf_p, fpgm_ ## func_name, \
2507 sizeof (fpgm_ ## func_name)); \
2508 buf_p += sizeof (fpgm_ ## func_name) \
2510 static FT_Error
2511 TA_table_build_fpgm(FT_Byte** fpgm,
2512 FT_ULong* fpgm_len,
2513 FONT* font)
2515 FT_UInt buf_len;
2516 FT_UInt len;
2517 FT_Byte* buf;
2518 FT_Byte* buf_p;
2521 buf_len = sizeof (FPGM(bci_compute_stem_width_a))
2523 + sizeof (FPGM(bci_compute_stem_width_b))
2525 + sizeof (FPGM(bci_compute_stem_width_c))
2526 + sizeof (FPGM(bci_loop))
2527 + sizeof (FPGM(bci_cvt_rescale))
2528 + sizeof (FPGM(bci_blue_round_a))
2530 + sizeof (FPGM(bci_blue_round_b))
2531 + sizeof (FPGM(bci_get_point_extrema))
2532 + sizeof (FPGM(bci_create_segment))
2533 + sizeof (FPGM(bci_create_segments))
2534 + sizeof (FPGM(bci_handle_segment))
2535 + sizeof (FPGM(bci_align_segment))
2536 + sizeof (FPGM(bci_handle_segments))
2537 + sizeof (FPGM(bci_align_segments))
2538 + sizeof (FPGM(bci_action_adjust_bound))
2539 + sizeof (FPGM(bci_action_stem_bound))
2540 + sizeof (FPGM(bci_action_link))
2541 + sizeof (FPGM(bci_action_anchor))
2542 + sizeof (FPGM(bci_action_blue_anchor))
2543 + sizeof (FPGM(bci_action_adjust))
2544 + sizeof (FPGM(bci_action_stem))
2545 + sizeof (FPGM(bci_action_blue))
2546 + sizeof (FPGM(bci_action_serif))
2547 + sizeof (FPGM(bci_action_serif_anchor))
2548 + sizeof (FPGM(bci_action_serif_link1))
2549 + sizeof (FPGM(bci_action_serif_link2))
2550 + sizeof (FPGM(bci_handle_action))
2551 + sizeof (FPGM(bci_hint_glyph));
2552 /* buffer length must be a multiple of four */
2553 len = (buf_len + 3) & ~3;
2554 buf = (FT_Byte*)malloc(len);
2555 if (!buf)
2556 return FT_Err_Out_Of_Memory;
2558 /* pad end of buffer with zeros */
2559 buf[len - 1] = 0x00;
2560 buf[len - 2] = 0x00;
2561 buf[len - 3] = 0x00;
2563 /* copy font program into buffer and fill in the missing variables */
2564 buf_p = buf;
2566 COPY_FPGM(bci_compute_stem_width_a);
2567 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
2568 COPY_FPGM(bci_compute_stem_width_b);
2569 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
2570 COPY_FPGM(bci_compute_stem_width_c);
2571 COPY_FPGM(bci_loop);
2572 COPY_FPGM(bci_cvt_rescale);
2573 COPY_FPGM(bci_blue_round_a);
2574 *(buf_p++) = (unsigned char)CVT_BLUES_SIZE(font);
2575 COPY_FPGM(bci_blue_round_b);
2576 COPY_FPGM(bci_get_point_extrema);
2577 COPY_FPGM(bci_create_segment);
2578 COPY_FPGM(bci_create_segments);
2579 COPY_FPGM(bci_handle_segment);
2580 COPY_FPGM(bci_align_segment);
2581 COPY_FPGM(bci_handle_segments);
2582 COPY_FPGM(bci_align_segments);
2583 COPY_FPGM(bci_action_adjust_bound);
2584 COPY_FPGM(bci_action_stem_bound);
2585 COPY_FPGM(bci_action_link);
2586 COPY_FPGM(bci_action_anchor);
2587 COPY_FPGM(bci_action_blue_anchor);
2588 COPY_FPGM(bci_action_adjust);
2589 COPY_FPGM(bci_action_stem);
2590 COPY_FPGM(bci_action_blue);
2591 COPY_FPGM(bci_action_serif);
2592 COPY_FPGM(bci_action_serif_anchor);
2593 COPY_FPGM(bci_action_serif_link1);
2594 COPY_FPGM(bci_action_serif_link2);
2595 COPY_FPGM(bci_handle_action);
2596 COPY_FPGM(bci_hint_glyph);
2598 *fpgm = buf;
2599 *fpgm_len = buf_len;
2601 return FT_Err_Ok;
2605 FT_Error
2606 TA_sfnt_build_fpgm_table(SFNT* sfnt,
2607 FONT* font)
2609 FT_Error error;
2611 FT_Byte* fpgm_buf;
2612 FT_ULong fpgm_len;
2615 error = TA_sfnt_add_table_info(sfnt);
2616 if (error)
2617 return error;
2619 error = TA_table_build_fpgm(&fpgm_buf, &fpgm_len, font);
2620 if (error)
2621 return error;
2623 /* in case of success, `fpgm_buf' gets linked */
2624 /* and is eventually freed in `TA_font_unload' */
2625 error = TA_font_add_table(font,
2626 &sfnt->table_infos[sfnt->num_table_infos - 1],
2627 TTAG_fpgm, fpgm_len, fpgm_buf);
2628 if (error)
2630 free(fpgm_buf);
2631 return error;
2634 return FT_Err_Ok;
2638 /* the `prep' instructions */
2640 #define PREP(snippet_name) prep_ ## snippet_name
2642 /* we often need 0x10000 which can't be pushed directly onto the stack, */
2643 /* thus we provide it in the storage area */
2645 unsigned char PREP(store_0x10000) [] = {
2647 PUSHB_1,
2648 sal_0x10000,
2649 PUSHW_2,
2650 0x08, /* 0x800 */
2651 0x00,
2652 0x08, /* 0x800 */
2653 0x00,
2654 MUL, /* 0x10000 */
2659 unsigned char PREP(align_top_a) [] = {
2661 /* optimize the alignment of the top of small letters to the pixel grid */
2663 PUSHB_1,
2667 /* %c, index of alignment blue zone */
2669 unsigned char PREP(align_top_b) [] = {
2671 RCVT,
2672 DUP,
2673 DUP,
2674 PUSHB_1,
2676 ADD,
2677 FLOOR, /* fitted = FLOOR(scaled + 40) */
2678 DUP, /* s: scaled scaled fitted fitted */
2679 ROLL,
2680 NEQ,
2681 IF, /* s: scaled fitted */
2682 PUSHB_1,
2683 sal_0x10000,
2685 MUL, /* scaled in 16.16 format */
2686 SWAP,
2687 DIV, /* (fitted / scaled) in 16.16 format */
2689 PUSHB_1,
2690 sal_scale,
2691 SWAP,
2696 unsigned char PREP(loop_cvt_a) [] = {
2698 /* loop over vertical CVT entries */
2699 PUSHB_4,
2703 /* %c, first vertical index */
2704 /* %c, last vertical index */
2706 unsigned char PREP(loop_cvt_b) [] = {
2708 bci_cvt_rescale,
2709 bci_loop,
2710 CALL,
2712 /* loop over blue refs */
2713 PUSHB_4,
2717 /* %c, first blue ref index */
2718 /* %c, last blue ref index */
2720 unsigned char PREP(loop_cvt_c) [] = {
2722 bci_cvt_rescale,
2723 bci_loop,
2724 CALL,
2726 /* loop over blue shoots */
2727 PUSHB_4,
2731 /* %c, first blue shoot index */
2732 /* %c, last blue shoot index */
2734 unsigned char PREP(loop_cvt_d) [] = {
2736 bci_cvt_rescale,
2737 bci_loop,
2738 CALL,
2739 EIF,
2743 unsigned char PREP(compute_extra_light_a) [] = {
2745 /* compute (vertical) `extra_light' flag */
2746 PUSHB_3,
2747 sal_is_extra_light,
2752 /* %c, index of vertical standard_width */
2754 unsigned char PREP(compute_extra_light_b) [] = {
2756 RCVT,
2757 GT, /* standard_width < 40 */
2762 unsigned char PREP(round_blues_a) [] = {
2764 /* use discrete values for blue zone widths */
2765 PUSHB_4,
2769 /* %c, first blue ref index */
2770 /* %c, last blue ref index */
2772 unsigned char PREP(round_blues_b) [] = {
2774 bci_blue_round,
2775 bci_loop,
2776 CALL
2780 /* XXX talatin.c: 1671 */
2781 /* XXX talatin.c: 1708 */
2782 /* XXX talatin.c: 2182 */
2785 #define COPY_PREP(snippet_name) \
2786 memcpy(buf_p, prep_ ## snippet_name, \
2787 sizeof (prep_ ## snippet_name)); \
2788 buf_p += sizeof (prep_ ## snippet_name);
2790 static FT_Error
2791 TA_table_build_prep(FT_Byte** prep,
2792 FT_ULong* prep_len,
2793 FONT* font)
2795 TA_LatinAxis vaxis;
2796 TA_LatinBlue blue_adjustment;
2797 FT_UInt i;
2799 FT_UInt buf_len;
2800 FT_UInt len;
2801 FT_Byte* buf;
2802 FT_Byte* buf_p;
2805 vaxis = &((TA_LatinMetrics)font->loader->hints.metrics)->axis[1];
2806 blue_adjustment = NULL;
2808 for (i = 0; i < vaxis->blue_count; i++)
2810 if (vaxis->blues[i].flags & TA_LATIN_BLUE_ADJUSTMENT)
2812 blue_adjustment = &vaxis->blues[i];
2813 break;
2817 buf_len = sizeof (PREP(store_0x10000));
2819 if (blue_adjustment)
2820 buf_len += sizeof (PREP(align_top_a))
2822 + sizeof (PREP(align_top_b))
2823 + sizeof (PREP(loop_cvt_a))
2825 + sizeof (PREP(loop_cvt_b))
2827 + sizeof (PREP(loop_cvt_c))
2829 + sizeof (PREP(loop_cvt_d));
2831 buf_len += sizeof (PREP(compute_extra_light_a))
2833 + sizeof (PREP(compute_extra_light_b));
2835 if (CVT_BLUES_SIZE(font))
2836 buf_len += sizeof (PREP(round_blues_a))
2838 + sizeof (PREP(round_blues_b));
2840 /* buffer length must be a multiple of four */
2841 len = (buf_len + 3) & ~3;
2842 buf = (FT_Byte*)malloc(len);
2843 if (!buf)
2844 return FT_Err_Out_Of_Memory;
2846 /* pad end of buffer with zeros */
2847 buf[len - 1] = 0x00;
2848 buf[len - 2] = 0x00;
2849 buf[len - 3] = 0x00;
2851 /* copy cvt program into buffer and fill in the missing variables */
2852 buf_p = buf;
2854 COPY_PREP(store_0x10000);
2856 if (blue_adjustment)
2858 COPY_PREP(align_top_a);
2859 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
2860 + blue_adjustment - vaxis->blues);
2861 COPY_PREP(align_top_b);
2863 COPY_PREP(loop_cvt_a);
2864 *(buf_p++) = (unsigned char)CVT_VERT_WIDTHS_OFFSET(font);
2865 *(buf_p++) = (unsigned char)(CVT_VERT_WIDTHS_OFFSET(font)
2866 + CVT_VERT_WIDTHS_SIZE(font) - 1);
2867 COPY_PREP(loop_cvt_b);
2868 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
2869 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
2870 + CVT_BLUES_SIZE(font) - 1);
2871 COPY_PREP(loop_cvt_c);
2872 *(buf_p++) = (unsigned char)CVT_BLUE_SHOOTS_OFFSET(font);
2873 *(buf_p++) = (unsigned char)(CVT_BLUE_SHOOTS_OFFSET(font)
2874 + CVT_BLUES_SIZE(font) - 1);
2875 COPY_PREP(loop_cvt_d);
2878 COPY_PREP(compute_extra_light_a);
2879 *(buf_p++) = (unsigned char)CVT_VERT_STANDARD_WIDTH_OFFSET(font);
2880 COPY_PREP(compute_extra_light_b);
2882 if (CVT_BLUES_SIZE(font))
2884 COPY_PREP(round_blues_a);
2885 *(buf_p++) = (unsigned char)CVT_BLUE_REFS_OFFSET(font);
2886 *(buf_p++) = (unsigned char)(CVT_BLUE_REFS_OFFSET(font)
2887 + CVT_BLUES_SIZE(font) - 1);
2888 COPY_PREP(round_blues_b);
2891 *prep = buf;
2892 *prep_len = buf_len;
2894 return FT_Err_Ok;
2898 FT_Error
2899 TA_sfnt_build_prep_table(SFNT* sfnt,
2900 FONT* font)
2902 FT_Error error;
2904 FT_Byte* prep_buf;
2905 FT_ULong prep_len;
2908 error = TA_sfnt_add_table_info(sfnt);
2909 if (error)
2910 return error;
2912 error = TA_table_build_prep(&prep_buf, &prep_len, font);
2913 if (error)
2914 return error;
2916 /* in case of success, `prep_buf' gets linked */
2917 /* and is eventually freed in `TA_font_unload' */
2918 error = TA_font_add_table(font,
2919 &sfnt->table_infos[sfnt->num_table_infos - 1],
2920 TTAG_prep, prep_len, prep_buf);
2921 if (error)
2923 free(prep_buf);
2924 return error;
2927 return FT_Err_Ok;
2931 /* we store the segments in the storage area; */
2932 /* each segment record consists of the first and last point */
2934 static FT_Byte*
2935 TA_sfnt_build_glyph_segments(SFNT* sfnt,
2936 Recorder* recorder,
2937 FT_Byte* bufp)
2939 FONT* font = recorder->font;
2940 TA_GlyphHints hints = &font->loader->hints;
2941 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
2942 TA_Point points = hints->points;
2943 TA_Segment segments = axis->segments;
2944 TA_Segment seg;
2945 TA_Segment seg_limit;
2947 FT_Outline outline = font->loader->gloader->base.outline;
2949 FT_UInt* args;
2950 FT_UInt* arg;
2951 FT_UInt num_args;
2952 FT_UInt nargs;
2953 FT_UInt num_segments;
2955 FT_UInt* wrap_around_segment;
2956 FT_UInt num_wrap_around_segments;
2958 FT_Bool need_words = 0;
2960 FT_Int n;
2961 FT_UInt i, j;
2962 FT_UInt num_storage;
2963 FT_UInt num_stack_elements;
2964 FT_UInt num_twilight_points;
2967 seg_limit = segments + axis->num_segments;
2968 num_segments = axis->num_segments;
2970 /* some segments can `wrap around' */
2971 /* a contour's start point like 24-25-26-0-1-2 */
2972 /* (there can be at most one such segment per contour); */
2973 /* we thus append additional records to split them into 24-26 and 0-2 */
2974 wrap_around_segment = recorder->wrap_around_segments;
2975 for (seg = segments; seg < seg_limit; seg++)
2976 if (seg->first > seg->last)
2978 /* the stored data is used later for edge linking */
2979 *(wrap_around_segment++) = seg - segments;
2982 num_wrap_around_segments = wrap_around_segment
2983 - recorder->wrap_around_segments;
2984 num_segments += num_wrap_around_segments;
2986 /* wrap-around segments are pushed with four arguments */
2987 num_args = 2 * num_segments + 2 * num_wrap_around_segments + 2;
2989 /* collect all arguments temporarily in an array (in reverse order) */
2990 /* so that we can easily split into chunks of 255 args */
2991 /* as needed by NPUSHB and NPUSHW, respectively */
2992 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
2993 if (!args)
2994 return NULL;
2996 arg = args + num_args - 1;
2998 if (num_segments > 0xFF)
2999 need_words = 1;
3001 *(arg--) = bci_create_segments;
3002 *(arg--) = num_segments;
3004 for (seg = segments; seg < seg_limit; seg++)
3006 FT_UInt first = seg->first - points;
3007 FT_UInt last = seg->last - points;
3010 *(arg--) = first;
3011 *(arg--) = last;
3013 /* we push the last and first contour point */
3014 /* as a third and fourth argument in wrap-around segments */
3015 if (first > last)
3017 for (n = 0; n < outline.n_contours; n++)
3019 FT_UInt end = (FT_UInt)outline.contours[n];
3022 if (first <= end)
3024 *(arg--) = end;
3025 if (end > 0xFF)
3026 need_words = 1;
3028 if (n == 0)
3029 *(arg--) = 0;
3030 else
3031 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
3032 break;
3037 if (last > 0xFF)
3038 need_words = 1;
3041 /* emit the second part of wrap-around segments as separate segments */
3042 /* so that edges can easily link to them */
3043 for (seg = segments; seg < seg_limit; seg++)
3045 FT_UInt first = seg->first - points;
3046 FT_UInt last = seg->last - points;
3049 if (first > last)
3051 for (n = 0; n < outline.n_contours; n++)
3053 if (first <= (FT_UInt)outline.contours[n])
3055 if (n == 0)
3056 *(arg--) = 0;
3057 else
3058 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
3059 break;
3063 *(arg--) = last;
3066 /* with most fonts it is very rare */
3067 /* that any of the pushed arguments is larger than 0xFF, */
3068 /* thus we refrain from further optimizing this case */
3070 arg = args;
3072 if (need_words)
3074 for (i = 0; i < num_args; i += 255)
3076 nargs = (num_args - i > 255) ? 255 : num_args - i;
3078 BCI(NPUSHW);
3079 BCI(nargs);
3080 for (j = 0; j < nargs; j++)
3082 BCI(HIGH(*arg));
3083 BCI(LOW(*arg));
3084 arg++;
3088 else
3090 for (i = 0; i < num_args; i += 255)
3092 nargs = (num_args - i > 255) ? 255 : num_args - i;
3094 BCI(NPUSHB);
3095 BCI(nargs);
3096 for (j = 0; j < nargs; j++)
3098 BCI(*arg);
3099 arg++;
3104 BCI(CALL);
3106 num_storage = sal_segment_offset + num_segments * 2;
3107 if (num_storage > sfnt->max_storage)
3108 sfnt->max_storage = num_storage;
3110 num_twilight_points = num_segments * 2;
3111 if (num_twilight_points > sfnt->max_twilight_points)
3112 sfnt->max_twilight_points = num_twilight_points;
3114 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
3115 if (num_stack_elements > sfnt->max_stack_elements)
3116 sfnt->max_stack_elements = num_stack_elements;
3118 free(args);
3120 return bufp;
3124 static FT_Bool
3125 TA_hints_record_is_different(Hints_Record* hints_records,
3126 FT_UInt num_hints_records,
3127 FT_Byte* start,
3128 FT_Byte* end)
3130 Hints_Record last_hints_record;
3133 if (!hints_records)
3134 return 1;
3136 /* we only need to compare with the last hints record */
3137 last_hints_record = hints_records[num_hints_records - 1];
3139 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
3140 return 1;
3142 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
3143 return 1;
3145 return 0;
3149 static FT_Error
3150 TA_add_hints_record(Hints_Record** hints_records,
3151 FT_UInt* num_hints_records,
3152 FT_Byte* start,
3153 Hints_Record hints_record)
3155 Hints_Record* hints_records_new;
3156 FT_UInt buf_len;
3157 /* at this point, `hints_record.buf' still points into `ins_buf' */
3158 FT_Byte* end = hints_record.buf;
3161 buf_len = (FT_UInt)(end - start);
3163 /* now fill the structure completely */
3164 hints_record.buf_len = buf_len;
3165 hints_record.buf = (FT_Byte*)malloc(buf_len);
3166 if (!hints_record.buf)
3167 return FT_Err_Out_Of_Memory;
3169 memcpy(hints_record.buf, start, buf_len);
3171 (*num_hints_records)++;
3172 hints_records_new =
3173 (Hints_Record*)realloc(*hints_records, *num_hints_records
3174 * sizeof (Hints_Record));
3175 if (!hints_records_new)
3177 free(hints_record.buf);
3178 (*num_hints_records)--;
3179 return FT_Err_Out_Of_Memory;
3181 else
3182 *hints_records = hints_records_new;
3184 (*hints_records)[*num_hints_records - 1] = hints_record;
3186 return FT_Err_Ok;
3190 static FT_Byte*
3191 TA_sfnt_emit_hints_record(SFNT* sfnt,
3192 Hints_Record* hints_record,
3193 FT_Byte* bufp)
3195 FT_Byte* p;
3196 FT_Byte* endp;
3197 FT_Bool need_words = 0;
3199 FT_UInt i, j;
3200 FT_UInt num_arguments;
3201 FT_UInt num_args;
3202 FT_UInt num_stack_elements;
3205 /* check whether any argument is larger than 0xFF */
3206 endp = hints_record->buf + hints_record->buf_len;
3207 for (p = hints_record->buf; p < endp; p += 2)
3208 if (*p)
3209 need_words = 1;
3211 /* with most fonts it is very rare */
3212 /* that any of the pushed arguments is larger than 0xFF, */
3213 /* thus we refrain from further optimizing this case */
3215 num_arguments = hints_record->buf_len / 2;
3216 p = endp - 2;
3218 if (need_words)
3220 for (i = 0; i < num_arguments; i += 255)
3222 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
3224 BCI(NPUSHW);
3225 BCI(num_args);
3226 for (j = 0; j < num_args; j++)
3228 BCI(*p);
3229 BCI(*(p + 1));
3230 p -= 2;
3234 else
3236 /* we only need the lower bytes */
3237 p++;
3239 for (i = 0; i < num_arguments; i += 255)
3241 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
3243 BCI(NPUSHB);
3244 BCI(num_args);
3245 for (j = 0; j < num_args; j++)
3247 BCI(*p);
3248 p -= 2;
3253 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_arguments;
3254 if (num_stack_elements > sfnt->max_stack_elements)
3255 sfnt->max_stack_elements = sfnt->max_stack_elements;
3257 return bufp;
3261 static FT_Byte*
3262 TA_sfnt_emit_hints_records(SFNT* sfnt,
3263 Hints_Record* hints_records,
3264 FT_UInt num_hints_records,
3265 FT_Byte* bufp)
3267 FT_UInt i;
3268 Hints_Record* hints_record;
3271 hints_record = hints_records;
3273 for (i = 0; i < num_hints_records - 1; i++)
3275 BCI(MPPEM);
3276 if (hints_record->size > 0xFF)
3278 BCI(PUSHW_1);
3279 BCI(HIGH((hints_record + 1)->size));
3280 BCI(LOW((hints_record + 1)->size));
3282 else
3284 BCI(PUSHB_1);
3285 BCI((hints_record + 1)->size);
3287 BCI(LT);
3288 BCI(IF);
3289 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
3290 BCI(ELSE);
3292 hints_record++;
3295 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
3297 for (i = 0; i < num_hints_records - 1; i++)
3298 BCI(EIF);
3300 BCI(PUSHB_1);
3301 BCI(bci_hint_glyph);
3302 BCI(CALL);
3304 return bufp;
3308 static void
3309 TA_free_hints_records(Hints_Record* hints_records,
3310 FT_UInt num_hints_records)
3312 FT_UInt i;
3315 for (i = 0; i < num_hints_records; i++)
3316 free(hints_records[i].buf);
3318 free(hints_records);
3322 static FT_Byte*
3323 TA_hints_recorder_handle_segments(FT_Byte* bufp,
3324 TA_AxisHints axis,
3325 TA_Edge edge,
3326 FT_UInt* wraps)
3328 TA_Segment segments = axis->segments;
3329 TA_Segment seg;
3330 FT_UInt seg_idx;
3331 FT_UInt num_segs = 0;
3332 FT_UInt* wrap;
3335 seg_idx = edge->first - segments;
3337 /* we store everything as 16bit numbers */
3338 *(bufp++) = HIGH(seg_idx);
3339 *(bufp++) = LOW(seg_idx);
3341 /* wrap-around segments are stored as two segments */
3342 if (edge->first->first > edge->first->last)
3343 num_segs++;
3345 seg = edge->first->edge_next;
3346 while (seg != edge->first)
3348 num_segs++;
3350 if (seg->first > seg->last)
3351 num_segs++;
3353 seg = seg->edge_next;
3356 *(bufp++) = HIGH(num_segs);
3357 *(bufp++) = LOW(num_segs);
3359 if (edge->first->first > edge->first->last)
3361 /* emit second part of wrap-around segment; */
3362 /* the bytecode positions such segments after `normal' ones */
3363 wrap = wraps;
3364 for (;;)
3366 if (seg_idx == *wrap)
3367 break;
3368 wrap++;
3371 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
3372 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
3375 seg = edge->first->edge_next;
3376 while (seg != edge->first)
3378 seg_idx = seg - segments;
3380 *(bufp++) = HIGH(seg_idx);
3381 *(bufp++) = LOW(seg_idx);
3383 if (seg->first > seg->last)
3385 wrap = wraps;
3386 for (;;)
3388 if (seg_idx == *wrap)
3389 break;
3390 wrap++;
3393 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
3394 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
3397 seg = seg->edge_next;
3400 return bufp;
3404 static void
3405 TA_hints_recorder(TA_Action action,
3406 TA_GlyphHints hints,
3407 TA_Dimension dim,
3408 void* arg1,
3409 void* arg2,
3410 void* arg3)
3412 TA_AxisHints axis = &hints->axis[dim];
3413 TA_Segment segments = axis->segments;
3415 Recorder* recorder = (Recorder*)hints->user;
3416 FONT* font = recorder->font;
3417 FT_UInt* wraps = recorder->wrap_around_segments;
3418 FT_Byte* p = recorder->hints_record.buf;
3421 if (dim == TA_DIMENSION_HORZ)
3422 return;
3424 /* we ignore the BOUND action since the information is handled */
3425 /* in `ta_adjust_bound' and `ta_stem_bound' */
3426 if (action == ta_bound)
3427 return;
3429 *(p++) = 0;
3430 *(p++) = (FT_Byte)action + ACTION_OFFSET;
3432 switch (action)
3434 case ta_adjust_bound:
3436 TA_Edge edge = (TA_Edge)arg1;
3437 TA_Edge edge2 = (TA_Edge)arg2;
3438 TA_Edge edge_minus_one = (TA_Edge)arg3;
3441 *(p++) = 0;
3442 *(p++) = edge2->flags & TA_EDGE_SERIF;
3443 *(p++) = 0;
3444 *(p++) = edge->flags & TA_EDGE_ROUND;
3445 *(p++) = HIGH(edge->first - segments);
3446 *(p++) = LOW(edge->first - segments);
3447 *(p++) = HIGH(edge2->first - segments);
3448 *(p++) = LOW(edge2->first - segments);
3449 *(p++) = HIGH(edge_minus_one->first - segments);
3450 *(p++) = LOW(edge_minus_one->first - segments);
3452 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3454 break;
3456 case ta_stem_bound:
3458 TA_Edge edge = (TA_Edge)arg1;
3459 TA_Edge edge2 = (TA_Edge)arg2;
3460 TA_Edge edge_minus_one = (TA_Edge)arg3;
3463 *(p++) = 0;
3464 *(p++) = edge2->flags & TA_EDGE_SERIF;
3465 *(p++) = 0;
3466 *(p++) = edge->flags & TA_EDGE_ROUND;
3467 *(p++) = HIGH(edge->first - segments);
3468 *(p++) = LOW(edge->first - segments);
3469 *(p++) = HIGH(edge2->first - segments);
3470 *(p++) = LOW(edge2->first - segments);
3471 *(p++) = HIGH(edge_minus_one->first - segments);
3472 *(p++) = LOW(edge_minus_one->first - segments);
3474 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3475 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
3477 break;
3479 case ta_link:
3481 TA_Edge base_edge = (TA_Edge)arg1;
3482 TA_Edge stem_edge = (TA_Edge)arg2;
3485 *(p++) = 0;
3486 *(p++) = stem_edge->flags & TA_EDGE_SERIF;
3487 *(p++) = 0;
3488 *(p++) = base_edge->flags & TA_EDGE_ROUND;
3489 *(p++) = HIGH(base_edge->first - segments);
3490 *(p++) = LOW(base_edge->first - segments);
3491 *(p++) = HIGH(stem_edge->first - segments);
3492 *(p++) = LOW(stem_edge->first - segments);
3494 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
3496 break;
3498 case ta_anchor:
3499 case ta_adjust:
3501 TA_Edge edge = (TA_Edge)arg1;
3502 TA_Edge edge2 = (TA_Edge)arg2;
3505 *(p++) = 0;
3506 *(p++) = edge2->flags & TA_EDGE_SERIF;
3507 *(p++) = 0;
3508 *(p++) = edge->flags & TA_EDGE_ROUND;
3509 *(p++) = HIGH(edge->first - segments);
3510 *(p++) = LOW(edge->first - segments);
3511 *(p++) = HIGH(edge2->first - segments);
3512 *(p++) = LOW(edge2->first - segments);
3514 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3516 break;
3518 case ta_blue_anchor:
3520 TA_Edge edge = (TA_Edge)arg1;
3521 TA_Edge blue = (TA_Edge)arg2;
3524 *(p++) = HIGH(blue->first - segments);
3525 *(p++) = LOW(blue->first - segments);
3527 if (edge->best_blue_is_shoot)
3529 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3530 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3532 else
3534 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3535 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3538 *(p++) = HIGH(edge->first - segments);
3539 *(p++) = LOW(edge->first - segments);
3541 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3543 break;
3545 case ta_stem:
3547 TA_Edge edge = (TA_Edge)arg1;
3548 TA_Edge edge2 = (TA_Edge)arg2;
3551 *(p++) = 0;
3552 *(p++) = edge2->flags & TA_EDGE_SERIF;
3553 *(p++) = 0;
3554 *(p++) = edge->flags & TA_EDGE_ROUND;
3555 *(p++) = HIGH(edge->first - segments);
3556 *(p++) = LOW(edge->first - segments);
3557 *(p++) = HIGH(edge2->first - segments);
3558 *(p++) = LOW(edge2->first - segments);
3560 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3561 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
3563 break;
3565 case ta_blue:
3567 TA_Edge edge = (TA_Edge)arg1;
3570 if (edge->best_blue_is_shoot)
3572 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3573 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
3575 else
3577 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3578 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
3581 *(p++) = HIGH(edge->first - segments);
3582 *(p++) = LOW(edge->first - segments);
3584 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
3586 break;
3588 case ta_serif:
3589 p = TA_hints_recorder_handle_segments(p, axis, (TA_Edge)arg1, wraps);
3590 break;
3592 case ta_serif_anchor:
3593 p = TA_hints_recorder_handle_segments(p, axis, (TA_Edge)arg1, wraps);
3594 break;
3596 case ta_serif_link1:
3597 p = TA_hints_recorder_handle_segments(p, axis, (TA_Edge)arg1, wraps);
3598 break;
3600 case ta_serif_link2:
3601 p = TA_hints_recorder_handle_segments(p, axis, (TA_Edge)arg1, wraps);
3602 break;
3604 /* to pacify the compiler */
3605 case ta_bound:
3606 break;
3609 recorder->hints_record.num_actions++;
3610 recorder->hints_record.buf = p;
3614 static FT_Error
3615 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
3616 FONT* font,
3617 FT_Long idx)
3619 FT_Face face = sfnt->face;
3620 FT_Error error;
3622 FT_Byte* ins_buf;
3623 FT_UInt ins_len;
3624 FT_Byte* bufp;
3626 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
3627 glyf_Data* data = (glyf_Data*)glyf_table->data;
3628 GLYPH* glyph = &data->glyphs[idx];
3630 TA_GlyphHints hints;
3632 FT_UInt num_hints_records;
3633 Hints_Record* hints_records;
3635 Recorder recorder;
3637 FT_UInt size;
3640 if (idx < 0)
3641 return FT_Err_Invalid_Argument;
3643 /* computing the segments is resolution independent, */
3644 /* thus the pixel size in this call is arbitrary */
3645 error = FT_Set_Pixel_Sizes(face, 20, 20);
3646 if (error)
3647 return error;
3649 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
3650 error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx, 0);
3651 if (error)
3652 return error;
3654 /* do nothing if we have an empty glyph */
3655 if (!face->glyph->outline.n_contours)
3656 return FT_Err_Ok;
3658 /* do nothing if the dummy hinter has been used */
3659 if (font->loader->metrics->clazz == &ta_dummy_script_class)
3660 return FT_Err_Ok;
3662 hints = &font->loader->hints;
3664 /* we allocate a buffer which is certainly large enough */
3665 /* to hold all of the created bytecode instructions; */
3666 /* later on it gets reallocated to its real size */
3667 ins_len = hints->num_points * 1000;
3668 ins_buf = (FT_Byte*)malloc(ins_len);
3669 if (!ins_buf)
3670 return FT_Err_Out_Of_Memory;
3672 /* initialize array with an invalid bytecode */
3673 /* so that we can easily find the array length at reallocation time */
3674 memset(ins_buf, INS_A0, ins_len);
3676 recorder.font = font;
3677 recorder.wrap_around_segments =
3678 (FT_UInt*)malloc(face->glyph->outline.n_contours * sizeof (FT_UInt));
3680 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf);
3682 /* now we loop over a large range of pixel sizes */
3683 /* to find hints records which get pushed onto the bytecode stack */
3684 num_hints_records = 0;
3685 hints_records = NULL;
3687 #ifdef DEBUGGING
3688 printf("glyph %ld\n", idx);
3689 #endif
3691 /* we temporarily use `ins_buf' to record the current glyph hints, */
3692 /* leaving two bytes at the beginning so that the number of actions */
3693 /* can be inserted later on */
3694 ta_loader_register_hints_recorder(font->loader,
3695 TA_hints_recorder,
3696 (void *)&recorder);
3698 for (size = 8; size <= 1000; size++)
3700 /* rewind buffer pointer for recorder */
3701 recorder.hints_record.buf = bufp + 2;
3702 recorder.hints_record.num_actions = 0;
3703 recorder.hints_record.size = size;
3705 error = FT_Set_Pixel_Sizes(face, size, size);
3706 if (error)
3707 goto Err;
3709 /* calling `ta_loader_load_glyph' uses the */
3710 /* `TA_hints_recorder' function as a callback, */
3711 /* modifying `hints_record' */
3712 error = ta_loader_load_glyph(font->loader, face, idx, 0);
3713 if (error)
3714 goto Err;
3716 /* store the number of actions in `ins_buf' */
3717 *bufp = HIGH(recorder.hints_record.num_actions);
3718 *(bufp + 1) = LOW(recorder.hints_record.num_actions);
3720 if (TA_hints_record_is_different(hints_records,
3721 num_hints_records,
3722 bufp, recorder.hints_record.buf))
3724 #ifdef DEBUGGING
3726 FT_Byte* p;
3729 printf(" %d:\n", size);
3730 for (p = bufp; p < recorder.hints_record.buf; p += 2)
3731 printf(" %2d", *p * 256 + *(p + 1));
3732 printf("\n");
3734 #endif
3736 error = TA_add_hints_record(&hints_records,
3737 &num_hints_records,
3738 bufp, recorder.hints_record);
3739 if (error)
3740 goto Err;
3744 if (num_hints_records == 1 && !hints_records[0].num_actions)
3746 /* don't emit anything if we only have a single empty record */
3747 ins_len = 0;
3749 else
3751 FT_Byte* p = bufp;
3754 /* otherwise, clear the temporarily used part of `ins_buf' */
3755 while (*p != INS_A0)
3756 *(p++) = INS_A0;
3758 bufp = TA_sfnt_emit_hints_records(sfnt,
3759 hints_records, num_hints_records,
3760 bufp);
3762 /* we are done, so reallocate the instruction array to its real size */
3763 if (*bufp == INS_A0)
3765 /* search backwards */
3766 while (*bufp == INS_A0)
3767 bufp--;
3768 bufp++;
3770 else
3772 /* search forwards */
3773 while (*bufp != INS_A0)
3774 bufp++;
3777 ins_len = bufp - ins_buf;
3780 if (ins_len > sfnt->max_instructions)
3781 sfnt->max_instructions = ins_len;
3783 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
3784 glyph->ins_len = ins_len;
3786 TA_free_hints_records(hints_records, num_hints_records);
3787 free(recorder.wrap_around_segments);
3789 return FT_Err_Ok;
3791 Err:
3792 TA_free_hints_records(hints_records, num_hints_records);
3793 free(recorder.wrap_around_segments);
3794 free(ins_buf);
3796 return error;
3800 FT_Error
3801 TA_sfnt_build_glyf_hints(SFNT* sfnt,
3802 FONT* font)
3804 FT_Face face = sfnt->face;
3805 FT_Long idx;
3806 FT_Error error;
3809 for (idx = 0; idx < face->num_glyphs; idx++)
3811 error = TA_sfnt_build_glyph_instructions(sfnt, font, idx);
3812 if (error)
3813 return error;
3816 return FT_Err_Ok;
3819 /* end of tabytecode.c */