1 /* tahints.c */
2 
3 /*
4  * Copyright (C) 2011-2021 by Werner Lemberg.
5  *
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.
10  *
11  * The file `COPYING' mentioned in the previous paragraph is distributed
12  * with the ttfautohint library.
13  */
14 
15 
16 /* originally file `afhints.c' (2011-Mar-28) from FreeType */
17 
18 /* heavily modified 2011 by Werner Lemberg <wl@gnu.org> */
19 
20 #include "ta.h"
21 
22 #include <string.h>
23 #include <stdlib.h>
24 #include "tahints.h"
25 
26 
27 /* get new segment for given axis */
28 
29 FT_Error
ta_axis_hints_new_segment(TA_AxisHints axis,TA_Segment * asegment)30 ta_axis_hints_new_segment(TA_AxisHints axis,
31                           TA_Segment* asegment)
32 {
33   FT_Error error = FT_Err_Ok;
34   TA_Segment segment = NULL;
35 
36 
37   if (axis->num_segments < TA_SEGMENTS_EMBEDDED)
38   {
39     if (!axis->segments)
40     {
41       axis->segments = axis->embedded.segments;
42       axis->max_segments = TA_SEGMENTS_EMBEDDED;
43     }
44   }
45   else if (axis->num_segments >= axis->max_segments)
46   {
47     TA_Segment segments_new;
48 
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));
52 
53 
54     if (old_max >= big_max)
55     {
56       error = FT_Err_Out_Of_Memory;
57       goto Exit;
58     }
59 
60     new_max += (new_max >> 2) + 4;
61     if (new_max < old_max
62         || new_max > big_max)
63       new_max = big_max;
64 
65     if (axis->segments == axis->embedded.segments)
66     {
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;
71 
72       memcpy(axis->segments, axis->embedded.segments,
73              sizeof (axis->embedded.segments));
74     }
75     else
76     {
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;
83     }
84 
85     axis->max_segments = new_max;
86   }
87 
88   segment = axis->segments + axis->num_segments++;
89 
90 Exit:
91   *asegment = segment;
92   return error;
93 }
94 
95 
96 /* get new edge for given axis, direction, and position, */
97 /* without initializing the edge itself */
98 
99 FT_Error
ta_axis_hints_new_edge(TA_AxisHints axis,FT_Int fpos,TA_Direction dir,FT_Bool top_to_bottom_hinting,TA_Edge * anedge)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)
105 {
106   FT_Error error = FT_Err_Ok;
107   TA_Edge edge = NULL;
108   TA_Edge edges;
109 
110 
111   if (axis->num_edges < TA_EDGES_EMBEDDED)
112   {
113     if (!axis->edges)
114     {
115       axis->edges = axis->embedded.edges;
116       axis->max_edges = TA_EDGES_EMBEDDED;
117     }
118   }
119   else if (axis->num_edges >= axis->max_edges)
120   {
121     TA_Edge edges_new;
122 
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));
126 
127 
128     if (old_max >= big_max)
129     {
130       error = FT_Err_Out_Of_Memory;
131       goto Exit;
132     }
133 
134     new_max += (new_max >> 2) + 4;
135     if (new_max < old_max
136         || new_max > big_max)
137       new_max = big_max;
138 
139     if (axis->edges == axis->embedded.edges)
140     {
141       axis->edges = (TA_Edge)malloc((size_t)new_max * sizeof (TA_EdgeRec));
142       if (!axis->edges)
143         return FT_Err_Out_Of_Memory;
144 
145       memcpy(axis->edges, axis->embedded.edges,
146              sizeof (axis->embedded.edges));
147     }
148     else
149     {
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;
155     }
156 
157     axis->max_edges = new_max;
158   }
159 
160   edges = axis->edges;
161   edge = edges + axis->num_edges;
162 
163   while (edge > edges)
164   {
165     if (top_to_bottom_hinting ? (edge[-1].fpos > fpos)
166                               : (edge[-1].fpos < fpos))
167       break;
168 
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;
174 
175     edge[0] = edge[-1];
176     edge--;
177   }
178 
179   axis->num_edges++;
180 
181 Exit:
182   *anedge = edge;
183   return error;
184 }
185 
186 
187 #ifdef TA_DEBUG
188 
189 #include <stdio.h>
190 #include <stdarg.h>
191 #include <string.h>
192 
193 
194 void
_ta_message(const char * format,...)195 _ta_message(const char* format,
196             ...)
197 {
198   va_list ap;
199 
200 
201   va_start(ap, format);
202   vfprintf(stderr, format, ap);
203   va_end(ap);
204 }
205 
206 
207 static const char*
ta_dir_str(TA_Direction dir)208 ta_dir_str(TA_Direction dir)
209 {
210   const char* result;
211 
212 
213   switch (dir)
214   {
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";
229   }
230 
231   return result;
232 }
233 
234 
235 #define TA_INDEX_NUM(ptr, base) \
236           (int)((ptr) ? ((ptr) - (base)) \
237                       : -1)
238 
239 
240 static char*
ta_print_idx(char * p,int idx)241 ta_print_idx(char* p,
242              int idx)
243 {
244   if (idx == -1)
245   {
246     p[0] = '-';
247     p[1] = '-';
248     p[2] = '\0';
249   }
250   else
251     sprintf(p, "%d", idx);
252 
253   return p;
254 }
255 
256 
257 static int
ta_get_segment_index(TA_GlyphHints hints,int point_idx,int dimension)258 ta_get_segment_index(TA_GlyphHints hints,
259                      int point_idx,
260                      int dimension)
261 {
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;
267 
268 
269   for (segment = segments; segment < limit; segment++)
270   {
271     if (segment->first <= segment->last)
272     {
273       if (point >= segment->first && point <= segment->last)
274         break;
275     }
276     else
277     {
278       TA_Point p = segment->first;
279 
280 
281       for (;;)
282       {
283         if (point == p)
284           goto Exit;
285 
286         if (p == segment->last)
287           break;
288 
289         p = p->next;
290       }
291     }
292   }
293 
294 Exit:
295   if (segment == limit)
296     return -1;
297 
298   return (int)(segment - segments);
299 }
300 
301 
302 static int
ta_get_edge_index(TA_GlyphHints hints,int segment_idx,int dimension)303 ta_get_edge_index(TA_GlyphHints hints,
304                   int segment_idx,
305                   int dimension)
306 {
307   TA_AxisHints axis = &hints->axis[dimension];
308   TA_Edge edges = axis->edges;
309   TA_Segment segment = axis->segments + segment_idx;
310 
311 
312   return segment_idx == -1 ? -1 : TA_INDEX_NUM(segment->edge, edges);
313 }
314 
315 
316 static int
ta_get_strong_edge_index(TA_GlyphHints hints,TA_Edge * strong_edges,int dimension)317 ta_get_strong_edge_index(TA_GlyphHints hints,
318                          TA_Edge* strong_edges,
319                          int dimension)
320 {
321   TA_AxisHints axis = &hints->axis[dimension];
322   TA_Edge edges = axis->edges;
323 
324 
325   return TA_INDEX_NUM(strong_edges[dimension], edges);
326 }
327 
328 
329 void
ta_glyph_hints_dump_points(TA_GlyphHints hints)330 ta_glyph_hints_dump_points(TA_GlyphHints hints)
331 {
332   TA_Point points = hints->points;
333   TA_Point limit = points + hints->num_points;
334   TA_Point* contour = hints->contours;
335   TA_Point* climit = contour + hints->num_contours;
336   TA_Point point;
337 
338 
339   TA_LOG(("Table of points:\n"));
340 
341   if (hints->num_points)
342   {
343     TA_LOG(("  index  hedge  hseg  flags"
344          /* "  XXXXX  XXXXX XXXXX   XXXX" */
345             "  xorg  yorg  xscale  yscale   xfit    yfit "
346          /* " XXXXX XXXXX XXXX.XX XXXX.XX XXXX.XX XXXX.XX" */
347             "  hbef  haft"));
348          /* " XXXXX XXXXX" */
349   }
350   else
351     TA_LOG(("  (none)\n"));
352 
353   for (point = points; point < limit; point++)
354   {
355     int point_idx = TA_INDEX_NUM(point, points);
356     int segment_idx_1 = ta_get_segment_index(hints, point_idx, 1);
357 
358     char buf1[16], buf2[16];
359     char buf5[16], buf6[16];
360 
361 
362     /* insert extra newline at the beginning of a contour */
363     if (contour < climit && *contour == point)
364     {
365       TA_LOG(("\n"));
366       contour++;
367     }
368 
369     /* we don't show vertical edges since they are never used */
370     TA_LOG(("  %5d  %5s %5s   %4s"
371             " %5d %5d %7.2f %7.2f %7.2f %7.2f"
372             " %5s %5s\n",
373             point_idx,
374             ta_print_idx(buf1,
375                          ta_get_edge_index(hints, segment_idx_1, 1)),
376             ta_print_idx(buf2, segment_idx_1),
377             (point->flags & TA_FLAG_WEAK_INTERPOLATION) ? "weak" : " -- ",
378 
379             point->fx,
380             point->fy,
381             point->ox / 64.0,
382             point->oy / 64.0,
383             point->x / 64.0,
384             point->y / 64.0,
385 
386             ta_print_idx(buf5, ta_get_strong_edge_index(hints,
387                                                         point->before,
388                                                         1)),
389             ta_print_idx(buf6, ta_get_strong_edge_index(hints,
390                                                         point->after,
391                                                         1))));
392   }
393   TA_LOG(("\n"));
394 }
395 
396 
397 static const char*
ta_edge_flags_to_string(FT_Byte flags)398 ta_edge_flags_to_string(FT_Byte flags)
399 {
400   static char temp[32];
401   int pos = 0;
402 
403 
404   if (flags & TA_EDGE_ROUND)
405   {
406     memcpy(temp + pos, "round", 5);
407     pos += 5;
408   }
409   if (flags & TA_EDGE_SERIF)
410   {
411     if (pos > 0)
412       temp[pos++] = ' ';
413     memcpy(temp + pos, "serif", 5);
414     pos += 5;
415   }
416   if (pos == 0)
417     return "normal";
418 
419   temp[pos] = '\0';
420 
421   return temp;
422 }
423 
424 
425 /* dump the array of linked segments */
426 
427 void
ta_glyph_hints_dump_segments(TA_GlyphHints hints)428 ta_glyph_hints_dump_segments(TA_GlyphHints hints)
429 {
430   FT_Int dimension;
431 
432 
433   for (dimension = TA_DEBUG_STARTDIM;
434        dimension >= TA_DEBUG_ENDDIM;
435        dimension--)
436   {
437     TA_AxisHints axis = &hints->axis[dimension];
438     TA_Point points = hints->points;
439     TA_Edge edges = axis->edges;
440     TA_Segment segments = axis->segments;
441     TA_Segment limit = segments + axis->num_segments;
442     TA_Segment seg;
443 
444     char buf1[16], buf2[16], buf3[16];
445 
446 
447     TA_LOG(("Table of %s segments:\n",
448             dimension == TA_DIMENSION_HORZ ? "vertical"
449                                            : "horizontal"));
450     if (axis->num_segments)
451     {
452       TA_LOG(("  index   pos   delta   dir   from   to "
453            /* "  XXXXX  XXXXX  XXXXX  XXXXX  XXXX  XXXX" */
454               "  link  serif  edge"
455            /* "  XXXX  XXXXX  XXXX" */
456               "  height  extra     flags\n"));
457            /* "  XXXXXX  XXXXX  XXXXXXXXXXX" */
458     }
459     else
460       TA_LOG(("  (none)\n"));
461 
462     for (seg = segments; seg < limit; seg++)
463       TA_LOG(("  %5d  %5d  %5d  %5s  %4d  %4d"
464               "  %4s  %5s  %4s"
465               "  %6d  %5d  %11s\n",
466               TA_INDEX_NUM(seg, segments),
467               seg->pos,
468               seg->delta,
469               ta_dir_str((TA_Direction)seg->dir),
470               TA_INDEX_NUM(seg->first, points),
471               TA_INDEX_NUM(seg->last, points),
472 
473               ta_print_idx(buf1, TA_INDEX_NUM(seg->link, segments)),
474               ta_print_idx(buf2, TA_INDEX_NUM(seg->serif, segments)),
475               ta_print_idx(buf3, TA_INDEX_NUM(seg->edge, edges)),
476 
477               seg->height,
478               seg->height - (seg->max_coord - seg->min_coord),
479               ta_edge_flags_to_string(seg->flags)));
480     TA_LOG(("\n"));
481   }
482 }
483 
484 
485 /* dump the array of linked edges */
486 
487 void
ta_glyph_hints_dump_edges(TA_GlyphHints hints)488 ta_glyph_hints_dump_edges(TA_GlyphHints hints)
489 {
490   FT_Int dimension;
491 
492 
493   for (dimension = TA_DEBUG_STARTDIM;
494        dimension >= TA_DEBUG_ENDDIM;
495        dimension--)
496   {
497     TA_AxisHints axis = &hints->axis[dimension];
498     TA_Edge edges = axis->edges;
499     TA_Edge limit = edges + axis->num_edges;
500     TA_Edge edge;
501 
502     char buf1[16], buf2[16];
503 
504 
505     /* note that TA_DIMENSION_HORZ corresponds to _vertical_ edges */
506     /* since they have a constant X coordinate */
507     if (dimension == TA_DIMENSION_HORZ)
508       TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
509               "vertical",
510               65536.0 * 64.0 / hints->x_scale,
511               10.0 * hints->x_scale / 65536.0 / 64.0));
512     else
513       TA_LOG(("Table of %s edges (1px=%.2fu, 10u=%.2fpx):\n",
514               "horizontal",
515               65536.0 * 64.0 / hints->y_scale,
516               10.0 * hints->y_scale / 65536.0 / 64.0));
517 
518     if (axis->num_edges)
519     {
520       TA_LOG(("  index    pos     dir   link  serif"
521            /* "  XXXXX  XXXX.XX  XXXXX  XXXX  XXXXX" */
522               "  blue    opos     pos       flags\n"));
523            /* "    X   XXXX.XX  XXXX.XX  XXXXXXXXXXX" */
524     }
525     else
526       TA_LOG(("  (none)\n"));
527 
528     for (edge = edges; edge < limit; edge++)
529       TA_LOG(("  %5d  %7.2f  %5s  %4s  %5s"
530               "    %c   %7.2f  %7.2f  %11s\n",
531               TA_INDEX_NUM(edge, edges),
532               (int)edge->opos / 64.0,
533               ta_dir_str((TA_Direction)edge->dir),
534               ta_print_idx(buf1, TA_INDEX_NUM(edge->link, edges)),
535               ta_print_idx(buf2, TA_INDEX_NUM(edge->serif, edges)),
536 
537               edge->blue_edge ? 'y' : 'n',
538               edge->opos / 64.0,
539               edge->pos / 64.0,
540               ta_edge_flags_to_string(edge->flags)));
541     TA_LOG(("\n"));
542   }
543 }
544 
545 #endif /* TA_DEBUG */
546 
547 
548 /* compute the direction value of a given vector */
549 
550 TA_Direction
ta_direction_compute(FT_Pos dx,FT_Pos dy)551 ta_direction_compute(FT_Pos dx,
552                      FT_Pos dy)
553 {
554   FT_Pos ll, ss; /* long and short arm lengths */
555   TA_Direction dir; /* candidate direction */
556 
557 
558   if (dy >= dx)
559   {
560     if (dy >= -dx)
561     {
562       dir = TA_DIR_UP;
563       ll = dy;
564       ss = dx;
565     }
566     else
567     {
568       dir = TA_DIR_LEFT;
569       ll = -dx;
570       ss = dy;
571     }
572   }
573   else /* dy < dx */
574   {
575     if (dy >= -dx)
576     {
577       dir = TA_DIR_RIGHT;
578       ll = dx;
579       ss = dy;
580     }
581     else
582     {
583       dir = TA_DIR_DOWN;
584       ll = -dy;
585       ss = dx;
586     }
587   }
588 
589   /* return no direction if arm lengths do not differ enough */
590   /* (value 14 is heuristic, corresponding to approx. 4.1 degrees); */
591   /* the long arm is never negative */
592   if (ll <= 14 * TA_ABS(ss))
593     dir = TA_DIR_NONE;
594 
595   return dir;
596 }
597 
598 
599 void
ta_glyph_hints_init(TA_GlyphHints hints)600 ta_glyph_hints_init(TA_GlyphHints hints)
601 {
602   /* no need to initialize the embedded items */
603   memset(hints, 0, sizeof (*hints) - sizeof (hints->embedded));
604 }
605 
606 
607 void
ta_glyph_hints_done(TA_GlyphHints hints)608 ta_glyph_hints_done(TA_GlyphHints hints)
609 {
610   int dim;
611 
612 
613   if (!hints)
614     return;
615 
616   /* we don't need to free the segment and edge buffers */
617   /* since they are really within the hints->points array */
618   for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
619   {
620     TA_AxisHints axis = &hints->axis[dim];
621 
622 
623     axis->num_segments = 0;
624     axis->max_segments = 0;
625     if (axis->segments != axis->embedded.segments)
626     {
627       free(axis->segments);
628       axis->segments = NULL;
629     }
630 
631     axis->num_edges = 0;
632     axis->max_edges = 0;
633     if (axis->edges != axis->embedded.edges)
634     {
635       free(axis->edges);
636       axis->edges = NULL;
637     }
638   }
639 
640   if (hints->contours != hints->embedded.contours)
641   {
642     free(hints->contours);
643     hints->contours = NULL;
644   }
645   hints->max_contours = 0;
646   hints->num_contours = 0;
647 
648   if (hints->points != hints->embedded.points)
649   {
650     free(hints->points);
651     hints->points = NULL;
652   }
653   hints->max_points = 0;
654   hints->num_points = 0;
655 }
656 
657 
658 /* reset metrics */
659 
660 void
ta_glyph_hints_rescale(TA_GlyphHints hints,TA_StyleMetrics metrics)661 ta_glyph_hints_rescale(TA_GlyphHints hints,
662                        TA_StyleMetrics metrics)
663 {
664   hints->metrics = metrics;
665   hints->scaler_flags = metrics->scaler.flags;
666 }
667 
668 
669 /* from FreeType's ftcalc.c */
670 
671 static FT_Int
ta_corner_is_flat(FT_Pos in_x,FT_Pos in_y,FT_Pos out_x,FT_Pos out_y)672 ta_corner_is_flat(FT_Pos in_x,
673                   FT_Pos in_y,
674                   FT_Pos out_x,
675                   FT_Pos out_y)
676 {
677   FT_Pos ax = in_x;
678   FT_Pos ay = in_y;
679 
680   FT_Pos d_in, d_out, d_corner;
681 
682 
683   if (ax < 0)
684     ax = -ax;
685   if (ay < 0)
686     ay = -ay;
687   d_in = ax + ay;
688 
689   ax = out_x;
690   if (ax < 0)
691     ax = -ax;
692   ay = out_y;
693   if (ay < 0)
694     ay = -ay;
695   d_out = ax + ay;
696 
697   ax = out_x + in_x;
698   if (ax < 0)
699     ax = -ax;
700   ay = out_y + in_y;
701   if (ay < 0)
702     ay = -ay;
703   d_corner = ax + ay;
704 
705   return (d_in + d_out - d_corner) < (d_corner >> 4);
706 }
707 
708 
709 /* recompute all TA_Point in TA_GlyphHints */
710 /* from the definitions in a source outline */
711 
712 FT_Error
ta_glyph_hints_reload(TA_GlyphHints hints,FT_Outline * outline)713 ta_glyph_hints_reload(TA_GlyphHints hints,
714                       FT_Outline* outline)
715 {
716   FT_Error error = FT_Err_Ok;
717   TA_Point points;
718   FT_UInt old_max, new_max;
719 
720   FT_Fixed x_scale = hints->x_scale;
721   FT_Fixed y_scale = hints->y_scale;
722   FT_Pos x_delta = hints->x_delta;
723   FT_Pos y_delta = hints->y_delta;
724 
725 
726   hints->num_points = 0;
727   hints->num_contours = 0;
728 
729   hints->axis[0].num_segments = 0;
730   hints->axis[0].num_edges = 0;
731   hints->axis[1].num_segments = 0;
732   hints->axis[1].num_edges = 0;
733 
734   /* first of all, reallocate the contours array if necessary */
735   new_max = (FT_UInt)outline->n_contours;
736   old_max = (FT_UInt)hints->max_contours;
737 
738   if (new_max <= TA_CONTOURS_EMBEDDED)
739   {
740     if (!hints->contours)
741     {
742       hints->contours = hints->embedded.contours;
743       hints->max_contours = TA_CONTOURS_EMBEDDED;
744     }
745   }
746   else if (new_max > old_max)
747   {
748     TA_Point* contours_new;
749 
750 
751     if (hints->contours == hints->embedded.contours)
752       hints->contours = NULL;
753 
754     new_max = (new_max + 3) & ~3U; /* round up to a multiple of 4 */
755 
756     contours_new = (TA_Point*)realloc(hints->contours,
757                                       new_max * sizeof (TA_Point));
758     if (!contours_new)
759       return FT_Err_Out_Of_Memory;
760 
761     hints->contours = contours_new;
762     hints->max_contours = (FT_Int)new_max;
763   }
764 
765   /* reallocate the points arrays if necessary -- we reserve */
766   /* two additional point positions, used to hint metrics appropriately */
767   new_max = (FT_UInt)(outline->n_points + 2);
768   old_max = (FT_UInt)hints->max_points;
769 
770   if (new_max <= TA_POINTS_EMBEDDED)
771   {
772     if (!hints->points)
773     {
774       hints->points = hints->embedded.points;
775       hints->max_points = TA_POINTS_EMBEDDED;
776     }
777   }
778   else if (new_max > old_max)
779   {
780     TA_Point points_new;
781 
782 
783     if (hints->points == hints->embedded.points)
784       hints->points = NULL;
785 
786     new_max = (new_max + 2 + 7) & ~7U; /* round up to a multiple of 8 */
787 
788     points_new = (TA_Point)realloc(hints->points,
789                                    new_max * sizeof (TA_PointRec));
790     if (!points_new)
791       return FT_Err_Out_Of_Memory;
792 
793     hints->points = points_new;
794     hints->max_points = (FT_Int)new_max;
795   }
796 
797   hints->num_points = outline->n_points;
798   hints->num_contours = outline->n_contours;
799 
800   /* we can't rely on the value of `FT_Outline.flags' to know the fill */
801   /* direction used for a glyph, given that some fonts are broken */
802   /* (e.g. the Arphic ones); we thus recompute it each time we need to */
803 
804   hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_UP;
805   hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_LEFT;
806 
807   if (FT_Outline_Get_Orientation(outline) == FT_ORIENTATION_POSTSCRIPT)
808   {
809     hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_DOWN;
810     hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_RIGHT;
811   }
812 
813   hints->x_scale = x_scale;
814   hints->y_scale = y_scale;
815   hints->x_delta = x_delta;
816   hints->y_delta = y_delta;
817 
818   hints->xmin_delta = 0;
819   hints->xmax_delta = 0;
820 
821   points = hints->points;
822   if (hints->num_points == 0)
823     goto Exit;
824 
825   {
826     TA_Point point;
827     TA_Point point_limit = points + hints->num_points;
828 
829 
830     /* compute coordinates & Bezier flags, next and prev */
831     {
832       FT_Vector* vec = outline->points;
833       char* tag = outline->tags;
834 
835       TA_Point end = points + outline->contours[0];
836       TA_Point prev = end;
837 
838       FT_Int contour_index = 0;
839 
840 
841       for (point = points; point < point_limit; point++, vec++, tag++)
842       {
843         point->in_dir = (FT_Char)TA_DIR_NONE;
844         point->out_dir = (FT_Char)TA_DIR_NONE;
845 
846         point->fx = (FT_Short)vec->x;
847         point->fy = (FT_Short)vec->y;
848         point->ox = point->x = FT_MulFix(vec->x, x_scale) + x_delta;
849         point->oy = point->y = FT_MulFix(vec->y, y_scale) + y_delta;
850 
851         switch (FT_CURVE_TAG(*tag))
852         {
853         case FT_CURVE_TAG_CONIC:
854           point->flags = TA_FLAG_CONIC;
855           break;
856         case FT_CURVE_TAG_CUBIC:
857           point->flags = TA_FLAG_CUBIC;
858           break;
859         default:
860           point->flags = TA_FLAG_NONE;
861         }
862 
863         point->prev = prev;
864         prev->next = point;
865         prev = point;
866 
867         if (point == end)
868         {
869           if (++contour_index < outline->n_contours)
870           {
871             end = points + outline->contours[contour_index];
872             prev = end;
873           }
874         }
875 
876 #ifdef TA_DEBUG
877         point->before[0] = NULL;
878         point->before[1] = NULL;
879         point->after[0] = NULL;
880         point->after[1] = NULL;
881 #endif
882       }
883     }
884 
885     /* set up the contours array */
886     {
887       TA_Point* contour = hints->contours;
888       TA_Point* contour_limit = contour + hints->num_contours;
889 
890       short* end = outline->contours;
891       short idx = 0;
892 
893 
894       for (; contour < contour_limit; contour++, end++)
895       {
896         contour[0] = points + idx;
897         idx = (short)(end[0] + 1);
898       }
899     }
900 
901     {
902       /*
903        *  Compute directions of `in' and `out' vectors.
904        *
905        *  Note that distances between points that are very near to each
906        *  other are accumulated.  In other words, the auto-hinter
907        *  prepends the small vectors between near points to the first
908        *  non-near vector.  All intermediate points are tagged as
909        *  weak; the directions are adjusted also to be equal to the
910        *  accumulated one.
911        */
912 
913       /* value 20 in `near_limit' is heuristic */
914       FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
915       FT_Int near_limit = 20 * units_per_em / 2048;
916       FT_Int near_limit2 = 2 * near_limit - 1;
917 
918       TA_Point* contour;
919       TA_Point* contour_limit = hints->contours + hints->num_contours;
920 
921 
922       for (contour = hints->contours; contour < contour_limit; contour++)
923       {
924         TA_Point first = *contour;
925         TA_Point next, prev, curr;
926 
927         FT_Pos out_x, out_y;
928 
929 
930         /* since the first point of a contour could be part of a */
931         /* series of near points, go backwards to find the first */
932         /* non-near point and adjust `first' */
933 
934         point = first;
935         prev = first->prev;
936 
937         while (prev != first)
938         {
939           out_x = point->fx - prev->fx;
940           out_y = point->fy - prev->fy;
941 
942           /*
943            *  We use Taxicab metrics to measure the vector length.
944            *
945            *  Note that the accumulated distances so far could have the
946            *  opposite direction of the distance measured here.  For this
947            *  reason we use `near_limit2' for the comparison to get a
948            *  non-near point even in the worst case.
949            */
950           if (TA_ABS(out_x) + TA_ABS(out_y) >= near_limit2)
951             break;
952 
953           point = prev;
954           prev = prev->prev;
955         }
956 
957         /* adjust first point */
958         first = point;
959 
960         /* now loop over all points of the contour to get */
961         /* `in' and `out' vector directions */
962 
963         curr = first;
964 
965         /*
966          *  We abuse the `u' and `v' fields to store index deltas to the
967          *  next and previous non-near point, respectively.
968          *
969          *  To avoid problems with not having non-near points, we point to
970          *  `first' by default as the next non-near point.
971          */
972         curr->u = (FT_Pos)(first - curr);
973         first->v = -curr->u;
974 
975         out_x = 0;
976         out_y = 0;
977 
978         next = first;
979         do
980         {
981           TA_Direction out_dir;
982 
983 
984           point = next;
985           next = point->next;
986 
987           out_x += next->fx - point->fx;
988           out_y += next->fy - point->fy;
989 
990           if (TA_ABS(out_x) + TA_ABS(out_y) < near_limit)
991           {
992             next->flags |= TA_FLAG_WEAK_INTERPOLATION;
993             continue;
994           }
995 
996           curr->u = (FT_Pos)(next - curr);
997           next->v = -curr->u;
998 
999           out_dir = ta_direction_compute(out_x, out_y);
1000 
1001           /* adjust directions for all points inbetween; */
1002           /* the loop also updates position of `curr' */
1003           curr->out_dir = (FT_Char)out_dir;
1004           for (curr = curr->next; curr != next; curr = curr->next)
1005           {
1006             curr->in_dir = (FT_Char)out_dir;
1007             curr->out_dir = (FT_Char)out_dir;
1008           }
1009           next->in_dir = (FT_Char)out_dir;
1010 
1011           curr->u = (FT_Pos)(first - curr);
1012           first->v = -curr->u;
1013 
1014           out_x = 0;
1015           out_y = 0;
1016 
1017         } while (next != first);
1018       }
1019 
1020       /*
1021        *  The next step is to `simplify' an outline's topology so that we
1022        *  can identify local extrema more reliably: A series of
1023        *  non-horizontal or non-vertical vectors pointing into the same
1024        *  quadrant are handled as a single, long vector.  From a
1025        *  topological point of the view, the intermediate points are of no
1026        *  interest and thus tagged as weak.
1027        */
1028 
1029       for (point = points; point < point_limit; point++)
1030       {
1031         if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1032           continue;
1033 
1034         if (point->in_dir == TA_DIR_NONE
1035             && point->out_dir == TA_DIR_NONE)
1036         {
1037           /* check whether both vectors point into the same quadrant */
1038 
1039           FT_Pos in_x, in_y;
1040           FT_Pos out_x, out_y;
1041 
1042           TA_Point next_u = point + point->u;
1043           TA_Point prev_v = point + point->v;
1044 
1045 
1046           in_x = point->fx - prev_v->fx;
1047           in_y = point->fy - prev_v->fy;
1048 
1049           out_x = next_u->fx - point->fx;
1050           out_y = next_u->fy - point->fy;
1051 
1052           if ((in_x ^ out_x) >= 0 && (in_y ^ out_y) >= 0)
1053           {
1054             /* yes, so tag current point as weak */
1055             /* and update index deltas */
1056 
1057             point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1058 
1059             prev_v->u = (FT_Pos)(next_u - prev_v);
1060             next_u->v = -prev_v->u;
1061           }
1062         }
1063       }
1064 
1065       /*
1066        *  Finally, check for remaining weak points.  Everything else not
1067        *  collected in edges so far is then implicitly classified as strong
1068        *  points.
1069        */
1070 
1071       for (point = points; point < point_limit; point++)
1072       {
1073         if (point->flags & TA_FLAG_WEAK_INTERPOLATION)
1074           continue;
1075 
1076         if (point->flags & TA_FLAG_CONTROL)
1077         {
1078           /* control points are always weak */
1079         Is_Weak_Point:
1080           point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1081         }
1082         else if (point->out_dir == point->in_dir)
1083         {
1084           if (point->out_dir != TA_DIR_NONE)
1085           {
1086             /* current point lies on a horizontal or */
1087             /* vertical segment (but doesn't start or end it) */
1088             goto Is_Weak_Point;
1089           }
1090 
1091           {
1092             TA_Point next_u = point + point->u;
1093             TA_Point prev_v = point + point->v;
1094 
1095 
1096             if (ta_corner_is_flat(point->fx - prev_v->fx,
1097                                   point->fy - prev_v->fy,
1098                                   next_u->fx - point->fx,
1099                                   next_u->fy - point->fy))
1100             {
1101               /* either the `in' or the `out' vector is much more */
1102               /* dominant than the other one, so tag current point */
1103               /* as weak and update index deltas */
1104 
1105               prev_v->u = (FT_Pos)(next_u - prev_v);
1106               next_u->v = -prev_v->u;
1107 
1108               goto Is_Weak_Point;
1109             }
1110           }
1111         }
1112         else if (point->in_dir == -point->out_dir)
1113         {
1114           /* current point forms a spike */
1115           goto Is_Weak_Point;
1116         }
1117       }
1118     }
1119   }
1120 
1121   /* change some directions at the user's request */
1122   /* to make ttfautohint insert one-point segments */
1123   /* or remove points from segments */
1124   {
1125     FONT* font;
1126     FT_Int idx;
1127     TA_Direction dir;
1128     int left_offset;
1129     int right_offset;
1130 
1131 
1132     /* `globals' is not set up while initializing metrics, */
1133     /* so exit early in this case */
1134     if (!hints->metrics->globals)
1135       goto Exit;
1136 
1137     font = hints->metrics->globals->font;
1138 
1139     /* start conditions are set with `TA_control_segment_dir_collect' */
1140     while (TA_control_segment_dir_get_next(font, &idx, &dir,
1141                                            &left_offset, &right_offset))
1142     {
1143       TA_Point point = &points[idx];
1144 
1145 
1146       point->out_dir = dir;
1147       if (dir == TA_DIR_NONE)
1148         point->flags |= TA_FLAG_WEAK_INTERPOLATION;
1149       else
1150         point->flags &= ~TA_FLAG_WEAK_INTERPOLATION;
1151       point->left_offset = (FT_Short)left_offset;
1152       point->right_offset = (FT_Short)right_offset;
1153     }
1154   }
1155 
1156 Exit:
1157   return error;
1158 }
1159 
1160 
1161 /* store the hinted outline in an FT_Outline structure */
1162 
1163 void
ta_glyph_hints_save(TA_GlyphHints hints,FT_Outline * outline)1164 ta_glyph_hints_save(TA_GlyphHints hints,
1165                     FT_Outline* outline)
1166 {
1167   TA_Point point = hints->points;
1168   TA_Point limit = point + hints->num_points;
1169 
1170   FT_Vector* vec = outline->points;
1171   char* tag = outline->tags;
1172 
1173 
1174   for (; point < limit; point++, vec++, tag++)
1175   {
1176     vec->x = point->x;
1177     vec->y = point->y;
1178 
1179     if (point->flags & TA_FLAG_CONIC)
1180       tag[0] = FT_CURVE_TAG_CONIC;
1181     else if (point->flags & TA_FLAG_CUBIC)
1182       tag[0] = FT_CURVE_TAG_CUBIC;
1183     else
1184       tag[0] = FT_CURVE_TAG_ON;
1185   }
1186 }
1187 
1188 
1189 /****************************************************************
1190  *
1191  *                     EDGE POINT GRID-FITTING
1192  *
1193  ****************************************************************/
1194 
1195 
1196 /* align all points of an edge to the same coordinate value, */
1197 /* either horizontally or vertically */
1198 
1199 void
ta_glyph_hints_align_edge_points(TA_GlyphHints hints,TA_Dimension dim)1200 ta_glyph_hints_align_edge_points(TA_GlyphHints hints,
1201                                  TA_Dimension dim)
1202 {
1203   TA_AxisHints axis = &hints->axis[dim];
1204   TA_Segment segments = axis->segments;
1205   TA_Segment segment_limit = segments + axis->num_segments;
1206   TA_Segment seg;
1207 
1208 
1209   if (dim == TA_DIMENSION_HORZ)
1210   {
1211     for (seg = segments; seg < segment_limit; seg++)
1212     {
1213       TA_Edge edge = seg->edge;
1214       TA_Point point, first, last;
1215 
1216 
1217       if (!edge)
1218         continue;
1219 
1220       first = seg->first;
1221       last = seg->last;
1222       point = first;
1223       for (;;)
1224       {
1225         point->x = edge->pos;
1226         point->flags |= TA_FLAG_TOUCH_X;
1227 
1228         if (point == last)
1229           break;
1230 
1231         point = point->next;
1232       }
1233     }
1234   }
1235   else
1236   {
1237     for (seg = segments; seg < segment_limit; seg++)
1238     {
1239       TA_Edge edge = seg->edge;
1240       TA_Point point, first, last;
1241 
1242 
1243       if (!edge)
1244         continue;
1245 
1246       first = seg->first;
1247       last = seg->last;
1248       point = first;
1249       for (;;)
1250       {
1251         point->y = edge->pos;
1252         point->flags |= TA_FLAG_TOUCH_Y;
1253 
1254         if (point == last)
1255           break;
1256 
1257         point = point->next;
1258       }
1259     }
1260   }
1261 }
1262 
1263 
1264 /****************************************************************
1265  *
1266  *                    STRONG POINT INTERPOLATION
1267  *
1268  ****************************************************************/
1269 
1270 
1271 /* hint the strong points -- */
1272 /* this is equivalent to the TrueType `IP' hinting instruction */
1273 
1274 void
ta_glyph_hints_align_strong_points(TA_GlyphHints hints,TA_Dimension dim)1275 ta_glyph_hints_align_strong_points(TA_GlyphHints hints,
1276                                    TA_Dimension dim)
1277 {
1278   TA_Point points = hints->points;
1279   TA_Point point_limit = points + hints->num_points;
1280 
1281   TA_AxisHints axis = &hints->axis[dim];
1282 
1283   TA_Edge edges = axis->edges;
1284   TA_Edge edge_limit = edges + axis->num_edges;
1285 
1286   FT_UShort touch_flag;
1287 
1288 
1289   if (dim == TA_DIMENSION_HORZ)
1290     touch_flag = TA_FLAG_TOUCH_X;
1291   else
1292     touch_flag = TA_FLAG_TOUCH_Y;
1293 
1294   if (edges < edge_limit)
1295   {
1296     TA_Point point;
1297     TA_Edge edge;
1298 
1299 
1300     for (point = points; point < point_limit; point++)
1301     {
1302       FT_Pos u, ou, fu; /* point position */
1303       FT_Pos delta;
1304 
1305 
1306       if (point->flags & touch_flag)
1307         continue;
1308 
1309       /* if this point is candidate to weak interpolation, we */
1310       /* interpolate it after all strong points have been processed */
1311 
1312       if ((point->flags & TA_FLAG_WEAK_INTERPOLATION))
1313         continue;
1314 
1315       if (dim == TA_DIMENSION_VERT)
1316       {
1317         u = point->fy;
1318         ou = point->oy;
1319       }
1320       else
1321       {
1322         u = point->fx;
1323         ou = point->ox;
1324       }
1325 
1326       fu = u;
1327 
1328       /* is the point before the first edge? */
1329       edge = edges;
1330       delta = edge->fpos - u;
1331       if (delta >= 0)
1332       {
1333         u = edge->pos - (edge->opos - ou);
1334 
1335 #ifdef TA_DEBUG
1336         point->before[dim] = edge;
1337         point->after[dim] = NULL;
1338 #endif
1339 
1340         if (hints->recorder)
1341           hints->recorder(ta_ip_before, hints, dim,
1342                           point, NULL, NULL, NULL, NULL);
1343 
1344         goto Store_Point;
1345       }
1346 
1347       /* is the point after the last edge? */
1348       edge = edge_limit - 1;
1349       delta = u - edge->fpos;
1350       if (delta >= 0)
1351       {
1352         u = edge->pos + (ou - edge->opos);
1353 
1354 #ifdef TA_DEBUG
1355         point->before[dim] = NULL;
1356         point->after[dim] = edge;
1357 #endif
1358 
1359         if (hints->recorder)
1360           hints->recorder(ta_ip_after, hints, dim,
1361                           point, NULL, NULL, NULL, NULL);
1362 
1363         goto Store_Point;
1364       }
1365 
1366       {
1367         FT_PtrDist min, max, mid;
1368         FT_Pos fpos;
1369 
1370 
1371         /* find enclosing edges */
1372         min = 0;
1373         max = edge_limit - edges;
1374 
1375         /* for a small number of edges, a linear search is better */
1376         if (max <= 8)
1377         {
1378           FT_PtrDist nn;
1379 
1380 
1381           for (nn = 0; nn < max; nn++)
1382             if (edges[nn].fpos >= u)
1383               break;
1384 
1385           if (edges[nn].fpos == u)
1386           {
1387             u = edges[nn].pos;
1388 
1389             if (hints->recorder)
1390               hints->recorder(ta_ip_on, hints, dim,
1391                               point, &edges[nn], NULL, NULL, NULL);
1392 
1393             goto Store_Point;
1394           }
1395           min = nn;
1396         }
1397         else
1398           while (min < max)
1399           {
1400             mid = (max + min) >> 1;
1401             edge = edges + mid;
1402             fpos = edge->fpos;
1403 
1404             if (u < fpos)
1405               max = mid;
1406             else if (u > fpos)
1407               min = mid + 1;
1408             else
1409             {
1410               /* we are on the edge */
1411               u = edge->pos;
1412 
1413 #ifdef TA_DEBUG
1414               point->before[dim] = NULL;
1415               point->after[dim] = NULL;
1416 #endif
1417 
1418               if (hints->recorder)
1419                 hints->recorder(ta_ip_on, hints, dim,
1420                                 point, edge, NULL, NULL, NULL);
1421 
1422               goto Store_Point;
1423             }
1424           }
1425 
1426         /* point is not on an edge */
1427         {
1428           TA_Edge before = edges + min - 1;
1429           TA_Edge after = edges + min + 0;
1430 
1431 
1432 #ifdef TA_DEBUG
1433           point->before[dim] = before;
1434           point->after[dim] = after;
1435 #endif
1436 
1437           /* assert(before && after && before != after) */
1438           if (before->scale == 0)
1439             before->scale = FT_DivFix(after->pos - before->pos,
1440                                       after->fpos - before->fpos);
1441 
1442           u = before->pos + FT_MulFix(fu - before->fpos,
1443                                       before->scale);
1444 
1445           if (hints->recorder)
1446             hints->recorder(ta_ip_between, hints, dim,
1447                             point, before, after, NULL, NULL);
1448         }
1449       }
1450 
1451     Store_Point:
1452       /* save the point position */
1453       if (dim == TA_DIMENSION_HORZ)
1454         point->x = u;
1455       else
1456         point->y = u;
1457 
1458       point->flags |= touch_flag;
1459     }
1460   }
1461 }
1462 
1463 
1464 /****************************************************************
1465  *
1466  *                    WEAK POINT INTERPOLATION
1467  *
1468  ****************************************************************/
1469 
1470 
1471 /* shift the original coordinates of all points between `p1' and */
1472 /* `p2' to get hinted coordinates, using the same difference as */
1473 /* given by `ref' */
1474 
1475 static void
ta_iup_shift(TA_Point p1,TA_Point p2,TA_Point ref)1476 ta_iup_shift(TA_Point p1,
1477              TA_Point p2,
1478              TA_Point ref)
1479 {
1480   TA_Point p;
1481   FT_Pos delta = ref->u - ref->v;
1482 
1483 
1484   if (delta == 0)
1485     return;
1486 
1487   for (p = p1; p < ref; p++)
1488     p->u = p->v + delta;
1489 
1490   for (p = ref + 1; p <= p2; p++)
1491     p->u = p->v + delta;
1492 }
1493 
1494 
1495 /* interpolate the original coordinates of all points between `p1' and */
1496 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1497 /* reference points; the `u' and `v' members are the current and */
1498 /* original coordinate values, respectively. */
1499 
1500 /* details can be found in the TrueType bytecode specification */
1501 
1502 static void
ta_iup_interp(TA_Point p1,TA_Point p2,TA_Point ref1,TA_Point ref2)1503 ta_iup_interp(TA_Point p1,
1504               TA_Point p2,
1505               TA_Point ref1,
1506               TA_Point ref2)
1507 {
1508   TA_Point p;
1509   FT_Pos u, v1, v2, u1, u2, d1, d2;
1510 
1511 
1512   if (p1 > p2)
1513     return;
1514 
1515   if (ref1->v > ref2->v)
1516   {
1517     p = ref1;
1518     ref1 = ref2;
1519     ref2 = p;
1520   }
1521 
1522   v1 = ref1->v;
1523   v2 = ref2->v;
1524   u1 = ref1->u;
1525   u2 = ref2->u;
1526   d1 = u1 - v1;
1527   d2 = u2 - v2;
1528 
1529   if (u1 == u2 || v1 == v2)
1530   {
1531     for (p = p1; p <= p2; p++)
1532     {
1533       u = p->v;
1534 
1535       if (u <= v1)
1536         u += d1;
1537       else if (u >= v2)
1538         u += d2;
1539       else
1540         u = u1;
1541 
1542       p->u = u;
1543     }
1544   }
1545   else
1546   {
1547     FT_Fixed scale = FT_DivFix(u2 - u1, v2 - v1);
1548 
1549 
1550     for (p = p1; p <= p2; p++)
1551     {
1552       u = p->v;
1553 
1554       if (u <= v1)
1555         u += d1;
1556       else if (u >= v2)
1557         u += d2;
1558       else
1559         u = u1 + FT_MulFix(u - v1, scale);
1560 
1561       p->u = u;
1562     }
1563   }
1564 }
1565 
1566 
1567 /* hint the weak points -- */
1568 /* this is equivalent to the TrueType `IUP' hinting instruction */
1569 
1570 void
ta_glyph_hints_align_weak_points(TA_GlyphHints hints,TA_Dimension dim)1571 ta_glyph_hints_align_weak_points(TA_GlyphHints hints,
1572                                  TA_Dimension dim)
1573 {
1574   TA_Point points = hints->points;
1575   TA_Point point_limit = points + hints->num_points;
1576 
1577   TA_Point* contour = hints->contours;
1578   TA_Point* contour_limit = contour + hints->num_contours;
1579 
1580   FT_UShort touch_flag;
1581   TA_Point point;
1582   TA_Point end_point;
1583   TA_Point first_point;
1584 
1585 
1586   /* pass 1: move segment points to edge positions */
1587 
1588   if (dim == TA_DIMENSION_HORZ)
1589   {
1590     touch_flag = TA_FLAG_TOUCH_X;
1591 
1592     for (point = points; point < point_limit; point++)
1593     {
1594       point->u = point->x;
1595       point->v = point->ox;
1596     }
1597   }
1598   else
1599   {
1600     touch_flag = TA_FLAG_TOUCH_Y;
1601 
1602     for (point = points; point < point_limit; point++)
1603     {
1604       point->u = point->y;
1605       point->v = point->oy;
1606     }
1607   }
1608 
1609   for (; contour < contour_limit; contour++)
1610   {
1611     TA_Point first_touched, last_touched;
1612 
1613 
1614     point = *contour;
1615     end_point = point->prev;
1616     first_point = point;
1617 
1618     /* find first touched point */
1619     for (;;)
1620     {
1621       if (point > end_point) /* no touched point in contour */
1622         goto NextContour;
1623 
1624       if (point->flags & touch_flag)
1625         break;
1626 
1627       point++;
1628     }
1629 
1630     first_touched = point;
1631 
1632     for (;;)
1633     {
1634       /* skip any touched neighbours */
1635       while (point < end_point
1636              && (point[1].flags & touch_flag) != 0)
1637         point++;
1638 
1639       last_touched = point;
1640 
1641       /* find the next touched point, if any */
1642       point++;
1643       for (;;)
1644       {
1645         if (point > end_point)
1646           goto EndContour;
1647 
1648         if ((point->flags & touch_flag) != 0)
1649           break;
1650 
1651         point++;
1652       }
1653 
1654       /* interpolate between last_touched and point */
1655       ta_iup_interp(last_touched + 1, point - 1,
1656                     last_touched, point);
1657     }
1658 
1659   EndContour:
1660     /* special case: only one point was touched */
1661     if (last_touched == first_touched)
1662       ta_iup_shift(first_point, end_point, first_touched);
1663 
1664     else /* interpolate the last part */
1665     {
1666       if (last_touched < end_point)
1667         ta_iup_interp(last_touched + 1, end_point,
1668                       last_touched, first_touched);
1669 
1670       if (first_touched > points)
1671         ta_iup_interp(first_point, first_touched - 1,
1672                       last_touched, first_touched);
1673     }
1674 
1675   NextContour:
1676     ;
1677   }
1678 
1679   /* now save the interpolated values back to x/y */
1680   if (dim == TA_DIMENSION_HORZ)
1681   {
1682     for (point = points; point < point_limit; point++)
1683       point->x = point->u;
1684   }
1685   else
1686   {
1687     for (point = points; point < point_limit; point++)
1688       point->y = point->u;
1689   }
1690 }
1691 
1692 
1693 #ifdef TA_CONFIG_OPTION_USE_WARPER
1694 
1695 /* apply (small) warp scale and warp delta for given dimension */
1696 
1697 static void
ta_glyph_hints_scale_dim(TA_GlyphHints hints,TA_Dimension dim,FT_Fixed scale,FT_Pos delta)1698 ta_glyph_hints_scale_dim(TA_GlyphHints hints,
1699                          TA_Dimension dim,
1700                          FT_Fixed scale,
1701                          FT_Pos delta)
1702 {
1703   TA_Point points = hints->points;
1704   TA_Point points_limit = points + hints->num_points;
1705   TA_Point point;
1706 
1707 
1708   if (dim == TA_DIMENSION_HORZ)
1709   {
1710     for (point = points; point < points_limit; point++)
1711       point->x = FT_MulFix(point->fx, scale) + delta;
1712   }
1713   else
1714   {
1715     for (point = points; point < points_limit; point++)
1716       point->y = FT_MulFix(point->fy, scale) + delta;
1717   }
1718 }
1719 
1720 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1721 
1722 /* end of tahints.c */
1723