Reduce memory consumption.
[ttfautohint.git] / lib / tahints.c
blob5ab9938a2e7314cca8d259b487678efef21637ed
1 /* tahints.c */
3 /*
4 * Copyright (C) 2011-2012 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_ScriptMetrics 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;
673 for (point = points; point < point_limit; point++)
675 TA_Point next;
676 FT_Pos out_x, out_y;
679 if (point == first)
681 prev = first->prev;
682 in_x = first->fx - prev->fx;
683 in_y = first->fy - prev->fy;
684 in_dir = ta_direction_compute(in_x, in_y);
685 first = prev + 1;
688 point->in_dir = (FT_Char)in_dir;
690 next = point->next;
691 out_x = next->fx - point->fx;
692 out_y = next->fy - point->fy;
694 in_dir = ta_direction_compute(out_x, out_y);
695 point->out_dir = (FT_Char)in_dir;
697 /* check for weak points */
699 if (point->flags & TA_FLAG_CONTROL)
701 Is_Weak_Point:
702 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
704 else if (point->out_dir == point->in_dir)
706 if (point->out_dir != TA_DIR_NONE)
707 goto Is_Weak_Point;
709 if (ta_corner_is_flat(in_x, in_y, out_x, out_y))
710 goto Is_Weak_Point;
712 else if (point->in_dir == -point->out_dir)
713 goto Is_Weak_Point;
715 in_x = out_x;
716 in_y = out_y;
717 prev = point;
722 Exit:
723 return error;
727 /* store the hinted outline in an FT_Outline structure */
729 void
730 ta_glyph_hints_save(TA_GlyphHints hints,
731 FT_Outline* outline)
733 TA_Point point = hints->points;
734 TA_Point limit = point + hints->num_points;
736 FT_Vector* vec = outline->points;
737 char* tag = outline->tags;
740 for (; point < limit; point++, vec++, tag++)
742 vec->x = point->x;
743 vec->y = point->y;
745 if (point->flags & TA_FLAG_CONIC)
746 tag[0] = FT_CURVE_TAG_CONIC;
747 else if (point->flags & TA_FLAG_CUBIC)
748 tag[0] = FT_CURVE_TAG_CUBIC;
749 else
750 tag[0] = FT_CURVE_TAG_ON;
755 /****************************************************************
757 * EDGE POINT GRID-FITTING
759 ****************************************************************/
762 /* align all points of an edge to the same coordinate value, */
763 /* either horizontally or vertically */
765 void
766 ta_glyph_hints_align_edge_points(TA_GlyphHints hints,
767 TA_Dimension dim)
769 TA_AxisHints axis = &hints->axis[dim];
770 TA_Segment segments = axis->segments;
771 TA_Segment segment_limit = segments + axis->num_segments;
772 TA_Segment seg;
775 if (dim == TA_DIMENSION_HORZ)
777 for (seg = segments; seg < segment_limit; seg++)
779 TA_Edge edge = seg->edge;
780 TA_Point point, first, last;
783 if (edge == NULL)
784 continue;
786 first = seg->first;
787 last = seg->last;
788 point = first;
789 for (;;)
791 point->x = edge->pos;
792 point->flags |= TA_FLAG_TOUCH_X;
794 if (point == last)
795 break;
797 point = point->next;
801 else
803 for (seg = segments; seg < segment_limit; seg++)
805 TA_Edge edge = seg->edge;
806 TA_Point point, first, last;
809 if (edge == NULL)
810 continue;
812 first = seg->first;
813 last = seg->last;
814 point = first;
815 for (;;)
817 point->y = edge->pos;
818 point->flags |= TA_FLAG_TOUCH_Y;
820 if (point == last)
821 break;
823 point = point->next;
830 /****************************************************************
832 * STRONG POINT INTERPOLATION
834 ****************************************************************/
837 /* hint the strong points -- */
838 /* this is equivalent to the TrueType `IP' hinting instruction */
840 void
841 ta_glyph_hints_align_strong_points(TA_GlyphHints hints,
842 TA_Dimension dim)
844 TA_Point points = hints->points;
845 TA_Point point_limit = points + hints->num_points;
847 TA_AxisHints axis = &hints->axis[dim];
849 TA_Edge edges = axis->edges;
850 TA_Edge edge_limit = edges + axis->num_edges;
852 FT_UShort touch_flag;
855 if (dim == TA_DIMENSION_HORZ)
856 touch_flag = TA_FLAG_TOUCH_X;
857 else
858 touch_flag = TA_FLAG_TOUCH_Y;
860 if (edges < edge_limit)
862 TA_Point point;
863 TA_Edge edge;
866 for (point = points; point < point_limit; point++)
868 FT_Pos u, ou, fu; /* point position */
869 FT_Pos delta;
872 if (point->flags & touch_flag)
873 continue;
875 /* if this point is candidate to weak interpolation, we */
876 /* interpolate it after all strong points have been processed */
878 if ((point->flags & TA_FLAG_WEAK_INTERPOLATION)
879 && !(point->flags & TA_FLAG_INFLECTION))
880 continue;
882 if (dim == TA_DIMENSION_VERT)
884 u = point->fy;
885 ou = point->oy;
887 else
889 u = point->fx;
890 ou = point->ox;
893 fu = u;
895 /* is the point before the first edge? */
896 edge = edges;
897 delta = edge->fpos - u;
898 if (delta >= 0)
900 u = edge->pos - (edge->opos - ou);
902 if (hints->recorder)
903 hints->recorder(ta_ip_before, hints, dim,
904 point, NULL, NULL, NULL, NULL);
906 goto Store_Point;
909 /* is the point after the last edge? */
910 edge = edge_limit - 1;
911 delta = u - edge->fpos;
912 if (delta >= 0)
914 u = edge->pos + (ou - edge->opos);
916 if (hints->recorder)
917 hints->recorder(ta_ip_after, hints, dim,
918 point, NULL, NULL, NULL, NULL);
920 goto Store_Point;
924 FT_PtrDist min, max, mid;
925 FT_Pos fpos;
928 /* find enclosing edges */
929 min = 0;
930 max = edge_limit - edges;
932 /* for a small number of edges, a linear search is better */
933 if (max <= 8)
935 FT_PtrDist nn;
938 for (nn = 0; nn < max; nn++)
939 if (edges[nn].fpos >= u)
940 break;
942 if (edges[nn].fpos == u)
944 u = edges[nn].pos;
946 if (hints->recorder)
947 hints->recorder(ta_ip_on, hints, dim,
948 point, &edges[nn], NULL, NULL, NULL);
950 goto Store_Point;
952 min = nn;
954 else
955 while (min < max)
957 mid = (max + min) >> 1;
958 edge = edges + mid;
959 fpos = edge->fpos;
961 if (u < fpos)
962 max = mid;
963 else if (u > fpos)
964 min = mid + 1;
965 else
967 /* we are on the edge */
968 u = edge->pos;
970 if (hints->recorder)
971 hints->recorder(ta_ip_on, hints, dim,
972 point, edge, NULL, NULL, NULL);
974 goto Store_Point;
978 /* point is not on an edge */
980 TA_Edge before = edges + min - 1;
981 TA_Edge after = edges + min + 0;
984 /* assert(before && after && before != after) */
985 if (before->scale == 0)
986 before->scale = FT_DivFix(after->pos - before->pos,
987 after->fpos - before->fpos);
989 u = before->pos + FT_MulFix(fu - before->fpos,
990 before->scale);
992 if (hints->recorder)
993 hints->recorder(ta_ip_between, hints, dim,
994 point, before, after, NULL, NULL);
998 Store_Point:
999 /* save the point position */
1000 if (dim == TA_DIMENSION_HORZ)
1001 point->x = u;
1002 else
1003 point->y = u;
1005 point->flags |= touch_flag;
1011 /****************************************************************
1013 * WEAK POINT INTERPOLATION
1015 ****************************************************************/
1018 /* shift the original coordinates of all points between `p1' and */
1019 /* `p2' to get hinted coordinates, using the same difference as */
1020 /* given by `ref' */
1022 static void
1023 ta_iup_shift(TA_Point p1,
1024 TA_Point p2,
1025 TA_Point ref)
1027 TA_Point p;
1028 FT_Pos delta = ref->u - ref->v;
1031 if (delta == 0)
1032 return;
1034 for (p = p1; p < ref; p++)
1035 p->u = p->v + delta;
1037 for (p = ref + 1; p <= p2; p++)
1038 p->u = p->v + delta;
1042 /* interpolate the original coordinates of all points between `p1' and */
1043 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1044 /* reference points; the `u' and `v' members are the current and */
1045 /* original coordinate values, respectively. */
1047 /* details can be found in the TrueType bytecode specification */
1049 static void
1050 ta_iup_interp(TA_Point p1,
1051 TA_Point p2,
1052 TA_Point ref1,
1053 TA_Point ref2)
1055 TA_Point p;
1056 FT_Pos u;
1057 FT_Pos v1 = ref1->v;
1058 FT_Pos v2 = ref2->v;
1059 FT_Pos d1 = ref1->u - v1;
1060 FT_Pos d2 = ref2->u - v2;
1063 if (p1 > p2)
1064 return;
1066 if (v1 == v2)
1068 for (p = p1; p <= p2; p++)
1070 u = p->v;
1072 if (u <= v1)
1073 u += d1;
1074 else
1075 u += d2;
1077 p->u = u;
1079 return;
1082 if (v1 < v2)
1084 for (p = p1; p <= p2; p++)
1086 u = p->v;
1088 if (u <= v1)
1089 u += d1;
1090 else if (u >= v2)
1091 u += d2;
1092 else
1093 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1095 p->u = u;
1098 else
1100 for (p = p1; p <= p2; p++)
1102 u = p->v;
1104 if (u <= v2)
1105 u += d2;
1106 else if (u >= v1)
1107 u += d1;
1108 else
1109 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1111 p->u = u;
1117 /* hint the weak points -- */
1118 /* this is equivalent to the TrueType `IUP' hinting instruction */
1120 void
1121 ta_glyph_hints_align_weak_points(TA_GlyphHints hints,
1122 TA_Dimension dim)
1124 TA_Point points = hints->points;
1125 TA_Point point_limit = points + hints->num_points;
1127 TA_Point* contour = hints->contours;
1128 TA_Point* contour_limit = contour + hints->num_contours;
1130 FT_UShort touch_flag;
1131 TA_Point point;
1132 TA_Point end_point;
1133 TA_Point first_point;
1136 /* pass 1: move segment points to edge positions */
1138 if (dim == TA_DIMENSION_HORZ)
1140 touch_flag = TA_FLAG_TOUCH_X;
1142 for (point = points; point < point_limit; point++)
1144 point->u = point->x;
1145 point->v = point->ox;
1148 else
1150 touch_flag = TA_FLAG_TOUCH_Y;
1152 for (point = points; point < point_limit; point++)
1154 point->u = point->y;
1155 point->v = point->oy;
1159 point = points;
1161 for (; contour < contour_limit; contour++)
1163 TA_Point first_touched, last_touched;
1166 point = *contour;
1167 end_point = point->prev;
1168 first_point = point;
1170 /* find first touched point */
1171 for (;;)
1173 if (point > end_point) /* no touched point in contour */
1174 goto NextContour;
1176 if (point->flags & touch_flag)
1177 break;
1179 point++;
1182 first_touched = point;
1183 last_touched = point;
1185 for (;;)
1187 /* skip any touched neighbours */
1188 while (point < end_point
1189 && (point[1].flags & touch_flag) != 0)
1190 point++;
1192 last_touched = point;
1194 /* find the next touched point, if any */
1195 point++;
1196 for (;;)
1198 if (point > end_point)
1199 goto EndContour;
1201 if ((point->flags & touch_flag) != 0)
1202 break;
1204 point++;
1207 /* interpolate between last_touched and point */
1208 ta_iup_interp(last_touched + 1, point - 1,
1209 last_touched, point);
1212 EndContour:
1213 /* special case: only one point was touched */
1214 if (last_touched == first_touched)
1215 ta_iup_shift(first_point, end_point, first_touched);
1217 else /* interpolate the last part */
1219 if (last_touched < end_point)
1220 ta_iup_interp(last_touched + 1, end_point,
1221 last_touched, first_touched);
1223 if (first_touched > points)
1224 ta_iup_interp(first_point, first_touched - 1,
1225 last_touched, first_touched);
1228 NextContour:
1232 /* now save the interpolated values back to x/y */
1233 if (dim == TA_DIMENSION_HORZ)
1235 for (point = points; point < point_limit; point++)
1236 point->x = point->u;
1238 else
1240 for (point = points; point < point_limit; point++)
1241 point->y = point->u;
1246 #ifdef TA_CONFIG_OPTION_USE_WARPER
1248 /* apply (small) warp scale and warp delta for given dimension */
1250 static void
1251 ta_glyph_hints_scale_dim(TA_GlyphHints hints,
1252 TA_Dimension dim,
1253 FT_Fixed scale,
1254 FT_Pos delta)
1256 TA_Point points = hints->points;
1257 TA_Point points_limit = points + hints->num_points;
1258 TA_Point point;
1261 if (dim == TA_DIMENSION_HORZ)
1263 for (point = points; point < points_limit; point++)
1264 point->x = FT_MulFix(point->fx, scale) + delta;
1266 else
1268 for (point = points; point < points_limit; point++)
1269 point->y = FT_MulFix(point->fy, scale) + delta;
1273 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1275 /* end of tahints.c */