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