Here is a third and I hope final version of the spline flattening
patch, incorporating the latest version of David Bevan's cubic spline
flattening routine.
Notes:
1. As with the previous versions, it solves the bug in flattening
s-shaped curves (cubic with control points on both sides of the chord).
2. I have incorporated David's latest code as is. However, I have
expanded his comments, most importantly (i) by giving the reference to
Hain's paper in full rather than as a tinyurl, so as to make it easier
to find elsewhere if it is moved or if the tinyurl service is
discontinued, and (ii) by incorporating a detailed explanation he
provided of the calculation of L.
3. It may be thought advisable to split the patch into two: one for
cubics, which is essential because it fixes a bug, and one for conics,
which is not essential but gives some speed improvement and simpler
code. I leave that up to Werner.
Graham
diff --git "a/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_c8f5b9.c"
"b/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_f4f03b.c"
index 0b94143..a3a7a31 100644
--- "a/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_c8f5b9.c"
+++ "b/C:\\DOCUME~1\\Graham\\LOCALS~1\\Temp\\ftgrays_f4f03b.c"
@@ -90,6 +90,9 @@
#undef FT_COMPONENT
#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_
@@ -354,8 +357,6 @@ typedef ptrdiff_t FT_PtrDist;
int band_size;
int band_shoot;
- int conic_level;
- int cubic_level;
ft_jmp_buf jump_buffer;
@@ -888,31 +889,18 @@ typedef ptrdiff_t FT_PtrDist;
if ( dx < dy )
dx = dy;
- level = 1;
- dx = dx / ras.conic_level;
- while ( dx > 0 )
+ if ( dx <= FT_MAX_CURVE_DEVIATION )
{
- dx >>= 2;
- level++;
+ gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) );
+ return;
}
- /* a shortcut to speed things up */
- if ( level <= 1 )
+ level = 1;
+ dx /= FT_MAX_CURVE_DEVIATION;
+ while ( dx > 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;
+ dx >>= 2;
+ level++;
}
arc = ras.bez_stack;
@@ -957,21 +945,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,55 +987,8 @@ 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 );
@@ -1071,60 +1000,98 @@ 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;
+ /* 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.
*/
+ {
+ TPos dx, dy, L, dx1, dy1, dx2, dy2, s, s_limit;
+
+
+ /* 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(dx2 + dy2) 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%. */
+ L = ( 236 * FT_MAX(labs(dx), labs(dy))
+ + 97 * FT_MIN(labs(dx), labs(dy))) >> 8;
+
+ /* Avoid possible arithmetic overflow below by splitting. */
+ if (L > 32767)
+ goto Split;
+
+ /* s is L * the perpendicular distance from P1 to the line P0-P3. */
+ s = labs( dy * (dx1 = arc[1].x - arc[0].x)
+ - dx * (dy1 = arc[1].y - arc[0].y));
+
+ /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
+ if (s > (s_limit = L * (TPos)(FT_MAX_CURVE_DEVIATION / 0.75)))
+ goto Split;
+
+ /* s is L * the perpendicular distance from P2 to the line P0-P3. */
+ s = labs( dy * (dx2 = arc[2].x - arc[0].x)
+ - dx * (dy2 = arc[2].y - arc[0].y));
+
+ /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
+ 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:
- 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;
- }
+ gray_split_cubic( arc );
+ arc += 3;
+ continue;
Draw:
- {
- TPos to_x, to_y;
+ gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
- 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;
+ if (arc == ras.bez_stack)
+ return;
- 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 +1727,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 )
_______________________________________________
Freetype-devel mailing list
[email protected]
http://lists.nongnu.org/mailman/listinfo/freetype-devel