Initialize properties of `globals' object.
[ttfautohint.git] / lib / tabytecode.c
blob94bcfbb5a9052faa8125bed612b5b4f3c1093bc5
1 /* tabytecode.c */
3 /*
4 * Copyright (C) 2011-2012 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 "ta.h"
17 #include <string.h>
20 #undef MISSING
21 #define MISSING (FT_UShort)~0
23 /* a simple macro to emit bytecode instructions */
24 #define BCI(code) *(bufp++) = (code)
26 /* we increase the stack depth by this amount */
27 #define ADDITIONAL_STACK_ELEMENTS 20
30 #define DEBUGGING
33 #ifdef TA_DEBUG
34 int _ta_debug = 0;
35 int _ta_debug_disable_horz_hints;
36 int _ta_debug_disable_vert_hints;
37 int _ta_debug_disable_blue_hints;
38 void* _ta_debug_hints;
39 #endif
42 typedef struct Hints_Record_
44 FT_UInt size;
45 FT_UInt num_actions;
46 FT_Byte* buf;
47 FT_UInt buf_len;
48 } Hints_Record;
50 typedef struct Recorder_
52 FONT* font;
53 GLYPH* glyph; /* the current glyph */
54 Hints_Record hints_record;
56 /* some segments can `wrap around' */
57 /* a contour's start point like 24-25-26-0-1-2 */
58 /* (there can be at most one such segment per contour); */
59 /* later on we append additional records */
60 /* to split them into 24-26 and 0-2 */
61 FT_UShort* wrap_around_segments;
62 FT_UShort num_wrap_around_segments;
64 FT_UShort num_stack_elements; /* the necessary stack depth so far */
66 /* data necessary for strong point interpolation */
67 FT_UShort* ip_before_points;
68 FT_UShort* ip_after_points;
69 FT_UShort* ip_on_point_array;
70 FT_UShort* ip_between_point_array;
72 FT_UShort num_strong_points;
73 FT_UShort num_segments;
74 } Recorder;
77 /* this is the bytecode of the `.ttfautohint' glyph */
79 FT_Byte ttfautohint_glyph_bytecode[7] =
82 /* increment `cvtl_is_subglyph' counter */
83 PUSHB_3,
84 cvtl_is_subglyph,
86 cvtl_is_subglyph,
87 RCVT,
88 ADD,
89 WCVTP,
94 /* We add a subglyph for each composite glyph. */
95 /* Since subglyphs must contain at least one point, */
96 /* we have to adjust all point indices accordingly. */
97 /* Using the `pointsums' array of the `GLYPH' structure */
98 /* it is straightforward to do that: */
99 /* Assuming that point with index x is in the interval */
100 /* pointsums[n] <= x < pointsums[n + 1], */
101 /* the new point index is x + n. */
103 static FT_UInt
104 TA_adjust_point_index(Recorder* recorder,
105 FT_UInt idx)
107 FONT* font = recorder->font;
108 GLYPH* glyph = recorder->glyph;
109 FT_UShort i;
112 if (!glyph->num_components || !font->hint_with_components)
113 return idx; /* not a composite glyph */
115 for (i = 0; i < glyph->num_pointsums; i++)
116 if (idx < glyph->pointsums[i])
117 break;
119 return idx + i;
123 /* we store the segments in the storage area; */
124 /* each segment record consists of the first and last point */
126 static FT_Byte*
127 TA_sfnt_build_glyph_segments(SFNT* sfnt,
128 Recorder* recorder,
129 FT_Byte* bufp,
130 FT_Bool optimize)
132 FONT* font = recorder->font;
133 TA_GlyphHints hints = &font->loader->hints;
134 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
135 TA_Point points = hints->points;
136 TA_Segment segments = axis->segments;
137 TA_Segment seg;
138 TA_Segment seg_limit;
140 FT_Outline outline = font->loader->gloader->base.outline;
142 FT_UInt* args;
143 FT_UInt* arg;
144 FT_UInt num_args;
145 FT_UInt nargs;
146 FT_UShort num_segments;
148 FT_Bool need_words = 0;
150 FT_Int n;
151 FT_UInt i, j;
152 FT_UInt base;
153 FT_UShort num_packed_segments;
154 FT_UShort num_storage;
155 FT_UShort num_stack_elements;
156 FT_UShort num_twilight_points;
159 seg_limit = segments + axis->num_segments;
160 num_segments = axis->num_segments;
162 /* to pack the data in the bytecode more tightly, */
163 /* we store up to the first nine segments in nibbles if possible, */
164 /* using delta values */
165 base = 0;
166 num_packed_segments = 0;
167 for (seg = segments; seg < seg_limit; seg++)
169 FT_UInt first = seg->first - points;
170 FT_UInt last = seg->last - points;
173 first = TA_adjust_point_index(recorder, first);
174 last = TA_adjust_point_index(recorder, last);
176 if (first - base >= 16)
177 break;
178 if (first > last || last - first >= 16)
179 break;
180 if (num_packed_segments == 9)
181 break;
182 num_packed_segments++;
183 base = last;
186 /* also handle wrap-around segments */
187 num_segments += recorder->num_wrap_around_segments;
189 /* wrap-around segments are pushed with four arguments; */
190 /* a segment stored in nibbles needs only one byte instead of two */
191 num_args = num_packed_segments
192 + 2 * (num_segments - num_packed_segments)
193 + 2 * recorder->num_wrap_around_segments
194 + 2;
196 /* collect all arguments temporarily in an array (in reverse order) */
197 /* so that we can easily split into chunks of 255 args */
198 /* as needed by NPUSHB and NPUSHW, respectively */
199 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
200 if (!args)
201 return NULL;
203 arg = args + num_args - 1;
205 if (num_segments > 0xFF)
206 need_words = 1;
208 /* the number of packed segments is indicated by the function number */
209 if (recorder->glyph->num_components && font->hint_with_components)
210 *(arg--) = bci_create_segments_composite_0 + num_packed_segments;
211 else
212 *(arg--) = bci_create_segments_0 + num_packed_segments;
213 *(arg--) = num_segments;
215 base = 0;
216 for (seg = segments; seg < segments + num_packed_segments; seg++)
218 FT_UInt first = seg->first - points;
219 FT_UInt last = seg->last - points;
220 FT_UInt low_nibble;
221 FT_UInt high_nibble;
224 first = TA_adjust_point_index(recorder, first);
225 last = TA_adjust_point_index(recorder, last);
227 low_nibble = first - base;
228 high_nibble = last - first;
230 *(arg--) = 16 * high_nibble + low_nibble;
232 base = last;
235 for (seg = segments + num_packed_segments; seg < seg_limit; seg++)
237 FT_UInt first = seg->first - points;
238 FT_UInt last = seg->last - points;
241 *(arg--) = TA_adjust_point_index(recorder, first);
242 *(arg--) = TA_adjust_point_index(recorder, last);
244 /* we push the last and first contour point */
245 /* as a third and fourth argument in wrap-around segments */
246 if (first > last)
248 for (n = 0; n < outline.n_contours; n++)
250 FT_UInt end = (FT_UInt)outline.contours[n];
253 if (first <= end)
255 *(arg--) = TA_adjust_point_index(recorder, end);
256 if (end > 0xFF)
257 need_words = 1;
259 if (n == 0)
260 *(arg--) = TA_adjust_point_index(recorder, 0);
261 else
262 *(arg--) = TA_adjust_point_index(recorder,
263 (FT_UInt)outline.contours[n - 1] + 1);
264 break;
269 if (last > 0xFF)
270 need_words = 1;
273 /* emit the second part of wrap-around segments as separate segments */
274 /* so that edges can easily link to them */
275 for (seg = segments; seg < seg_limit; seg++)
277 FT_UInt first = seg->first - points;
278 FT_UInt last = seg->last - points;
281 if (first > last)
283 for (n = 0; n < outline.n_contours; n++)
285 if (first <= (FT_UInt)outline.contours[n])
287 if (n == 0)
288 *(arg--) = TA_adjust_point_index(recorder, 0);
289 else
290 *(arg--) = TA_adjust_point_index(recorder,
291 (FT_UInt)outline.contours[n - 1] + 1);
292 break;
296 *(arg--) = TA_adjust_point_index(recorder, last);
299 /* with most fonts it is very rare */
300 /* that any of the pushed arguments is larger than 0xFF, */
301 /* thus we refrain from further optimizing this case */
303 arg = args;
305 if (need_words)
307 for (i = 0; i < num_args; i += 255)
309 nargs = (num_args - i > 255) ? 255 : num_args - i;
312 if (optimize && nargs <= 8)
313 BCI(PUSHW_1 - 1 + nargs);
314 else
316 BCI(NPUSHW);
317 BCI(nargs);
319 for (j = 0; j < nargs; j++)
321 BCI(HIGH(*arg));
322 BCI(LOW(*arg));
323 arg++;
327 else
329 for (i = 0; i < num_args; i += 255)
331 nargs = (num_args - i > 255) ? 255 : num_args - i;
333 if (optimize && nargs <= 8)
334 BCI(PUSHB_1 - 1 + nargs);
335 else
337 BCI(NPUSHB);
338 BCI(nargs);
340 for (j = 0; j < nargs; j++)
342 BCI(*arg);
343 arg++;
348 BCI(CALL);
350 num_storage = sal_segment_offset + num_segments * 2;
351 if (num_storage > sfnt->max_storage)
352 sfnt->max_storage = num_storage;
354 num_twilight_points = num_segments * 2;
355 if (num_twilight_points > sfnt->max_twilight_points)
356 sfnt->max_twilight_points = num_twilight_points;
358 /* both this function and `TA_emit_hints_record' */
359 /* push data onto the stack */
360 num_stack_elements = ADDITIONAL_STACK_ELEMENTS
361 + recorder->num_stack_elements + num_args;
362 if (num_stack_elements > sfnt->max_stack_elements)
363 sfnt->max_stack_elements = num_stack_elements;
365 free(args);
367 return bufp;
371 static FT_Byte*
372 TA_sfnt_build_glyph_scaler(SFNT* sfnt,
373 Recorder* recorder,
374 FT_Byte* bufp)
376 FONT* font = recorder->font;
377 FT_GlyphSlot glyph = sfnt->face->glyph;
378 FT_Vector* points = glyph->outline.points;
379 FT_Int num_contours = glyph->outline.n_contours;
381 FT_UInt* args;
382 FT_UInt* arg;
383 FT_UInt num_args;
384 FT_UInt nargs;
386 FT_Bool need_words = 0;
387 FT_Int p, q;
388 FT_UInt i, j;
389 FT_Int start, end;
390 FT_UShort num_stack_elements;
393 num_args = 2 * num_contours + 2;
395 /* collect all arguments temporarily in an array (in reverse order) */
396 /* so that we can easily split into chunks of 255 args */
397 /* as needed by NPUSHB and NPUSHW, respectively */
398 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
399 if (!args)
400 return NULL;
402 arg = args + num_args - 1;
404 if (num_args > 0xFF)
405 need_words = 1;
407 if (recorder->glyph->num_components && font->hint_with_components)
408 *(arg--) = bci_scale_composite_glyph;
409 else
410 *(arg--) = bci_scale_glyph;
411 *(arg--) = num_contours;
413 start = 0;
414 end = 0;
416 for (p = 0; p < num_contours; p++)
418 FT_Int max = start;
419 FT_Int min = start;
422 end = glyph->outline.contours[p];
424 for (q = start; q <= end; q++)
426 if (points[q].y < points[min].y)
427 min = q;
428 if (points[q].y > points[max].y)
429 max = q;
432 if (min > max)
434 *(arg--) = TA_adjust_point_index(recorder, max);
435 *(arg--) = TA_adjust_point_index(recorder, min);
437 else
439 *(arg--) = TA_adjust_point_index(recorder, min);
440 *(arg--) = TA_adjust_point_index(recorder, max);
443 start = end + 1;
446 if (end > 0xFF)
447 need_words = 1;
449 /* with most fonts it is very rare */
450 /* that any of the pushed arguments is larger than 0xFF, */
451 /* thus we refrain from further optimizing this case */
453 arg = args;
455 if (need_words)
457 for (i = 0; i < num_args; i += 255)
459 nargs = (num_args - i > 255) ? 255 : num_args - i;
461 if (nargs <= 8)
462 BCI(PUSHW_1 - 1 + nargs);
463 else
465 BCI(NPUSHW);
466 BCI(nargs);
468 for (j = 0; j < nargs; j++)
470 BCI(HIGH(*arg));
471 BCI(LOW(*arg));
472 arg++;
476 else
478 for (i = 0; i < num_args; i += 255)
480 nargs = (num_args - i > 255) ? 255 : num_args - i;
482 if (nargs <= 8)
483 BCI(PUSHB_1 - 1 + nargs);
484 else
486 BCI(NPUSHB);
487 BCI(nargs);
489 for (j = 0; j < nargs; j++)
491 BCI(*arg);
492 arg++;
497 BCI(CALL);
499 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
500 if (num_stack_elements > sfnt->max_stack_elements)
501 sfnt->max_stack_elements = num_stack_elements;
503 free(args);
505 return bufp;
509 static FT_Byte*
510 TA_font_build_subglyph_shifter(FONT* font,
511 FT_Byte* bufp)
513 FT_Face face = font->loader->face;
514 FT_GlyphSlot glyph = face->glyph;
516 TA_GlyphLoader gloader = font->loader->gloader;
518 TA_SubGlyph subglyphs = gloader->base.subglyphs;
519 TA_SubGlyph subglyph_limit = subglyphs + gloader->base.num_subglyphs;
520 TA_SubGlyph subglyph;
522 FT_Int curr_contour = 0;
525 for (subglyph = subglyphs; subglyph < subglyph_limit; subglyph++)
527 FT_Error error;
529 FT_UShort flags = subglyph->flags;
530 FT_Pos y_offset = subglyph->arg2;
532 FT_Int num_contours;
535 /* load subglyph to get the number of contours */
536 error = FT_Load_Glyph(face, subglyph->index, FT_LOAD_NO_SCALE);
537 if (error)
538 return NULL;
539 num_contours = glyph->outline.n_contours;
541 /* nothing to do if there is a point-to-point alignment */
542 if (!(flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
543 goto End;
545 /* nothing to do if y offset is zero */
546 if (!y_offset)
547 goto End;
549 /* nothing to do if there are no contours */
550 if (!num_contours)
551 goto End;
553 /* note that calling `FT_Load_Glyph' without FT_LOAD_NO_RECURSE */
554 /* ensures that composite subglyphs are represented as simple glyphs */
556 if (num_contours > 0xFF
557 || curr_contour > 0xFF)
559 BCI(PUSHW_2);
560 BCI(HIGH(curr_contour));
561 BCI(LOW(curr_contour));
562 BCI(HIGH(num_contours));
563 BCI(LOW(num_contours));
565 else
567 BCI(PUSHB_2);
568 BCI(curr_contour);
569 BCI(num_contours);
572 /* there are high chances that this value needs PUSHW, */
573 /* thus we handle it separately */
574 if (y_offset > 0xFF || y_offset < 0)
576 BCI(PUSHW_1);
577 BCI(HIGH(y_offset));
578 BCI(LOW(y_offset));
580 else
582 BCI(PUSHB_1);
583 BCI(y_offset);
586 BCI(PUSHB_1);
587 BCI(bci_shift_subglyph);
588 BCI(CALL);
590 End:
591 curr_contour += num_contours;
594 return bufp;
599 * The four `ta_ip_*' actions in the `TA_hints_recorder' callback store its
600 * data in four arrays (which are simple but waste a lot of memory). The
601 * function below converts them into bytecode.
603 * For `ta_ip_before' and `ta_ip_after', the collected points are emitted
604 * together with the edge they correspond to.
606 * For both `ta_ip_on' and `ta_ip_between', an outer loop is constructed to
607 * loop over the edge or edge pairs, respectively, and each edge or edge
608 * pair contains an inner loop to emit the correponding points.
611 static void
612 TA_build_point_hints(Recorder* recorder,
613 TA_GlyphHints hints)
615 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
616 TA_Segment segments = axis->segments;
617 TA_Edge edges = axis->edges;
619 TA_Edge edge;
620 TA_Edge before;
621 TA_Edge after;
623 FT_Byte* p = recorder->hints_record.buf;
624 FT_UShort num_edges = axis->num_edges;
625 FT_UShort num_strong_points = recorder->num_strong_points;
627 FT_UShort i;
628 FT_UShort j;
629 FT_UShort k;
631 FT_UShort* ip;
632 FT_UShort* iq;
633 FT_UShort* ir;
634 FT_UShort* ip_limit;
635 FT_UShort* iq_limit;
636 FT_UShort* ir_limit;
639 /* we store everything as 16bit numbers; */
640 /* the function numbers (`ta_ip_before', etc.) */
641 /* reflect the order in the TA_Action enumeration */
643 /* ip_before_points */
645 i = 0;
646 ip = recorder->ip_before_points;
647 ip_limit = ip + num_strong_points;
648 for (; ip < ip_limit; ip++)
650 if (*ip != MISSING)
651 i++;
652 else
653 break;
656 if (i)
658 recorder->hints_record.num_actions++;
660 edge = edges;
662 *(p++) = 0;
663 *(p++) = (FT_Byte)ta_ip_before + ACTION_OFFSET;
664 *(p++) = HIGH(edge->first - segments);
665 *(p++) = LOW(edge->first - segments);
666 *(p++) = HIGH(i);
667 *(p++) = LOW(i);
669 ip = recorder->ip_before_points;
670 ip_limit = ip + i;
671 for (; ip < ip_limit; ip++)
673 FT_UInt point = TA_adjust_point_index(recorder, *ip);
676 *(p++) = HIGH(point);
677 *(p++) = LOW(point);
681 /* ip_after_points */
683 i = 0;
684 ip = recorder->ip_after_points;
685 ip_limit = ip + num_strong_points;
686 for (; ip < ip_limit; ip++)
688 if (*ip != MISSING)
689 i++;
690 else
691 break;
694 if (i)
696 recorder->hints_record.num_actions++;
698 edge = edges + axis->num_edges - 1;
700 *(p++) = 0;
701 *(p++) = (FT_Byte)ta_ip_after + ACTION_OFFSET;
702 *(p++) = HIGH(edge->first - segments);
703 *(p++) = LOW(edge->first - segments);
704 *(p++) = HIGH(i);
705 *(p++) = LOW(i);
707 ip = recorder->ip_after_points;
708 ip_limit = ip + i;
709 for (; ip < ip_limit; ip++)
711 FT_UInt point = TA_adjust_point_index(recorder, *ip);
714 *(p++) = HIGH(point);
715 *(p++) = LOW(point);
719 /* ip_on_point_array */
721 i = 0;
722 ip = recorder->ip_on_point_array;
723 ip_limit = ip + num_edges * num_strong_points;
724 for (; ip < ip_limit; ip += num_strong_points)
725 if (*ip != MISSING)
726 i++;
728 if (i)
730 recorder->hints_record.num_actions++;
732 *(p++) = 0;
733 *(p++) = (FT_Byte)ta_ip_on + ACTION_OFFSET;
734 *(p++) = HIGH(i);
735 *(p++) = LOW(i);
737 i = 0;
738 ip = recorder->ip_on_point_array;
739 ip_limit = ip + num_edges * num_strong_points;
740 for (; ip < ip_limit; ip += num_strong_points, i++)
742 if (*ip == MISSING)
743 continue;
745 edge = edges + i;
747 *(p++) = HIGH(edge->first - segments);
748 *(p++) = LOW(edge->first - segments);
750 j = 0;
751 iq = ip;
752 iq_limit = iq + num_strong_points;
753 for (; iq < iq_limit; iq++)
755 if (*iq != MISSING)
756 j++;
757 else
758 break;
761 *(p++) = HIGH(j);
762 *(p++) = LOW(j);
764 iq = ip;
765 iq_limit = iq + j;
766 for (; iq < iq_limit; iq++)
768 FT_UInt point = TA_adjust_point_index(recorder, *iq);
771 *(p++) = HIGH(point);
772 *(p++) = LOW(point);
777 /* ip_between_point_array */
779 i = 0;
780 ip = recorder->ip_between_point_array;
781 ip_limit = ip + num_edges * num_edges * num_strong_points;
782 for (; ip < ip_limit; ip += num_strong_points)
783 if (*ip != MISSING)
784 i++;
786 if (i)
788 recorder->hints_record.num_actions++;
790 *(p++) = 0;
791 *(p++) = (FT_Byte)ta_ip_between + ACTION_OFFSET;
792 *(p++) = HIGH(i);
793 *(p++) = LOW(i);
795 i = 0;
796 ip = recorder->ip_between_point_array;
797 ip_limit = ip + num_edges * num_edges * num_strong_points;
798 for (;
799 ip < ip_limit;
800 ip += num_edges * num_strong_points, i++)
802 before = edges + i;
804 j = 0;
805 iq = ip;
806 iq_limit = iq + num_edges * num_strong_points;
807 for (; iq < iq_limit; iq += num_strong_points, j++)
809 if (*iq == MISSING)
810 continue;
812 after = edges + j;
814 *(p++) = HIGH(after->first - segments);
815 *(p++) = LOW(after->first - segments);
816 *(p++) = HIGH(before->first - segments);
817 *(p++) = LOW(before->first - segments);
819 k = 0;
820 ir = iq;
821 ir_limit = ir + num_strong_points;
822 for (; ir < ir_limit; ir++)
824 if (*ir != MISSING)
825 k++;
826 else
827 break;
830 *(p++) = HIGH(k);
831 *(p++) = LOW(k);
833 ir = iq;
834 ir_limit = ir + k;
835 for (; ir < ir_limit; ir++)
837 FT_UInt point = TA_adjust_point_index(recorder, *ir);
840 *(p++) = HIGH(point);
841 *(p++) = LOW(point);
847 recorder->hints_record.buf = p;
851 static FT_Bool
852 TA_hints_record_is_different(Hints_Record* hints_records,
853 FT_UInt num_hints_records,
854 FT_Byte* start,
855 FT_Byte* end)
857 Hints_Record last_hints_record;
860 if (!hints_records)
861 return 1;
863 /* we only need to compare with the last hints record */
864 last_hints_record = hints_records[num_hints_records - 1];
866 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
867 return 1;
869 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
870 return 1;
872 return 0;
876 static FT_Error
877 TA_add_hints_record(Hints_Record** hints_records,
878 FT_UInt* num_hints_records,
879 FT_Byte* start,
880 Hints_Record hints_record)
882 Hints_Record* hints_records_new;
883 FT_UInt buf_len;
884 /* at this point, `hints_record.buf' still points into `ins_buf' */
885 FT_Byte* end = hints_record.buf;
888 buf_len = (FT_UInt)(end - start);
890 /* now fill the structure completely */
891 hints_record.buf_len = buf_len;
892 hints_record.buf = (FT_Byte*)malloc(buf_len);
893 if (!hints_record.buf)
894 return FT_Err_Out_Of_Memory;
896 memcpy(hints_record.buf, start, buf_len);
898 (*num_hints_records)++;
899 hints_records_new =
900 (Hints_Record*)realloc(*hints_records, *num_hints_records
901 * sizeof (Hints_Record));
902 if (!hints_records_new)
904 free(hints_record.buf);
905 (*num_hints_records)--;
906 return FT_Err_Out_Of_Memory;
908 else
909 *hints_records = hints_records_new;
911 (*hints_records)[*num_hints_records - 1] = hints_record;
913 return FT_Err_Ok;
917 static FT_Byte*
918 TA_emit_hints_record(Recorder* recorder,
919 Hints_Record* hints_record,
920 FT_Byte* bufp,
921 FT_Bool optimize)
923 FT_Byte* p;
924 FT_Byte* endp;
925 FT_Bool need_words = 0;
927 FT_UInt i, j;
928 FT_UInt num_arguments;
929 FT_UInt num_args;
932 /* check whether any argument is larger than 0xFF */
933 endp = hints_record->buf + hints_record->buf_len;
934 for (p = hints_record->buf; p < endp; p += 2)
935 if (*p)
936 need_words = 1;
938 /* with most fonts it is very rare */
939 /* that any of the pushed arguments is larger than 0xFF, */
940 /* thus we refrain from further optimizing this case */
942 num_arguments = hints_record->buf_len / 2;
943 p = endp - 2;
945 if (need_words)
947 for (i = 0; i < num_arguments; i += 255)
949 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
951 if (optimize && num_args <= 8)
952 BCI(PUSHW_1 - 1 + num_args);
953 else
955 BCI(NPUSHW);
956 BCI(num_args);
958 for (j = 0; j < num_args; j++)
960 BCI(*p);
961 BCI(*(p + 1));
962 p -= 2;
966 else
968 /* we only need the lower bytes */
969 p++;
971 for (i = 0; i < num_arguments; i += 255)
973 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
975 if (optimize && num_args <= 8)
976 BCI(PUSHB_1 - 1 + num_args);
977 else
979 BCI(NPUSHB);
980 BCI(num_args);
982 for (j = 0; j < num_args; j++)
984 BCI(*p);
985 p -= 2;
990 /* collect stack depth data */
991 if (num_arguments > recorder->num_stack_elements)
992 recorder->num_stack_elements = num_arguments;
994 return bufp;
998 static FT_Byte*
999 TA_emit_hints_records(Recorder* recorder,
1000 Hints_Record* hints_records,
1001 FT_UInt num_hints_records,
1002 FT_Byte* bufp,
1003 FT_Bool optimize)
1005 FT_UInt i;
1006 Hints_Record* hints_record;
1009 hints_record = hints_records;
1011 for (i = 0; i < num_hints_records - 1; i++)
1013 BCI(MPPEM);
1014 if (hints_record->size > 0xFF)
1016 BCI(PUSHW_1);
1017 BCI(HIGH((hints_record + 1)->size));
1018 BCI(LOW((hints_record + 1)->size));
1020 else
1022 BCI(PUSHB_1);
1023 BCI((hints_record + 1)->size);
1025 BCI(LT);
1026 BCI(IF);
1027 bufp = TA_emit_hints_record(recorder, hints_record, bufp, optimize);
1028 BCI(ELSE);
1030 hints_record++;
1033 bufp = TA_emit_hints_record(recorder, hints_record, bufp, optimize);
1035 for (i = 0; i < num_hints_records - 1; i++)
1036 BCI(EIF);
1038 return bufp;
1042 static void
1043 TA_free_hints_records(Hints_Record* hints_records,
1044 FT_UInt num_hints_records)
1046 FT_UInt i;
1049 for (i = 0; i < num_hints_records; i++)
1050 free(hints_records[i].buf);
1052 free(hints_records);
1056 static FT_Byte*
1057 TA_hints_recorder_handle_segments(FT_Byte* bufp,
1058 TA_AxisHints axis,
1059 TA_Edge edge,
1060 FT_UShort* wraps)
1062 TA_Segment segments = axis->segments;
1063 TA_Segment seg;
1064 FT_UShort seg_idx;
1065 FT_UShort num_segs = 0;
1066 FT_UShort* wrap;
1069 seg_idx = edge->first - segments;
1071 /* we store everything as 16bit numbers */
1072 *(bufp++) = HIGH(seg_idx);
1073 *(bufp++) = LOW(seg_idx);
1075 /* wrap-around segments are stored as two segments */
1076 if (edge->first->first > edge->first->last)
1077 num_segs++;
1079 seg = edge->first->edge_next;
1080 while (seg != edge->first)
1082 num_segs++;
1084 if (seg->first > seg->last)
1085 num_segs++;
1087 seg = seg->edge_next;
1090 *(bufp++) = HIGH(num_segs);
1091 *(bufp++) = LOW(num_segs);
1093 if (edge->first->first > edge->first->last)
1095 /* emit second part of wrap-around segment; */
1096 /* the bytecode positions such segments after `normal' ones */
1097 wrap = wraps;
1098 for (;;)
1100 if (seg_idx == *wrap)
1101 break;
1102 wrap++;
1105 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
1106 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
1109 seg = edge->first->edge_next;
1110 while (seg != edge->first)
1112 seg_idx = seg - segments;
1114 *(bufp++) = HIGH(seg_idx);
1115 *(bufp++) = LOW(seg_idx);
1117 if (seg->first > seg->last)
1119 wrap = wraps;
1120 for (;;)
1122 if (seg_idx == *wrap)
1123 break;
1124 wrap++;
1127 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
1128 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
1131 seg = seg->edge_next;
1134 return bufp;
1138 static void
1139 TA_hints_recorder(TA_Action action,
1140 TA_GlyphHints hints,
1141 TA_Dimension dim,
1142 void* arg1,
1143 TA_Edge arg2,
1144 TA_Edge arg3,
1145 TA_Edge lower_bound,
1146 TA_Edge upper_bound)
1148 TA_AxisHints axis = &hints->axis[dim];
1149 TA_Edge edges = axis->edges;
1150 TA_Segment segments = axis->segments;
1151 TA_Point points = hints->points;
1153 Recorder* recorder = (Recorder*)hints->user;
1154 FONT* font = recorder->font;
1155 FT_UShort* wraps = recorder->wrap_around_segments;
1156 FT_Byte* p = recorder->hints_record.buf;
1158 FT_UShort* ip;
1159 FT_UShort* limit;
1162 if (dim == TA_DIMENSION_HORZ)
1163 return;
1165 /* we collect point hints for later processing */
1166 switch (action)
1168 case ta_ip_before:
1170 TA_Point point = (TA_Point)arg1;
1173 ip = recorder->ip_before_points;
1174 limit = ip + recorder->num_strong_points;
1175 for (; ip < limit; ip++)
1177 if (*ip == MISSING)
1179 *ip = point - points;
1180 break;
1184 return;
1186 case ta_ip_after:
1188 TA_Point point = (TA_Point)arg1;
1191 ip = recorder->ip_after_points;
1192 limit = ip + recorder->num_strong_points;
1193 for (; ip < limit; ip++)
1195 if (*ip == MISSING)
1197 *ip = point - points;
1198 break;
1202 return;
1204 case ta_ip_on:
1206 TA_Point point = (TA_Point)arg1;
1207 TA_Edge edge = arg2;
1210 ip = recorder->ip_on_point_array
1211 + recorder->num_strong_points
1212 * (edge - edges);
1213 limit = ip + recorder->num_strong_points;
1214 for (; ip < limit; ip++)
1216 if (*ip == MISSING)
1218 *ip = point - points;
1219 break;
1223 return;
1225 case ta_ip_between:
1227 TA_Point point = (TA_Point)arg1;
1228 TA_Edge before = arg2;
1229 TA_Edge after = arg3;
1232 /* note that `recorder->num_segments' has been used for allocation, */
1233 /* but `axis->num_edges' is used for accessing this array */
1234 ip = recorder->ip_between_point_array
1235 + recorder->num_strong_points * axis->num_edges
1236 * (before - edges)
1237 + recorder->num_strong_points
1238 * (after - edges);
1239 limit = ip + recorder->num_strong_points;
1240 for (; ip < limit; ip++)
1242 if (*ip == MISSING)
1244 *ip = point - points;
1245 break;
1249 return;
1251 case ta_bound:
1252 /* we ignore the BOUND action since we signal this information */
1253 /* with the proper function number */
1254 return;
1256 default:
1257 break;
1260 /* some enum values correspond to four or eight bytecode functions; */
1261 /* if the value is n, the function numbers are n, ..., n+7, */
1262 /* to be differentiated with flags */
1264 switch (action)
1266 case ta_link:
1268 TA_Edge base_edge = (TA_Edge)arg1;
1269 TA_Edge stem_edge = arg2;
1272 *(p++) = 0;
1273 *(p++) = (FT_Byte)action + ACTION_OFFSET
1274 + ((stem_edge->flags & TA_EDGE_SERIF) != 0)
1275 + 2 * ((base_edge->flags & TA_EDGE_ROUND) != 0);
1277 *(p++) = HIGH(base_edge->first - segments);
1278 *(p++) = LOW(base_edge->first - segments);
1279 *(p++) = HIGH(stem_edge->first - segments);
1280 *(p++) = LOW(stem_edge->first - segments);
1282 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
1284 break;
1286 case ta_anchor:
1288 TA_Edge edge = (TA_Edge)arg1;
1289 TA_Edge edge2 = arg2;
1292 *(p++) = 0;
1293 *(p++) = (FT_Byte)action + ACTION_OFFSET
1294 + ((edge2->flags & TA_EDGE_SERIF) != 0)
1295 + 2 * ((edge->flags & TA_EDGE_ROUND) != 0);
1297 *(p++) = HIGH(edge->first - segments);
1298 *(p++) = LOW(edge->first - segments);
1299 *(p++) = HIGH(edge2->first - segments);
1300 *(p++) = LOW(edge2->first - segments);
1302 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1304 break;
1306 case ta_adjust:
1308 TA_Edge edge = (TA_Edge)arg1;
1309 TA_Edge edge2 = arg2;
1310 TA_Edge edge_minus_one = lower_bound;
1313 *(p++) = 0;
1314 *(p++) = (FT_Byte)action + ACTION_OFFSET
1315 + ((edge2->flags & TA_EDGE_SERIF) != 0)
1316 + 2 * ((edge->flags & TA_EDGE_ROUND) != 0)
1317 + 4 * (edge_minus_one != NULL);
1319 *(p++) = HIGH(edge->first - segments);
1320 *(p++) = LOW(edge->first - segments);
1321 *(p++) = HIGH(edge2->first - segments);
1322 *(p++) = LOW(edge2->first - segments);
1324 if (edge_minus_one)
1326 *(p++) = HIGH(edge_minus_one->first - segments);
1327 *(p++) = LOW(edge_minus_one->first - segments);
1330 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1332 break;
1334 case ta_blue_anchor:
1336 TA_Edge edge = (TA_Edge)arg1;
1337 TA_Edge blue = arg2;
1340 *(p++) = 0;
1341 *(p++) = (FT_Byte)action + ACTION_OFFSET;
1343 *(p++) = HIGH(blue->first - segments);
1344 *(p++) = LOW(blue->first - segments);
1346 if (edge->best_blue_is_shoot)
1348 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1349 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1351 else
1353 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1354 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1357 *(p++) = HIGH(edge->first - segments);
1358 *(p++) = LOW(edge->first - segments);
1360 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1362 break;
1364 case ta_stem:
1366 TA_Edge edge = (TA_Edge)arg1;
1367 TA_Edge edge2 = arg2;
1368 TA_Edge edge_minus_one = lower_bound;
1371 *(p++) = 0;
1372 *(p++) = (FT_Byte)action + ACTION_OFFSET
1373 + ((edge2->flags & TA_EDGE_SERIF) != 0)
1374 + 2 * ((edge->flags & TA_EDGE_ROUND) != 0)
1375 + 4 * (edge_minus_one != NULL);
1377 *(p++) = HIGH(edge->first - segments);
1378 *(p++) = LOW(edge->first - segments);
1379 *(p++) = HIGH(edge2->first - segments);
1380 *(p++) = LOW(edge2->first - segments);
1382 if (edge_minus_one)
1384 *(p++) = HIGH(edge_minus_one->first - segments);
1385 *(p++) = LOW(edge_minus_one->first - segments);
1388 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1389 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
1391 break;
1393 case ta_blue:
1395 TA_Edge edge = (TA_Edge)arg1;
1398 *(p++) = 0;
1399 *(p++) = (FT_Byte)action + ACTION_OFFSET;
1401 if (edge->best_blue_is_shoot)
1403 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1404 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1406 else
1408 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1409 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1412 *(p++) = HIGH(edge->first - segments);
1413 *(p++) = LOW(edge->first - segments);
1415 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1417 break;
1419 case ta_serif:
1421 TA_Edge serif = (TA_Edge)arg1;
1422 TA_Edge base = serif->serif;
1425 *(p++) = 0;
1426 *(p++) = (FT_Byte)action + ACTION_OFFSET
1427 + (lower_bound != NULL)
1428 + 2 * (upper_bound != NULL);
1430 *(p++) = HIGH(serif->first - segments);
1431 *(p++) = LOW(serif->first - segments);
1432 *(p++) = HIGH(base->first - segments);
1433 *(p++) = LOW(base->first - segments);
1435 if (lower_bound)
1437 *(p++) = HIGH(lower_bound->first - segments);
1438 *(p++) = LOW(lower_bound->first - segments);
1440 if (upper_bound)
1442 *(p++) = HIGH(upper_bound->first - segments);
1443 *(p++) = LOW(upper_bound->first - segments);
1446 p = TA_hints_recorder_handle_segments(p, axis, serif, wraps);
1448 break;
1450 case ta_serif_anchor:
1451 case ta_serif_link2:
1453 TA_Edge edge = (TA_Edge)arg1;
1456 *(p++) = 0;
1457 *(p++) = (FT_Byte)action + ACTION_OFFSET
1458 + (lower_bound != NULL)
1459 + 2 * (upper_bound != NULL);
1461 *(p++) = HIGH(edge->first - segments);
1462 *(p++) = LOW(edge->first - segments);
1464 if (lower_bound)
1466 *(p++) = HIGH(lower_bound->first - segments);
1467 *(p++) = LOW(lower_bound->first - segments);
1469 if (upper_bound)
1471 *(p++) = HIGH(upper_bound->first - segments);
1472 *(p++) = LOW(upper_bound->first - segments);
1475 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1477 break;
1479 case ta_serif_link1:
1481 TA_Edge edge = (TA_Edge)arg1;
1482 TA_Edge before = arg2;
1483 TA_Edge after = arg3;
1486 *(p++) = 0;
1487 *(p++) = (FT_Byte)action + ACTION_OFFSET
1488 + (lower_bound != NULL)
1489 + 2 * (upper_bound != NULL);
1491 *(p++) = HIGH(before->first - segments);
1492 *(p++) = LOW(before->first - segments);
1493 *(p++) = HIGH(edge->first - segments);
1494 *(p++) = LOW(edge->first - segments);
1495 *(p++) = HIGH(after->first - segments);
1496 *(p++) = LOW(after->first - segments);
1498 if (lower_bound)
1500 *(p++) = HIGH(lower_bound->first - segments);
1501 *(p++) = LOW(lower_bound->first - segments);
1503 if (upper_bound)
1505 *(p++) = HIGH(upper_bound->first - segments);
1506 *(p++) = LOW(upper_bound->first - segments);
1509 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1511 break;
1513 default:
1514 /* there are more cases in the enumeration */
1515 /* which are handled with flags */
1516 break;
1519 recorder->hints_record.num_actions++;
1520 recorder->hints_record.buf = p;
1524 static FT_Error
1525 TA_init_recorder(Recorder* recorder,
1526 FONT* font,
1527 GLYPH* glyph,
1528 TA_GlyphHints hints)
1530 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1531 TA_Point points = hints->points;
1532 TA_Point point_limit = points + hints->num_points;
1533 TA_Point point;
1535 TA_Segment segments = axis->segments;
1536 TA_Segment seg_limit = segments + axis->num_segments;
1537 TA_Segment seg;
1539 FT_UShort num_strong_points = 0;
1540 FT_UShort* wrap_around_segment;
1542 recorder->font = font;
1543 recorder->glyph = glyph;
1544 recorder->num_segments = axis->num_segments;
1546 recorder->ip_before_points = NULL;
1547 recorder->ip_after_points = NULL;
1548 recorder->ip_on_point_array = NULL;
1549 recorder->ip_between_point_array = NULL;
1551 recorder->num_stack_elements = 0;
1553 /* no need to clean up allocated arrays in case of error; */
1554 /* this is handled later by `TA_free_recorder' */
1556 recorder->num_wrap_around_segments = 0;
1557 for (seg = segments; seg < seg_limit; seg++)
1558 if (seg->first > seg->last)
1559 recorder->num_wrap_around_segments++;
1561 recorder->wrap_around_segments =
1562 (FT_UShort*)malloc(recorder->num_wrap_around_segments
1563 * sizeof (FT_UShort));
1564 if (!recorder->wrap_around_segments)
1565 return FT_Err_Out_Of_Memory;
1567 wrap_around_segment = recorder->wrap_around_segments;
1568 for (seg = segments; seg < seg_limit; seg++)
1569 if (seg->first > seg->last)
1570 *(wrap_around_segment++) = seg - segments;
1572 /* get number of strong points */
1573 for (point = points; point < point_limit; point++)
1575 /* actually, we need to test `TA_FLAG_TOUCH_Y' also; */
1576 /* however, this value isn't known yet */
1577 /* (or rather, it can vary between different pixel sizes) */
1578 if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1579 continue;
1581 num_strong_points++;
1584 recorder->num_strong_points = num_strong_points;
1586 recorder->ip_before_points =
1587 (FT_UShort*)malloc(num_strong_points * sizeof (FT_UShort));
1588 if (!recorder->ip_before_points)
1589 return FT_Err_Out_Of_Memory;
1591 recorder->ip_after_points =
1592 (FT_UShort*)malloc(num_strong_points * sizeof (FT_UShort));
1593 if (!recorder->ip_after_points)
1594 return FT_Err_Out_Of_Memory;
1596 /* actually, we need `hints->num_edges' for the array sizes; */
1597 /* however, this value isn't known yet */
1598 /* (or rather, it can vary between different pixel sizes) */
1599 recorder->ip_on_point_array =
1600 (FT_UShort*)malloc(axis->num_segments
1601 * num_strong_points * sizeof (FT_UShort));
1602 if (!recorder->ip_on_point_array)
1603 return FT_Err_Out_Of_Memory;
1605 recorder->ip_between_point_array =
1606 (FT_UShort*)malloc(axis->num_segments * axis->num_segments
1607 * num_strong_points * sizeof (FT_UShort));
1608 if (!recorder->ip_between_point_array)
1609 return FT_Err_Out_Of_Memory;
1611 return FT_Err_Ok;
1615 static void
1616 TA_reset_recorder(Recorder* recorder,
1617 FT_Byte* bufp)
1619 recorder->hints_record.buf = bufp;
1620 recorder->hints_record.num_actions = 0;
1624 static void
1625 TA_rewind_recorder(Recorder* recorder,
1626 FT_Byte* bufp,
1627 FT_UInt size)
1629 TA_reset_recorder(recorder, bufp);
1631 recorder->hints_record.size = size;
1633 /* We later check with MISSING (which expands to 0xFF bytes) */
1635 memset(recorder->ip_before_points, 0xFF,
1636 recorder->num_strong_points * sizeof (FT_UShort));
1637 memset(recorder->ip_after_points, 0xFF,
1638 recorder->num_strong_points * sizeof (FT_UShort));
1640 memset(recorder->ip_on_point_array, 0xFF,
1641 recorder->num_segments
1642 * recorder->num_strong_points * sizeof (FT_UShort));
1643 memset(recorder->ip_between_point_array, 0xFF,
1644 recorder->num_segments * recorder->num_segments
1645 * recorder->num_strong_points * sizeof (FT_UShort));
1649 static void
1650 TA_free_recorder(Recorder* recorder)
1652 free(recorder->wrap_around_segments);
1654 free(recorder->ip_before_points);
1655 free(recorder->ip_after_points);
1656 free(recorder->ip_on_point_array);
1657 free(recorder->ip_between_point_array);
1661 FT_Error
1662 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
1663 FONT* font,
1664 FT_Long idx)
1666 FT_Face face = sfnt->face;
1667 FT_Error error;
1669 FT_Byte* ins_buf;
1670 FT_UInt ins_len;
1671 FT_Byte* bufp;
1672 FT_Byte* p;
1674 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
1675 glyf_Data* data = (glyf_Data*)glyf_table->data;
1676 /* `idx' is never negative */
1677 GLYPH* glyph = &data->glyphs[idx];
1679 TA_GlyphHints hints;
1681 FT_UInt num_action_hints_records = 0;
1682 FT_UInt num_point_hints_records = 0;
1683 Hints_Record* action_hints_records = NULL;
1684 Hints_Record* point_hints_records = NULL;
1686 Recorder recorder;
1687 FT_UShort num_stack_elements;
1688 FT_Bool optimize = 0;
1690 FT_Int32 load_flags;
1691 FT_UInt size;
1693 FT_Byte* pos[3];
1695 #ifdef TA_DEBUG
1696 int _ta_debug_save;
1697 #endif
1700 /* XXX: right now, we abuse this flag to control */
1701 /* the global behaviour of the auto-hinter */
1702 load_flags = 1 << 29; /* vertical hinting only */
1703 if (!font->pre_hinting)
1705 if (font->hint_with_components)
1706 load_flags |= FT_LOAD_NO_SCALE;
1707 else
1708 load_flags |= FT_LOAD_NO_RECURSE;
1711 /* computing the segments is resolution independent, */
1712 /* thus the pixel size in this call is arbitrary */
1713 error = FT_Set_Pixel_Sizes(face, 20, 20);
1714 if (error)
1715 return error;
1717 #ifdef TA_DEBUG
1718 /* temporarily disable debugging output */
1719 /* to avoid getting the information twice */
1720 _ta_debug_save = _ta_debug;
1721 _ta_debug = 0;
1722 #endif
1724 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
1725 error = ta_loader_load_glyph(font, face, (FT_UInt)idx, load_flags);
1727 #ifdef TA_DEBUG
1728 _ta_debug = _ta_debug_save;
1729 #endif
1731 if (error)
1732 return error;
1734 /* do nothing if we have an empty glyph */
1735 if (!face->glyph->outline.n_contours)
1736 return FT_Err_Ok;
1738 hints = &font->loader->hints;
1740 /* do nothing if the setup delivered the dummy module only */
1741 if (!hints->num_points)
1742 return FT_Err_Ok;
1744 /* we allocate a buffer which is certainly large enough */
1745 /* to hold all of the created bytecode instructions; */
1746 /* later on it gets reallocated to its real size */
1747 ins_len = hints->num_points * 1000;
1748 ins_buf = (FT_Byte*)malloc(ins_len);
1749 if (!ins_buf)
1750 return FT_Err_Out_Of_Memory;
1752 /* initialize array with an invalid bytecode */
1753 /* so that we can easily find the array length at reallocation time */
1754 memset(ins_buf, INS_A0, ins_len);
1756 /* handle composite glyph */
1757 if (font->loader->gloader->base.num_subglyphs)
1759 bufp = TA_font_build_subglyph_shifter(font, ins_buf);
1760 if (!bufp)
1762 error = FT_Err_Out_Of_Memory;
1763 goto Err;
1766 goto Done1;
1769 /* only scale the glyph if the dummy hinter has been used */
1770 if (font->loader->metrics->clazz == &ta_dummy_script_class)
1772 /* since `TA_init_recorder' hasn't been called yet, */
1773 /* we manually initialize the `font' and `glyph' fields */
1774 recorder.font = font;
1775 recorder.glyph = glyph;
1777 bufp = TA_sfnt_build_glyph_scaler(sfnt, &recorder, ins_buf);
1778 if (!bufp)
1780 error = FT_Err_Out_Of_Memory;
1781 goto Err;
1784 goto Done1;
1787 error = TA_init_recorder(&recorder, font, glyph, hints);
1788 if (error)
1789 goto Err;
1791 /* loop over a large range of pixel sizes */
1792 /* to find hints records which get pushed onto the bytecode stack */
1794 #ifdef DEBUGGING
1795 if (font->debug)
1797 int num_chars, i;
1798 char buf[256];
1801 (void)FT_Get_Glyph_Name(face, idx, buf, 256);
1803 num_chars = fprintf(stderr, "glyph %ld", idx);
1804 if (*buf)
1805 num_chars += fprintf(stderr, " (%s)", buf);
1806 fprintf(stderr, "\n");
1807 for (i = 0; i < num_chars; i++)
1808 putc('=', stderr);
1809 fprintf(stderr, "\n\n");
1812 #endif
1814 /* we temporarily use `ins_buf' to record the current glyph hints */
1815 ta_loader_register_hints_recorder(font->loader,
1816 TA_hints_recorder,
1817 (void*)&recorder);
1819 for (size = font->hinting_range_min;
1820 size <= font->hinting_range_max;
1821 size++)
1823 #ifdef DEBUGGING
1824 int have_dumps = 0;
1825 #endif
1828 TA_rewind_recorder(&recorder, ins_buf, size);
1830 error = FT_Set_Pixel_Sizes(face, size, size);
1831 if (error)
1832 goto Err;
1834 #ifdef DEBUGGING
1835 if (font->debug)
1837 int num_chars, i;
1840 num_chars = fprintf(stderr, "size %d\n", size);
1841 for (i = 0; i < num_chars - 1; i++)
1842 putc('-', stderr);
1843 fprintf(stderr, "\n\n");
1845 #endif
1847 /* calling `ta_loader_load_glyph' uses the */
1848 /* `TA_hints_recorder' function as a callback, */
1849 /* modifying `hints_record' */
1850 error = ta_loader_load_glyph(font, face, idx, load_flags);
1851 if (error)
1852 goto Err;
1854 if (TA_hints_record_is_different(action_hints_records,
1855 num_action_hints_records,
1856 ins_buf, recorder.hints_record.buf))
1858 #ifdef DEBUGGING
1859 if (font->debug)
1861 have_dumps = 1;
1863 ta_glyph_hints_dump_edges(_ta_debug_hints);
1864 ta_glyph_hints_dump_segments(_ta_debug_hints);
1865 ta_glyph_hints_dump_points(_ta_debug_hints);
1867 fprintf(stderr, "action hints record:\n");
1868 if (ins_buf == recorder.hints_record.buf)
1869 fprintf(stderr, " (none)");
1870 else
1872 fprintf(stderr, " ");
1873 for (p = ins_buf; p < recorder.hints_record.buf; p += 2)
1874 fprintf(stderr, " %2d", *p * 256 + *(p + 1));
1876 fprintf(stderr, "\n");
1878 #endif
1880 error = TA_add_hints_record(&action_hints_records,
1881 &num_action_hints_records,
1882 ins_buf, recorder.hints_record);
1883 if (error)
1884 goto Err;
1887 /* now handle point records */
1889 TA_reset_recorder(&recorder, ins_buf);
1891 /* use the point hints data collected in `TA_hints_recorder' */
1892 TA_build_point_hints(&recorder, hints);
1894 if (TA_hints_record_is_different(point_hints_records,
1895 num_point_hints_records,
1896 ins_buf, recorder.hints_record.buf))
1898 #ifdef DEBUGGING
1899 if (font->debug)
1901 if (!have_dumps)
1903 int num_chars, i;
1906 num_chars = fprintf(stderr, "size %d\n", size);
1907 for (i = 0; i < num_chars - 1; i++)
1908 putc('-', stderr);
1909 fprintf(stderr, "\n\n");
1911 ta_glyph_hints_dump_edges(_ta_debug_hints);
1912 ta_glyph_hints_dump_segments(_ta_debug_hints);
1913 ta_glyph_hints_dump_points(_ta_debug_hints);
1916 fprintf(stderr, "point hints record:\n");
1917 if (ins_buf == recorder.hints_record.buf)
1918 fprintf(stderr, " (none)");
1919 else
1921 fprintf(stderr, " ");
1922 for (p = ins_buf; p < recorder.hints_record.buf; p += 2)
1923 fprintf(stderr, " %2d", *p * 256 + *(p + 1));
1925 fprintf(stderr, "\n\n");
1927 #endif
1929 error = TA_add_hints_record(&point_hints_records,
1930 &num_point_hints_records,
1931 ins_buf, recorder.hints_record);
1932 if (error)
1933 goto Err;
1937 if (num_action_hints_records == 1 && !action_hints_records[0].num_actions)
1939 /* since we only have a single empty record we just scale the glyph */
1940 bufp = TA_sfnt_build_glyph_scaler(sfnt, &recorder, ins_buf);
1941 if (!bufp)
1943 error = FT_Err_Out_Of_Memory;
1944 goto Err;
1947 /* clear the rest of the temporarily used part of `ins_buf' */
1948 p = bufp;
1949 while (*p != INS_A0)
1950 *(p++) = INS_A0;
1952 goto Done;
1955 /* if there is only a single record, */
1956 /* we do a global optimization later on */
1957 if (num_action_hints_records > 1)
1958 optimize = 1;
1960 /* store the hints records and handle stack depth */
1961 pos[0] = ins_buf;
1962 bufp = TA_emit_hints_records(&recorder,
1963 point_hints_records,
1964 num_point_hints_records,
1965 ins_buf,
1966 optimize);
1968 num_stack_elements = recorder.num_stack_elements;
1969 recorder.num_stack_elements = 0;
1971 pos[1] = bufp;
1972 bufp = TA_emit_hints_records(&recorder,
1973 action_hints_records,
1974 num_action_hints_records,
1975 bufp,
1976 optimize);
1978 recorder.num_stack_elements += num_stack_elements;
1980 pos[2] = bufp;
1981 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, bufp, optimize);
1982 if (!bufp)
1984 error = FT_Err_Out_Of_Memory;
1985 goto Err;
1988 /* XXX improve handling of NPUSHW */
1989 if (num_action_hints_records == 1
1990 && *(pos[0]) != NPUSHW && *(pos[1]) != NPUSHW && *(pos[2]) != NPUSHW)
1993 * we optimize two common cases, replacing
1995 * NPUSHB A ... NPUSHB B [... NPUSHB C] ... CALL
1997 * with
1999 * NPUSHB (A+B[+C]) ... CALL
2001 * if possible
2003 FT_Byte sizes[3];
2004 FT_Byte new_size1;
2005 FT_Byte new_size2;
2007 FT_UInt sum;
2008 FT_UInt i;
2009 FT_UInt pos_idx;
2012 /* the point hints records block can be missing */
2013 if (pos[0] == pos[1])
2015 pos[1] = pos[2];
2016 pos[2] = NULL;
2019 /* there are at least two NPUSHB instructions */
2020 /* (one of them directly at the start) */
2021 sizes[0] = *(pos[0] + 1);
2022 sizes[1] = *(pos[1] + 1);
2023 sizes[2] = pos[2] ? *(pos[2] + 1) : 0;
2025 sum = sizes[0] + sizes[1] + sizes[2];
2027 if (sum > 2 * 0xFF)
2028 goto Done2; /* nothing to do since we need three NPUSHB */
2029 else if (!sizes[2] && (sum > 0xFF))
2030 goto Done2; /* nothing to do since we need two NPUSHB */
2032 if (sum > 0xFF)
2034 /* reduce three NPUSHB to two */
2035 new_size1 = 0xFF;
2036 new_size2 = sum - 0xFF;
2038 else
2040 /* reduce two or three NPUSHB to one */
2041 new_size1 = sum;
2042 new_size2 = 0;
2045 /* pack data */
2046 p = ins_buf;
2047 bufp = ins_buf;
2048 pos_idx = 0;
2050 if (new_size1 <= 8)
2051 BCI(PUSHB_1 - 1 + new_size1);
2052 else
2054 BCI(NPUSHB);
2055 BCI(new_size1);
2057 for (i = 0; i < new_size1; i++)
2059 if (p == pos[pos_idx])
2061 pos_idx++;
2062 p += 2; /* skip old NPUSHB */
2064 *(bufp++) = *(p++);
2067 if (new_size2)
2069 if (new_size2 <= 8)
2070 BCI(PUSHB_1 - 1 + new_size2);
2071 else
2073 BCI(NPUSHB);
2074 BCI(new_size2);
2076 for (i = 0; i < new_size2; i++)
2078 if (p == pos[pos_idx])
2080 pos_idx++;
2081 p += 2;
2083 *(bufp++) = *(p++);
2087 BCI(CALL);
2090 Done2:
2091 /* clear the rest of the temporarily used part of `ins_buf' */
2092 p = bufp;
2093 while (*p != INS_A0)
2094 *(p++) = INS_A0;
2096 Done:
2097 TA_free_hints_records(action_hints_records, num_action_hints_records);
2098 TA_free_hints_records(point_hints_records, num_point_hints_records);
2099 TA_free_recorder(&recorder);
2101 /* we are done, so reallocate the instruction array to its real size */
2102 if (*bufp == INS_A0)
2104 /* search backwards */
2105 while (*bufp == INS_A0)
2106 bufp--;
2107 bufp++;
2109 else
2111 /* search forwards */
2112 while (*bufp != INS_A0)
2113 bufp++;
2116 Done1:
2117 ins_len = bufp - ins_buf;
2119 if (ins_len > sfnt->max_instructions)
2120 sfnt->max_instructions = ins_len;
2122 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
2123 glyph->ins_len = ins_len;
2125 return FT_Err_Ok;
2127 Err:
2128 TA_free_hints_records(action_hints_records, num_action_hints_records);
2129 TA_free_hints_records(point_hints_records, num_point_hints_records);
2130 TA_free_recorder(&recorder);
2131 free(ins_buf);
2133 return error;
2137 /* end of tabytecode.c */