Handle options `progress-callback' and `progress-callback-data'.
[ttfautohint.git] / src / tabytecode.c
blob397dfade686180d05e913321dce0bce76c541fbe
1 /* tabytecode.c */
3 /* written 2011 by Werner Lemberg <wl@gnu.org> */
5 #include "ta.h"
6 #include "tabytecode.h"
9 #undef MISSING
10 #define MISSING (FT_UInt)~0
12 /* a simple macro to emit bytecode instructions */
13 #define BCI(code) *(bufp++) = (code)
15 /* we increase the stack depth by this amount */
16 #define ADDITIONAL_STACK_ELEMENTS 20
19 /* #define DEBUGGING */
22 #ifdef TA_DEBUG
23 int _ta_debug = 1;
24 int _ta_debug_disable_horz_hints;
25 int _ta_debug_disable_vert_hints;
26 int _ta_debug_disable_blue_hints;
27 void* _ta_debug_hints;
28 #endif
31 typedef struct Hints_Record_ {
32 FT_UInt size;
33 FT_UInt num_actions;
34 FT_Byte* buf;
35 FT_UInt buf_len;
36 } Hints_Record;
38 typedef struct Recorder_ {
39 FONT* font;
40 Hints_Record hints_record;
42 /* see explanations in `TA_sfnt_build_glyph_segments' */
43 FT_UInt* wrap_around_segments;
45 /* data necessary for strong point interpolation */
46 FT_UInt* ip_before_points;
47 FT_UInt* ip_after_points;
48 FT_UInt* ip_on_point_array;
49 FT_UInt* ip_between_point_array;
51 FT_UInt num_strong_points;
52 FT_UInt num_segments;
53 } Recorder;
56 /* we store the segments in the storage area; */
57 /* each segment record consists of the first and last point */
59 static FT_Byte*
60 TA_sfnt_build_glyph_segments(SFNT* sfnt,
61 Recorder* recorder,
62 FT_Byte* bufp)
64 FONT* font = recorder->font;
65 TA_GlyphHints hints = &font->loader->hints;
66 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
67 TA_Point points = hints->points;
68 TA_Segment segments = axis->segments;
69 TA_Segment seg;
70 TA_Segment seg_limit;
72 FT_Outline outline = font->loader->gloader->base.outline;
74 FT_UInt* args;
75 FT_UInt* arg;
76 FT_UInt num_args;
77 FT_UInt nargs;
78 FT_UInt num_segments;
80 FT_UInt* wrap_around_segment;
81 FT_UInt num_wrap_around_segments;
83 FT_Bool need_words = 0;
85 FT_Int n;
86 FT_UInt i, j;
87 FT_UInt num_storage;
88 FT_UInt num_stack_elements;
89 FT_UInt num_twilight_points;
92 seg_limit = segments + axis->num_segments;
93 num_segments = axis->num_segments;
95 /* some segments can `wrap around' */
96 /* a contour's start point like 24-25-26-0-1-2 */
97 /* (there can be at most one such segment per contour); */
98 /* we thus append additional records to split them into 24-26 and 0-2 */
99 wrap_around_segment = recorder->wrap_around_segments;
100 for (seg = segments; seg < seg_limit; seg++)
101 if (seg->first > seg->last)
103 /* the stored data is used later for edge linking */
104 *(wrap_around_segment++) = seg - segments;
107 num_wrap_around_segments = wrap_around_segment
108 - recorder->wrap_around_segments;
109 num_segments += num_wrap_around_segments;
111 /* wrap-around segments are pushed with four arguments */
112 num_args = 2 * num_segments + 2 * num_wrap_around_segments + 2;
114 /* collect all arguments temporarily in an array (in reverse order) */
115 /* so that we can easily split into chunks of 255 args */
116 /* as needed by NPUSHB and NPUSHW, respectively */
117 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
118 if (!args)
119 return NULL;
121 arg = args + num_args - 1;
123 if (num_segments > 0xFF)
124 need_words = 1;
126 *(arg--) = bci_create_segments;
127 *(arg--) = num_segments;
129 for (seg = segments; seg < seg_limit; seg++)
131 FT_UInt first = seg->first - points;
132 FT_UInt last = seg->last - points;
135 *(arg--) = first;
136 *(arg--) = last;
138 /* we push the last and first contour point */
139 /* as a third and fourth argument in wrap-around segments */
140 if (first > last)
142 for (n = 0; n < outline.n_contours; n++)
144 FT_UInt end = (FT_UInt)outline.contours[n];
147 if (first <= end)
149 *(arg--) = end;
150 if (end > 0xFF)
151 need_words = 1;
153 if (n == 0)
154 *(arg--) = 0;
155 else
156 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
157 break;
162 if (last > 0xFF)
163 need_words = 1;
166 /* emit the second part of wrap-around segments as separate segments */
167 /* so that edges can easily link to them */
168 for (seg = segments; seg < seg_limit; seg++)
170 FT_UInt first = seg->first - points;
171 FT_UInt last = seg->last - points;
174 if (first > last)
176 for (n = 0; n < outline.n_contours; n++)
178 if (first <= (FT_UInt)outline.contours[n])
180 if (n == 0)
181 *(arg--) = 0;
182 else
183 *(arg--) = (FT_UInt)outline.contours[n - 1] + 1;
184 break;
188 *(arg--) = last;
191 /* with most fonts it is very rare */
192 /* that any of the pushed arguments is larger than 0xFF, */
193 /* thus we refrain from further optimizing this case */
195 arg = args;
197 if (need_words)
199 for (i = 0; i < num_args; i += 255)
201 nargs = (num_args - i > 255) ? 255 : num_args - i;
203 BCI(NPUSHW);
204 BCI(nargs);
205 for (j = 0; j < nargs; j++)
207 BCI(HIGH(*arg));
208 BCI(LOW(*arg));
209 arg++;
213 else
215 for (i = 0; i < num_args; i += 255)
217 nargs = (num_args - i > 255) ? 255 : num_args - i;
219 BCI(NPUSHB);
220 BCI(nargs);
221 for (j = 0; j < nargs; j++)
223 BCI(*arg);
224 arg++;
229 BCI(CALL);
231 num_storage = sal_segment_offset + num_segments * 2;
232 if (num_storage > sfnt->max_storage)
233 sfnt->max_storage = num_storage;
235 num_twilight_points = num_segments * 2;
236 if (num_twilight_points > sfnt->max_twilight_points)
237 sfnt->max_twilight_points = num_twilight_points;
239 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
240 if (num_stack_elements > sfnt->max_stack_elements)
241 sfnt->max_stack_elements = num_stack_elements;
243 free(args);
245 return bufp;
249 static FT_Byte*
250 TA_sfnt_build_glyph_scaler(SFNT* sfnt,
251 FT_Byte* bufp)
253 FT_GlyphSlot glyph = sfnt->face->glyph;
254 FT_Vector* points = glyph->outline.points;
255 FT_Int num_contours = glyph->outline.n_contours;
257 FT_UInt* args;
258 FT_UInt* arg;
259 FT_UInt num_args;
260 FT_UInt nargs;
262 FT_Bool need_words = 0;
263 FT_Int p, q;
264 FT_UInt i, j;
265 FT_Int start, end;
266 FT_UInt num_stack_elements;
268 num_args = 2 * num_contours + 2;
270 /* collect all arguments temporarily in an array (in reverse order) */
271 /* so that we can easily split into chunks of 255 args */
272 /* as needed by NPUSHB and NPUSHW, respectively */
273 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
274 if (!args)
275 return NULL;
277 arg = args + num_args - 1;
279 if (num_args > 0xFF)
280 need_words = 1;
282 *(arg--) = bci_scale_glyph;
283 *(arg--) = num_contours;
285 start = 0;
286 end = 0;
288 for (p = 0; p < num_contours; p++)
290 FT_Int max = start;
291 FT_Int min = start;
293 end = glyph->outline.contours[p];
295 for (q = start; q <= end; q++)
297 if (points[q].y < points[min].y)
298 min = q;
299 if (points[q].y > points[max].y)
300 max = q;
303 *(arg--) = min;
304 *(arg--) = max;
306 start = end + 1;
309 if (end > 0xFF)
310 need_words = 1;
312 /* with most fonts it is very rare */
313 /* that any of the pushed arguments is larger than 0xFF, */
314 /* thus we refrain from further optimizing this case */
316 arg = args;
318 if (need_words)
320 for (i = 0; i < num_args; i += 255)
322 nargs = (num_args - i > 255) ? 255 : num_args - i;
324 BCI(NPUSHW);
325 BCI(nargs);
326 for (j = 0; j < nargs; j++)
328 BCI(HIGH(*arg));
329 BCI(LOW(*arg));
330 arg++;
334 else
336 for (i = 0; i < num_args; i += 255)
338 nargs = (num_args - i > 255) ? 255 : num_args - i;
340 BCI(NPUSHB);
341 BCI(nargs);
342 for (j = 0; j < nargs; j++)
344 BCI(*arg);
345 arg++;
350 BCI(CALL);
352 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
353 if (num_stack_elements > sfnt->max_stack_elements)
354 sfnt->max_stack_elements = num_stack_elements;
356 free(args);
358 return bufp;
362 static FT_Byte*
363 TA_font_build_subglyph_shifter(FONT* font,
364 FT_Byte* bufp)
366 FT_Face face = font->loader->face;
367 FT_GlyphSlot glyph = face->glyph;
369 TA_GlyphLoader gloader = font->loader->gloader;
371 TA_SubGlyph subglyphs = gloader->base.subglyphs;
372 TA_SubGlyph subglyph_limit = subglyphs + gloader->base.num_subglyphs;
373 TA_SubGlyph subglyph;
375 FT_Int curr_contour = 0;
378 for (subglyph = subglyphs; subglyph < subglyph_limit; subglyph++)
380 FT_Error error;
382 FT_UShort flags = subglyph->flags;
383 FT_Pos y_offset = subglyph->arg2;
385 FT_Int num_contours;
388 /* load subglyph to get the number of contours */
389 error = FT_Load_Glyph(face, subglyph->index, FT_LOAD_NO_SCALE);
390 if (error)
391 return NULL;
392 num_contours = glyph->outline.n_contours;
394 /* nothing to do if there is a point-to-point alignment */
395 if (!(flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES))
396 goto End;
398 /* nothing to do if y offset is zero */
399 if (!y_offset)
400 goto End;
402 /* nothing to do if there are no contours */
403 if (!num_contours)
404 goto End;
406 /* note that calling `FT_Load_Glyph' without FT_LOAD_NO_RECURSE */
407 /* ensures that composites are resolved into simple glyphs */
409 if (num_contours > 0xFF
410 || curr_contour > 0xFF)
412 BCI(PUSHW_2);
413 BCI(HIGH(curr_contour));
414 BCI(LOW(curr_contour));
415 BCI(HIGH(num_contours));
416 BCI(LOW(num_contours));
418 else
420 BCI(PUSHB_2);
421 BCI(curr_contour);
422 BCI(num_contours);
425 /* there are high chances that this value needs PUSHW, */
426 /* thus we handle it separately */
427 if (y_offset > 0xFF || y_offset < 0)
429 BCI(PUSHW_1);
430 BCI(HIGH(y_offset));
431 BCI(LOW(y_offset));
433 else
435 BCI(PUSHB_1);
436 BCI(y_offset);
439 BCI(PUSHB_1);
440 BCI(bci_shift_subglyph);
441 BCI(CALL);
443 End:
444 curr_contour += num_contours;
447 return bufp;
452 * The four `ta_ip_*' actions in the `TA_hints_recorder' callback store its
453 * data in four arrays (which are simple but waste a lot of memory). The
454 * function below converts them into bytecode.
456 * For `ta_ip_before' and `ta_ip_after', the collected points are emitted
457 * together with the edge they correspond to.
459 * For both `ta_ip_on' and `ta_ip_between', an outer loop is constructed to
460 * loop over the edge or edge pairs, respectively, and each edge or edge
461 * pair contains an inner loop to emit the correponding points.
464 static void
465 TA_build_point_hints(Recorder* recorder,
466 TA_GlyphHints hints)
468 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
469 TA_Segment segments = axis->segments;
470 TA_Edge edges = axis->edges;
472 TA_Edge edge;
473 TA_Edge before;
474 TA_Edge after;
476 FT_Byte* p = recorder->hints_record.buf;
477 FT_UInt num_edges = axis->num_edges;
478 FT_UInt num_strong_points = recorder->num_strong_points;
480 FT_UInt i;
481 FT_UInt j;
482 FT_UInt k;
484 FT_UInt* ip;
485 FT_UInt* iq;
486 FT_UInt* ir;
487 FT_UInt* ip_limit;
488 FT_UInt* iq_limit;
489 FT_UInt* ir_limit;
492 /* we store everything as 16bit numbers; */
493 /* the function numbers (`ta_ip_before', etc.) */
494 /* reflect the order in the TA_Action enumeration */
496 /* ip_before_points */
498 i = 0;
499 ip = recorder->ip_before_points;
500 ip_limit = ip + num_strong_points;
501 for (; ip < ip_limit; ip++)
503 if (*ip != MISSING)
504 i++;
505 else
506 break;
509 if (i)
511 recorder->hints_record.num_actions++;
513 edge = edges;
515 *(p++) = 0;
516 *(p++) = (FT_Byte)ta_ip_before + ACTION_OFFSET;
517 *(p++) = HIGH(edge->first - segments);
518 *(p++) = LOW(edge->first - segments);
519 *(p++) = HIGH(i);
520 *(p++) = LOW(i);
522 ip = recorder->ip_before_points;
523 ip_limit = ip + i;
524 for (; ip < ip_limit; ip++)
526 *(p++) = HIGH(*ip);
527 *(p++) = LOW(*ip);
531 /* ip_after_points */
533 i = 0;
534 ip = recorder->ip_after_points;
535 ip_limit = ip + num_strong_points;
536 for (; ip < ip_limit; ip++)
538 if (*ip != MISSING)
539 i++;
540 else
541 break;
544 if (i)
546 recorder->hints_record.num_actions++;
548 edge = edges + axis->num_edges - 1;
550 *(p++) = 0;
551 *(p++) = (FT_Byte)ta_ip_after + ACTION_OFFSET;
552 *(p++) = HIGH(edge->first - segments);
553 *(p++) = LOW(edge->first - segments);
554 *(p++) = HIGH(i);
555 *(p++) = LOW(i);
557 ip = recorder->ip_after_points;
558 ip_limit = ip + i;
559 for (; ip < ip_limit; ip++)
561 *(p++) = HIGH(*ip);
562 *(p++) = LOW(*ip);
566 /* ip_on_point_array */
568 i = 0;
569 ip = recorder->ip_on_point_array;
570 ip_limit = ip + num_edges * num_strong_points;
571 for (; ip < ip_limit; ip += num_strong_points)
572 if (*ip != MISSING)
573 i++;
575 if (i)
577 recorder->hints_record.num_actions++;
579 *(p++) = 0;
580 *(p++) = (FT_Byte)ta_ip_on + ACTION_OFFSET;
581 *(p++) = HIGH(i);
582 *(p++) = LOW(i);
584 i = 0;
585 ip = recorder->ip_on_point_array;
586 ip_limit = ip + num_edges * num_strong_points;
587 for (; ip < ip_limit; ip += num_strong_points, i++)
589 if (*ip == MISSING)
590 continue;
592 edge = edges + i;
594 *(p++) = HIGH(edge->first - segments);
595 *(p++) = LOW(edge->first - segments);
597 j = 0;
598 iq = ip;
599 iq_limit = iq + num_strong_points;
600 for (; iq < iq_limit; iq++)
602 if (*iq != MISSING)
603 j++;
604 else
605 break;
608 *(p++) = HIGH(j);
609 *(p++) = LOW(j);
611 iq = ip;
612 iq_limit = iq + j;
613 for (; iq < iq_limit; iq++)
615 *(p++) = HIGH(*iq);
616 *(p++) = LOW(*iq);
621 /* ip_between_point_array */
623 i = 0;
624 ip = recorder->ip_between_point_array;
625 ip_limit = ip + num_edges * num_edges * num_strong_points;
626 for (; ip < ip_limit; ip += num_strong_points)
627 if (*ip != MISSING)
628 i++;
630 if (i)
632 recorder->hints_record.num_actions++;
634 *(p++) = 0;
635 *(p++) = (FT_Byte)ta_ip_between + ACTION_OFFSET;
636 *(p++) = HIGH(i);
637 *(p++) = LOW(i);
639 i = 0;
640 ip = recorder->ip_between_point_array;
641 ip_limit = ip + num_edges * num_edges * num_strong_points;
642 for (;
643 ip < ip_limit;
644 ip += num_edges * num_strong_points, i++)
646 before = edges + i;
648 j = 0;
649 iq = ip;
650 iq_limit = iq + num_edges * num_strong_points;
651 for (; iq < iq_limit; iq += num_strong_points, j++)
653 if (*iq == MISSING)
654 continue;
656 after = edges + j;
658 *(p++) = HIGH(after->first - segments);
659 *(p++) = LOW(after->first - segments);
660 *(p++) = HIGH(before->first - segments);
661 *(p++) = LOW(before->first - segments);
663 k = 0;
664 ir = iq;
665 ir_limit = ir + num_strong_points;
666 for (; ir < ir_limit; ir++)
668 if (*ir != MISSING)
669 k++;
670 else
671 break;
674 *(p++) = HIGH(k);
675 *(p++) = LOW(k);
677 ir = iq;
678 ir_limit = ir + k;
679 for (; ir < ir_limit; ir++)
681 *(p++) = HIGH(*ir);
682 *(p++) = LOW(*ir);
688 recorder->hints_record.buf = p;
692 static FT_Bool
693 TA_hints_record_is_different(Hints_Record* hints_records,
694 FT_UInt num_hints_records,
695 FT_Byte* start,
696 FT_Byte* end)
698 Hints_Record last_hints_record;
701 if (!hints_records)
702 return 1;
704 /* we only need to compare with the last hints record */
705 last_hints_record = hints_records[num_hints_records - 1];
707 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
708 return 1;
710 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
711 return 1;
713 return 0;
717 static FT_Error
718 TA_add_hints_record(Hints_Record** hints_records,
719 FT_UInt* num_hints_records,
720 FT_Byte* start,
721 Hints_Record hints_record)
723 Hints_Record* hints_records_new;
724 FT_UInt buf_len;
725 /* at this point, `hints_record.buf' still points into `ins_buf' */
726 FT_Byte* end = hints_record.buf;
729 buf_len = (FT_UInt)(end - start);
731 /* now fill the structure completely */
732 hints_record.buf_len = buf_len;
733 hints_record.buf = (FT_Byte*)malloc(buf_len);
734 if (!hints_record.buf)
735 return FT_Err_Out_Of_Memory;
737 memcpy(hints_record.buf, start, buf_len);
739 (*num_hints_records)++;
740 hints_records_new =
741 (Hints_Record*)realloc(*hints_records, *num_hints_records
742 * sizeof (Hints_Record));
743 if (!hints_records_new)
745 free(hints_record.buf);
746 (*num_hints_records)--;
747 return FT_Err_Out_Of_Memory;
749 else
750 *hints_records = hints_records_new;
752 (*hints_records)[*num_hints_records - 1] = hints_record;
754 return FT_Err_Ok;
758 static FT_Byte*
759 TA_sfnt_emit_hints_record(SFNT* sfnt,
760 Hints_Record* hints_record,
761 FT_Byte* bufp)
763 FT_Byte* p;
764 FT_Byte* endp;
765 FT_Bool need_words = 0;
767 FT_UInt i, j;
768 FT_UInt num_arguments;
769 FT_UInt num_args;
770 FT_UInt num_stack_elements;
773 /* check whether any argument is larger than 0xFF */
774 endp = hints_record->buf + hints_record->buf_len;
775 for (p = hints_record->buf; p < endp; p += 2)
776 if (*p)
777 need_words = 1;
779 /* with most fonts it is very rare */
780 /* that any of the pushed arguments is larger than 0xFF, */
781 /* thus we refrain from further optimizing this case */
783 num_arguments = hints_record->buf_len / 2;
784 p = endp - 2;
786 if (need_words)
788 for (i = 0; i < num_arguments; i += 255)
790 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
792 BCI(NPUSHW);
793 BCI(num_args);
794 for (j = 0; j < num_args; j++)
796 BCI(*p);
797 BCI(*(p + 1));
798 p -= 2;
802 else
804 /* we only need the lower bytes */
805 p++;
807 for (i = 0; i < num_arguments; i += 255)
809 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
811 BCI(NPUSHB);
812 BCI(num_args);
813 for (j = 0; j < num_args; j++)
815 BCI(*p);
816 p -= 2;
821 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_arguments;
822 if (num_stack_elements > sfnt->max_stack_elements)
823 sfnt->max_stack_elements = num_stack_elements;
825 return bufp;
829 static FT_Byte*
830 TA_sfnt_emit_hints_records(SFNT* sfnt,
831 Hints_Record* hints_records,
832 FT_UInt num_hints_records,
833 FT_Byte* bufp)
835 FT_UInt i;
836 Hints_Record* hints_record;
839 hints_record = hints_records;
841 for (i = 0; i < num_hints_records - 1; i++)
843 BCI(MPPEM);
844 if (hints_record->size > 0xFF)
846 BCI(PUSHW_1);
847 BCI(HIGH((hints_record + 1)->size));
848 BCI(LOW((hints_record + 1)->size));
850 else
852 BCI(PUSHB_1);
853 BCI((hints_record + 1)->size);
855 BCI(LT);
856 BCI(IF);
857 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
858 BCI(ELSE);
860 hints_record++;
863 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
865 for (i = 0; i < num_hints_records - 1; i++)
866 BCI(EIF);
868 BCI(PUSHB_1);
869 BCI(bci_hint_glyph);
870 BCI(CALL);
872 return bufp;
876 static void
877 TA_free_hints_records(Hints_Record* hints_records,
878 FT_UInt num_hints_records)
880 FT_UInt i;
883 for (i = 0; i < num_hints_records; i++)
884 free(hints_records[i].buf);
886 free(hints_records);
890 static FT_Byte*
891 TA_hints_recorder_handle_segments(FT_Byte* bufp,
892 TA_AxisHints axis,
893 TA_Edge edge,
894 FT_UInt* wraps)
896 TA_Segment segments = axis->segments;
897 TA_Segment seg;
898 FT_UInt seg_idx;
899 FT_UInt num_segs = 0;
900 FT_UInt* wrap;
903 seg_idx = edge->first - segments;
905 /* we store everything as 16bit numbers */
906 *(bufp++) = HIGH(seg_idx);
907 *(bufp++) = LOW(seg_idx);
909 /* wrap-around segments are stored as two segments */
910 if (edge->first->first > edge->first->last)
911 num_segs++;
913 seg = edge->first->edge_next;
914 while (seg != edge->first)
916 num_segs++;
918 if (seg->first > seg->last)
919 num_segs++;
921 seg = seg->edge_next;
924 *(bufp++) = HIGH(num_segs);
925 *(bufp++) = LOW(num_segs);
927 if (edge->first->first > edge->first->last)
929 /* emit second part of wrap-around segment; */
930 /* the bytecode positions such segments after `normal' ones */
931 wrap = wraps;
932 for (;;)
934 if (seg_idx == *wrap)
935 break;
936 wrap++;
939 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
940 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
943 seg = edge->first->edge_next;
944 while (seg != edge->first)
946 seg_idx = seg - segments;
948 *(bufp++) = HIGH(seg_idx);
949 *(bufp++) = LOW(seg_idx);
951 if (seg->first > seg->last)
953 wrap = wraps;
954 for (;;)
956 if (seg_idx == *wrap)
957 break;
958 wrap++;
961 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
962 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
965 seg = seg->edge_next;
968 return bufp;
972 static void
973 TA_hints_recorder(TA_Action action,
974 TA_GlyphHints hints,
975 TA_Dimension dim,
976 void* arg1,
977 TA_Edge arg2,
978 TA_Edge arg3,
979 TA_Edge lower_bound,
980 TA_Edge upper_bound)
982 TA_AxisHints axis = &hints->axis[dim];
983 TA_Edge edges = axis->edges;
984 TA_Segment segments = axis->segments;
985 TA_Point points = hints->points;
987 Recorder* recorder = (Recorder*)hints->user;
988 FONT* font = recorder->font;
989 FT_UInt* wraps = recorder->wrap_around_segments;
990 FT_Byte* p = recorder->hints_record.buf;
992 FT_Byte bound_offset = 0;
994 FT_UInt* ip;
995 FT_UInt* limit;
998 if (dim == TA_DIMENSION_HORZ)
999 return;
1001 /* we collect point hints for later processing */
1002 switch (action)
1004 case ta_ip_before:
1006 TA_Point point = (TA_Point)arg1;
1009 ip = recorder->ip_before_points;
1010 limit = ip + recorder->num_strong_points;
1011 for (; ip < limit; ip++)
1013 if (*ip == MISSING)
1015 *ip = point - points;
1016 break;
1020 return;
1022 case ta_ip_after:
1024 TA_Point point = (TA_Point)arg1;
1027 ip = recorder->ip_after_points;
1028 limit = ip + recorder->num_strong_points;
1029 for (; ip < limit; ip++)
1031 if (*ip == MISSING)
1033 *ip = point - points;
1034 break;
1038 return;
1040 case ta_ip_on:
1042 TA_Point point = (TA_Point)arg1;
1043 TA_Edge edge = arg2;
1046 ip = recorder->ip_on_point_array
1047 + recorder->num_strong_points
1048 * (edge - edges);
1049 limit = ip + recorder->num_strong_points;
1050 for (; ip < limit; ip++)
1052 if (*ip == MISSING)
1054 *ip = point - points;
1055 break;
1059 return;
1061 case ta_ip_between:
1063 TA_Point point = (TA_Point)arg1;
1064 TA_Edge before = arg2;
1065 TA_Edge after = arg3;
1068 /* note that `recorder->num_segments' has been used for allocation, */
1069 /* but `axis->num_edges' is used for accessing this array */
1070 ip = recorder->ip_between_point_array
1071 + recorder->num_strong_points * axis->num_edges
1072 * (before - edges)
1073 + recorder->num_strong_points
1074 * (after - edges);
1075 limit = ip + recorder->num_strong_points;
1076 for (; ip < limit; ip++)
1078 if (*ip == MISSING)
1080 *ip = point - points;
1081 break;
1085 return;
1087 case ta_bound:
1088 /* we ignore the BOUND action since we signal this information */
1089 /* with the `bound_offset' parameter below */
1090 return;
1092 default:
1093 break;
1096 if (lower_bound)
1097 bound_offset += 1;
1098 if (upper_bound)
1099 bound_offset += 2;
1101 /* this reflects the order in the TA_Action enumeration */
1102 *(p++) = 0;
1103 *(p++) = (FT_Byte)action + bound_offset + ACTION_OFFSET;
1105 switch (action)
1107 case ta_link:
1109 TA_Edge base_edge = (TA_Edge)arg1;
1110 TA_Edge stem_edge = arg2;
1113 *(p++) = 0;
1114 *(p++) = stem_edge->flags & TA_EDGE_SERIF;
1115 *(p++) = 0;
1116 *(p++) = base_edge->flags & TA_EDGE_ROUND;
1117 *(p++) = HIGH(base_edge->first - segments);
1118 *(p++) = LOW(base_edge->first - segments);
1119 *(p++) = HIGH(stem_edge->first - segments);
1120 *(p++) = LOW(stem_edge->first - segments);
1122 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
1124 break;
1126 case ta_anchor:
1128 TA_Edge edge = (TA_Edge)arg1;
1129 TA_Edge edge2 = arg2;
1132 *(p++) = 0;
1133 *(p++) = edge2->flags & TA_EDGE_SERIF;
1134 *(p++) = 0;
1135 *(p++) = edge->flags & TA_EDGE_ROUND;
1136 *(p++) = HIGH(edge->first - segments);
1137 *(p++) = LOW(edge->first - segments);
1138 *(p++) = HIGH(edge2->first - segments);
1139 *(p++) = LOW(edge2->first - segments);
1141 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1143 break;
1145 case ta_adjust:
1147 TA_Edge edge = (TA_Edge)arg1;
1148 TA_Edge edge2 = arg2;
1149 TA_Edge edge_minus_one = lower_bound;
1152 *(p++) = 0;
1153 *(p++) = edge2->flags & TA_EDGE_SERIF;
1154 *(p++) = 0;
1155 *(p++) = edge->flags & TA_EDGE_ROUND;
1156 *(p++) = HIGH(edge->first - segments);
1157 *(p++) = LOW(edge->first - segments);
1158 *(p++) = HIGH(edge2->first - segments);
1159 *(p++) = LOW(edge2->first - segments);
1161 if (edge_minus_one)
1163 *(p++) = HIGH(edge_minus_one->first - segments);
1164 *(p++) = LOW(edge_minus_one->first - segments);
1167 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1169 break;
1171 case ta_blue_anchor:
1173 TA_Edge edge = (TA_Edge)arg1;
1174 TA_Edge blue = arg2;
1177 *(p++) = HIGH(blue->first - segments);
1178 *(p++) = LOW(blue->first - segments);
1180 if (edge->best_blue_is_shoot)
1182 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1183 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1185 else
1187 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1188 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1191 *(p++) = HIGH(edge->first - segments);
1192 *(p++) = LOW(edge->first - segments);
1194 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1196 break;
1198 case ta_stem:
1200 TA_Edge edge = (TA_Edge)arg1;
1201 TA_Edge edge2 = arg2;
1202 TA_Edge edge_minus_one = lower_bound;
1205 *(p++) = 0;
1206 *(p++) = edge2->flags & TA_EDGE_SERIF;
1207 *(p++) = 0;
1208 *(p++) = edge->flags & TA_EDGE_ROUND;
1209 *(p++) = HIGH(edge->first - segments);
1210 *(p++) = LOW(edge->first - segments);
1211 *(p++) = HIGH(edge2->first - segments);
1212 *(p++) = LOW(edge2->first - segments);
1214 if (edge_minus_one)
1216 *(p++) = HIGH(edge_minus_one->first - segments);
1217 *(p++) = LOW(edge_minus_one->first - segments);
1220 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1221 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
1223 break;
1225 case ta_blue:
1227 TA_Edge edge = (TA_Edge)arg1;
1230 if (edge->best_blue_is_shoot)
1232 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1233 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1235 else
1237 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1238 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1241 *(p++) = HIGH(edge->first - segments);
1242 *(p++) = LOW(edge->first - segments);
1244 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1246 break;
1248 case ta_serif:
1250 TA_Edge serif = (TA_Edge)arg1;
1251 TA_Edge base = serif->serif;
1254 *(p++) = HIGH(serif->first - segments);
1255 *(p++) = LOW(serif->first - segments);
1256 *(p++) = HIGH(base->first - segments);
1257 *(p++) = LOW(base->first - segments);
1259 if (lower_bound)
1261 *(p++) = HIGH(lower_bound->first - segments);
1262 *(p++) = LOW(lower_bound->first - segments);
1264 if (upper_bound)
1266 *(p++) = HIGH(upper_bound->first - segments);
1267 *(p++) = LOW(upper_bound->first - segments);
1270 p = TA_hints_recorder_handle_segments(p, axis, serif, wraps);
1272 break;
1274 case ta_serif_anchor:
1275 case ta_serif_link2:
1277 TA_Edge edge = (TA_Edge)arg1;
1280 *(p++) = HIGH(edge->first - segments);
1281 *(p++) = LOW(edge->first - segments);
1283 if (lower_bound)
1285 *(p++) = HIGH(lower_bound->first - segments);
1286 *(p++) = LOW(lower_bound->first - segments);
1288 if (upper_bound)
1290 *(p++) = HIGH(upper_bound->first - segments);
1291 *(p++) = LOW(upper_bound->first - segments);
1294 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1296 break;
1298 case ta_serif_link1:
1300 TA_Edge edge = (TA_Edge)arg1;
1301 TA_Edge before = arg2;
1302 TA_Edge after = arg3;
1305 *(p++) = HIGH(before->first - segments);
1306 *(p++) = LOW(before->first - segments);
1307 *(p++) = HIGH(edge->first - segments);
1308 *(p++) = LOW(edge->first - segments);
1309 *(p++) = HIGH(after->first - segments);
1310 *(p++) = LOW(after->first - segments);
1312 if (lower_bound)
1314 *(p++) = HIGH(lower_bound->first - segments);
1315 *(p++) = LOW(lower_bound->first - segments);
1317 if (upper_bound)
1319 *(p++) = HIGH(upper_bound->first - segments);
1320 *(p++) = LOW(upper_bound->first - segments);
1323 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1325 break;
1327 default:
1328 /* there are more cases in the enumeration */
1329 /* which are handled with the `bound_offset' parameter */
1330 break;
1333 recorder->hints_record.num_actions++;
1334 recorder->hints_record.buf = p;
1338 static FT_Error
1339 TA_init_recorder(Recorder *recorder,
1340 FT_UInt wrap_around_size,
1341 FONT* font,
1342 TA_GlyphHints hints)
1344 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1345 TA_Point points = hints->points;
1346 TA_Point point_limit = points + hints->num_points;
1347 TA_Point point;
1349 FT_UInt num_strong_points = 0;
1352 recorder->font = font;
1353 recorder->num_segments = axis->num_segments;
1355 recorder->ip_before_points = NULL;
1356 recorder->ip_after_points = NULL;
1357 recorder->ip_on_point_array = NULL;
1358 recorder->ip_between_point_array = NULL;
1360 /* no need to clean up allocated arrays in case of error; */
1361 /* this is handled later by `TA_free_recorder' */
1363 recorder->wrap_around_segments =
1364 (FT_UInt*)malloc(wrap_around_size * sizeof (FT_UInt));
1365 if (!recorder->wrap_around_segments)
1366 return FT_Err_Out_Of_Memory;
1368 /* get number of strong points */
1369 for (point = points; point < point_limit; point++)
1371 /* actually, we need to test `TA_FLAG_TOUCH_Y' also; */
1372 /* however, this value isn't known yet */
1373 /* (or rather, it can vary between different pixel sizes) */
1374 if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1375 continue;
1377 num_strong_points++;
1380 recorder->num_strong_points = num_strong_points;
1382 recorder->ip_before_points =
1383 (FT_UInt*)malloc(num_strong_points * sizeof (FT_UInt));
1384 if (!recorder->ip_before_points)
1385 return FT_Err_Out_Of_Memory;
1387 recorder->ip_after_points =
1388 (FT_UInt*)malloc(num_strong_points * sizeof (FT_UInt));
1389 if (!recorder->ip_after_points)
1390 return FT_Err_Out_Of_Memory;
1392 /* actually, we need `hints->num_edges' for the array sizes; */
1393 /* however, this value isn't known yet */
1394 /* (or rather, it can vary between different pixel sizes) */
1395 recorder->ip_on_point_array =
1396 (FT_UInt*)malloc(axis->num_segments
1397 * num_strong_points * sizeof (FT_UInt));
1398 if (!recorder->ip_on_point_array)
1399 return FT_Err_Out_Of_Memory;
1401 recorder->ip_between_point_array =
1402 (FT_UInt*)malloc(axis->num_segments * axis->num_segments
1403 * num_strong_points * sizeof (FT_UInt));
1404 if (!recorder->ip_between_point_array)
1405 return FT_Err_Out_Of_Memory;
1407 return FT_Err_Ok;
1411 static void
1412 TA_rewind_recorder(Recorder* recorder,
1413 FT_Byte* bufp,
1414 FT_UInt size)
1416 recorder->hints_record.buf = bufp + 2;
1417 recorder->hints_record.num_actions = 0;
1418 recorder->hints_record.size = size;
1420 /* We later check with MISSING (which expands to 0xFF bytes) */
1422 memset(recorder->ip_before_points, 0xFF,
1423 recorder->num_strong_points * sizeof (FT_UInt));
1424 memset(recorder->ip_after_points, 0xFF,
1425 recorder->num_strong_points * sizeof (FT_UInt));
1427 memset(recorder->ip_on_point_array, 0xFF,
1428 recorder->num_segments
1429 * recorder->num_strong_points * sizeof (FT_UInt));
1430 memset(recorder->ip_between_point_array, 0xFF,
1431 recorder->num_segments * recorder->num_segments
1432 * recorder->num_strong_points * sizeof (FT_UInt));
1437 static void
1438 TA_free_recorder(Recorder *recorder)
1440 free(recorder->wrap_around_segments);
1442 free(recorder->ip_before_points);
1443 free(recorder->ip_after_points);
1444 free(recorder->ip_on_point_array);
1445 free(recorder->ip_between_point_array);
1449 static FT_Error
1450 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
1451 FONT* font,
1452 FT_Long idx)
1454 FT_Face face = sfnt->face;
1455 FT_Error error;
1457 FT_Byte* ins_buf;
1458 FT_UInt ins_len;
1459 FT_Byte* bufp;
1460 FT_Byte* p;
1462 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
1463 glyf_Data* data = (glyf_Data*)glyf_table->data;
1464 GLYPH* glyph = &data->glyphs[idx];
1466 TA_GlyphHints hints;
1468 FT_UInt num_hints_records;
1469 Hints_Record* hints_records;
1471 Recorder recorder;
1473 FT_UInt size;
1476 if (idx < 0)
1477 return FT_Err_Invalid_Argument;
1479 /* computing the segments is resolution independent, */
1480 /* thus the pixel size in this call is arbitrary */
1481 error = FT_Set_Pixel_Sizes(face, 20, 20);
1482 if (error)
1483 return error;
1485 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
1486 error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx,
1487 FT_LOAD_NO_RECURSE);
1488 if (error)
1489 return error;
1491 /* do nothing if we have an empty glyph */
1492 if (!face->glyph->outline.n_contours)
1493 return FT_Err_Ok;
1495 hints = &font->loader->hints;
1497 /* we allocate a buffer which is certainly large enough */
1498 /* to hold all of the created bytecode instructions; */
1499 /* later on it gets reallocated to its real size */
1500 ins_len = hints->num_points * 1000;
1501 ins_buf = (FT_Byte*)malloc(ins_len);
1502 if (!ins_buf)
1503 return FT_Err_Out_Of_Memory;
1505 /* initialize array with an invalid bytecode */
1506 /* so that we can easily find the array length at reallocation time */
1507 memset(ins_buf, INS_A0, ins_len);
1509 num_hints_records = 0;
1510 hints_records = NULL;
1512 /* handle composite glyph */
1513 if (font->loader->gloader->base.num_subglyphs)
1515 bufp = TA_font_build_subglyph_shifter(font, ins_buf);
1516 if (!bufp)
1518 error = FT_Err_Out_Of_Memory;
1519 goto Err;
1522 goto Done1;
1525 /* only scale the glyph if the dummy hinter has been used */
1526 if (font->loader->metrics->clazz == &ta_dummy_script_class)
1528 bufp = TA_sfnt_build_glyph_scaler(sfnt, ins_buf);
1529 if (!bufp)
1531 error = FT_Err_Out_Of_Memory;
1532 goto Err;
1535 goto Done1;
1538 error = TA_init_recorder(&recorder, face->glyph->outline.n_contours,
1539 font, hints);
1540 if (error)
1541 goto Err;
1543 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf);
1544 if (!bufp)
1546 error = FT_Err_Out_Of_Memory;
1547 goto Err;
1550 /* now we loop over a large range of pixel sizes */
1551 /* to find hints records which get pushed onto the bytecode stack */
1553 #ifdef DEBUGGING
1554 printf("glyph %ld\n", idx);
1555 #endif
1557 /* we temporarily use `ins_buf' to record the current glyph hints, */
1558 /* leaving two bytes at the beginning so that the number of actions */
1559 /* can be inserted later on */
1560 ta_loader_register_hints_recorder(font->loader,
1561 TA_hints_recorder,
1562 (void *)&recorder);
1564 for (size = font->hinting_range_min;
1565 size <= font->hinting_range_max;
1566 size++)
1568 TA_rewind_recorder(&recorder, bufp, size);
1570 error = FT_Set_Pixel_Sizes(face, size, size);
1571 if (error)
1572 goto Err;
1574 /* calling `ta_loader_load_glyph' uses the */
1575 /* `TA_hints_recorder' function as a callback, */
1576 /* modifying `hints_record' */
1577 error = ta_loader_load_glyph(font->loader, face, idx, 0);
1578 if (error)
1579 goto Err;
1581 /* append the point hints data collected in `TA_hints_recorder' */
1582 TA_build_point_hints(&recorder, hints);
1584 /* store the number of actions in `ins_buf' */
1585 *bufp = HIGH(recorder.hints_record.num_actions);
1586 *(bufp + 1) = LOW(recorder.hints_record.num_actions);
1588 if (TA_hints_record_is_different(hints_records,
1589 num_hints_records,
1590 bufp, recorder.hints_record.buf))
1592 #ifdef DEBUGGING
1594 printf(" %d:\n", size);
1595 for (p = bufp; p < recorder.hints_record.buf; p += 2)
1596 printf(" %2d", *p * 256 + *(p + 1));
1597 printf("\n");
1599 #endif
1601 error = TA_add_hints_record(&hints_records,
1602 &num_hints_records,
1603 bufp, recorder.hints_record);
1604 if (error)
1605 goto Err;
1609 if (num_hints_records == 1 && !hints_records[0].num_actions)
1611 /* since we only have a single empty record we just scale the glyph, */
1612 /* overwriting the data from `TA_sfnt_build_glyph_segments' */
1613 bufp = TA_sfnt_build_glyph_scaler(sfnt, ins_buf);
1614 if (!bufp)
1616 error = FT_Err_Out_Of_Memory;
1617 goto Err;
1620 /* clear the rest of the temporarily used part of `ins_buf' */
1621 p = bufp;
1622 while (*p != INS_A0)
1623 *(p++) = INS_A0;
1625 goto Done;
1628 /* in most cases, the output of `TA_sfnt_build_glyph_segments' */
1629 /* is shorter than the previously stored data, */
1630 /* so clear the rest of the temporarily used part of `ins_buf' */
1631 /* before appending the hints records */
1632 p = bufp;
1633 while (*p != INS_A0)
1634 *(p++) = INS_A0;
1636 bufp = TA_sfnt_emit_hints_records(sfnt,
1637 hints_records, num_hints_records,
1638 bufp);
1640 Done:
1641 TA_free_hints_records(hints_records, num_hints_records);
1642 TA_free_recorder(&recorder);
1644 /* we are done, so reallocate the instruction array to its real size */
1645 if (*bufp == INS_A0)
1647 /* search backwards */
1648 while (*bufp == INS_A0)
1649 bufp--;
1650 bufp++;
1652 else
1654 /* search forwards */
1655 while (*bufp != INS_A0)
1656 bufp++;
1659 Done1:
1660 ins_len = bufp - ins_buf;
1662 if (ins_len > sfnt->max_instructions)
1663 sfnt->max_instructions = ins_len;
1665 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
1666 glyph->ins_len = ins_len;
1668 return FT_Err_Ok;
1670 Err:
1671 TA_free_hints_records(hints_records, num_hints_records);
1672 TA_free_recorder(&recorder);
1673 free(ins_buf);
1675 return error;
1679 FT_Error
1680 TA_sfnt_build_glyf_hints(SFNT* sfnt,
1681 FONT* font)
1683 FT_Face face = sfnt->face;
1684 FT_Long idx;
1685 FT_Error error;
1688 for (idx = 0; idx < face->num_glyphs; idx++)
1690 error = TA_sfnt_build_glyph_instructions(sfnt, font, idx);
1691 if (error)
1692 return error;
1693 if (font->progress)
1694 font->progress(idx, face->num_glyphs,
1695 sfnt - font->sfnts, font->num_sfnts,
1696 font->progress_data);
1699 return FT_Err_Ok;
1702 /* end of tabytecode.c */