summaryrefslogtreecommitdiffstats
path: root/src/autofit/afhints.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/autofit/afhints.c')
-rw-r--r--src/autofit/afhints.c269
1 files changed, 189 insertions, 80 deletions
diff --git a/src/autofit/afhints.c b/src/autofit/afhints.c
index ce504cc..88a97d4 100644
--- a/src/autofit/afhints.c
+++ b/src/autofit/afhints.c
@@ -4,7 +4,7 @@
/* */
/* Auto-fitter hinting routines (body). */
/* */
-/* Copyright 2003-2007, 2009-2013 by */
+/* Copyright 2003-2007, 2009-2014 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
@@ -345,7 +345,9 @@
af_glyph_hints_get_segment_offset( AF_GlyphHints hints,
FT_Int dimension,
FT_Int idx,
- FT_Pos* offset )
+ FT_Pos *offset,
+ FT_Bool *is_blue,
+ FT_Pos *blue_offset )
{
AF_Dimension dim;
AF_AxisHints axis;
@@ -362,9 +364,18 @@
if ( idx < 0 || idx >= axis->num_segments )
return FT_THROW( Invalid_Argument );
- seg = &axis->segments[idx];
- *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->ox
- : seg->first->oy;
+ seg = &axis->segments[idx];
+ *offset = ( dim == AF_DIMENSION_HORZ ) ? seg->first->ox
+ : seg->first->oy;
+ if ( seg->edge )
+ *is_blue = (FT_Bool)( seg->edge->blue_edge != 0 );
+ else
+ *is_blue = FALSE;
+
+ if ( *is_blue )
+ *blue_offset = seg->edge->blue_edge->cur;
+ else
+ *blue_offset = 0;
return FT_Err_Ok;
}
@@ -533,8 +544,8 @@
/* Reset metrics. */
FT_LOCAL_DEF( void )
- af_glyph_hints_rescale( AF_GlyphHints hints,
- AF_ScriptMetrics metrics )
+ af_glyph_hints_rescale( AF_GlyphHints hints,
+ AF_StyleMetrics metrics )
{
hints->metrics = metrics;
hints->scaler_flags = metrics->scaler.flags;
@@ -640,6 +651,9 @@
for ( point = points; point < point_limit; point++, vec++, tag++ )
{
+ point->in_dir = (FT_Char)AF_DIR_NONE;
+ point->out_dir = (FT_Char)AF_DIR_NONE;
+
point->fx = (FT_Short)vec->x;
point->fy = (FT_Short)vec->y;
point->ox = point->x = FT_MulFix( vec->x, x_scale ) + x_delta;
@@ -687,91 +701,186 @@
}
}
- /* compute directions of in & out vectors */
{
- AF_Point first = points;
- AF_Point prev = NULL;
- FT_Pos in_x = 0;
- FT_Pos in_y = 0;
- AF_Direction in_dir = AF_DIR_NONE;
-
- FT_Pos last_good_in_x = 0;
- FT_Pos last_good_in_y = 0;
-
+ /*
+ * Compute directions of `in' and `out' vectors.
+ *
+ * Note that distances between points that are very near to each
+ * other are accumulated. In other words, the auto-hinter
+ * prepends the small vectors between near points to the first
+ * non-near vector. All intermediate points are tagged as
+ * weak; the directions are adjusted also to be equal to the
+ * accumulated one.
+ */
+
+ /* value 20 in `near_limit' is heuristic */
FT_UInt units_per_em = hints->metrics->scaler.face->units_per_EM;
FT_Int near_limit = 20 * units_per_em / 2048;
+ FT_Int near_limit2 = 2 * near_limit - 1;
+ AF_Point* contour;
+ AF_Point* contour_limit = hints->contours + hints->num_contours;
- for ( point = points; point < point_limit; point++ )
+
+ for ( contour = hints->contours; contour < contour_limit; contour++ )
{
- AF_Point next;
- FT_Pos out_x, out_y;
+ AF_Point first = *contour;
+ AF_Point next, prev, curr;
+
+ FT_Pos out_x, out_y;
+
+ FT_Bool is_first;
- if ( point == first )
+ /* since the first point of a contour could be part of a */
+ /* series of near points, go backwards to find the first */
+ /* non-near point and adjust `first' */
+
+ point = first;
+ prev = first->prev;
+
+ while ( prev != first )
{
- prev = first->prev;
+ out_x = point->fx - prev->fx;
+ out_y = point->fy - prev->fy;
+
+ /*
+ * We use Taxicab metrics to measure the vector length.
+ *
+ * Note that the accumulated distances so far could have the
+ * opposite direction of the distance measured here. For this
+ * reason we use `near_limit2' for the comparison to get a
+ * non-near point even in the worst case.
+ */
+ if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit2 )
+ break;
+
+ point = prev;
+ prev = prev->prev;
+ }
- in_x = first->fx - prev->fx;
- in_y = first->fy - prev->fy;
+ /* adjust first point */
+ first = point;
- last_good_in_x = in_x;
- last_good_in_y = in_y;
+ /* now loop over all points of the contour to get */
+ /* `in' and `out' vector directions */
- if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
- {
- /* search first non-near point to get a good `in_dir' value */
+ curr = first;
- AF_Point point_ = prev;
+ /*
+ * We abuse the `u' and `v' fields to store index deltas to the
+ * next and previous non-near point, respectively.
+ *
+ * To avoid problems with not having non-near points, we point to
+ * `first' by default as the next non-near point.
+ *
+ */
+ curr->u = (FT_Pos)( first - curr );
+ first->v = -curr->u;
+ out_x = 0;
+ out_y = 0;
- while ( point_ != first )
- {
- AF_Point prev_ = point_->prev;
+ is_first = 1;
- FT_Pos in_x_ = point_->fx - prev_->fx;
- FT_Pos in_y_ = point_->fy - prev_->fy;
+ for ( point = first;
+ point != first || is_first;
+ point = point->next )
+ {
+ AF_Direction out_dir;
- if ( FT_ABS( in_x_ ) + FT_ABS( in_y_ ) >= near_limit )
- {
- last_good_in_x = in_x_;
- last_good_in_y = in_y_;
+ is_first = 0;
- break;
- }
+ next = point->next;
- point_ = prev_;
- }
+ out_x += next->fx - point->fx;
+ out_y += next->fy - point->fy;
+
+ if ( FT_ABS( out_x ) + FT_ABS( out_y ) < near_limit )
+ {
+ next->flags |= AF_FLAG_WEAK_INTERPOLATION;
+ continue;
}
- in_dir = af_direction_compute( in_x, in_y );
- first = prev + 1;
+ curr->u = (FT_Pos)( next - curr );
+ next->v = -curr->u;
+
+ out_dir = af_direction_compute( out_x, out_y );
+
+ /* adjust directions for all points inbetween; */
+ /* the loop also updates position of `curr' */
+ curr->out_dir = (FT_Char)out_dir;
+ for ( curr = curr->next; curr != next; curr = curr->next )
+ {
+ curr->in_dir = (FT_Char)out_dir;
+ curr->out_dir = (FT_Char)out_dir;
+ }
+ next->in_dir = (FT_Char)out_dir;
+
+ curr->u = (FT_Pos)( first - curr );
+ first->v = -curr->u;
+
+ out_x = 0;
+ out_y = 0;
}
+ }
- point->in_dir = (FT_Char)in_dir;
+ /*
+ * The next step is to `simplify' an outline's topology so that we
+ * can identify local extrema more reliably: A series of
+ * non-horizontal or non-vertical vectors pointing into the same
+ * quadrant are handled as a single, long vector. From a
+ * topological point of the view, the intermediate points are of no
+ * interest and thus tagged as weak.
+ */
- /* check whether the current point is near to the previous one */
- /* (value 20 in `near_limit' is heuristic; we use Taxicab */
- /* metrics for the test) */
+ for ( point = points; point < point_limit; point++ )
+ {
+ if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
+ continue;
- if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
- point->flags |= AF_FLAG_NEAR;
- else
+ if ( point->in_dir == AF_DIR_NONE &&
+ point->out_dir == AF_DIR_NONE )
{
- last_good_in_x = in_x;
- last_good_in_y = in_y;
- }
+ /* check whether both vectors point into the same quadrant */
+
+ FT_Pos in_x, in_y;
+ FT_Pos out_x, out_y;
+
+ AF_Point next_u = point + point->u;
+ AF_Point prev_v = point + point->v;
+
- next = point->next;
- out_x = next->fx - point->fx;
- out_y = next->fy - point->fy;
+ in_x = point->fx - prev_v->fx;
+ in_y = point->fy - prev_v->fy;
+
+ out_x = next_u->fx - point->fx;
+ out_y = next_u->fy - point->fy;
+
+ if ( ( in_x ^ out_x ) >= 0 && ( in_y ^ out_y ) >= 0 )
+ {
+ /* yes, so tag current point as weak */
+ /* and update index deltas */
+
+ point->flags |= AF_FLAG_WEAK_INTERPOLATION;
+
+ prev_v->u = (FT_Pos)( next_u - prev_v );
+ next_u->v = -prev_v->u;
+ }
+ }
+ }
- in_dir = af_direction_compute( out_x, out_y );
- point->out_dir = (FT_Char)in_dir;
+ /*
+ * Finally, check for remaining weak points. Everything else not
+ * collected in edges so far is then implicitly classified as strong
+ * points.
+ */
- /* Check for weak points. The remaining points not collected */
- /* in edges are then implicitly classified as strong points. */
+ for ( point = points; point < point_limit; point++ )
+ {
+ if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
+ continue;
if ( point->flags & AF_FLAG_CONTROL )
{
@@ -788,18 +897,25 @@
goto Is_Weak_Point;
}
- /* test whether `in' and `out' direction is approximately */
- /* the same (and use the last good `in' vector in case */
- /* the current point is near to the previous one) */
- if ( ft_corner_is_flat(
- point->flags & AF_FLAG_NEAR ? last_good_in_x : in_x,
- point->flags & AF_FLAG_NEAR ? last_good_in_y : in_y,
- out_x,
- out_y ) )
{
- /* current point lies on a straight, diagonal line */
- /* (more or less) */
- goto Is_Weak_Point;
+ AF_Point next_u = point + point->u;
+ AF_Point prev_v = point + point->v;
+
+
+ if ( ft_corner_is_flat( point->fx - prev_v->fx,
+ point->fy - prev_v->fy,
+ next_u->fx - point->fx,
+ next_u->fy - point->fy ) )
+ {
+ /* either the `in' or the `out' vector is much more */
+ /* dominant than the other one, so tag current point */
+ /* as weak and update index deltas */
+
+ prev_v->u = (FT_Pos)( next_u - prev_v );
+ next_u->v = -prev_v->u;
+
+ goto Is_Weak_Point;
+ }
}
}
else if ( point->in_dir == -point->out_dir )
@@ -807,10 +923,6 @@
/* current point forms a spike */
goto Is_Weak_Point;
}
-
- in_x = out_x;
- in_y = out_y;
- prev = point;
}
}
}
@@ -1224,8 +1336,6 @@
}
}
- point = points;
-
for ( ; contour < contour_limit; contour++ )
{
AF_Point first_touched, last_touched;
@@ -1248,7 +1358,6 @@
}
first_touched = point;
- last_touched = point;
for (;;)
{