Add code to properly scale glyphs not handled by the autohinter.
[ttfautohint.git] / src / tabytecode.c
blobfb08ed616c43cb6930410b344c32fa8ba0dc716e
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;
250 * The four `ta_ip_*' actions in the `TA_hints_recorder' callback store its
251 * data in four arrays (which are simple but waste a lot of memory). The
252 * function below converts them into bytecode.
254 * For `ta_ip_before' and `ta_ip_after', the collected points are emitted
255 * together with the edge they correspond to.
257 * For both `ta_ip_on' and `ta_ip_between', an outer loop is constructed to
258 * loop over the edge or edge pairs, respectively, and each edge or edge
259 * pair contains an inner loop to emit the correponding points.
262 static void
263 TA_build_point_hints(Recorder* recorder,
264 TA_GlyphHints hints)
266 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
267 TA_Segment segments = axis->segments;
268 TA_Edge edges = axis->edges;
270 TA_Edge edge;
271 TA_Edge before;
272 TA_Edge after;
274 FT_Byte* p = recorder->hints_record.buf;
275 FT_UInt num_edges = axis->num_edges;
276 FT_UInt num_strong_points = recorder->num_strong_points;
278 FT_UInt i;
279 FT_UInt j;
280 FT_UInt k;
282 FT_UInt* ip;
283 FT_UInt* iq;
284 FT_UInt* ir;
285 FT_UInt* ip_limit;
286 FT_UInt* iq_limit;
287 FT_UInt* ir_limit;
290 /* we store everything as 16bit numbers; */
291 /* the function numbers (`ta_ip_before', etc.) */
292 /* reflect the order in the TA_Action enumeration */
294 /* ip_before_points */
296 i = 0;
297 ip = recorder->ip_before_points;
298 ip_limit = ip + num_strong_points;
299 for (; ip < ip_limit; ip++)
301 if (*ip != MISSING)
302 i++;
303 else
304 break;
307 if (i)
309 recorder->hints_record.num_actions++;
311 edge = edges;
313 *(p++) = 0;
314 *(p++) = (FT_Byte)ta_ip_before + ACTION_OFFSET;
315 *(p++) = HIGH(edge->first - segments);
316 *(p++) = LOW(edge->first - segments);
317 *(p++) = HIGH(i);
318 *(p++) = LOW(i);
320 ip = recorder->ip_before_points;
321 ip_limit = ip + i;
322 for (; ip < ip_limit; ip++)
324 *(p++) = HIGH(*ip);
325 *(p++) = LOW(*ip);
329 /* ip_after_points */
331 i = 0;
332 ip = recorder->ip_after_points;
333 ip_limit = ip + num_strong_points;
334 for (; ip < ip_limit; ip++)
336 if (*ip != MISSING)
337 i++;
338 else
339 break;
342 if (i)
344 recorder->hints_record.num_actions++;
346 edge = edges + axis->num_edges - 1;
348 *(p++) = 0;
349 *(p++) = (FT_Byte)ta_ip_after + ACTION_OFFSET;
350 *(p++) = HIGH(edge->first - segments);
351 *(p++) = LOW(edge->first - segments);
352 *(p++) = HIGH(i);
353 *(p++) = LOW(i);
355 ip = recorder->ip_after_points;
356 ip_limit = ip + i;
357 for (; ip < ip_limit; ip++)
359 *(p++) = HIGH(*ip);
360 *(p++) = LOW(*ip);
364 /* ip_on_point_array */
366 i = 0;
367 ip = recorder->ip_on_point_array;
368 ip_limit = ip + num_edges * num_strong_points;
369 for (; ip < ip_limit; ip += num_strong_points)
370 if (*ip != MISSING)
371 i++;
373 if (i)
375 recorder->hints_record.num_actions++;
377 *(p++) = 0;
378 *(p++) = (FT_Byte)ta_ip_on + ACTION_OFFSET;
379 *(p++) = HIGH(i);
380 *(p++) = LOW(i);
382 i = 0;
383 ip = recorder->ip_on_point_array;
384 ip_limit = ip + num_edges * num_strong_points;
385 for (; ip < ip_limit; ip += num_strong_points, i++)
387 if (*ip == MISSING)
388 continue;
390 edge = edges + i;
392 *(p++) = HIGH(edge->first - segments);
393 *(p++) = LOW(edge->first - segments);
395 j = 0;
396 iq = ip;
397 iq_limit = iq + num_strong_points;
398 for (; iq < iq_limit; iq++)
400 if (*iq != MISSING)
401 j++;
402 else
403 break;
406 *(p++) = HIGH(j);
407 *(p++) = LOW(j);
409 iq = ip;
410 iq_limit = iq + j;
411 for (; iq < iq_limit; iq++)
413 *(p++) = HIGH(*iq);
414 *(p++) = LOW(*iq);
419 /* ip_between_point_array */
421 i = 0;
422 ip = recorder->ip_between_point_array;
423 ip_limit = ip + num_edges * num_edges * num_strong_points;
424 for (; ip < ip_limit; ip += num_strong_points)
425 if (*ip != MISSING)
426 i++;
428 if (i)
430 recorder->hints_record.num_actions++;
432 *(p++) = 0;
433 *(p++) = (FT_Byte)ta_ip_between + ACTION_OFFSET;
434 *(p++) = HIGH(i);
435 *(p++) = LOW(i);
437 i = 0;
438 ip = recorder->ip_between_point_array;
439 ip_limit = ip + num_edges * num_edges * num_strong_points;
440 for (;
441 ip < ip_limit;
442 ip += num_edges * num_strong_points, i++)
444 before = edges + i;
446 j = 0;
447 iq = ip;
448 iq_limit = iq + num_edges * num_strong_points;
449 for (; iq < iq_limit; iq += num_strong_points, j++)
451 if (*iq == MISSING)
452 continue;
454 after = edges + j;
456 *(p++) = HIGH(after->first - segments);
457 *(p++) = LOW(after->first - segments);
458 *(p++) = HIGH(before->first - segments);
459 *(p++) = LOW(before->first - segments);
461 k = 0;
462 ir = iq;
463 ir_limit = ir + num_strong_points;
464 for (; ir < ir_limit; ir++)
466 if (*ir != MISSING)
467 k++;
468 else
469 break;
472 *(p++) = HIGH(k);
473 *(p++) = LOW(k);
475 ir = iq;
476 ir_limit = ir + k;
477 for (; ir < ir_limit; ir++)
479 *(p++) = HIGH(*ir);
480 *(p++) = LOW(*ir);
486 recorder->hints_record.buf = p;
490 static FT_Bool
491 TA_hints_record_is_different(Hints_Record* hints_records,
492 FT_UInt num_hints_records,
493 FT_Byte* start,
494 FT_Byte* end)
496 Hints_Record last_hints_record;
499 if (!hints_records)
500 return 1;
502 /* we only need to compare with the last hints record */
503 last_hints_record = hints_records[num_hints_records - 1];
505 if ((FT_UInt)(end - start) != last_hints_record.buf_len)
506 return 1;
508 if (memcmp(start, last_hints_record.buf, last_hints_record.buf_len))
509 return 1;
511 return 0;
515 static FT_Error
516 TA_add_hints_record(Hints_Record** hints_records,
517 FT_UInt* num_hints_records,
518 FT_Byte* start,
519 Hints_Record hints_record)
521 Hints_Record* hints_records_new;
522 FT_UInt buf_len;
523 /* at this point, `hints_record.buf' still points into `ins_buf' */
524 FT_Byte* end = hints_record.buf;
527 buf_len = (FT_UInt)(end - start);
529 /* now fill the structure completely */
530 hints_record.buf_len = buf_len;
531 hints_record.buf = (FT_Byte*)malloc(buf_len);
532 if (!hints_record.buf)
533 return FT_Err_Out_Of_Memory;
535 memcpy(hints_record.buf, start, buf_len);
537 (*num_hints_records)++;
538 hints_records_new =
539 (Hints_Record*)realloc(*hints_records, *num_hints_records
540 * sizeof (Hints_Record));
541 if (!hints_records_new)
543 free(hints_record.buf);
544 (*num_hints_records)--;
545 return FT_Err_Out_Of_Memory;
547 else
548 *hints_records = hints_records_new;
550 (*hints_records)[*num_hints_records - 1] = hints_record;
552 return FT_Err_Ok;
556 static FT_Byte*
557 TA_sfnt_build_glyph_scaler(SFNT* sfnt,
558 FT_Byte* bufp)
560 FT_GlyphSlot glyph = sfnt->face->glyph;
561 FT_Vector* points = glyph->outline.points;
562 FT_Int num_contours = glyph->outline.n_contours;
564 FT_UInt* args;
565 FT_UInt* arg;
566 FT_UInt num_args;
567 FT_UInt nargs;
569 FT_Bool need_words = 0;
570 FT_Int p, q;
571 FT_UInt i, j;
572 FT_Int start, end;
573 FT_UInt num_stack_elements;
575 num_args = 2 * num_contours + 2;
577 /* collect all arguments temporarily in an array (in reverse order) */
578 /* so that we can easily split into chunks of 255 args */
579 /* as needed by NPUSHB and NPUSHW, respectively */
580 args = (FT_UInt*)malloc(num_args * sizeof (FT_UInt));
581 if (!args)
582 return NULL;
584 arg = args + num_args - 1;
586 if (num_args > 0xFF)
587 need_words = 1;
589 *(arg--) = bci_scale_glyph;
590 *(arg--) = num_contours;
592 start = 0;
594 for (p = 0; p < num_contours; p++)
596 FT_Int max = start;
597 FT_Int min = start;
599 end = glyph->outline.contours[p];
601 for (q = start; q <= end; q++)
603 if (points[q].y < points[min].y)
604 min = q;
605 if (points[q].y > points[max].y)
606 max = q;
609 *(arg--) = min;
610 *(arg--) = max;
612 start = end + 1;
615 if (end > 0xFF)
616 need_words = 1;
618 /* with most fonts it is very rare */
619 /* that any of the pushed arguments is larger than 0xFF, */
620 /* thus we refrain from further optimizing this case */
622 arg = args;
624 if (need_words)
626 for (i = 0; i < num_args; i += 255)
628 nargs = (num_args - i > 255) ? 255 : num_args - i;
630 BCI(NPUSHW);
631 BCI(nargs);
632 for (j = 0; j < nargs; j++)
634 BCI(HIGH(*arg));
635 BCI(LOW(*arg));
636 arg++;
640 else
642 for (i = 0; i < num_args; i += 255)
644 nargs = (num_args - i > 255) ? 255 : num_args - i;
646 BCI(NPUSHB);
647 BCI(nargs);
648 for (j = 0; j < nargs; j++)
650 BCI(*arg);
651 arg++;
656 BCI(CALL);
658 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_args;
659 if (num_stack_elements > sfnt->max_stack_elements)
660 sfnt->max_stack_elements = num_stack_elements;
662 free(args);
664 return bufp;
668 static FT_Byte*
669 TA_sfnt_emit_hints_record(SFNT* sfnt,
670 Hints_Record* hints_record,
671 FT_Byte* bufp)
673 FT_Byte* p;
674 FT_Byte* endp;
675 FT_Bool need_words = 0;
677 FT_UInt i, j;
678 FT_UInt num_arguments;
679 FT_UInt num_args;
680 FT_UInt num_stack_elements;
683 /* check whether any argument is larger than 0xFF */
684 endp = hints_record->buf + hints_record->buf_len;
685 for (p = hints_record->buf; p < endp; p += 2)
686 if (*p)
687 need_words = 1;
689 /* with most fonts it is very rare */
690 /* that any of the pushed arguments is larger than 0xFF, */
691 /* thus we refrain from further optimizing this case */
693 num_arguments = hints_record->buf_len / 2;
694 p = endp - 2;
696 if (need_words)
698 for (i = 0; i < num_arguments; i += 255)
700 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
702 BCI(NPUSHW);
703 BCI(num_args);
704 for (j = 0; j < num_args; j++)
706 BCI(*p);
707 BCI(*(p + 1));
708 p -= 2;
712 else
714 /* we only need the lower bytes */
715 p++;
717 for (i = 0; i < num_arguments; i += 255)
719 num_args = (num_arguments - i > 255) ? 255 : (num_arguments - i);
721 BCI(NPUSHB);
722 BCI(num_args);
723 for (j = 0; j < num_args; j++)
725 BCI(*p);
726 p -= 2;
731 num_stack_elements = ADDITIONAL_STACK_ELEMENTS + num_arguments;
732 if (num_stack_elements > sfnt->max_stack_elements)
733 sfnt->max_stack_elements = num_stack_elements;
735 return bufp;
739 static FT_Byte*
740 TA_sfnt_emit_hints_records(SFNT* sfnt,
741 Hints_Record* hints_records,
742 FT_UInt num_hints_records,
743 FT_Byte* bufp)
745 FT_UInt i;
746 Hints_Record* hints_record;
749 hints_record = hints_records;
751 for (i = 0; i < num_hints_records - 1; i++)
753 BCI(MPPEM);
754 if (hints_record->size > 0xFF)
756 BCI(PUSHW_1);
757 BCI(HIGH((hints_record + 1)->size));
758 BCI(LOW((hints_record + 1)->size));
760 else
762 BCI(PUSHB_1);
763 BCI((hints_record + 1)->size);
765 BCI(LT);
766 BCI(IF);
767 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
768 BCI(ELSE);
770 hints_record++;
773 bufp = TA_sfnt_emit_hints_record(sfnt, hints_record, bufp);
775 for (i = 0; i < num_hints_records - 1; i++)
776 BCI(EIF);
778 BCI(PUSHB_1);
779 BCI(bci_hint_glyph);
780 BCI(CALL);
782 return bufp;
786 static void
787 TA_free_hints_records(Hints_Record* hints_records,
788 FT_UInt num_hints_records)
790 FT_UInt i;
793 for (i = 0; i < num_hints_records; i++)
794 free(hints_records[i].buf);
796 free(hints_records);
800 static FT_Byte*
801 TA_hints_recorder_handle_segments(FT_Byte* bufp,
802 TA_AxisHints axis,
803 TA_Edge edge,
804 FT_UInt* wraps)
806 TA_Segment segments = axis->segments;
807 TA_Segment seg;
808 FT_UInt seg_idx;
809 FT_UInt num_segs = 0;
810 FT_UInt* wrap;
813 seg_idx = edge->first - segments;
815 /* we store everything as 16bit numbers */
816 *(bufp++) = HIGH(seg_idx);
817 *(bufp++) = LOW(seg_idx);
819 /* wrap-around segments are stored as two segments */
820 if (edge->first->first > edge->first->last)
821 num_segs++;
823 seg = edge->first->edge_next;
824 while (seg != edge->first)
826 num_segs++;
828 if (seg->first > seg->last)
829 num_segs++;
831 seg = seg->edge_next;
834 *(bufp++) = HIGH(num_segs);
835 *(bufp++) = LOW(num_segs);
837 if (edge->first->first > edge->first->last)
839 /* emit second part of wrap-around segment; */
840 /* the bytecode positions such segments after `normal' ones */
841 wrap = wraps;
842 for (;;)
844 if (seg_idx == *wrap)
845 break;
846 wrap++;
849 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
850 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
853 seg = edge->first->edge_next;
854 while (seg != edge->first)
856 seg_idx = seg - segments;
858 *(bufp++) = HIGH(seg_idx);
859 *(bufp++) = LOW(seg_idx);
861 if (seg->first > seg->last)
863 wrap = wraps;
864 for (;;)
866 if (seg_idx == *wrap)
867 break;
868 wrap++;
871 *(bufp++) = HIGH(axis->num_segments + (wrap - wraps));
872 *(bufp++) = LOW(axis->num_segments + (wrap - wraps));
875 seg = seg->edge_next;
878 return bufp;
882 static void
883 TA_hints_recorder(TA_Action action,
884 TA_GlyphHints hints,
885 TA_Dimension dim,
886 void* arg1,
887 TA_Edge arg2,
888 TA_Edge arg3,
889 TA_Edge lower_bound,
890 TA_Edge upper_bound)
892 TA_AxisHints axis = &hints->axis[dim];
893 TA_Edge edges = axis->edges;
894 TA_Segment segments = axis->segments;
895 TA_Point points = hints->points;
897 Recorder* recorder = (Recorder*)hints->user;
898 FONT* font = recorder->font;
899 FT_UInt* wraps = recorder->wrap_around_segments;
900 FT_Byte* p = recorder->hints_record.buf;
902 FT_Byte bound_offset = 0;
904 FT_UInt* ip;
905 FT_UInt* limit;
908 if (dim == TA_DIMENSION_HORZ)
909 return;
911 /* we collect point hints for later processing */
912 switch (action)
914 case ta_ip_before:
916 TA_Point point = (TA_Point)arg1;
919 ip = recorder->ip_before_points;
920 limit = ip + recorder->num_strong_points;
921 for (; ip < limit; ip++)
923 if (*ip == MISSING)
925 *ip = point - points;
926 break;
930 return;
932 case ta_ip_after:
934 TA_Point point = (TA_Point)arg1;
937 ip = recorder->ip_after_points;
938 limit = ip + recorder->num_strong_points;
939 for (; ip < limit; ip++)
941 if (*ip == MISSING)
943 *ip = point - points;
944 break;
948 return;
950 case ta_ip_on:
952 TA_Point point = (TA_Point)arg1;
953 TA_Edge edge = arg2;
956 ip = recorder->ip_on_point_array
957 + recorder->num_strong_points
958 * (edge - edges);
959 limit = ip + recorder->num_strong_points;
960 for (; ip < limit; ip++)
962 if (*ip == MISSING)
964 *ip = point - points;
965 break;
969 return;
971 case ta_ip_between:
973 TA_Point point = (TA_Point)arg1;
974 TA_Edge before = arg2;
975 TA_Edge after = arg3;
978 /* note that `recorder->num_segments' has been used for allocation, */
979 /* but `axis->num_edges' is used for accessing this array */
980 ip = recorder->ip_between_point_array
981 + recorder->num_strong_points * axis->num_edges
982 * (before - edges)
983 + recorder->num_strong_points
984 * (after - edges);
985 limit = ip + recorder->num_strong_points;
986 for (; ip < limit; ip++)
988 if (*ip == MISSING)
990 *ip = point - points;
991 break;
995 return;
997 case ta_bound:
998 /* we ignore the BOUND action since we signal this information */
999 /* with the `bound_offset' parameter below */
1000 return;
1002 default:
1003 break;
1006 if (lower_bound)
1007 bound_offset += 1;
1008 if (upper_bound)
1009 bound_offset += 2;
1011 /* this reflects the order in the TA_Action enumeration */
1012 *(p++) = 0;
1013 *(p++) = (FT_Byte)action + bound_offset + ACTION_OFFSET;
1015 switch (action)
1017 case ta_link:
1019 TA_Edge base_edge = (TA_Edge)arg1;
1020 TA_Edge stem_edge = arg2;
1023 *(p++) = 0;
1024 *(p++) = stem_edge->flags & TA_EDGE_SERIF;
1025 *(p++) = 0;
1026 *(p++) = base_edge->flags & TA_EDGE_ROUND;
1027 *(p++) = HIGH(base_edge->first - segments);
1028 *(p++) = LOW(base_edge->first - segments);
1029 *(p++) = HIGH(stem_edge->first - segments);
1030 *(p++) = LOW(stem_edge->first - segments);
1032 p = TA_hints_recorder_handle_segments(p, axis, stem_edge, wraps);
1034 break;
1036 case ta_anchor:
1038 TA_Edge edge = (TA_Edge)arg1;
1039 TA_Edge edge2 = arg2;
1042 *(p++) = 0;
1043 *(p++) = edge2->flags & TA_EDGE_SERIF;
1044 *(p++) = 0;
1045 *(p++) = edge->flags & TA_EDGE_ROUND;
1046 *(p++) = HIGH(edge->first - segments);
1047 *(p++) = LOW(edge->first - segments);
1048 *(p++) = HIGH(edge2->first - segments);
1049 *(p++) = LOW(edge2->first - segments);
1051 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1053 break;
1055 case ta_adjust:
1057 TA_Edge edge = (TA_Edge)arg1;
1058 TA_Edge edge2 = arg2;
1059 TA_Edge edge_minus_one = lower_bound;
1062 *(p++) = 0;
1063 *(p++) = edge2->flags & TA_EDGE_SERIF;
1064 *(p++) = 0;
1065 *(p++) = edge->flags & TA_EDGE_ROUND;
1066 *(p++) = HIGH(edge->first - segments);
1067 *(p++) = LOW(edge->first - segments);
1068 *(p++) = HIGH(edge2->first - segments);
1069 *(p++) = LOW(edge2->first - segments);
1071 if (edge_minus_one)
1073 *(p++) = HIGH(edge_minus_one->first - segments);
1074 *(p++) = LOW(edge_minus_one->first - segments);
1077 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1079 break;
1081 case ta_blue_anchor:
1083 TA_Edge edge = (TA_Edge)arg1;
1084 TA_Edge blue = arg2;
1087 *(p++) = HIGH(blue->first - segments);
1088 *(p++) = LOW(blue->first - segments);
1090 if (edge->best_blue_is_shoot)
1092 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1093 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1095 else
1097 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1098 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1101 *(p++) = HIGH(edge->first - segments);
1102 *(p++) = LOW(edge->first - segments);
1104 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1106 break;
1108 case ta_stem:
1110 TA_Edge edge = (TA_Edge)arg1;
1111 TA_Edge edge2 = arg2;
1112 TA_Edge edge_minus_one = lower_bound;
1115 *(p++) = 0;
1116 *(p++) = edge2->flags & TA_EDGE_SERIF;
1117 *(p++) = 0;
1118 *(p++) = edge->flags & TA_EDGE_ROUND;
1119 *(p++) = HIGH(edge->first - segments);
1120 *(p++) = LOW(edge->first - segments);
1121 *(p++) = HIGH(edge2->first - segments);
1122 *(p++) = LOW(edge2->first - segments);
1124 if (edge_minus_one)
1126 *(p++) = HIGH(edge_minus_one->first - segments);
1127 *(p++) = LOW(edge_minus_one->first - segments);
1130 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1131 p = TA_hints_recorder_handle_segments(p, axis, edge2, wraps);
1133 break;
1135 case ta_blue:
1137 TA_Edge edge = (TA_Edge)arg1;
1140 if (edge->best_blue_is_shoot)
1142 *(p++) = HIGH(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1143 *(p++) = LOW(CVT_BLUE_SHOOTS_OFFSET(font) + edge->best_blue_idx);
1145 else
1147 *(p++) = HIGH(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1148 *(p++) = LOW(CVT_BLUE_REFS_OFFSET(font) + edge->best_blue_idx);
1151 *(p++) = HIGH(edge->first - segments);
1152 *(p++) = LOW(edge->first - segments);
1154 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1156 break;
1158 case ta_serif:
1160 TA_Edge serif = (TA_Edge)arg1;
1161 TA_Edge base = serif->serif;
1164 *(p++) = HIGH(serif->first - segments);
1165 *(p++) = LOW(serif->first - segments);
1166 *(p++) = HIGH(base->first - segments);
1167 *(p++) = LOW(base->first - segments);
1169 if (lower_bound)
1171 *(p++) = HIGH(lower_bound->first - segments);
1172 *(p++) = LOW(lower_bound->first - segments);
1174 if (upper_bound)
1176 *(p++) = HIGH(upper_bound->first - segments);
1177 *(p++) = LOW(upper_bound->first - segments);
1180 p = TA_hints_recorder_handle_segments(p, axis, serif, wraps);
1182 break;
1184 case ta_serif_anchor:
1185 case ta_serif_link2:
1187 TA_Edge edge = (TA_Edge)arg1;
1190 *(p++) = HIGH(edge->first - segments);
1191 *(p++) = LOW(edge->first - segments);
1193 if (lower_bound)
1195 *(p++) = HIGH(lower_bound->first - segments);
1196 *(p++) = LOW(lower_bound->first - segments);
1198 if (upper_bound)
1200 *(p++) = HIGH(upper_bound->first - segments);
1201 *(p++) = LOW(upper_bound->first - segments);
1204 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1206 break;
1208 case ta_serif_link1:
1210 TA_Edge edge = (TA_Edge)arg1;
1211 TA_Edge before = arg2;
1212 TA_Edge after = arg3;
1215 *(p++) = HIGH(before->first - segments);
1216 *(p++) = LOW(before->first - segments);
1217 *(p++) = HIGH(edge->first - segments);
1218 *(p++) = LOW(edge->first - segments);
1219 *(p++) = HIGH(after->first - segments);
1220 *(p++) = LOW(after->first - segments);
1222 if (lower_bound)
1224 *(p++) = HIGH(lower_bound->first - segments);
1225 *(p++) = LOW(lower_bound->first - segments);
1227 if (upper_bound)
1229 *(p++) = HIGH(upper_bound->first - segments);
1230 *(p++) = LOW(upper_bound->first - segments);
1233 p = TA_hints_recorder_handle_segments(p, axis, edge, wraps);
1235 break;
1237 default:
1238 /* there are more cases in the enumeration */
1239 /* which are handled with the `bound_offset' parameter */
1240 break;
1243 recorder->hints_record.num_actions++;
1244 recorder->hints_record.buf = p;
1248 static FT_Error
1249 TA_init_recorder(Recorder *recorder,
1250 FT_UInt wrap_around_size,
1251 FONT* font,
1252 TA_GlyphHints hints)
1254 TA_AxisHints axis = &hints->axis[TA_DIMENSION_VERT];
1255 TA_Point points = hints->points;
1256 TA_Point point_limit = points + hints->num_points;
1257 TA_Point point;
1259 FT_UInt num_strong_points = 0;
1262 recorder->font = font;
1263 recorder->num_segments = axis->num_segments;
1265 recorder->ip_before_points = NULL;
1266 recorder->ip_after_points = NULL;
1267 recorder->ip_on_point_array = NULL;
1268 recorder->ip_between_point_array = NULL;
1270 /* no need to clean up allocated arrays in case of error; */
1271 /* this is handled later by `TA_free_recorder' */
1273 recorder->wrap_around_segments =
1274 (FT_UInt*)malloc(wrap_around_size * sizeof (FT_UInt));
1275 if (!recorder->wrap_around_segments)
1276 return FT_Err_Out_Of_Memory;
1278 /* get number of strong points */
1279 for (point = points; point < point_limit; point++)
1281 /* actually, we need to test `TA_FLAG_TOUCH_Y' also; */
1282 /* however, this value isn't known yet */
1283 /* (or rather, it can vary between different pixel sizes) */
1284 if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1285 continue;
1287 num_strong_points++;
1290 recorder->num_strong_points = num_strong_points;
1292 recorder->ip_before_points =
1293 (FT_UInt*)malloc(num_strong_points * sizeof (FT_UInt));
1294 if (!recorder->ip_before_points)
1295 return FT_Err_Out_Of_Memory;
1297 recorder->ip_after_points =
1298 (FT_UInt*)malloc(num_strong_points * sizeof (FT_UInt));
1299 if (!recorder->ip_after_points)
1300 return FT_Err_Out_Of_Memory;
1302 /* actually, we need `hints->num_edges' for the array sizes; */
1303 /* however, this value isn't known yet */
1304 /* (or rather, it can vary between different pixel sizes) */
1305 recorder->ip_on_point_array =
1306 (FT_UInt*)malloc(axis->num_segments
1307 * num_strong_points * sizeof (FT_UInt));
1308 if (!recorder->ip_on_point_array)
1309 return FT_Err_Out_Of_Memory;
1311 recorder->ip_between_point_array =
1312 (FT_UInt*)malloc(axis->num_segments * axis->num_segments
1313 * num_strong_points * sizeof (FT_UInt));
1314 if (!recorder->ip_between_point_array)
1315 return FT_Err_Out_Of_Memory;
1317 return FT_Err_Ok;
1321 static void
1322 TA_rewind_recorder(Recorder* recorder,
1323 FT_Byte* bufp,
1324 FT_UInt size)
1326 recorder->hints_record.buf = bufp + 2;
1327 recorder->hints_record.num_actions = 0;
1328 recorder->hints_record.size = size;
1330 /* We later check with MISSING (which expands to 0xFF bytes) */
1332 memset(recorder->ip_before_points, 0xFF,
1333 recorder->num_strong_points * sizeof (FT_UInt));
1334 memset(recorder->ip_after_points, 0xFF,
1335 recorder->num_strong_points * sizeof (FT_UInt));
1337 memset(recorder->ip_on_point_array, 0xFF,
1338 recorder->num_segments
1339 * recorder->num_strong_points * sizeof (FT_UInt));
1340 memset(recorder->ip_between_point_array, 0xFF,
1341 recorder->num_segments * recorder->num_segments
1342 * recorder->num_strong_points * sizeof (FT_UInt));
1347 static void
1348 TA_free_recorder(Recorder *recorder)
1350 free(recorder->wrap_around_segments);
1352 free(recorder->ip_before_points);
1353 free(recorder->ip_after_points);
1354 free(recorder->ip_on_point_array);
1355 free(recorder->ip_between_point_array);
1359 static FT_Error
1360 TA_sfnt_build_glyph_instructions(SFNT* sfnt,
1361 FONT* font,
1362 FT_Long idx)
1364 FT_Face face = sfnt->face;
1365 FT_Error error;
1367 FT_Byte* ins_buf;
1368 FT_UInt ins_len;
1369 FT_Byte* bufp;
1370 FT_Byte* p;
1372 SFNT_Table* glyf_table = &font->tables[sfnt->glyf_idx];
1373 glyf_Data* data = (glyf_Data*)glyf_table->data;
1374 GLYPH* glyph = &data->glyphs[idx];
1376 TA_GlyphHints hints;
1378 FT_UInt num_hints_records;
1379 Hints_Record* hints_records;
1381 Recorder recorder;
1383 FT_UInt size;
1386 if (idx < 0)
1387 return FT_Err_Invalid_Argument;
1389 /* computing the segments is resolution independent, */
1390 /* thus the pixel size in this call is arbitrary */
1391 error = FT_Set_Pixel_Sizes(face, 20, 20);
1392 if (error)
1393 return error;
1395 ta_loader_register_hints_recorder(font->loader, NULL, NULL);
1396 error = ta_loader_load_glyph(font->loader, face, (FT_UInt)idx,
1397 FT_LOAD_NO_RECURSE);
1398 if (error)
1399 return error;
1401 /* do nothing if we have an empty glyph */
1402 if (!face->glyph->outline.n_contours)
1403 return FT_Err_Ok;
1405 hints = &font->loader->hints;
1407 /* we allocate a buffer which is certainly large enough */
1408 /* to hold all of the created bytecode instructions; */
1409 /* later on it gets reallocated to its real size */
1410 ins_len = hints->num_points * 1000;
1411 ins_buf = (FT_Byte*)malloc(ins_len);
1412 if (!ins_buf)
1413 return FT_Err_Out_Of_Memory;
1415 /* initialize array with an invalid bytecode */
1416 /* so that we can easily find the array length at reallocation time */
1417 memset(ins_buf, INS_A0, ins_len);
1419 num_hints_records = 0;
1420 hints_records = NULL;
1422 /* do XXX if we have a composite glyph */
1423 if (font->loader->gloader->base.num_subglyphs)
1425 bufp = ins_buf;
1426 goto Done2; /* XXX */
1429 /* only scale the glyph if the dummy hinter has been used */
1430 if (font->loader->metrics->clazz == &ta_dummy_script_class)
1432 bufp = TA_sfnt_build_glyph_scaler(sfnt, ins_buf);
1433 if (!bufp)
1435 error = FT_Err_Out_Of_Memory;
1436 goto Err;
1439 goto Done1;
1442 error = TA_init_recorder(&recorder, face->glyph->outline.n_contours,
1443 font, hints);
1444 if (error)
1445 goto Err;
1447 bufp = TA_sfnt_build_glyph_segments(sfnt, &recorder, ins_buf);
1448 if (!bufp)
1450 error = FT_Err_Out_Of_Memory;
1451 goto Err;
1454 /* now we loop over a large range of pixel sizes */
1455 /* to find hints records which get pushed onto the bytecode stack */
1457 #ifdef DEBUGGING
1458 printf("glyph %ld\n", idx);
1459 #endif
1461 /* we temporarily use `ins_buf' to record the current glyph hints, */
1462 /* leaving two bytes at the beginning so that the number of actions */
1463 /* can be inserted later on */
1464 ta_loader_register_hints_recorder(font->loader,
1465 TA_hints_recorder,
1466 (void *)&recorder);
1468 for (size = 8; size <= 1000; size++)
1470 TA_rewind_recorder(&recorder, bufp, size);
1472 error = FT_Set_Pixel_Sizes(face, size, size);
1473 if (error)
1474 goto Err;
1476 /* calling `ta_loader_load_glyph' uses the */
1477 /* `TA_hints_recorder' function as a callback, */
1478 /* modifying `hints_record' */
1479 error = ta_loader_load_glyph(font->loader, face, idx, 0);
1480 if (error)
1481 goto Err;
1483 /* append the point hints data collected in `TA_hints_recorder' */
1484 TA_build_point_hints(&recorder, hints);
1486 /* store the number of actions in `ins_buf' */
1487 *bufp = HIGH(recorder.hints_record.num_actions);
1488 *(bufp + 1) = LOW(recorder.hints_record.num_actions);
1490 if (TA_hints_record_is_different(hints_records,
1491 num_hints_records,
1492 bufp, recorder.hints_record.buf))
1494 #ifdef DEBUGGING
1496 printf(" %d:\n", size);
1497 for (p = bufp; p < recorder.hints_record.buf; p += 2)
1498 printf(" %2d", *p * 256 + *(p + 1));
1499 printf("\n");
1501 #endif
1503 error = TA_add_hints_record(&hints_records,
1504 &num_hints_records,
1505 bufp, recorder.hints_record);
1506 if (error)
1507 goto Err;
1511 if (num_hints_records == 1 && !hints_records[0].num_actions)
1513 /* since we only have a single empty record we just scale the glyph, */
1514 /* overwriting the data from `TA_sfnt_build_glyph_segments' */
1515 bufp = TA_sfnt_build_glyph_scaler(sfnt, ins_buf);
1516 if (!bufp)
1518 error = FT_Err_Out_Of_Memory;
1519 goto Err;
1522 /* clear the rest of the temporarily used part of `ins_buf' */
1523 p = bufp;
1524 while (*p != INS_A0)
1525 *(p++) = INS_A0;
1527 goto Done;
1530 /* in most cases, the output of `TA_sfnt_build_glyph_segments' */
1531 /* is shorter than the previously stored data, */
1532 /* so clear the rest of the temporarily used part of `ins_buf' */
1533 /* before appending the hints records */
1534 p = bufp;
1535 while (*p != INS_A0)
1536 *(p++) = INS_A0;
1538 bufp = TA_sfnt_emit_hints_records(sfnt,
1539 hints_records, num_hints_records,
1540 bufp);
1542 Done:
1543 TA_free_hints_records(hints_records, num_hints_records);
1544 TA_free_recorder(&recorder);
1546 Done1:
1547 /* we are done, so reallocate the instruction array to its real size */
1548 if (*bufp == INS_A0)
1550 /* search backwards */
1551 while (*bufp == INS_A0)
1552 bufp--;
1553 bufp++;
1555 else
1557 /* search forwards */
1558 while (*bufp != INS_A0)
1559 bufp++;
1562 Done2:
1563 ins_len = bufp - ins_buf;
1565 if (ins_len > sfnt->max_instructions)
1566 sfnt->max_instructions = ins_len;
1568 glyph->ins_buf = (FT_Byte*)realloc(ins_buf, ins_len);
1569 glyph->ins_len = ins_len;
1571 return FT_Err_Ok;
1573 Err:
1574 TA_free_hints_records(hints_records, num_hints_records);
1575 TA_free_recorder(&recorder);
1576 free(ins_buf);
1578 return error;
1582 FT_Error
1583 TA_sfnt_build_glyf_hints(SFNT* sfnt,
1584 FONT* font)
1586 FT_Face face = sfnt->face;
1587 FT_Long idx;
1588 FT_Error error;
1591 for (idx = 0; idx < face->num_glyphs; idx++)
1593 error = TA_sfnt_build_glyph_instructions(sfnt, font, idx);
1594 if (error)
1595 return error;
1598 return FT_Err_Ok;
1601 /* end of tabytecode.c */