s/lower_upper/upper_lower/.
[ttfautohint.git] / lib / tahints.c
blobe79025500bfd69ab1aaa67066a8aeb5ad1c556a9
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 fprintf(stderr, "Table of points:\n");
206 fprintf(stderr, " [ index | xorg | yorg | xscale | yscale"
207 " | xfit | yfit | flags ]\n");
209 for (point = points; point < limit; point++)
211 fprintf(stderr, " [ %5d | %5d | %5d | %6.2f | %6.2f"
212 " | %5.2f | %5.2f | %c%c%c%c%c%c ]\n",
213 point - points,
214 point->fx,
215 point->fy,
216 point->ox / 64.0,
217 point->oy / 64.0,
218 point->x / 64.0,
219 point->y / 64.0,
220 (point->flags & TA_FLAG_WEAK_INTERPOLATION) ? 'w' : ' ',
221 (point->flags & TA_FLAG_INFLECTION) ? 'i' : ' ',
222 (point->flags & TA_FLAG_EXTREMA_X) ? '<' : ' ',
223 (point->flags & TA_FLAG_EXTREMA_Y) ? 'v' : ' ',
224 (point->flags & TA_FLAG_ROUND_X) ? '(' : ' ',
225 (point->flags & TA_FLAG_ROUND_Y) ? 'u' : ' ');
227 fprintf(stderr, "\n");
231 static const char*
232 ta_edge_flags_to_string(FT_Byte flags)
234 static char temp[32];
235 int pos = 0;
238 if (flags & TA_EDGE_ROUND)
240 memcpy(temp + pos, "round", 5);
241 pos += 5;
243 if (flags & TA_EDGE_SERIF)
245 if (pos > 0)
246 temp[pos++] = ' ';
247 memcpy(temp + pos, "serif", 5);
248 pos += 5;
250 if (pos == 0)
251 return "normal";
253 temp[pos] = '\0';
255 return temp;
259 /* dump the array of linked segments */
261 void
262 ta_glyph_hints_dump_segments(TA_GlyphHints hints)
264 FT_Int dimension;
267 for (dimension = TA_DEBUG_STARTDIM;
268 dimension >= TA_DEBUG_ENDDIM;
269 dimension--)
271 TA_AxisHints axis = &hints->axis[dimension];
272 TA_Point points = hints->points;
273 TA_Edge edges = axis->edges;
274 TA_Segment segments = axis->segments;
275 TA_Segment limit = segments + axis->num_segments;
276 TA_Segment seg;
279 fprintf(stderr, "Table of %s segments:\n",
280 dimension == TA_DIMENSION_HORZ ? "vertical"
281 : "horizontal");
282 fprintf(stderr, " [ index | pos | dir | from | to | link | serif | edge |"
283 " height | extra | flags ]\n");
285 for (seg = segments; seg < limit; seg++)
287 fprintf(stderr, " [ %5d | %5.2g | %5s | %4d | %4d | %4d | %5d | %4d |"
288 " %6d | %5d | %11s ]\n",
289 seg - segments,
290 dimension == TA_DIMENSION_HORZ ? (int)seg->first->ox / 64.0
291 : (int)seg->first->oy / 64.0,
292 ta_dir_str((TA_Direction)seg->dir),
293 TA_INDEX_NUM(seg->first, points),
294 TA_INDEX_NUM(seg->last, points),
295 TA_INDEX_NUM(seg->link, segments),
296 TA_INDEX_NUM(seg->serif, segments),
297 TA_INDEX_NUM(seg->edge, edges),
298 seg->height,
299 seg->height - (seg->max_coord - seg->min_coord),
300 ta_edge_flags_to_string(seg->flags));
302 fprintf(stderr, "\n");
307 /* dump the array of linked edges */
309 void
310 ta_glyph_hints_dump_edges(TA_GlyphHints hints)
312 FT_Int dimension;
315 for (dimension = TA_DEBUG_STARTDIM;
316 dimension >= TA_DEBUG_ENDDIM;
317 dimension--)
319 TA_AxisHints axis = &hints->axis[dimension];
320 TA_Edge edges = axis->edges;
321 TA_Edge limit = edges + axis->num_edges;
322 TA_Edge edge;
325 /* note that TA_DIMENSION_HORZ corresponds to _vertical_ edges */
326 /* since they have a constant X coordinate */
327 fprintf(stderr, "Table of %s edges:\n",
328 dimension == TA_DIMENSION_HORZ ? "vertical"
329 : "horizontal");
330 fprintf(stderr, " [ index | pos | dir | link |"
331 " serif | blue | opos | pos | flags ]\n");
333 for (edge = edges; edge < limit; edge++)
335 fprintf(stderr, " [ %5d | %5.2g | %5s | %4d |"
336 " %5d | %c | %5.2f | %5.2f | %11s ]\n",
337 edge - edges,
338 (int)edge->opos / 64.0,
339 ta_dir_str((TA_Direction)edge->dir),
340 TA_INDEX_NUM(edge->link, edges),
341 TA_INDEX_NUM(edge->serif, edges),
342 edge->blue_edge ? 'y' : 'n',
343 edge->opos / 64.0,
344 edge->pos / 64.0,
345 ta_edge_flags_to_string(edge->flags));
347 fprintf(stderr, "\n");
352 void
353 ta_glyph_hints_dump_edge_links(TA_GlyphHints hints)
355 FT_Int dimension;
358 for (dimension = TA_DEBUG_STARTDIM;
359 dimension >= TA_DEBUG_ENDDIM;
360 dimension--)
362 TA_AxisHints axis = &hints->axis[dimension];
363 TA_Segment segments = axis->segments;
364 TA_Edge edges = axis->edges;
365 TA_Edge limit = edges + axis->num_edges;
367 TA_Edge edge;
368 TA_Segment seg;
371 fprintf(stderr, "%s edges consist of the following segments:\n",
372 dimension == TA_DIMENSION_HORZ ? "Vertical"
373 : "Horizontal");
374 for (edge = edges; edge < limit; edge++)
376 fprintf(stderr, " %2d:", edge - edges);
378 seg = edge->first;
381 fprintf(stderr, " %d", seg - segments);
382 seg = seg->edge_next;
383 } while (seg != edge->first);
385 fprintf(stderr, "\n");
387 fprintf(stderr, "\n");
391 #endif /* TA_DEBUG */
394 /* compute the direction value of a given vector */
396 TA_Direction
397 ta_direction_compute(FT_Pos dx,
398 FT_Pos dy)
400 FT_Pos ll, ss; /* long and short arm lengths */
401 TA_Direction dir; /* candidate direction */
404 if (dy >= dx)
406 if (dy >= -dx)
408 dir = TA_DIR_UP;
409 ll = dy;
410 ss = dx;
412 else
414 dir = TA_DIR_LEFT;
415 ll = -dx;
416 ss = dy;
419 else /* dy < dx */
421 if (dy >= -dx)
423 dir = TA_DIR_RIGHT;
424 ll = dx;
425 ss = dy;
427 else
429 dir = TA_DIR_DOWN;
430 ll = dy;
431 ss = dx;
435 /* return no direction if arm lengths differ too much */
436 /* (value 14 is heuristic) */
437 ss *= 14;
438 if (TA_ABS(ll) <= TA_ABS(ss))
439 dir = TA_DIR_NONE;
441 return dir;
445 void
446 ta_glyph_hints_init(TA_GlyphHints hints)
448 memset(hints, 0, sizeof (TA_GlyphHintsRec));
452 void
453 ta_glyph_hints_done(TA_GlyphHints hints)
455 int dim;
458 if (!hints)
459 return;
461 /* we don't need to free the segment and edge buffers */
462 /* since they are really within the hints->points array */
463 for (dim = 0; dim < TA_DIMENSION_MAX; dim++)
465 TA_AxisHints axis = &hints->axis[dim];
468 axis->num_segments = 0;
469 axis->max_segments = 0;
470 free(axis->segments);
471 axis->segments = NULL;
473 axis->num_edges = 0;
474 axis->max_edges = 0;
475 free(axis->edges);
476 axis->edges = NULL;
479 free(hints->contours);
480 hints->contours = NULL;
481 hints->max_contours = 0;
482 hints->num_contours = 0;
484 free(hints->points);
485 hints->points = NULL;
486 hints->num_points = 0;
487 hints->max_points = 0;
491 /* reset metrics */
493 void
494 ta_glyph_hints_rescale(TA_GlyphHints hints,
495 TA_ScriptMetrics metrics)
497 hints->metrics = metrics;
498 hints->scaler_flags = metrics->scaler.flags;
502 /* from FreeType's ftcalc.c */
504 static FT_Int
505 ta_corner_is_flat(FT_Pos in_x,
506 FT_Pos in_y,
507 FT_Pos out_x,
508 FT_Pos out_y)
510 FT_Pos ax = in_x;
511 FT_Pos ay = in_y;
513 FT_Pos d_in, d_out, d_corner;
516 if (ax < 0)
517 ax = -ax;
518 if (ay < 0)
519 ay = -ay;
520 d_in = ax + ay;
522 ax = out_x;
523 if (ax < 0)
524 ax = -ax;
525 ay = out_y;
526 if (ay < 0)
527 ay = -ay;
528 d_out = ax + ay;
530 ax = out_x + in_x;
531 if (ax < 0)
532 ax = -ax;
533 ay = out_y + in_y;
534 if (ay < 0)
535 ay = -ay;
536 d_corner = ax + ay;
538 return (d_in + d_out - d_corner) < (d_corner >> 4);
542 /* recompute all TA_Point in TA_GlyphHints */
543 /* from the definitions in a source outline */
545 FT_Error
546 ta_glyph_hints_reload(TA_GlyphHints hints,
547 FT_Outline* outline)
549 FT_Error error = FT_Err_Ok;
550 TA_Point points;
551 FT_UInt old_max, new_max;
553 FT_Fixed x_scale = hints->x_scale;
554 FT_Fixed y_scale = hints->y_scale;
555 FT_Pos x_delta = hints->x_delta;
556 FT_Pos y_delta = hints->y_delta;
559 hints->num_points = 0;
560 hints->num_contours = 0;
562 hints->axis[0].num_segments = 0;
563 hints->axis[0].num_edges = 0;
564 hints->axis[1].num_segments = 0;
565 hints->axis[1].num_edges = 0;
567 /* first of all, reallocate the contours array if necessary */
568 new_max = (FT_UInt)outline->n_contours;
569 old_max = hints->max_contours;
570 if (new_max > old_max)
572 TA_Point* contours_new;
575 new_max = (new_max + 3) & ~3; /* round up to a multiple of 4 */
577 contours_new = (TA_Point*)realloc(hints->contours,
578 new_max * sizeof (TA_Point));
579 if (!contours_new)
580 return FT_Err_Out_Of_Memory;
582 hints->contours = contours_new;
583 hints->max_contours = new_max;
586 /* reallocate the points arrays if necessary -- we reserve */
587 /* two additional point positions, used to hint metrics appropriately */
588 new_max = (FT_UInt)(outline->n_points + 2);
589 old_max = hints->max_points;
590 if (new_max > old_max)
592 TA_Point points_new;
595 new_max = (new_max + 2 + 7) & ~7; /* round up to a multiple of 8 */
597 points_new = (TA_Point)realloc(hints->points,
598 new_max * sizeof (TA_PointRec));
599 if (!points_new)
600 return FT_Err_Out_Of_Memory;
602 hints->points = points_new;
603 hints->max_points = new_max;
606 hints->num_points = outline->n_points;
607 hints->num_contours = outline->n_contours;
609 /* we can't rely on the value of `FT_Outline.flags' to know the fill */
610 /* direction used for a glyph, given that some fonts are broken */
611 /* (e.g. the Arphic ones); we thus recompute it each time we need to */
613 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_UP;
614 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_LEFT;
616 if (FT_Outline_Get_Orientation(outline) == FT_ORIENTATION_POSTSCRIPT)
618 hints->axis[TA_DIMENSION_HORZ].major_dir = TA_DIR_DOWN;
619 hints->axis[TA_DIMENSION_VERT].major_dir = TA_DIR_RIGHT;
622 hints->x_scale = x_scale;
623 hints->y_scale = y_scale;
624 hints->x_delta = x_delta;
625 hints->y_delta = y_delta;
627 hints->xmin_delta = 0;
628 hints->xmax_delta = 0;
630 points = hints->points;
631 if (hints->num_points == 0)
632 goto Exit;
635 TA_Point point;
636 TA_Point point_limit = points + hints->num_points;
639 /* compute coordinates & Bezier flags, next and prev */
641 FT_Vector* vec = outline->points;
642 char* tag = outline->tags;
644 TA_Point end = points + outline->contours[0];
645 TA_Point prev = end;
647 FT_Int contour_index = 0;
650 for (point = points; point < point_limit; point++, vec++, tag++)
652 point->fx = (FT_Short)vec->x;
653 point->fy = (FT_Short)vec->y;
654 point->ox = point->x = FT_MulFix(vec->x, x_scale) + x_delta;
655 point->oy = point->y = FT_MulFix(vec->y, y_scale) + y_delta;
657 switch (FT_CURVE_TAG(*tag))
659 case FT_CURVE_TAG_CONIC:
660 point->flags = TA_FLAG_CONIC;
661 break;
662 case FT_CURVE_TAG_CUBIC:
663 point->flags = TA_FLAG_CUBIC;
664 break;
665 default:
666 point->flags = TA_FLAG_NONE;
669 point->prev = prev;
670 prev->next = point;
671 prev = point;
673 if (point == end)
675 if (++contour_index < outline->n_contours)
677 end = points + outline->contours[contour_index];
678 prev = end;
684 /* set up the contours array */
686 TA_Point* contour = hints->contours;
687 TA_Point* contour_limit = contour + hints->num_contours;
689 short* end = outline->contours;
690 short idx = 0;
693 for (; contour < contour_limit; contour++, end++)
695 contour[0] = points + idx;
696 idx = (short)(end[0] + 1);
700 /* compute directions of in & out vectors */
702 TA_Point first = points;
703 TA_Point prev = NULL;
705 FT_Pos in_x = 0;
706 FT_Pos in_y = 0;
708 TA_Direction in_dir = TA_DIR_NONE;
711 for (point = points; point < point_limit; point++)
713 TA_Point next;
714 FT_Pos out_x, out_y;
717 if (point == first)
719 prev = first->prev;
720 in_x = first->fx - prev->fx;
721 in_y = first->fy - prev->fy;
722 in_dir = ta_direction_compute(in_x, in_y);
723 first = prev + 1;
726 point->in_dir = (FT_Char)in_dir;
728 next = point->next;
729 out_x = next->fx - point->fx;
730 out_y = next->fy - point->fy;
732 in_dir = ta_direction_compute(out_x, out_y);
733 point->out_dir = (FT_Char)in_dir;
735 /* check for weak points */
737 if (point->flags & TA_FLAG_CONTROL)
739 Is_Weak_Point:
740 point->flags |= TA_FLAG_WEAK_INTERPOLATION;
742 else if (point->out_dir == point->in_dir)
744 if (point->out_dir != TA_DIR_NONE)
745 goto Is_Weak_Point;
747 if (ta_corner_is_flat(in_x, in_y, out_x, out_y))
748 goto Is_Weak_Point;
750 else if (point->in_dir == -point->out_dir)
751 goto Is_Weak_Point;
753 in_x = out_x;
754 in_y = out_y;
755 prev = point;
760 Exit:
761 return error;
765 /* store the hinted outline in an FT_Outline structure */
767 void
768 ta_glyph_hints_save(TA_GlyphHints hints,
769 FT_Outline* outline)
771 TA_Point point = hints->points;
772 TA_Point limit = point + hints->num_points;
774 FT_Vector* vec = outline->points;
775 char* tag = outline->tags;
778 for (; point < limit; point++, vec++, tag++)
780 vec->x = point->x;
781 vec->y = point->y;
783 if (point->flags & TA_FLAG_CONIC)
784 tag[0] = FT_CURVE_TAG_CONIC;
785 else if (point->flags & TA_FLAG_CUBIC)
786 tag[0] = FT_CURVE_TAG_CUBIC;
787 else
788 tag[0] = FT_CURVE_TAG_ON;
793 /****************************************************************
795 * EDGE POINT GRID-FITTING
797 ****************************************************************/
800 /* align all points of an edge to the same coordinate value, */
801 /* either horizontally or vertically */
803 void
804 ta_glyph_hints_align_edge_points(TA_GlyphHints hints,
805 TA_Dimension dim)
807 TA_AxisHints axis = &hints->axis[dim];
808 TA_Segment segments = axis->segments;
809 TA_Segment segment_limit = segments + axis->num_segments;
810 TA_Segment seg;
813 if (dim == TA_DIMENSION_HORZ)
815 for (seg = segments; seg < segment_limit; seg++)
817 TA_Edge edge = seg->edge;
818 TA_Point point, first, last;
821 if (edge == NULL)
822 continue;
824 first = seg->first;
825 last = seg->last;
826 point = first;
827 for (;;)
829 point->x = edge->pos;
830 point->flags |= TA_FLAG_TOUCH_X;
832 if (point == last)
833 break;
835 point = point->next;
839 else
841 for (seg = segments; seg < segment_limit; seg++)
843 TA_Edge edge = seg->edge;
844 TA_Point point, first, last;
847 if (edge == NULL)
848 continue;
850 first = seg->first;
851 last = seg->last;
852 point = first;
853 for (;;)
855 point->y = edge->pos;
856 point->flags |= TA_FLAG_TOUCH_Y;
858 if (point == last)
859 break;
861 point = point->next;
868 /****************************************************************
870 * STRONG POINT INTERPOLATION
872 ****************************************************************/
875 /* hint the strong points -- */
876 /* this is equivalent to the TrueType `IP' hinting instruction */
878 void
879 ta_glyph_hints_align_strong_points(TA_GlyphHints hints,
880 TA_Dimension dim)
882 TA_Point points = hints->points;
883 TA_Point point_limit = points + hints->num_points;
885 TA_AxisHints axis = &hints->axis[dim];
887 TA_Edge edges = axis->edges;
888 TA_Edge edge_limit = edges + axis->num_edges;
890 FT_UShort touch_flag;
893 if (dim == TA_DIMENSION_HORZ)
894 touch_flag = TA_FLAG_TOUCH_X;
895 else
896 touch_flag = TA_FLAG_TOUCH_Y;
898 if (edges < edge_limit)
900 TA_Point point;
901 TA_Edge edge;
904 for (point = points; point < point_limit; point++)
906 FT_Pos u, ou, fu; /* point position */
907 FT_Pos delta;
910 if (point->flags & touch_flag)
911 continue;
913 /* if this point is candidate to weak interpolation, we */
914 /* interpolate it after all strong points have been processed */
916 if ((point->flags & TA_FLAG_WEAK_INTERPOLATION)
917 && !(point->flags & TA_FLAG_INFLECTION))
918 continue;
920 if (dim == TA_DIMENSION_VERT)
922 u = point->fy;
923 ou = point->oy;
925 else
927 u = point->fx;
928 ou = point->ox;
931 fu = u;
933 /* is the point before the first edge? */
934 edge = edges;
935 delta = edge->fpos - u;
936 if (delta >= 0)
938 u = edge->pos - (edge->opos - ou);
940 if (hints->recorder)
941 hints->recorder(ta_ip_before, hints, dim,
942 point, NULL, NULL, NULL, NULL);
944 goto Store_Point;
947 /* is the point after the last edge? */
948 edge = edge_limit - 1;
949 delta = u - edge->fpos;
950 if (delta >= 0)
952 u = edge->pos + (ou - edge->opos);
954 if (hints->recorder)
955 hints->recorder(ta_ip_after, hints, dim,
956 point, NULL, NULL, NULL, NULL);
958 goto Store_Point;
962 FT_PtrDist min, max, mid;
963 FT_Pos fpos;
966 /* find enclosing edges */
967 min = 0;
968 max = edge_limit - edges;
970 /* for a small number of edges, a linear search is better */
971 if (max <= 8)
973 FT_PtrDist nn;
976 for (nn = 0; nn < max; nn++)
977 if (edges[nn].fpos >= u)
978 break;
980 if (edges[nn].fpos == u)
982 u = edges[nn].pos;
984 if (hints->recorder)
985 hints->recorder(ta_ip_on, hints, dim,
986 point, &edges[nn], NULL, NULL, NULL);
988 goto Store_Point;
990 min = nn;
992 else
993 while (min < max)
995 mid = (max + min) >> 1;
996 edge = edges + mid;
997 fpos = edge->fpos;
999 if (u < fpos)
1000 max = mid;
1001 else if (u > fpos)
1002 min = mid + 1;
1003 else
1005 /* we are on the edge */
1006 u = edge->pos;
1008 if (hints->recorder)
1009 hints->recorder(ta_ip_on, hints, dim,
1010 point, edge, NULL, NULL, NULL);
1012 goto Store_Point;
1016 /* point is not on an edge */
1018 TA_Edge before = edges + min - 1;
1019 TA_Edge after = edges + min + 0;
1022 /* assert(before && after && before != after) */
1023 if (before->scale == 0)
1024 before->scale = FT_DivFix(after->pos - before->pos,
1025 after->fpos - before->fpos);
1027 u = before->pos + FT_MulFix(fu - before->fpos,
1028 before->scale);
1030 if (hints->recorder)
1031 hints->recorder(ta_ip_between, hints, dim,
1032 point, before, after, NULL, NULL);
1036 Store_Point:
1037 /* save the point position */
1038 if (dim == TA_DIMENSION_HORZ)
1039 point->x = u;
1040 else
1041 point->y = u;
1043 point->flags |= touch_flag;
1049 /****************************************************************
1051 * WEAK POINT INTERPOLATION
1053 ****************************************************************/
1056 /* shift the original coordinates of all points between `p1' and */
1057 /* `p2' to get hinted coordinates, using the same difference as */
1058 /* given by `ref' */
1060 static void
1061 ta_iup_shift(TA_Point p1,
1062 TA_Point p2,
1063 TA_Point ref)
1065 TA_Point p;
1066 FT_Pos delta = ref->u - ref->v;
1069 if (delta == 0)
1070 return;
1072 for (p = p1; p < ref; p++)
1073 p->u = p->v + delta;
1075 for (p = ref + 1; p <= p2; p++)
1076 p->u = p->v + delta;
1080 /* interpolate the original coordinates of all points between `p1' and */
1081 /* `p2' to get hinted coordinates, using `ref1' and `ref2' as the */
1082 /* reference points; the `u' and `v' members are the current and */
1083 /* original coordinate values, respectively. */
1085 /* details can be found in the TrueType bytecode specification */
1087 static void
1088 ta_iup_interp(TA_Point p1,
1089 TA_Point p2,
1090 TA_Point ref1,
1091 TA_Point ref2)
1093 TA_Point p;
1094 FT_Pos u;
1095 FT_Pos v1 = ref1->v;
1096 FT_Pos v2 = ref2->v;
1097 FT_Pos d1 = ref1->u - v1;
1098 FT_Pos d2 = ref2->u - v2;
1101 if (p1 > p2)
1102 return;
1104 if (v1 == v2)
1106 for (p = p1; p <= p2; p++)
1108 u = p->v;
1110 if (u <= v1)
1111 u += d1;
1112 else
1113 u += d2;
1115 p->u = u;
1117 return;
1120 if (v1 < v2)
1122 for (p = p1; p <= p2; p++)
1124 u = p->v;
1126 if (u <= v1)
1127 u += d1;
1128 else if (u >= v2)
1129 u += d2;
1130 else
1131 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1133 p->u = u;
1136 else
1138 for (p = p1; p <= p2; p++)
1140 u = p->v;
1142 if (u <= v2)
1143 u += d2;
1144 else if (u >= v1)
1145 u += d1;
1146 else
1147 u = ref1->u + FT_MulDiv(u - v1, ref2->u - ref1->u, v2 - v1);
1149 p->u = u;
1155 /* hint the weak points -- */
1156 /* this is equivalent to the TrueType `IUP' hinting instruction */
1158 void
1159 ta_glyph_hints_align_weak_points(TA_GlyphHints hints,
1160 TA_Dimension dim)
1162 TA_Point points = hints->points;
1163 TA_Point point_limit = points + hints->num_points;
1165 TA_Point* contour = hints->contours;
1166 TA_Point* contour_limit = contour + hints->num_contours;
1168 FT_UShort touch_flag;
1169 TA_Point point;
1170 TA_Point end_point;
1171 TA_Point first_point;
1174 /* pass 1: move segment points to edge positions */
1176 if (dim == TA_DIMENSION_HORZ)
1178 touch_flag = TA_FLAG_TOUCH_X;
1180 for (point = points; point < point_limit; point++)
1182 point->u = point->x;
1183 point->v = point->ox;
1186 else
1188 touch_flag = TA_FLAG_TOUCH_Y;
1190 for (point = points; point < point_limit; point++)
1192 point->u = point->y;
1193 point->v = point->oy;
1197 point = points;
1199 for (; contour < contour_limit; contour++)
1201 TA_Point first_touched, last_touched;
1204 point = *contour;
1205 end_point = point->prev;
1206 first_point = point;
1208 /* find first touched point */
1209 for (;;)
1211 if (point > end_point) /* no touched point in contour */
1212 goto NextContour;
1214 if (point->flags & touch_flag)
1215 break;
1217 point++;
1220 first_touched = point;
1221 last_touched = point;
1223 for (;;)
1225 /* skip any touched neighbours */
1226 while (point < end_point
1227 && (point[1].flags & touch_flag) != 0)
1228 point++;
1230 last_touched = point;
1232 /* find the next touched point, if any */
1233 point++;
1234 for (;;)
1236 if (point > end_point)
1237 goto EndContour;
1239 if ((point->flags & touch_flag) != 0)
1240 break;
1242 point++;
1245 /* interpolate between last_touched and point */
1246 ta_iup_interp(last_touched + 1, point - 1,
1247 last_touched, point);
1250 EndContour:
1251 /* special case: only one point was touched */
1252 if (last_touched == first_touched)
1253 ta_iup_shift(first_point, end_point, first_touched);
1255 else /* interpolate the last part */
1257 if (last_touched < end_point)
1258 ta_iup_interp(last_touched + 1, end_point,
1259 last_touched, first_touched);
1261 if (first_touched > points)
1262 ta_iup_interp(first_point, first_touched - 1,
1263 last_touched, first_touched);
1266 NextContour:
1270 /* now save the interpolated values back to x/y */
1271 if (dim == TA_DIMENSION_HORZ)
1273 for (point = points; point < point_limit; point++)
1274 point->x = point->u;
1276 else
1278 for (point = points; point < point_limit; point++)
1279 point->y = point->u;
1284 #ifdef TA_CONFIG_OPTION_USE_WARPER
1286 /* apply (small) warp scale and warp delta for given dimension */
1288 static void
1289 ta_glyph_hints_scale_dim(TA_GlyphHints hints,
1290 TA_Dimension dim,
1291 FT_Fixed scale,
1292 FT_Pos delta)
1294 TA_Point points = hints->points;
1295 TA_Point points_limit = points + hints->num_points;
1296 TA_Point point;
1299 if (dim == TA_DIMENSION_HORZ)
1301 for (point = points; point < points_limit; point++)
1302 point->x = FT_MulFix(point->fx, scale) + delta;
1304 else
1306 for (point = points; point < points_limit; point++)
1307 point->y = FT_MulFix(point->fy, scale) + delta;
1311 #endif /* TA_CONFIG_OPTION_USE_WARPER */
1313 /* end of tahints.c */