[autofit] Redesign the recognition algorithm of strong points.
In particular, local extrema without horizontal or vertical segments are better recognized: + A + D \ / \ / \ / \ / \ + C \ / B +/ If the distances AB and CD are large, point B wasn't previously detected as an extremum since the `ft_corner_is_flat' function `swallowed' BC regardless of its direction, tagging point B as weak. The next iteration started at B and made `ft_corner_is_flat' swallow point C, tagging it as weak also, et voilà. To improve that, another pass gets now performed before calling `ft_corner_is_flat' to improve the `topology' of an outline: A sequence of non-horizontal or non-vertical vectors that point into the same quadrant are handled as a single, large vector. Additionally, distances of near points are now accumulated, which makes the auto-hinter handle them as if they were prepended to the next non-near vector. This generally improves the auto-hinter's rendering results. * src/autofit/afhints.c (af_glyph_hints_reload): Implement it. * src/autofit/afhints.h (AF_FLAGS): Remove no longer used flag `AF_FLAG_NEAR'.
This commit is contained in:
parent
71f53e122b
commit
8a94b1efd6
38
ChangeLog
38
ChangeLog
|
@ -1,3 +1,41 @@
|
|||
2014-04-12 Werner Lemberg <wl@gnu.org>
|
||||
|
||||
[autofit] Redesign the recognition algorithm of strong points.
|
||||
|
||||
In particular, local extrema without horizontal or vertical segments
|
||||
are better recognized:
|
||||
|
||||
+ A + D
|
||||
\ /
|
||||
\ /
|
||||
\ /
|
||||
\ /
|
||||
\ + C
|
||||
\ /
|
||||
B +/
|
||||
|
||||
If the distances AB and CD are large, point B wasn't previously
|
||||
detected as an extremum since the `ft_corner_is_flat' function
|
||||
`swallowed' BC regardless of its direction, tagging point B as weak.
|
||||
The next iteration started at B and made `ft_corner_is_flat' swallow
|
||||
point C, tagging it as weak also, et voilà.
|
||||
|
||||
To improve that, another pass gets now performed before calling
|
||||
`ft_corner_is_flat' to improve the `topology' of an outline: A
|
||||
sequence of non-horizontal or non-vertical vectors that point into
|
||||
the same quadrant are handled as a single, large vector.
|
||||
|
||||
Additionally, distances of near points are now accumulated, which
|
||||
makes the auto-hinter handle them as if they were prepended to the
|
||||
next non-near vector.
|
||||
|
||||
This generally improves the auto-hinter's rendering results.
|
||||
|
||||
* src/autofit/afhints.c (af_glyph_hints_reload): Implement it.
|
||||
|
||||
* src/autofit/afhints.h (AF_FLAGS): Remove no longer used flag
|
||||
`AF_FLAG_NEAR'.
|
||||
|
||||
2014-04-05 Werner Lemberg <wl@gnu.org>
|
||||
|
||||
[autofit] Improve scoring algorithm for identifying stems.
|
||||
|
|
|
@ -698,91 +698,165 @@
|
|||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
AF_Point* contour;
|
||||
AF_Point* contour_limit = hints->contours + hints->num_contours;
|
||||
|
||||
|
||||
for ( contour = hints->contours; contour < contour_limit; contour++ )
|
||||
{
|
||||
AF_Point first = *contour;
|
||||
AF_Point next, prev, curr;
|
||||
|
||||
FT_Pos out_x, out_y;
|
||||
|
||||
FT_Bool is_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 )
|
||||
{
|
||||
out_x = point->fx - prev->fx;
|
||||
out_y = point->fy - prev->fy;
|
||||
|
||||
/* we use Taxicab metrics to measure the vector length */
|
||||
if ( FT_ABS( out_x ) + FT_ABS( out_y ) >= near_limit )
|
||||
break;
|
||||
|
||||
point = prev;
|
||||
prev = prev->prev;
|
||||
}
|
||||
|
||||
/* adjust first point */
|
||||
first = point;
|
||||
|
||||
/* now loop over all points of the contour to get */
|
||||
/* `in' and `out' vector directions */
|
||||
|
||||
curr = first;
|
||||
out_x = 0;
|
||||
out_y = 0;
|
||||
|
||||
is_first = 1;
|
||||
|
||||
for ( point = first;
|
||||
point != first || is_first;
|
||||
point = point->next )
|
||||
{
|
||||
AF_Direction out_dir;
|
||||
|
||||
|
||||
is_first = 0;
|
||||
|
||||
next = point->next;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* we abuse the `u' and `v' fields to store index deltas */
|
||||
/* to the next and previous non-near point, respectively */
|
||||
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;
|
||||
|
||||
out_x = 0;
|
||||
out_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
for ( point = points; point < point_limit; point++ )
|
||||
{
|
||||
AF_Point next;
|
||||
FT_Pos out_x, out_y;
|
||||
if ( point->flags & AF_FLAG_WEAK_INTERPOLATION )
|
||||
continue;
|
||||
|
||||
|
||||
if ( point == first )
|
||||
if ( point->in_dir == AF_DIR_NONE &&
|
||||
point->out_dir == AF_DIR_NONE )
|
||||
{
|
||||
prev = first->prev;
|
||||
/* check whether both vectors point into the same quadrant */
|
||||
|
||||
in_x = first->fx - prev->fx;
|
||||
in_y = first->fy - prev->fy;
|
||||
FT_Pos in_x, in_y;
|
||||
FT_Pos out_x, out_y;
|
||||
|
||||
last_good_in_x = in_x;
|
||||
last_good_in_y = in_y;
|
||||
AF_Point next_u = point + point->u;
|
||||
AF_Point prev_v = point + point->v;
|
||||
|
||||
if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
|
||||
|
||||
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 )
|
||||
{
|
||||
/* search first non-near point to get a good `in_dir' value */
|
||||
/* yes, so tag current point as weak */
|
||||
/* and update index deltas */
|
||||
|
||||
AF_Point point_ = prev;
|
||||
point->flags |= AF_FLAG_WEAK_INTERPOLATION;
|
||||
|
||||
|
||||
while ( point_ != first )
|
||||
{
|
||||
AF_Point prev_ = point_->prev;
|
||||
|
||||
FT_Pos in_x_ = point_->fx - prev_->fx;
|
||||
FT_Pos in_y_ = point_->fy - prev_->fy;
|
||||
|
||||
|
||||
if ( FT_ABS( in_x_ ) + FT_ABS( in_y_ ) >= near_limit )
|
||||
{
|
||||
last_good_in_x = in_x_;
|
||||
last_good_in_y = in_y_;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
point_ = prev_;
|
||||
}
|
||||
prev_v->u = (FT_Pos)( next_u - prev_v );
|
||||
next_u->v = -prev_v->u;
|
||||
}
|
||||
|
||||
in_dir = af_direction_compute( in_x, in_y );
|
||||
first = prev + 1;
|
||||
}
|
||||
}
|
||||
|
||||
point->in_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 whether the current point is near to the previous one */
|
||||
/* (value 20 in `near_limit' is heuristic; we use Taxicab */
|
||||
/* metrics for the test) */
|
||||
|
||||
if ( FT_ABS( in_x ) + FT_ABS( in_y ) < near_limit )
|
||||
point->flags |= AF_FLAG_NEAR;
|
||||
else
|
||||
{
|
||||
last_good_in_x = in_x;
|
||||
last_good_in_y = in_y;
|
||||
}
|
||||
|
||||
next = point->next;
|
||||
out_x = next->fx - point->fx;
|
||||
out_y = next->fy - point->fy;
|
||||
|
||||
in_dir = af_direction_compute( out_x, out_y );
|
||||
point->out_dir = (FT_Char)in_dir;
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
|
@ -799,18 +873,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 )
|
||||
|
@ -818,9 +899,6 @@
|
|||
/* current point forms a spike */
|
||||
goto Is_Weak_Point;
|
||||
}
|
||||
|
||||
in_x = out_x;
|
||||
in_y = out_y;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
/* */
|
||||
/* Auto-fitter hinting routines (specification). */
|
||||
/* */
|
||||
/* Copyright 2003-2008, 2010-2012 by */
|
||||
/* Copyright 2003-2008, 2010-2012, 2014 by */
|
||||
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
|
||||
/* */
|
||||
/* This file is part of the FreeType project, and may only be used, */
|
||||
|
@ -236,10 +236,7 @@ FT_BEGIN_HEADER
|
|||
AF_FLAG_WEAK_INTERPOLATION = 1 << 8,
|
||||
|
||||
/* all inflection points in the outline have this flag set */
|
||||
AF_FLAG_INFLECTION = 1 << 9,
|
||||
|
||||
/* the current point is very near to another one */
|
||||
AF_FLAG_NEAR = 1 << 10
|
||||
AF_FLAG_INFLECTION = 1 << 9
|
||||
|
||||
} AF_Flags;
|
||||
|
||||
|
|
Loading…
Reference in New Issue