Synchronize with FreeType.
[ttfautohint.git] / lib / tahints.c
blobe7ba9c4258ae6ded566e5dc11a96059bca63482f
1 /* tahints.c */
3 /*
4 * Copyright (C) 2011-2014 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 <string.h>
21 #include <stdlib.h>
22 #include "tahints.h"
25 /* get new segment for given axis */
27 FT_Error
28 ta_axis_hints_new_segment(TA_AxisHints axis,
29 TA_Segment* asegment)
31 FT_Error error = FT_Err_Ok;
32 TA_Segment segment = NULL;
35 if (axis->num_segments >= axis->max_segments)
37 TA_Segment segments_new;
39 FT_Int old_max = axis->max_segments;
40 FT_Int new_max = old_max;
41 FT_Int big_max = (FT_Int)(FT_INT_MAX / sizeof (*segment));
44 if (old_max >= big_max)
46 error = FT_Err_Out_Of_Memory;
47 goto Exit;
50 new_max += (new_max >> 2) + 4;
51 if (new_max < old_max
52 || new_max > big_max)
53 new_max = big_max;
55 segments_new = (TA_Segment)realloc(axis->segments,
56 new_max * sizeof (TA_SegmentRec));
57 if (!segments_new)
58 return FT_Err_Out_Of_Memory;
60 axis->segments = segments_new;
61 axis->max_segments = new_max;
64 segment = axis->segments + axis->num_segments++;
66 Exit:
67 *asegment = segment;
68 return error;
72 /* get new edge for given axis, direction, and position */
74 FT_Error
75 ta_axis_hints_new_edge(TA_AxisHints axis,
76 FT_Int fpos,
77 TA_Direction dir,
78 TA_Edge* anedge)
80 FT_Error error = FT_Err_Ok;
81 TA_Edge edge = NULL;
82 TA_Edge edges;
85 if (axis->num_edges >= axis->max_edges)
87 TA_Edge edges_new;
89 FT_Int old_max = axis->max_edges;
90 FT_Int new_max = old_max;
91 FT_Int big_max = (FT_Int)(FT_INT_MAX / sizeof (*edge));
94 if (old_max >= big_max)
96 error = FT_Err_Out_Of_Memory;
97 goto Exit;
100 new_max += (new_max >> 2) + 4;
101 if (new_max < old_max
102 || new_max > big_max)
103 new_max = big_max;
105 edges_new = (TA_Edge)realloc(axis->edges,
106 new_max * sizeof (TA_EdgeRec));
107 if (!edges_new)
108 return FT_Err_Out_Of_Memory;
110 axis->edges = edges_new;
111 axis->max_edges = new_max;
114 edges = axis->edges;
115 edge = edges + axis->num_edges;
117 while (edge > edges)
119 if (edge[-1].fpos < fpos)
120 break;
122 /* we want the edge with same position and minor direction */
123 /* to appear before those in the major one in the list */
124 if (edge[-1].fpos == fpos
125 && dir == axis->major_dir)
126 break;
128 edge[0] = edge[-1];
129 edge--;
132 axis->num_edges++;
134 memset(edge, 0, sizeof (TA_EdgeRec));
135 edge->fpos = (FT_Short)fpos;
136 edge->dir = (FT_Char)dir;
138 Exit:
139 *anedge = edge;
140 return error;
144 #ifdef TA_DEBUG
146 #include <stdio.h>
147 #include <stdarg.h>
148 #include <string.h>
151 void
152 _ta_message(const char* format,
153 ...)
155 va_list ap;
158 va_start(ap, format);
159 vfprintf(stderr, format, ap);
160 va_end(ap);
164 static const char*
165 ta_dir_str(TA_Direction dir)
167 const char* result;
170 switch (dir)
172 case TA_DIR_UP:
173 result = "up";
174 break;
175 case TA_DIR_DOWN:
176 result = "down";
177 break;
178 case TA_DIR_LEFT:
179 result = "left";
180 break;
181 case TA_DIR_RIGHT:
182 result = "right";
183 break;
184 default:
185 result = "none";
188 return result;
192 #define TA_INDEX_NUM(ptr, base) \
193 ((ptr) ? ((ptr) - (base)) \
194 : -1)
197 void
198 ta_glyph_hints_dump_points(TA_GlyphHints hints)
200 TA_Point points = hints->points;
201 TA_Point limit = points + hints->num_points;
202 TA_Point point;
205 TA_LOG(("Table of points:\n"
206 " [ index | xorg | yorg | xscale | yscale"
207 " | xfit | yfit | flags ]\n"));
209 for (point = points; point < limit; point++)
210 TA_LOG((" [ %5d | %5d | %5d | %6.2f | %6.2f"
211 " | %5.2f | %5.2f | %c%c%c%c%c%c ]\n",
212 point - points,
213 point->fx,
214 point->fy,
215 point->ox / 64.0,
216 point->oy / 64.0,
217 point->x / 64.0,
218 point->y / 64.0,
219 (point->flags & TA_FLAG_WEAK_INTERPOLATION) ? 'w' : ' ',
220 (point->flags & TA_FLAG_INFLECTION) ? 'i' : ' ',
221 (point->flags & TA_FLAG_EXTREMA_X) ? '<' : ' ',
222 (point->flags & TA_FLAG_EXTREMA_Y) ? 'v' : ' ',
223 (point->flags & TA_FLAG_ROUND_X) ? '(' : ' ',
224 (point->flags & TA_FLAG_ROUND_Y) ? 'u' : ' '));
225 TA_LOG(("\n"));
229 static const char*
230 ta_edge_flags_to_string(FT_Byte flags)
232 static char temp[32];
233 int pos = 0;
236 if (flags & TA_EDGE_ROUND)
238 memcpy(temp + pos, "round", 5);
239 pos += 5;
241 if (flags & TA_EDGE_SERIF)
243 if (pos > 0)
244 temp[pos++] = ' ';
245 memcpy(temp + pos, "serif", 5);
246 pos += 5;
248 if (pos == 0)
249 return "normal";
251 temp[pos] = '\0';
253 return temp;
257 /* dump the array of linked segments */
259 void
260 ta_glyph_hints_dump_segments(TA_GlyphHints hints)
262 FT_Int dimension;
265 for (dimension = TA_DEBUG_STARTDIM;
266 dimension >= TA_DEBUG_ENDDIM;
267 dimension--)
269 TA_AxisHints axis = &hints->axis[dimension];
270 TA_Point points = hints->points;
271 TA_Edge edges = axis->edges;
272 TA_Segment segments = axis->segments;
273 TA_Segment limit = segments + axis->num_segments;
274 TA_Segment seg;
277 TA_LOG(("Table of %s segments:\n",
278 dimension == TA_DIMENSION_HORZ ? "vertical"
279 : "horizontal"));
280 if (axis->num_segments)
281 TA_LOG((" [ index | pos | dir | from"
282 " | to | link | serif | edge"
283 " | height | extra | flags ]\n"));
284 else
285 TA_LOG((" (none)\n"));
287 for (seg = segments; seg < limit; seg++)
288 TA_LOG((" [ %5d | %5.2g | %5s | %4d"
289 " | %4d | %4d | %5d | %4d"
290 " | %6d | %5d | %11s ]\n",
291 seg - segments,
292 dimension == TA_DIMENSION_HORZ ? (int)seg->first->ox / 64.0
293 : (int)seg->first->oy / 64.0,
294 ta_dir_str((TA_Direction)seg->dir),
295 TA_INDEX_NUM(seg->first, points),
296 TA_INDEX_NUM(seg->last, points),
297 TA_INDEX_NUM(seg->link, segments),
298 TA_INDEX_NUM(seg->serif, segments),
299 TA_INDEX_NUM(seg->edge, edges),
300 seg->height,
301 seg->height - (seg->max_coord - seg->min_coord),
302 ta_edge_flags_to_string(seg->flags)));
303 TA_LOG(("\n"));
308 /* dump the array of linked edges */
310 void
311 ta_glyph_hints_dump_edges(TA_GlyphHints hints)
313 FT_Int dimension;
316 for (dimension = TA_DEBUG_STARTDIM;
317 dimension >= TA_DEBUG_ENDDIM;
318 dimension--)
320 TA_AxisHints axis = &hints->axis[dimension];
321 TA_Edge edges = axis->edges;
322 TA_Edge limit = edges + axis->num_edges;
323 TA_Edge edge;
326 /* note that TA_DIMENSION_HORZ corresponds to _vertical_ edges */
327 /* since they have a constant X coordinate */
328 TA_LOG(("Table of %s edges:\n",
329 dimension == TA_DIMENSION_HORZ ? "vertical"
330 : "horizontal"));
331 if (axis->num_edges)
332 TA_LOG((" [ index | pos | dir | link"
333 " | serif | blue | opos | pos | flags ]\n"));
334 else
335 TA_LOG((" (none)\n"));
337 for (edge = edges; edge < limit; edge++)
338 TA_LOG((" [ %5d | %5.2g | %5s | %4d"
339 " | %5d | %c | %5.2f | %5.2f | %11s ]\n",
340 edge - edges,
341 (int)edge->opos / 64.0,
342 ta_dir_str((TA_Direction)edge->dir),
343 TA_INDEX_NUM(edge->link, edges),
344 TA_INDEX_NUM(edge->serif, edges),
345 edge->blue_edge ? 'y' : 'n',
346 edge->opos / 64.0,
347 edge->pos / 64.0,
348 ta_edge_flags_to_string(edge->flags)));
349 TA_LOG(("\n"));
353 #endif /* TA_DEBUG */
356 /* compute the direction value of a given vector */
358 TA_Direction
359 ta_direction_compute(FT_Pos dx,
360 FT_Pos dy)
362 FT_Pos ll, ss; /* long and short arm lengths */
363 TA_Direction dir; /* candidate direction */
366 if (dy >= dx)
368 if (dy >= -dx)
370 dir = TA_DIR_UP;
371 ll = dy;
372 ss = dx;
374 else
376 dir = TA_DIR_LEFT;
377 ll = -dx;
378 ss = dy;
381 else /* dy < dx */
383 if (dy >= -dx)
385 dir = TA_DIR_RIGHT;
386 ll = dx;
387 ss = dy;
389 else
391 dir = TA_DIR_DOWN;
392 ll = dy;
393 ss = dx;
397 /* return no direction if arm lengths differ too much */
398 /* (value 14 is heuristic, corresponding to approx. 4.1 degrees) */
399 ss *= 14;
400 if (TA_ABS(ll) <= TA_ABS(ss))
401 dir = TA_DIR_NONE;
403 return dir;
407 void
408 ta_glyph_hints_init(TA_GlyphHints hints)
410 memset(hints, 0, sizeof (TA_GlyphHintsRec));
414 void
415 ta_glyph_hints_done(TA_GlyphHints hints)
417 int dim;
420 if (!hints)
421 return;
423 /* we don't need to free the segment and edge buffers */
424 /* since they are really within the hints->points array */
425 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
427 TA_AxisHints axis = &hints->axis[dim];
430 axis->num_segments = 0;
431 axis->max_segments = 0;
432 free(axis->segments);
433 axis->segments = NULL;
435 axis->num_edges = 0;
436 axis->max_edges = 0;
437 free(axis->edges);
438 axis->edges = NULL;
441 free(hints->contours);
442 hints->contours = NULL;
443 hints->max_contours = 0;
444 hints->num_contours = 0;
446 free(hints->points);
447 hints->points = NULL;
448 hints->num_points = 0;
449 hints->max_points = 0;
453 /* reset metrics */
455 void
456 ta_glyph_hints_rescale(TA_GlyphHints hints,
457 TA_StyleMetrics metrics)
459 hints->metrics = metrics;
460 hints->scaler_flags = metrics->scaler.flags;
464 /* from FreeType's ftcalc.c */
466 static FT_Int
467 ta_corner_is_flat(FT_Pos in_x,
468 FT_Pos in_y,
469 FT_Pos out_x,
470 FT_Pos out_y)
472 FT_Pos ax = in_x;
473 FT_Pos ay = in_y;
475 FT_Pos d_in, d_out, d_corner;
478 if (ax < 0)
479 ax = -ax;
480 if (ay < 0)
481 ay = -ay;
482 d_in = ax + ay;
484 ax = out_x;
485 if (ax < 0)
486 ax = -ax;
487 ay = out_y;
488 if (ay < 0)
489 ay = -ay;
490 d_out = ax + ay;
492 ax = out_x + in_x;
493 if (ax < 0)
494 ax = -ax;
495 ay = out_y + in_y;
496 if (ay < 0)
497 ay = -ay;
498 d_corner = ax + ay;
500 return (d_in + d_out - d_corner) < (d_corner >> 4);
504 /* recompute all TA_Point in TA_GlyphHints */
505 /* from the definitions in a source outline */
507 FT_Error
508 ta_glyph_hints_reload(TA_GlyphHints hints,
509 FT_Outline* outline)
511 FT_Error error = FT_Err_Ok;
512 TA_Point points;
513 FT_UInt old_max, new_max;
515 FT_Fixed x_scale = hints->x_scale;
516 FT_Fixed y_scale = hints->y_scale;
517 FT_Pos x_delta = hints->x_delta;
518 FT_Pos y_delta = hints->y_delta;
521 hints->num_points = 0;
522 hints->num_contours = 0;
524 hints->axis[0].num_segments = 0;
525 hints->axis[0].num_edges = 0;
526 hints->axis[1].num_segments = 0;
527 hints->axis[1].num_edges = 0;
529 /* first of all, reallocate the contours array if necessary */
530 new_max = (FT_UInt)outline->n_contours;
531 old_max = hints->max_contours;
532 if (new_max > old_max)
534 TA_Point* contours_new;
537 new_max = (new_max + 3) & ~3; /* round up to a multiple of 4 */
539 contours_new = (TA_Point*)realloc(hints->contours,
540 new_max * sizeof (TA_Point));
541 if (!contours_new)
542 return FT_Err_Out_Of_Memory;
544 hints->contours = contours_new;
545 hints->max_contours = new_max;
548 /* reallocate the points arrays if necessary -- we reserve */
549 /* two additional point positions, used to hint metrics appropriately */
550 new_max = (FT_UInt)(outline->n_points + 2);
551 old_max = hints->max_points;
552 if (new_max > old_max)
554 TA_Point points_new;
557 new_max = (new_max + 2 + 7) & ~7; /* round up to a multiple of 8 */
559 points_new = (TA_Point)realloc(hints->points,
560 new_max * sizeof (TA_PointRec));
561 if (!points_new)
562 return FT_Err_Out_Of_Memory;
564 hints->points = points_new;
565 hints->max_points = new_max;
568 hints->num_points = outline->n_points;
569 hints->num_contours = outline->n_contours;
571 /* we can't rely on the value of `FT_Outline.flags' to know the fill */
572 /* direction used for a glyph, given that some fonts are broken */
573 /* (e.g. the Arphic ones); we thus recompute it each time we need to */
575 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_UP;
576 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_LEFT;
578 if (FT_Outline_Get_Orientation(outline) == FT_ORIENTATION_POSTSCRIPT)
580 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_DOWN;
581 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_RIGHT;
584 hints->x_scale = x_scale;
585 hints->y_scale = y_scale;
586 hints->x_delta = x_delta;
587 hints->y_delta = y_delta;
589 hints->xmin_delta = 0;
590 hints->xmax_delta = 0;
592 points = hints->points;
593 if (hints->num_points == 0)
594 goto Exit;
597 TA_Point point;
598 TA_Point point_limit = points + hints->num_points;
601 /* compute coordinates & Bezier flags, next and prev */
603 FT_Vector* vec = outline->points;
604 char* tag = outline->tags;
606 TA_Point end = points + outline->contours[0];
607 TA_Point prev = end;
609 FT_Int contour_index = 0;
612 for (point = points; point < point_limit; point++, vec++, tag++)
614 point->fx = (FT_Short)vec->x;
615 point->fy = (FT_Short)vec->y;
616 point->ox = point->x = FT_MulFix(vec->x, x_scale) + x_delta;
617 point->oy = point->y = FT_MulFix(vec->y, y_scale) + y_delta;
619 switch (FT_CURVE_TAG(*tag))
621 case FT_CURVE_TAG_CONIC:
622 point->flags = TA_FLAG_CONIC;
623 break;
624 case FT_CURVE_TAG_CUBIC:
625 point->flags = TA_FLAG_CUBIC;
626 break;
627 default:
628 point->flags = TA_FLAG_NONE;
631 point->prev = prev;
632 prev->next = point;
633 prev = point;
635 if (point == end)
637 if (++contour_index < outline->n_contours)
639 end = points + outline->contours[contour_index];
640 prev = end;
646 /* set up the contours array */
648 TA_Point* contour = hints->contours;
649 TA_Point* contour_limit = contour + hints->num_contours;
651 short* end = outline->contours;
652 short idx = 0;
655 for (; contour < contour_limit; contour++, end++)
657 contour[0] = points + idx;
658 idx = (short)(end[0] + 1);
662 /* compute directions of in & out vectors */
664 TA_Point first = points;
665 TA_Point prev = NULL;
667 FT_Pos in_x = 0;
668 FT_Pos in_y = 0;
670 TA_Direction in_dir = TA_DIR_NONE;
672 FT_Pos last_good_in_x = 0;
673 FT_Pos last_good_in_y = 0;
675 FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
676 FT_Int near_limit = 20 * units_per_em / 2048;
679 for (point = points; point < point_limit; point++)
681 TA_Point next;
682 FT_Pos out_x, out_y;
685 if (point == first)
687 prev = first->prev;
689 in_x = first->fx - prev->fx;
690 in_y = first->fy - prev->fy;
692 last_good_in_x = in_x;
693 last_good_in_y = in_y;
695 if (TA_ABS(in_x) + TA_ABS(in_y) < near_limit)
697 /* search first non-near point to get a good `in_dir' value */
699 TA_Point point_ = prev;
702 while (point_ != first)
704 TA_Point prev_ = point_->prev;
706 FT_Pos in_x_ = point_->fx - prev_->fx;
707 FT_Pos in_y_ = point_->fy - prev_->fy;
710 if (TA_ABS(in_x_) + TA_ABS(in_y_) >= near_limit)
712 last_good_in_x = in_x_;
713 last_good_in_y = in_y_;
715 break;
718 point_ = prev_;
722 in_dir = ta_direction_compute(in_x, in_y);
723 first = prev + 1;
726 point->in_dir = (FT_Char)in_dir;
728 /* check whether the current point is near to the previous one */
729 /* (value 20 in `near_limit' is heuristic; we use Taxicab */
730 /* metrics for the test) */
732 if (TA_ABS(in_x) + TA_ABS(in_y) < near_limit)
733 point->flags |= TA_FLAG_NEAR;
734 else
736 last_good_in_x = in_x;
737 last_good_in_y = in_y;
740 next = point->next;
741 out_x = next->fx - point->fx;
742 out_y = next->fy - point->fy;
744 in_dir = ta_direction_compute(out_x, out_y);
745 point->out_dir = (FT_Char)in_dir;
747 /* Check for weak points. The remaining points not collected */
748 /* in edges are then implicitly classified as strong points. */
750 if (point->flags & TA_FLAG_CONTROL)
752 /* control points are always weak */
753 Is_Weak_Point:
754 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
756 else if (point->out_dir == point->in_dir)
758 if (point->out_dir != TA_DIR_NONE)
760 /* current point lies on a horizontal or */
761 /* vertical segment (but doesn't start or end it) */
762 goto Is_Weak_Point;
765 /* test whether `in' and `out' direction is approximately */
766 /* the same (and use the last good `in' vector in case */
767 /* the current point is near to the previous one) */
768 if (ta_corner_is_flat(point->flags & TA_FLAG_NEAR ? last_good_in_x
769 : in_x,
770 point->flags & TA_FLAG_NEAR ? last_good_in_y
771 : in_y,
772 out_x,
773 out_y))
775 /* current point lies on a straight, diagonal line */
776 /* (more or less) */
777 goto Is_Weak_Point;
780 else if (point->in_dir == -point->out_dir)
782 /* current point forms a spike */
783 goto Is_Weak_Point;
786 in_x = out_x;
787 in_y = out_y;
792 Exit:
793 return error;
797 /* store the hinted outline in an FT_Outline structure */
799 void
800 ta_glyph_hints_save(TA_GlyphHints hints,
801 FT_Outline* outline)
803 TA_Point point = hints->points;
804 TA_Point limit = point + hints->num_points;
806 FT_Vector* vec = outline->points;
807 char* tag = outline->tags;
810 for (; point < limit; point++, vec++, tag++)
812 vec->x = point->x;
813 vec->y = point->y;
815 if (point->flags & TA_FLAG_CONIC)
816 tag[0] = FT_CURVE_TAG_CONIC;
817 else if (point->flags & TA_FLAG_CUBIC)
818 tag[0] = FT_CURVE_TAG_CUBIC;
819 else
820 tag[0] = FT_CURVE_TAG_ON;
825 /****************************************************************
827 * EDGE POINT GRID-FITTING
829 ****************************************************************/
832 /* align all points of an edge to the same coordinate value, */
833 /* either horizontally or vertically */
835 void
836 ta_glyph_hints_align_edge_points(TA_GlyphHints hints,
837 TA_Dimension dim)
839 TA_AxisHints axis = &hints->axis[dim];
840 TA_Segment segments = axis->segments;
841 TA_Segment segment_limit = segments + axis->num_segments;
842 TA_Segment seg;
845 if (dim == TA_DIMENSION_HORZ)
847 for (seg = segments; seg < segment_limit; seg++)
849 TA_Edge edge = seg->edge;
850 TA_Point point, first, last;
853 if (edge == NULL)
854 continue;
856 first = seg->first;
857 last = seg->last;
858 point = first;
859 for (;;)
861 point->x = edge->pos;
862 point->flags |= TA_FLAG_TOUCH_X;
864 if (point == last)
865 break;
867 point = point->next;
871 else
873 for (seg = segments; seg < segment_limit; seg++)
875 TA_Edge edge = seg->edge;
876 TA_Point point, first, last;
879 if (edge == NULL)
880 continue;
882 first = seg->first;
883 last = seg->last;
884 point = first;
885 for (;;)
887 point->y = edge->pos;
888 point->flags |= TA_FLAG_TOUCH_Y;
890 if (point == last)
891 break;
893 point = point->next;
900 /****************************************************************
902 * STRONG POINT INTERPOLATION
904 ****************************************************************/
907 /* hint the strong points -- */
908 /* this is equivalent to the TrueType `IP' hinting instruction */
910 void
911 ta_glyph_hints_align_strong_points(TA_GlyphHints hints,
912 TA_Dimension dim)
914 TA_Point points = hints->points;
915 TA_Point point_limit = points + hints->num_points;
917 TA_AxisHints axis = &hints->axis[dim];
919 TA_Edge edges = axis->edges;
920 TA_Edge edge_limit = edges + axis->num_edges;
922 FT_UShort touch_flag;
925 if (dim == TA_DIMENSION_HORZ)
926 touch_flag = TA_FLAG_TOUCH_X;
927 else
928 touch_flag = TA_FLAG_TOUCH_Y;
930 if (edges < edge_limit)
932 TA_Point point;
933 TA_Edge edge;
936 for (point = points; point < point_limit; point++)
938 FT_Pos u, ou, fu; /* point position */
939 FT_Pos delta;
942 if (point->flags & touch_flag)
943 continue;
945 /* if this point is candidate to weak interpolation, we */
946 /* interpolate it after all strong points have been processed */
948 if ((point->flags & TA_FLAG_WEAK_INTERPOLATION)
949 && !(point->flags & TA_FLAG_INFLECTION))
950 continue;
952 if (dim == TA_DIMENSION_VERT)
954 u = point->fy;
955 ou = point->oy;
957 else
959 u = point->fx;
960 ou = point->ox;
963 fu = u;
965 /* is the point before the first edge? */
966 edge = edges;
967 delta = edge->fpos - u;
968 if (delta >= 0)
970 u = edge->pos - (edge->opos - ou);
972 if (hints->recorder)
973 hints->recorder(ta_ip_before, hints, dim,
974 point, NULL, NULL, NULL, NULL);
976 goto Store_Point;
979 /* is the point after the last edge? */
980 edge = edge_limit - 1;
981 delta = u - edge->fpos;
982 if (delta >= 0)
984 u = edge->pos + (ou - edge->opos);
986 if (hints->recorder)
987 hints->recorder(ta_ip_after, hints, dim,
988 point, NULL, NULL, NULL, NULL);
990 goto Store_Point;
994 FT_PtrDist min, max, mid;
995 FT_Pos fpos;
998 /* find enclosing edges */
999 min = 0;
1000 max = edge_limit - edges;
1002 /* for a small number of edges, a linear search is better */
1003 if (max <= 8)
1005 FT_PtrDist nn;
1008 for (nn = 0; nn < max; nn++)
1009 if (edges[nn].fpos >= u)
1010 break;
1012 if (edges[nn].fpos == u)
1014 u = edges[nn].pos;
1016 if (hints->recorder)
1017 hints->recorder(ta_ip_on, hints, dim,
1018 point, &edges[nn], NULL, NULL, NULL);
1020 goto Store_Point;
1022 min = nn;
1024 else
1025 while (min < max)
1027 mid = (max + min) >> 1;
1028 edge = edges + mid;
1029 fpos = edge->fpos;
1031 if (u < fpos)
1032 max = mid;
1033 else if (u > fpos)
1034 min = mid + 1;
1035 else
1037 /* we are on the edge */
1038 u = edge->pos;
1040 if (hints->recorder)
1041 hints->recorder(ta_ip_on, hints, dim,
1042 point, edge, NULL, NULL, NULL);
1044 goto Store_Point;
1048 /* point is not on an edge */
1050 TA_Edge before = edges + min - 1;
1051 TA_Edge after = edges + min + 0;
1054 /* assert(before && after && before != after) */
1055 if (before->scale == 0)
1056 before->scale = FT_DivFix(after->pos - before->pos,
1057 after->fpos - before->fpos);
1059 u = before->pos + FT_MulFix(fu - before->fpos,
1060 before->scale);
1062 if (hints->recorder)
1063 hints->recorder(ta_ip_between, hints, dim,
1064 point, before, after, NULL, NULL);
1068 Store_Point:
1069 /* save the point position */
1070 if (dim == TA_DIMENSION_HORZ)
1071 point->x = u;
1072 else
1073 point->y = u;
1075 point->flags |= touch_flag;
1081 /****************************************************************
1083 * WEAK POINT INTERPOLATION
1085 ****************************************************************/
1088 /* shift the original coordinates of all points between `p1' and */
1089 /* `p2' to get hinted coordinates, using the same difference as */
1090 /* given by `ref' */
1092 static void
1093 ta_iup_shift(TA_Point p1,
1094 TA_Point p2,
1095 TA_Point ref)
1097 TA_Point p;
1098 FT_Pos delta = ref->u - ref->v;
1101 if (delta == 0)
1102 return;
1104 for (p = p1; p < ref; p++)
1105 p->u = p->v + delta;
1107 for (p = ref + 1; p <= p2; p++)
1108 p->u = p->v + delta;
1112 /* interpolate the original coordinates of all points between `p1' and */
1113 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1114 /* reference points; the `u' and `v' members are the current and */
1115 /* original coordinate values, respectively. */
1117 /* details can be found in the TrueType bytecode specification */
1119 static void
1120 ta_iup_interp(TA_Point p1,
1121 TA_Point p2,
1122 TA_Point ref1,
1123 TA_Point ref2)
1125 TA_Point p;
1126 FT_Pos u;
1127 FT_Pos v1 = ref1->v;
1128 FT_Pos v2 = ref2->v;
1129 FT_Pos d1 = ref1->u - v1;
1130 FT_Pos d2 = ref2->u - v2;
1133 if (p1 > p2)
1134 return;
1136 if (v1 == v2)
1138 for (p = p1; p <= p2; p++)
1140 u = p->v;
1142 if (u <= v1)
1143 u += d1;
1144 else
1145 u += d2;
1147 p->u = u;
1149 return;
1152 if (v1 < v2)
1154 for (p = p1; p <= p2; p++)
1156 u = p->v;
1158 if (u <= v1)
1159 u += d1;
1160 else if (u >= v2)
1161 u += d2;
1162 else
1163 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1165 p->u = u;
1168 else
1170 for (p = p1; p <= p2; p++)
1172 u = p->v;
1174 if (u <= v2)
1175 u += d2;
1176 else if (u >= v1)
1177 u += d1;
1178 else
1179 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1181 p->u = u;
1187 /* hint the weak points -- */
1188 /* this is equivalent to the TrueType `IUP' hinting instruction */
1190 void
1191 ta_glyph_hints_align_weak_points(TA_GlyphHints hints,
1192 TA_Dimension dim)
1194 TA_Point points = hints->points;
1195 TA_Point point_limit = points + hints->num_points;
1197 TA_Point* contour = hints->contours;
1198 TA_Point* contour_limit = contour + hints->num_contours;
1200 FT_UShort touch_flag;
1201 TA_Point point;
1202 TA_Point end_point;
1203 TA_Point first_point;
1206 /* pass 1: move segment points to edge positions */
1208 if (dim == TA_DIMENSION_HORZ)
1210 touch_flag = TA_FLAG_TOUCH_X;
1212 for (point = points; point < point_limit; point++)
1214 point->u = point->x;
1215 point->v = point->ox;
1218 else
1220 touch_flag = TA_FLAG_TOUCH_Y;
1222 for (point = points; point < point_limit; point++)
1224 point->u = point->y;
1225 point->v = point->oy;
1229 for (; contour < contour_limit; contour++)
1231 TA_Point first_touched, last_touched;
1234 point = *contour;
1235 end_point = point->prev;
1236 first_point = point;
1238 /* find first touched point */
1239 for (;;)
1241 if (point > end_point) /* no touched point in contour */
1242 goto NextContour;
1244 if (point->flags & touch_flag)
1245 break;
1247 point++;
1250 first_touched = point;
1252 for (;;)
1254 /* skip any touched neighbours */
1255 while (point < end_point
1256 && (point[1].flags & touch_flag) != 0)
1257 point++;
1259 last_touched = point;
1261 /* find the next touched point, if any */
1262 point++;
1263 for (;;)
1265 if (point > end_point)
1266 goto EndContour;
1268 if ((point->flags & touch_flag) != 0)
1269 break;
1271 point++;
1274 /* interpolate between last_touched and point */
1275 ta_iup_interp(last_touched + 1, point - 1,
1276 last_touched, point);
1279 EndContour:
1280 /* special case: only one point was touched */
1281 if (last_touched == first_touched)
1282 ta_iup_shift(first_point, end_point, first_touched);
1284 else /* interpolate the last part */
1286 if (last_touched < end_point)
1287 ta_iup_interp(last_touched + 1, end_point,
1288 last_touched, first_touched);
1290 if (first_touched > points)
1291 ta_iup_interp(first_point, first_touched - 1,
1292 last_touched, first_touched);
1295 NextContour:
1299 /* now save the interpolated values back to x/y */
1300 if (dim == TA_DIMENSION_HORZ)
1302 for (point = points; point < point_limit; point++)
1303 point->x = point->u;
1305 else
1307 for (point = points; point < point_limit; point++)
1308 point->y = point->u;
1313 #ifdef TA_CONFIG_OPTION_USE_WARPER
1315 /* apply (small) warp scale and warp delta for given dimension */
1317 static void
1318 ta_glyph_hints_scale_dim(TA_GlyphHints hints,
1319 TA_Dimension dim,
1320 FT_Fixed scale,
1321 FT_Pos delta)
1323 TA_Point points = hints->points;
1324 TA_Point points_limit = points + hints->num_points;
1325 TA_Point point;
1328 if (dim == TA_DIMENSION_HORZ)
1330 for (point = points; point < points_limit; point++)
1331 point->x = FT_MulFix(point->fx, scale) + delta;
1333 else
1335 for (point = points; point < points_limit; point++)
1336 point->y = FT_MulFix(point->fy, scale) + delta;
1340 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1342 /* end of tahints.c */