[smooth] Fix and improve spline flattening.
This fixes the flattening of cubic, S-shaped curves and speeds up the handling of both the conic and cubic arcs. See the discussions on the freetype-devel mailing list in late August and September 2010 for details. * src/smooth/ftgrays.c (FT_MAX_CURVE_DEVIATION): New macro. (TWorker): Remove `conic_level' and `cubic_level' elements. (gray_render_conic): Simplify algorithm. (gray_render_cubic): New algorithm; details are given in the code comments. (gray_convert_glyph): Remove heuristics.
This commit is contained in:
parent
d38ba0c92d
commit
e0a9a93330
18
ChangeLog
18
ChangeLog
@ -1,3 +1,21 @@
|
||||
2010-09-20 Graham Asher <graham.asher@btinternet.com>
|
||||
David Bevan <david.bevan@pb.com>
|
||||
|
||||
[smooth] Fix and improve spline flattening.
|
||||
|
||||
This fixes the flattening of cubic, S-shaped curves and speeds up
|
||||
the handling of both the conic and cubic arcs.
|
||||
|
||||
See the discussions on the freetype-devel mailing list in late
|
||||
August and September 2010 for details.
|
||||
|
||||
* src/smooth/ftgrays.c (FT_MAX_CURVE_DEVIATION): New macro.
|
||||
(TWorker): Remove `conic_level' and `cubic_level' elements.
|
||||
(gray_render_conic): Simplify algorithm.
|
||||
(gray_render_cubic): New algorithm; details are given in the code
|
||||
comments.
|
||||
(gray_convert_glyph): Remove heuristics.
|
||||
|
||||
2010-09-19 Werner Lemberg <wl@gnu.org>
|
||||
|
||||
Minor fixes.
|
||||
|
@ -91,6 +91,11 @@
|
||||
#define FT_COMPONENT trace_smooth
|
||||
|
||||
|
||||
/* The maximum distance of a curve from the chord, in 64ths of a pixel; */
|
||||
/* used when flattening curves. */
|
||||
#define FT_MAX_CURVE_DEVIATION 16
|
||||
|
||||
|
||||
#ifdef _STANDALONE_
|
||||
|
||||
|
||||
@ -187,7 +192,7 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
shift_, \
|
||||
delta_ \
|
||||
};
|
||||
|
||||
|
||||
#define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, \
|
||||
raster_new_, raster_reset_, \
|
||||
raster_set_mode_, raster_render_, \
|
||||
@ -354,8 +359,6 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
|
||||
int band_size;
|
||||
int band_shoot;
|
||||
int conic_level;
|
||||
int cubic_level;
|
||||
|
||||
ft_jmp_buf jump_buffer;
|
||||
|
||||
@ -888,33 +891,20 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
if ( dx < dy )
|
||||
dx = dy;
|
||||
|
||||
if ( dx <= FT_MAX_CURVE_DEVIATION )
|
||||
{
|
||||
gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
|
||||
return;
|
||||
}
|
||||
|
||||
level = 1;
|
||||
dx = dx / ras.conic_level;
|
||||
while ( dx > 0 )
|
||||
dx /= FT_MAX_CURVE_DEVIATION;
|
||||
while ( dx > 1 )
|
||||
{
|
||||
dx >>= 2;
|
||||
level++;
|
||||
}
|
||||
|
||||
/* a shortcut to speed things up */
|
||||
if ( level <= 1 )
|
||||
{
|
||||
/* we compute the mid-point directly in order to avoid */
|
||||
/* calling gray_split_conic() */
|
||||
TPos to_x, to_y, mid_x, mid_y;
|
||||
|
||||
|
||||
to_x = UPSCALE( to->x );
|
||||
to_y = UPSCALE( to->y );
|
||||
mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
|
||||
mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
|
||||
|
||||
gray_render_line( RAS_VAR_ mid_x, mid_y );
|
||||
gray_render_line( RAS_VAR_ to_x, to_y );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
arc = ras.bez_stack;
|
||||
levels = ras.lev_stack;
|
||||
top = 0;
|
||||
@ -957,21 +947,9 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
}
|
||||
|
||||
Draw:
|
||||
{
|
||||
TPos to_x, to_y, mid_x, mid_y;
|
||||
|
||||
|
||||
to_x = arc[0].x;
|
||||
to_y = arc[0].y;
|
||||
mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
|
||||
mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
|
||||
|
||||
gray_render_line( RAS_VAR_ mid_x, mid_y );
|
||||
gray_render_line( RAS_VAR_ to_x, to_y );
|
||||
|
||||
top--;
|
||||
arc -= 2;
|
||||
}
|
||||
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
|
||||
top--;
|
||||
arc -= 2;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -1011,56 +989,9 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
const FT_Vector* control2,
|
||||
const FT_Vector* to )
|
||||
{
|
||||
int top, level;
|
||||
int* levels;
|
||||
FT_Vector* arc;
|
||||
int mid_x = ( DOWNSCALE( ras.x ) + to->x +
|
||||
3 * (control1->x + control2->x ) ) / 8;
|
||||
int mid_y = ( DOWNSCALE( ras.y ) + to->y +
|
||||
3 * (control1->y + control2->y ) ) / 8;
|
||||
TPos dx = DOWNSCALE( ras.x ) + to->x - ( mid_x << 1 );
|
||||
TPos dy = DOWNSCALE( ras.y ) + to->y - ( mid_y << 1 );
|
||||
|
||||
|
||||
if ( dx < 0 )
|
||||
dx = -dx;
|
||||
if ( dy < 0 )
|
||||
dy = -dy;
|
||||
if ( dx < dy )
|
||||
dx = dy;
|
||||
|
||||
level = 1;
|
||||
dx /= ras.cubic_level;
|
||||
while ( dx > 0 )
|
||||
{
|
||||
dx >>= 2;
|
||||
level++;
|
||||
}
|
||||
|
||||
if ( level <= 1 )
|
||||
{
|
||||
TPos to_x, to_y;
|
||||
|
||||
|
||||
to_x = UPSCALE( to->x );
|
||||
to_y = UPSCALE( to->y );
|
||||
|
||||
/* Recalculation of midpoint is needed only if */
|
||||
/* UPSCALE and DOWNSCALE have any effect. */
|
||||
|
||||
#if ( PIXEL_BITS != 6 )
|
||||
mid_x = ( ras.x + to_x +
|
||||
3 * UPSCALE( control1->x + control2->x ) ) / 8;
|
||||
mid_y = ( ras.y + to_y +
|
||||
3 * UPSCALE( control1->y + control2->y ) ) / 8;
|
||||
#endif
|
||||
|
||||
gray_render_line( RAS_VAR_ mid_x, mid_y );
|
||||
gray_render_line( RAS_VAR_ to_x, to_y );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
arc = ras.bez_stack;
|
||||
arc[0].x = UPSCALE( to->x );
|
||||
arc[0].y = UPSCALE( to->y );
|
||||
@ -1071,60 +1002,123 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
arc[3].x = ras.x;
|
||||
arc[3].y = ras.y;
|
||||
|
||||
levels = ras.lev_stack;
|
||||
top = 0;
|
||||
levels[0] = level;
|
||||
|
||||
while ( top >= 0 )
|
||||
for (;;)
|
||||
{
|
||||
level = levels[top];
|
||||
if ( level > 1 )
|
||||
/* Check that the arc crosses the current band. */
|
||||
TPos min, max, y;
|
||||
|
||||
|
||||
min = max = arc[0].y;
|
||||
|
||||
y = arc[1].y;
|
||||
if ( y < min )
|
||||
min = y;
|
||||
if ( y > max )
|
||||
max = y;
|
||||
|
||||
y = arc[2].y;
|
||||
if ( y < min )
|
||||
min = y;
|
||||
if ( y > max )
|
||||
max = y;
|
||||
|
||||
y = arc[3].y;
|
||||
if ( y < min )
|
||||
min = y;
|
||||
if ( y > max )
|
||||
max = y;
|
||||
|
||||
if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
|
||||
goto Draw;
|
||||
|
||||
/* Decide whether to split or draw. See `Rapid Termination */
|
||||
/* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
|
||||
/* F. Hain, at */
|
||||
/* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
|
||||
|
||||
{
|
||||
/* check that the arc crosses the current band */
|
||||
TPos min, max, y;
|
||||
TPos dx, dy, dx_, dy_;
|
||||
TPos dx1, dy1, dx2, dy2;
|
||||
TPos L, s, s_limit;
|
||||
|
||||
|
||||
min = max = arc[0].y;
|
||||
y = arc[1].y;
|
||||
if ( y < min ) min = y;
|
||||
if ( y > max ) max = y;
|
||||
y = arc[2].y;
|
||||
if ( y < min ) min = y;
|
||||
if ( y > max ) max = y;
|
||||
y = arc[3].y;
|
||||
if ( y < min ) min = y;
|
||||
if ( y > max ) max = y;
|
||||
if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
|
||||
goto Draw;
|
||||
gray_split_cubic( arc );
|
||||
arc += 3;
|
||||
top ++;
|
||||
levels[top] = levels[top - 1] = level - 1;
|
||||
continue;
|
||||
/* dx and dy are x and y components of the P0-P3 chord vector. */
|
||||
dx = arc[3].x - arc[0].x;
|
||||
dy = arc[3].y - arc[0].y;
|
||||
|
||||
/* L is an (under)estimate of the Euclidean distance P0-P3. */
|
||||
/* */
|
||||
/* If dx >= dy, then r = sqrt(dx^2 + dy^2) can be overestimated */
|
||||
/* with least maximum error by */
|
||||
/* */
|
||||
/* r_upperbound = dx + (sqrt(2) - 1) * dy , */
|
||||
/* */
|
||||
/* where sqrt(2) - 1 can be (over)estimated by 107/256, giving an */
|
||||
/* error of no more than 8.4%. */
|
||||
/* */
|
||||
/* Similarly, some elementary calculus shows that r can be */
|
||||
/* underestimated with least maximum error by */
|
||||
/* */
|
||||
/* r_lowerbound = sqrt(2 + sqrt(2)) / 2 * dx */
|
||||
/* + sqrt(2 - sqrt(2)) / 2 * dy . */
|
||||
/* */
|
||||
/* 236/256 and 97/256 are (under)estimates of the two algebraic */
|
||||
/* numbers, giving an error of no more than 8.1%. */
|
||||
|
||||
dx_ = FT_ABS( dx );
|
||||
dy_ = FT_ABS( dy );
|
||||
L = ( 236 * FT_MAX( dx_, dy_ ) + 97 * FT_MIN( dx_, dy_ ) ) >> 8;
|
||||
|
||||
/* Avoid possible arithmetic overflow below by splitting. */
|
||||
if ( L > 32767 )
|
||||
goto Split;
|
||||
|
||||
/* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
|
||||
s_limit = L * (TPos)( FT_MAX_CURVE_DEVIATION / 0.75 );
|
||||
|
||||
/* s is L * the perpendicular distance from P1 to the line P0-P3. */
|
||||
dx1 = arc[1].x - arc[0].x;
|
||||
dy1 = arc[1].y - arc[0].y;
|
||||
s = FT_ABS( dy * dx1 - dx * dy1 );
|
||||
|
||||
if ( s > s_limit )
|
||||
goto Split;
|
||||
|
||||
/* s is L * the perpendicular distance from P2 to the line P0-P3. */
|
||||
dx2 = arc[2].x - arc[0].x;
|
||||
dy2 = arc[2].y - arc[0].y;
|
||||
s = FT_ABS( dy * dx2 - dx * dy2 );
|
||||
|
||||
if ( s > s_limit )
|
||||
goto Split;
|
||||
|
||||
/* If P1 or P2 is outside P0-P3, split the curve. */
|
||||
if ( dy * dy1 + dx * dx1 < 0 ||
|
||||
dy * dy2 + dx * dx2 < 0 ||
|
||||
dy * (arc[3].y - arc[1].y) + dx * (arc[3].x - arc[1].x) < 0 ||
|
||||
dy * (arc[3].y - arc[2].y) + dx * (arc[3].x - arc[2].x) < 0 )
|
||||
goto Split;
|
||||
|
||||
/* No reason to split. */
|
||||
goto Draw;
|
||||
}
|
||||
|
||||
Split:
|
||||
gray_split_cubic( arc );
|
||||
arc += 3;
|
||||
continue;
|
||||
|
||||
Draw:
|
||||
{
|
||||
TPos to_x, to_y;
|
||||
gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
|
||||
|
||||
if ( arc == ras.bez_stack )
|
||||
return;
|
||||
|
||||
to_x = arc[0].x;
|
||||
to_y = arc[0].y;
|
||||
mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
|
||||
mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
|
||||
|
||||
gray_render_line( RAS_VAR_ mid_x, mid_y );
|
||||
gray_render_line( RAS_VAR_ to_x, to_y );
|
||||
top --;
|
||||
arc -= 3;
|
||||
}
|
||||
arc -= 3;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int
|
||||
gray_move_to( const FT_Vector* to,
|
||||
PWorker worker )
|
||||
@ -1760,25 +1754,6 @@ typedef ptrdiff_t FT_PtrDist;
|
||||
ras.count_ex = ras.max_ex - ras.min_ex;
|
||||
ras.count_ey = ras.max_ey - ras.min_ey;
|
||||
|
||||
/* simple heuristic used to speed up the bezier decomposition -- see */
|
||||
/* the code in gray_render_conic() and gray_render_cubic() for more */
|
||||
/* details */
|
||||
ras.conic_level = 32;
|
||||
ras.cubic_level = 16;
|
||||
|
||||
{
|
||||
int level = 0;
|
||||
|
||||
|
||||
if ( ras.count_ex > 24 || ras.count_ey > 24 )
|
||||
level++;
|
||||
if ( ras.count_ex > 120 || ras.count_ey > 120 )
|
||||
level++;
|
||||
|
||||
ras.conic_level <<= level;
|
||||
ras.cubic_level <<= level;
|
||||
}
|
||||
|
||||
/* set up vertical bands */
|
||||
num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
|
||||
if ( num_bands == 0 )
|
||||
|
Loading…
Reference in New Issue
Block a user