[numberset] Add `wrap_range_prepend'.
[ttfautohint.git] / lib / tahints.c
blobea6446fbd46e2ef0cf6b2452a6acb1625ec0b0ce
1 /* tahints.c */
3 /*
4 * Copyright (C) 2011-2017 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 /* originally file `afhints.c' (2011-Mar-28) from FreeType */
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
20 #include "ta.h"
22 #include <string.h>
23 #include <stdlib.h>
24 #include "tahints.h"
27 /* get new segment for given axis */
29 FT_Error
30 ta_axis_hints_new_segment(TA_AxisHints axis,
31 TA_Segment* asegment)
33 FT_Error error = FT_Err_Ok;
34 TA_Segment segment = NULL;
37 if (axis->num_segments < TA_SEGMENTS_EMBEDDED)
39 if (!axis->segments)
41 axis->segments = axis->embedded.segments;
42 axis->max_segments = TA_SEGMENTS_EMBEDDED;
45 else if (axis->num_segments >= axis->max_segments)
47 TA_Segment segments_new;
49 FT_Int old_max = axis->max_segments;
50 FT_Int new_max = old_max;
51 FT_Int big_max = (FT_Int)(FT_INT_MAX / sizeof (*segment));
54 if (old_max >= big_max)
56 error = FT_Err_Out_Of_Memory;
57 goto Exit;
60 new_max += (new_max >> 2) + 4;
61 if (new_max < old_max
62 || new_max > big_max)
63 new_max = big_max;
65 if (axis->segments == axis->embedded.segments)
67 axis->segments = (TA_Segment)malloc(
68 (size_t)new_max * sizeof (TA_SegmentRec));
69 if (!axis->segments)
70 return FT_Err_Out_Of_Memory;
72 memcpy(axis->segments, axis->embedded.segments,
73 sizeof (axis->embedded.segments));
75 else
77 segments_new = (TA_Segment)realloc(
78 axis->segments,
79 (size_t)new_max * sizeof (TA_SegmentRec));
80 if (!segments_new)
81 return FT_Err_Out_Of_Memory;
82 axis->segments = segments_new;
85 axis->max_segments = new_max;
88 segment = axis->segments + axis->num_segments++;
90 Exit:
91 *asegment = segment;
92 return error;
96 /* get new edge for given axis, direction, and position, */
97 /* without initializing the edge itself */
99 FT_Error
100 ta_axis_hints_new_edge(TA_AxisHints axis,
101 FT_Int fpos,
102 TA_Direction dir,
103 FT_Bool top_to_bottom_hinting,
104 TA_Edge* anedge)
106 FT_Error error = FT_Err_Ok;
107 TA_Edge edge = NULL;
108 TA_Edge edges;
111 if (axis->num_edges < TA_EDGES_EMBEDDED)
113 if (!axis->edges)
115 axis->edges = axis->embedded.edges;
116 axis->max_edges = TA_EDGES_EMBEDDED;
119 else if (axis->num_edges >= axis->max_edges)
121 TA_Edge edges_new;
123 FT_Int old_max = axis->max_edges;
124 FT_Int new_max = old_max;
125 FT_Int big_max = (FT_Int)(FT_INT_MAX / sizeof (*edge));
128 if (old_max >= big_max)
130 error = FT_Err_Out_Of_Memory;
131 goto Exit;
134 new_max += (new_max >> 2) + 4;
135 if (new_max < old_max
136 || new_max > big_max)
137 new_max = big_max;
139 if (axis->edges == axis->embedded.edges)
141 axis->edges = (TA_Edge)malloc((size_t)new_max * sizeof (TA_EdgeRec));
142 if (!axis->edges)
143 return FT_Err_Out_Of_Memory;
145 memcpy(axis->edges, axis->embedded.edges,
146 sizeof (axis->embedded.edges));
148 else
150 edges_new = (TA_Edge)realloc(axis->edges,
151 (size_t)new_max * sizeof (TA_EdgeRec));
152 if (!edges_new)
153 return FT_Err_Out_Of_Memory;
154 axis->edges = edges_new;
157 axis->max_edges = new_max;
160 edges = axis->edges;
161 edge = edges + axis->num_edges;
163 while (edge > edges)
165 if (top_to_bottom_hinting ? (edge[-1].fpos > fpos)
166 : (edge[-1].fpos < fpos))
167 break;
169 /* we want the edge with same position and minor direction */
170 /* to appear before those in the major one in the list */
171 if (edge[-1].fpos == fpos
172 && dir == axis->major_dir)
173 break;
175 edge[0] = edge[-1];
176 edge--;
179 axis->num_edges++;
181 Exit:
182 *anedge = edge;
183 return error;
187 #ifdef TA_DEBUG
189 #include <stdio.h>
190 #include <stdarg.h>
191 #include <string.h>
194 void
195 _ta_message(const char* format,
196 ...)
198 va_list ap;
201 va_start(ap, format);
202 vfprintf(stderr, format, ap);
203 va_end(ap);
207 static const char*
208 ta_dir_str(TA_Direction dir)
210 const char* result;
213 switch (dir)
215 case TA_DIR_UP:
216 result = "up";
217 break;
218 case TA_DIR_DOWN:
219 result = "down";
220 break;
221 case TA_DIR_LEFT:
222 result = "left";
223 break;
224 case TA_DIR_RIGHT:
225 result = "right";
226 break;
227 default:
228 result = "none";
231 return result;
235 #define TA_INDEX_NUM(ptr, base) \
236 (int)((ptr) ? ((ptr) - (base)) \
237 : -1)
240 static char*
241 ta_print_idx(char* p,
242 int idx)
244 if (idx == -1)
246 p[0] = '-';
247 p[1] = '-';
248 p[2] = '\0';
250 else
251 sprintf(p, "%d", idx);
253 return p;
257 static int
258 ta_get_segment_index(TA_GlyphHints hints,
259 int point_idx,
260 int dimension)
262 TA_AxisHints axis = &hints->axis[dimension];
263 TA_Point point = hints->points + point_idx;
264 TA_Segment segments = axis->segments;
265 TA_Segment limit = segments + axis->num_segments;
266 TA_Segment segment;
269 for (segment = segments; segment < limit; segment++)
271 if (segment->first <= segment->last)
273 if (point >= segment->first && point <= segment->last)
274 break;
276 else
278 TA_Point p = segment->first;
281 for (;;)
283 if (point == p)
284 goto Exit;
286 if (p == segment->last)
287 break;
289 p = p->next;
294 Exit:
295 if (segment == limit)
296 return -1;
298 return (int)(segment - segments);
302 static int
303 ta_get_edge_index(TA_GlyphHints hints,
304 int segment_idx,
305 int dimension)
307 TA_AxisHints axis = &hints->axis[dimension];
308 TA_Edge edges = axis->edges;
309 TA_Segment segment = axis->segments + segment_idx;
312 return segment_idx == -1 ? -1 : TA_INDEX_NUM(segment->edge, edges);
316 void
317 ta_glyph_hints_dump_points(TA_GlyphHints hints)
319 TA_Point points = hints->points;
320 TA_Point limit = points + hints->num_points;
321 TA_Point* contour = hints->contours;
322 TA_Point* climit = contour + hints->num_contours;
323 TA_Point point;
326 TA_LOG(("Table of points:\n"));
328 if (hints->num_points)
330 TA_LOG((" index hedge hseg flags"
331 /* " XXXXX XXXXX XXXXX XXXX" */
332 " xorg yorg xscale yscale xfit yfit"));
333 /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
335 else
336 TA_LOG((" (none)\n"));
338 for (point = points; point < limit; point++)
340 int point_idx = TA_INDEX_NUM(point, points);
341 int segment_idx_1 = ta_get_segment_index(hints, point_idx, 1);
343 char buf1[16], buf2[16];
346 /* insert extra newline at the beginning of a contour */
347 if (contour < climit && *contour == point)
349 TA_LOG(("\n"));
350 contour++;
353 /* we don't show vertical edges since they are never used */
354 TA_LOG((" %5d %5s %5s %4s"
355 " %5d %5d %7.2f %7.2f %7.2f %7.2f\n",
356 point_idx,
357 ta_print_idx(buf1,
358 ta_get_edge_index(hints, segment_idx_1, 1)),
359 ta_print_idx(buf2, segment_idx_1),
360 (point->flags & TA_FLAG_WEAK_INTERPOLATION) ? "weak" : " -- ",
362 point->fx,
363 point->fy,
364 point->ox / 64.0,
365 point->oy / 64.0,
366 point->x / 64.0,
367 point->y / 64.0));
369 TA_LOG(("\n"));
373 static const char*
374 ta_edge_flags_to_string(FT_Byte flags)
376 static char temp[32];
377 int pos = 0;
380 if (flags & TA_EDGE_ROUND)
382 memcpy(temp + pos, "round", 5);
383 pos += 5;
385 if (flags & TA_EDGE_SERIF)
387 if (pos > 0)
388 temp[pos++] = ' ';
389 memcpy(temp + pos, "serif", 5);
390 pos += 5;
392 if (pos == 0)
393 return "normal";
395 temp[pos] = '\0';
397 return temp;
401 /* dump the array of linked segments */
403 void
404 ta_glyph_hints_dump_segments(TA_GlyphHints hints)
406 FT_Int dimension;
409 for (dimension = TA_DEBUG_STARTDIM;
410 dimension >= TA_DEBUG_ENDDIM;
411 dimension--)
413 TA_AxisHints axis = &hints->axis[dimension];
414 TA_Point points = hints->points;
415 TA_Edge edges = axis->edges;
416 TA_Segment segments = axis->segments;
417 TA_Segment limit = segments + axis->num_segments;
418 TA_Segment seg;
420 char buf1[16], buf2[16], buf3[16];
423 TA_LOG(("Table of %s segments:\n",
424 dimension == TA_DIMENSION_HORZ ? "vertical"
425 : "horizontal"));
426 if (axis->num_segments)
428 TA_LOG((" index pos delta dir from to "
429 /* " XXXXX XXXXX XXXXX XXXXX XXXX XXXX" */
430 " link serif edge"
431 /* " XXXX XXXXX XXXX" */
432 " height extra flags\n"));
433 /* " XXXXXX XXXXX XXXXXXXXXXX" */
435 else
436 TA_LOG((" (none)\n"));
438 for (seg = segments; seg < limit; seg++)
439 TA_LOG((" %5d %5d %5d %5s %4d %4d"
440 " %4s %5s %4s"
441 " %6d %5d %11s\n",
442 TA_INDEX_NUM(seg, segments),
443 seg->pos,
444 seg->delta,
445 ta_dir_str((TA_Direction)seg->dir),
446 TA_INDEX_NUM(seg->first, points),
447 TA_INDEX_NUM(seg->last, points),
449 ta_print_idx(buf1, TA_INDEX_NUM(seg->link, segments)),
450 ta_print_idx(buf2, TA_INDEX_NUM(seg->serif, segments)),
451 ta_print_idx(buf3, TA_INDEX_NUM(seg->edge, edges)),
453 seg->height,
454 seg->height - (seg->max_coord - seg->min_coord),
455 ta_edge_flags_to_string(seg->flags)));
456 TA_LOG(("\n"));
461 /* dump the array of linked edges */
463 void
464 ta_glyph_hints_dump_edges(TA_GlyphHints hints)
466 FT_Int dimension;
469 for (dimension = TA_DEBUG_STARTDIM;
470 dimension >= TA_DEBUG_ENDDIM;
471 dimension--)
473 TA_AxisHints axis = &hints->axis[dimension];
474 TA_Edge edges = axis->edges;
475 TA_Edge limit = edges + axis->num_edges;
476 TA_Edge edge;
478 char buf1[16], buf2[16];
481 /* note that TA_DIMENSION_HORZ corresponds to _vertical_ edges */
482 /* since they have a constant X coordinate */
483 if (dimension == TA_DIMENSION_HORZ)
484 TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
485 "vertical",
486 65536.0 * 64.0 / hints->x_scale,
487 10.0 * hints->x_scale / 65536.0 / 64.0));
488 else
489 TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
490 "horizontal",
491 65536.0 * 64.0 / hints->y_scale,
492 10.0 * hints->y_scale / 65536.0 / 64.0));
494 if (axis->num_edges)
496 TA_LOG((" index pos dir link serif"
497 /* " XXXXX XXXX.XX XXXXX XXXX XXXXX" */
498 " blue opos pos flags\n"));
499 /* " X XXXX.XX XXXX.XX XXXXXXXXXXX" */
501 else
502 TA_LOG((" (none)\n"));
504 for (edge = edges; edge < limit; edge++)
505 TA_LOG((" %5d %7.2f %5s %4s %5s"
506 " %c %7.2f %7.2f %11s\n",
507 TA_INDEX_NUM(edge, edges),
508 (int)edge->opos / 64.0,
509 ta_dir_str((TA_Direction)edge->dir),
510 ta_print_idx(buf1, TA_INDEX_NUM(edge->link, edges)),
511 ta_print_idx(buf2, TA_INDEX_NUM(edge->serif, edges)),
513 edge->blue_edge ? 'y' : 'n',
514 edge->opos / 64.0,
515 edge->pos / 64.0,
516 ta_edge_flags_to_string(edge->flags)));
517 TA_LOG(("\n"));
521 #endif /* TA_DEBUG */
524 /* compute the direction value of a given vector */
526 TA_Direction
527 ta_direction_compute(FT_Pos dx,
528 FT_Pos dy)
530 FT_Pos ll, ss; /* long and short arm lengths */
531 TA_Direction dir; /* candidate direction */
534 if (dy >= dx)
536 if (dy >= -dx)
538 dir = TA_DIR_UP;
539 ll = dy;
540 ss = dx;
542 else
544 dir = TA_DIR_LEFT;
545 ll = -dx;
546 ss = dy;
549 else /* dy < dx */
551 if (dy >= -dx)
553 dir = TA_DIR_RIGHT;
554 ll = dx;
555 ss = dy;
557 else
559 dir = TA_DIR_DOWN;
560 ll = -dy;
561 ss = dx;
565 /* return no direction if arm lengths do not differ enough */
566 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees); */
567 /* the long arm is never negative */
568 if (ll <= 14 * TA_ABS(ss))
569 dir = TA_DIR_NONE;
571 return dir;
575 void
576 ta_glyph_hints_init(TA_GlyphHints hints)
578 /* no need to initialize the embedded items */
579 memset(hints, 0, sizeof (*hints) - sizeof (hints->embedded));
583 void
584 ta_glyph_hints_done(TA_GlyphHints hints)
586 int dim;
589 if (!hints)
590 return;
592 /* we don't need to free the segment and edge buffers */
593 /* since they are really within the hints->points array */
594 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
596 TA_AxisHints axis = &hints->axis[dim];
599 axis->num_segments = 0;
600 axis->max_segments = 0;
601 if (axis->segments != axis->embedded.segments)
603 free(axis->segments);
604 axis->segments = NULL;
607 axis->num_edges = 0;
608 axis->max_edges = 0;
609 if (axis->edges != axis->embedded.edges)
611 free(axis->edges);
612 axis->edges = NULL;
616 if (hints->contours != hints->embedded.contours)
618 free(hints->contours);
619 hints->contours = NULL;
621 hints->max_contours = 0;
622 hints->num_contours = 0;
624 if (hints->points != hints->embedded.points)
626 free(hints->points);
627 hints->points = NULL;
629 hints->max_points = 0;
630 hints->num_points = 0;
634 /* reset metrics */
636 void
637 ta_glyph_hints_rescale(TA_GlyphHints hints,
638 TA_StyleMetrics metrics)
640 hints->metrics = metrics;
641 hints->scaler_flags = metrics->scaler.flags;
645 /* from FreeType's ftcalc.c */
647 static FT_Int
648 ta_corner_is_flat(FT_Pos in_x,
649 FT_Pos in_y,
650 FT_Pos out_x,
651 FT_Pos out_y)
653 FT_Pos ax = in_x;
654 FT_Pos ay = in_y;
656 FT_Pos d_in, d_out, d_corner;
659 if (ax < 0)
660 ax = -ax;
661 if (ay < 0)
662 ay = -ay;
663 d_in = ax + ay;
665 ax = out_x;
666 if (ax < 0)
667 ax = -ax;
668 ay = out_y;
669 if (ay < 0)
670 ay = -ay;
671 d_out = ax + ay;
673 ax = out_x + in_x;
674 if (ax < 0)
675 ax = -ax;
676 ay = out_y + in_y;
677 if (ay < 0)
678 ay = -ay;
679 d_corner = ax + ay;
681 return (d_in + d_out - d_corner) < (d_corner >> 4);
685 /* recompute all TA_Point in TA_GlyphHints */
686 /* from the definitions in a source outline */
688 FT_Error
689 ta_glyph_hints_reload(TA_GlyphHints hints,
690 FT_Outline* outline)
692 FT_Error error = FT_Err_Ok;
693 TA_Point points;
694 FT_UInt old_max, new_max;
696 FT_Fixed x_scale = hints->x_scale;
697 FT_Fixed y_scale = hints->y_scale;
698 FT_Pos x_delta = hints->x_delta;
699 FT_Pos y_delta = hints->y_delta;
702 hints->num_points = 0;
703 hints->num_contours = 0;
705 hints->axis[0].num_segments = 0;
706 hints->axis[0].num_edges = 0;
707 hints->axis[1].num_segments = 0;
708 hints->axis[1].num_edges = 0;
710 /* first of all, reallocate the contours array if necessary */
711 new_max = (FT_UInt)outline->n_contours;
712 old_max = (FT_UInt)hints->max_contours;
714 if (new_max <= TA_CONTOURS_EMBEDDED)
716 if (!hints->contours)
718 hints->contours = hints->embedded.contours;
719 hints->max_contours = TA_CONTOURS_EMBEDDED;
722 else if (new_max > old_max)
724 TA_Point* contours_new;
727 if (hints->contours == hints->embedded.contours)
728 hints->contours = NULL;
730 new_max = (new_max + 3) & ~3U; /* round up to a multiple of 4 */
732 contours_new = (TA_Point*)realloc(hints->contours,
733 new_max * sizeof (TA_Point));
734 if (!contours_new)
735 return FT_Err_Out_Of_Memory;
737 hints->contours = contours_new;
738 hints->max_contours = (FT_Int)new_max;
741 /* reallocate the points arrays if necessary -- we reserve */
742 /* two additional point positions, used to hint metrics appropriately */
743 new_max = (FT_UInt)(outline->n_points + 2);
744 old_max = (FT_UInt)hints->max_points;
746 if (new_max <= TA_POINTS_EMBEDDED)
748 if (!hints->points)
750 hints->points = hints->embedded.points;
751 hints->max_points = TA_POINTS_EMBEDDED;
754 else if (new_max > old_max)
756 TA_Point points_new;
759 if (hints->points == hints->embedded.points)
760 hints->points = NULL;
762 new_max = (new_max + 2 + 7) & ~7U; /* round up to a multiple of 8 */
764 points_new = (TA_Point)realloc(hints->points,
765 new_max * sizeof (TA_PointRec));
766 if (!points_new)
767 return FT_Err_Out_Of_Memory;
769 hints->points = points_new;
770 hints->max_points = (FT_Int)new_max;
773 hints->num_points = outline->n_points;
774 hints->num_contours = outline->n_contours;
776 /* we can't rely on the value of `FT_Outline.flags' to know the fill */
777 /* direction used for a glyph, given that some fonts are broken */
778 /* (e.g. the Arphic ones); we thus recompute it each time we need to */
780 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_UP;
781 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_LEFT;
783 if (FT_Outline_Get_Orientation(outline) == FT_ORIENTATION_POSTSCRIPT)
785 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_DOWN;
786 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_RIGHT;
789 hints->x_scale = x_scale;
790 hints->y_scale = y_scale;
791 hints->x_delta = x_delta;
792 hints->y_delta = y_delta;
794 hints->xmin_delta = 0;
795 hints->xmax_delta = 0;
797 points = hints->points;
798 if (hints->num_points == 0)
799 goto Exit;
802 TA_Point point;
803 TA_Point point_limit = points + hints->num_points;
806 /* compute coordinates & Bezier flags, next and prev */
808 FT_Vector* vec = outline->points;
809 char* tag = outline->tags;
811 TA_Point end = points + outline->contours[0];
812 TA_Point prev = end;
814 FT_Int contour_index = 0;
817 for (point = points; point < point_limit; point++, vec++, tag++)
819 point->in_dir = (FT_Char)TA_DIR_NONE;
820 point->out_dir = (FT_Char)TA_DIR_NONE;
822 point->fx = (FT_Short)vec->x;
823 point->fy = (FT_Short)vec->y;
824 point->ox = point->x = FT_MulFix(vec->x, x_scale) + x_delta;
825 point->oy = point->y = FT_MulFix(vec->y, y_scale) + y_delta;
827 switch (FT_CURVE_TAG(*tag))
829 case FT_CURVE_TAG_CONIC:
830 point->flags = TA_FLAG_CONIC;
831 break;
832 case FT_CURVE_TAG_CUBIC:
833 point->flags = TA_FLAG_CUBIC;
834 break;
835 default:
836 point->flags = TA_FLAG_NONE;
839 point->prev = prev;
840 prev->next = point;
841 prev = point;
843 if (point == end)
845 if (++contour_index < outline->n_contours)
847 end = points + outline->contours[contour_index];
848 prev = end;
854 /* set up the contours array */
856 TA_Point* contour = hints->contours;
857 TA_Point* contour_limit = contour + hints->num_contours;
859 short* end = outline->contours;
860 short idx = 0;
863 for (; contour < contour_limit; contour++, end++)
865 contour[0] = points + idx;
866 idx = (short)(end[0] + 1);
872 * Compute directions of `in' and `out' vectors.
874 * Note that distances between points that are very near to each
875 * other are accumulated. In other words, the auto-hinter
876 * prepends the small vectors between near points to the first
877 * non-near vector. All intermediate points are tagged as
878 * weak; the directions are adjusted also to be equal to the
879 * accumulated one.
882 /* value 20 in `near_limit' is heuristic */
883 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
884 FT_Int near_limit = 20 * units_per_em / 2048;
885 FT_Int near_limit2 = 2 * near_limit - 1;
887 TA_Point* contour;
888 TA_Point* contour_limit = hints->contours + hints->num_contours;
891 for (contour = hints->contours; contour < contour_limit; contour++)
893 TA_Point first = *contour;
894 TA_Point next, prev, curr;
896 FT_Pos out_x, out_y;
899 /* since the first point of a contour could be part of a */
900 /* series of near points, go backwards to find the first */
901 /* non-near point and adjust `first' */
903 point = first;
904 prev = first->prev;
906 while (prev != first)
908 out_x = point->fx - prev->fx;
909 out_y = point->fy - prev->fy;
912 * We use Taxicab metrics to measure the vector length.
914 * Note that the accumulated distances so far could have the
915 * opposite direction of the distance measured here. For this
916 * reason we use `near_limit2' for the comparison to get a
917 * non-near point even in the worst case.
919 if (TA_ABS(out_x) + TA_ABS(out_y) >= near_limit2)
920 break;
922 point = prev;
923 prev = prev->prev;
926 /* adjust first point */
927 first = point;
929 /* now loop over all points of the contour to get */
930 /* `in' and `out' vector directions */
932 curr = first;
935 * We abuse the `u' and `v' fields to store index deltas to the
936 * next and previous non-near point, respectively.
938 * To avoid problems with not having non-near points, we point to
939 * `first' by default as the next non-near point.
941 curr->u = (FT_Pos)(first - curr);
942 first->v = -curr->u;
944 out_x = 0;
945 out_y = 0;
947 next = first;
950 TA_Direction out_dir;
953 point = next;
954 next = point->next;
956 out_x += next->fx - point->fx;
957 out_y += next->fy - point->fy;
959 if (TA_ABS(out_x) + TA_ABS(out_y) < near_limit)
961 next->flags |= TA_FLAG_WEAK_INTERPOLATION;
962 continue;
965 curr->u = (FT_Pos)(next - curr);
966 next->v = -curr->u;
968 out_dir = ta_direction_compute(out_x, out_y);
970 /* adjust directions for all points inbetween; */
971 /* the loop also updates position of `curr' */
972 curr->out_dir = (FT_Char)out_dir;
973 for (curr = curr->next; curr != next; curr = curr->next)
975 curr->in_dir = (FT_Char)out_dir;
976 curr->out_dir = (FT_Char)out_dir;
978 next->in_dir = (FT_Char)out_dir;
980 curr->u = (FT_Pos)(first - curr);
981 first->v = -curr->u;
983 out_x = 0;
984 out_y = 0;
986 } while (next != first);
990 * The next step is to `simplify' an outline's topology so that we
991 * can identify local extrema more reliably: A series of
992 * non-horizontal or non-vertical vectors pointing into the same
993 * quadrant are handled as a single, long vector. From a
994 * topological point of the view, the intermediate points are of no
995 * interest and thus tagged as weak.
998 for (point = points; point < point_limit; point++)
1000 if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1001 continue;
1003 if (point->in_dir == TA_DIR_NONE
1004 && point->out_dir == TA_DIR_NONE)
1006 /* check whether both vectors point into the same quadrant */
1008 FT_Pos in_x, in_y;
1009 FT_Pos out_x, out_y;
1011 TA_Point next_u = point + point->u;
1012 TA_Point prev_v = point + point->v;
1015 in_x = point->fx - prev_v->fx;
1016 in_y = point->fy - prev_v->fy;
1018 out_x = next_u->fx - point->fx;
1019 out_y = next_u->fy - point->fy;
1021 if ((in_x ^ out_x) >= 0 && (in_y ^ out_y) >= 0)
1023 /* yes, so tag current point as weak */
1024 /* and update index deltas */
1026 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1028 prev_v->u = (FT_Pos)(next_u - prev_v);
1029 next_u->v = -prev_v->u;
1035 * Finally, check for remaining weak points. Everything else not
1036 * collected in edges so far is then implicitly classified as strong
1037 * points.
1040 for (point = points; point < point_limit; point++)
1042 if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1043 continue;
1045 if (point->flags & TA_FLAG_CONTROL)
1047 /* control points are always weak */
1048 Is_Weak_Point:
1049 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1051 else if (point->out_dir == point->in_dir)
1053 if (point->out_dir != TA_DIR_NONE)
1055 /* current point lies on a horizontal or */
1056 /* vertical segment (but doesn't start or end it) */
1057 goto Is_Weak_Point;
1061 TA_Point next_u = point + point->u;
1062 TA_Point prev_v = point + point->v;
1065 if (ta_corner_is_flat(point->fx - prev_v->fx,
1066 point->fy - prev_v->fy,
1067 next_u->fx - point->fx,
1068 next_u->fy - point->fy))
1070 /* either the `in' or the `out' vector is much more */
1071 /* dominant than the other one, so tag current point */
1072 /* as weak and update index deltas */
1074 prev_v->u = (FT_Pos)(next_u - prev_v);
1075 next_u->v = -prev_v->u;
1077 goto Is_Weak_Point;
1081 else if (point->in_dir == -point->out_dir)
1083 /* current point forms a spike */
1084 goto Is_Weak_Point;
1090 /* change some directions at the user's request */
1091 /* to make ttfautohint insert one-point segments */
1092 /* or remove points from segments */
1094 FONT* font;
1095 FT_Int idx;
1096 TA_Direction dir;
1097 int left_offset;
1098 int right_offset;
1101 /* `globals' is not set up while initializing metrics, */
1102 /* so exit early in this case */
1103 if (!hints->metrics->globals)
1104 goto Exit;
1106 font = hints->metrics->globals->font;
1108 /* start conditions are set with `TA_control_segment_dir_collect' */
1109 while (TA_control_segment_dir_get_next(font, &idx, &dir,
1110 &left_offset, &right_offset))
1112 TA_Point point = &points[idx];
1115 point->out_dir = dir;
1116 if (dir == TA_DIR_NONE)
1117 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1118 else
1119 point->flags &= ~TA_FLAG_WEAK_INTERPOLATION;
1120 point->left_offset = (FT_Short)left_offset;
1121 point->right_offset = (FT_Short)right_offset;
1125 Exit:
1126 return error;
1130 /* store the hinted outline in an FT_Outline structure */
1132 void
1133 ta_glyph_hints_save(TA_GlyphHints hints,
1134 FT_Outline* outline)
1136 TA_Point point = hints->points;
1137 TA_Point limit = point + hints->num_points;
1139 FT_Vector* vec = outline->points;
1140 char* tag = outline->tags;
1143 for (; point < limit; point++, vec++, tag++)
1145 vec->x = point->x;
1146 vec->y = point->y;
1148 if (point->flags & TA_FLAG_CONIC)
1149 tag[0] = FT_CURVE_TAG_CONIC;
1150 else if (point->flags & TA_FLAG_CUBIC)
1151 tag[0] = FT_CURVE_TAG_CUBIC;
1152 else
1153 tag[0] = FT_CURVE_TAG_ON;
1158 /****************************************************************
1160 * EDGE POINT GRID-FITTING
1162 ****************************************************************/
1165 /* align all points of an edge to the same coordinate value, */
1166 /* either horizontally or vertically */
1168 void
1169 ta_glyph_hints_align_edge_points(TA_GlyphHints hints,
1170 TA_Dimension dim)
1172 TA_AxisHints axis = &hints->axis[dim];
1173 TA_Segment segments = axis->segments;
1174 TA_Segment segment_limit = segments + axis->num_segments;
1175 TA_Segment seg;
1178 if (dim == TA_DIMENSION_HORZ)
1180 for (seg = segments; seg < segment_limit; seg++)
1182 TA_Edge edge = seg->edge;
1183 TA_Point point, first, last;
1186 if (!edge)
1187 continue;
1189 first = seg->first;
1190 last = seg->last;
1191 point = first;
1192 for (;;)
1194 point->x = edge->pos;
1195 point->flags |= TA_FLAG_TOUCH_X;
1197 if (point == last)
1198 break;
1200 point = point->next;
1204 else
1206 for (seg = segments; seg < segment_limit; seg++)
1208 TA_Edge edge = seg->edge;
1209 TA_Point point, first, last;
1212 if (!edge)
1213 continue;
1215 first = seg->first;
1216 last = seg->last;
1217 point = first;
1218 for (;;)
1220 point->y = edge->pos;
1221 point->flags |= TA_FLAG_TOUCH_Y;
1223 if (point == last)
1224 break;
1226 point = point->next;
1233 /****************************************************************
1235 * STRONG POINT INTERPOLATION
1237 ****************************************************************/
1240 /* hint the strong points -- */
1241 /* this is equivalent to the TrueType `IP' hinting instruction */
1243 void
1244 ta_glyph_hints_align_strong_points(TA_GlyphHints hints,
1245 TA_Dimension dim)
1247 TA_Point points = hints->points;
1248 TA_Point point_limit = points + hints->num_points;
1250 TA_AxisHints axis = &hints->axis[dim];
1252 TA_Edge edges = axis->edges;
1253 TA_Edge edge_limit = edges + axis->num_edges;
1255 FT_UShort touch_flag;
1258 if (dim == TA_DIMENSION_HORZ)
1259 touch_flag = TA_FLAG_TOUCH_X;
1260 else
1261 touch_flag = TA_FLAG_TOUCH_Y;
1263 if (edges < edge_limit)
1265 TA_Point point;
1266 TA_Edge edge;
1269 for (point = points; point < point_limit; point++)
1271 FT_Pos u, ou, fu; /* point position */
1272 FT_Pos delta;
1275 if (point->flags & touch_flag)
1276 continue;
1278 /* if this point is candidate to weak interpolation, we */
1279 /* interpolate it after all strong points have been processed */
1281 if ((point->flags & TA_FLAG_WEAK_INTERPOLATION))
1282 continue;
1284 if (dim == TA_DIMENSION_VERT)
1286 u = point->fy;
1287 ou = point->oy;
1289 else
1291 u = point->fx;
1292 ou = point->ox;
1295 fu = u;
1297 /* is the point before the first edge? */
1298 edge = edges;
1299 delta = edge->fpos - u;
1300 if (delta >= 0)
1302 u = edge->pos - (edge->opos - ou);
1304 if (hints->recorder)
1305 hints->recorder(ta_ip_before, hints, dim,
1306 point, NULL, NULL, NULL, NULL);
1308 goto Store_Point;
1311 /* is the point after the last edge? */
1312 edge = edge_limit - 1;
1313 delta = u - edge->fpos;
1314 if (delta >= 0)
1316 u = edge->pos + (ou - edge->opos);
1318 if (hints->recorder)
1319 hints->recorder(ta_ip_after, hints, dim,
1320 point, NULL, NULL, NULL, NULL);
1322 goto Store_Point;
1326 FT_PtrDist min, max, mid;
1327 FT_Pos fpos;
1330 /* find enclosing edges */
1331 min = 0;
1332 max = edge_limit - edges;
1334 /* for a small number of edges, a linear search is better */
1335 if (max <= 8)
1337 FT_PtrDist nn;
1340 for (nn = 0; nn < max; nn++)
1341 if (edges[nn].fpos >= u)
1342 break;
1344 if (edges[nn].fpos == u)
1346 u = edges[nn].pos;
1348 if (hints->recorder)
1349 hints->recorder(ta_ip_on, hints, dim,
1350 point, &edges[nn], NULL, NULL, NULL);
1352 goto Store_Point;
1354 min = nn;
1356 else
1357 while (min < max)
1359 mid = (max + min) >> 1;
1360 edge = edges + mid;
1361 fpos = edge->fpos;
1363 if (u < fpos)
1364 max = mid;
1365 else if (u > fpos)
1366 min = mid + 1;
1367 else
1369 /* we are on the edge */
1370 u = edge->pos;
1372 if (hints->recorder)
1373 hints->recorder(ta_ip_on, hints, dim,
1374 point, edge, NULL, NULL, NULL);
1376 goto Store_Point;
1380 /* point is not on an edge */
1382 TA_Edge before = edges + min - 1;
1383 TA_Edge after = edges + min + 0;
1386 /* assert(before && after && before != after) */
1387 if (before->scale == 0)
1388 before->scale = FT_DivFix(after->pos - before->pos,
1389 after->fpos - before->fpos);
1391 u = before->pos + FT_MulFix(fu - before->fpos,
1392 before->scale);
1394 if (hints->recorder)
1395 hints->recorder(ta_ip_between, hints, dim,
1396 point, before, after, NULL, NULL);
1400 Store_Point:
1401 /* save the point position */
1402 if (dim == TA_DIMENSION_HORZ)
1403 point->x = u;
1404 else
1405 point->y = u;
1407 point->flags |= touch_flag;
1413 /****************************************************************
1415 * WEAK POINT INTERPOLATION
1417 ****************************************************************/
1420 /* shift the original coordinates of all points between `p1' and */
1421 /* `p2' to get hinted coordinates, using the same difference as */
1422 /* given by `ref' */
1424 static void
1425 ta_iup_shift(TA_Point p1,
1426 TA_Point p2,
1427 TA_Point ref)
1429 TA_Point p;
1430 FT_Pos delta = ref->u - ref->v;
1433 if (delta == 0)
1434 return;
1436 for (p = p1; p < ref; p++)
1437 p->u = p->v + delta;
1439 for (p = ref + 1; p <= p2; p++)
1440 p->u = p->v + delta;
1444 /* interpolate the original coordinates of all points between `p1' and */
1445 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1446 /* reference points; the `u' and `v' members are the current and */
1447 /* original coordinate values, respectively. */
1449 /* details can be found in the TrueType bytecode specification */
1451 static void
1452 ta_iup_interp(TA_Point p1,
1453 TA_Point p2,
1454 TA_Point ref1,
1455 TA_Point ref2)
1457 TA_Point p;
1458 FT_Pos u, v1, v2, u1, u2, d1, d2;
1461 if (p1 > p2)
1462 return;
1464 if (ref1->v > ref2->v)
1466 p = ref1;
1467 ref1 = ref2;
1468 ref2 = p;
1471 v1 = ref1->v;
1472 v2 = ref2->v;
1473 u1 = ref1->u;
1474 u2 = ref2->u;
1475 d1 = u1 - v1;
1476 d2 = u2 - v2;
1478 if (u1 == u2 || v1 == v2)
1480 for (p = p1; p <= p2; p++)
1482 u = p->v;
1484 if (u <= v1)
1485 u += d1;
1486 else if (u >= v2)
1487 u += d2;
1488 else
1489 u = u1;
1491 p->u = u;
1494 else
1496 FT_Fixed scale = FT_DivFix(u2 - u1, v2 - v1);
1499 for (p = p1; p <= p2; p++)
1501 u = p->v;
1503 if (u <= v1)
1504 u += d1;
1505 else if (u >= v2)
1506 u += d2;
1507 else
1508 u = u1 + FT_MulFix(u - v1, scale);
1510 p->u = u;
1516 /* hint the weak points -- */
1517 /* this is equivalent to the TrueType `IUP' hinting instruction */
1519 void
1520 ta_glyph_hints_align_weak_points(TA_GlyphHints hints,
1521 TA_Dimension dim)
1523 TA_Point points = hints->points;
1524 TA_Point point_limit = points + hints->num_points;
1526 TA_Point* contour = hints->contours;
1527 TA_Point* contour_limit = contour + hints->num_contours;
1529 FT_UShort touch_flag;
1530 TA_Point point;
1531 TA_Point end_point;
1532 TA_Point first_point;
1535 /* pass 1: move segment points to edge positions */
1537 if (dim == TA_DIMENSION_HORZ)
1539 touch_flag = TA_FLAG_TOUCH_X;
1541 for (point = points; point < point_limit; point++)
1543 point->u = point->x;
1544 point->v = point->ox;
1547 else
1549 touch_flag = TA_FLAG_TOUCH_Y;
1551 for (point = points; point < point_limit; point++)
1553 point->u = point->y;
1554 point->v = point->oy;
1558 for (; contour < contour_limit; contour++)
1560 TA_Point first_touched, last_touched;
1563 point = *contour;
1564 end_point = point->prev;
1565 first_point = point;
1567 /* find first touched point */
1568 for (;;)
1570 if (point > end_point) /* no touched point in contour */
1571 goto NextContour;
1573 if (point->flags & touch_flag)
1574 break;
1576 point++;
1579 first_touched = point;
1581 for (;;)
1583 /* skip any touched neighbours */
1584 while (point < end_point
1585 && (point[1].flags & touch_flag) != 0)
1586 point++;
1588 last_touched = point;
1590 /* find the next touched point, if any */
1591 point++;
1592 for (;;)
1594 if (point > end_point)
1595 goto EndContour;
1597 if ((point->flags & touch_flag) != 0)
1598 break;
1600 point++;
1603 /* interpolate between last_touched and point */
1604 ta_iup_interp(last_touched + 1, point - 1,
1605 last_touched, point);
1608 EndContour:
1609 /* special case: only one point was touched */
1610 if (last_touched == first_touched)
1611 ta_iup_shift(first_point, end_point, first_touched);
1613 else /* interpolate the last part */
1615 if (last_touched < end_point)
1616 ta_iup_interp(last_touched + 1, end_point,
1617 last_touched, first_touched);
1619 if (first_touched > points)
1620 ta_iup_interp(first_point, first_touched - 1,
1621 last_touched, first_touched);
1624 NextContour:
1628 /* now save the interpolated values back to x/y */
1629 if (dim == TA_DIMENSION_HORZ)
1631 for (point = points; point < point_limit; point++)
1632 point->x = point->u;
1634 else
1636 for (point = points; point < point_limit; point++)
1637 point->y = point->u;
1642 #ifdef TA_CONFIG_OPTION_USE_WARPER
1644 /* apply (small) warp scale and warp delta for given dimension */
1646 static void
1647 ta_glyph_hints_scale_dim(TA_GlyphHints hints,
1648 TA_Dimension dim,
1649 FT_Fixed scale,
1650 FT_Pos delta)
1652 TA_Point points = hints->points;
1653 TA_Point points_limit = points + hints->num_points;
1654 TA_Point point;
1657 if (dim == TA_DIMENSION_HORZ)
1659 for (point = points; point < points_limit; point++)
1660 point->x = FT_MulFix(point->fx, scale) + delta;
1662 else
1664 for (point = points; point < points_limit; point++)
1665 point->y = FT_MulFix(point->fy, scale) + delta;
1669 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1671 /* end of tahints.c */