From fc32e1c8cc67b0481d3e563e39dd11f120585b7f Mon Sep 17 00:00:00 2001 From: Alexei Podtelezhnikov Date: Mon, 19 Aug 2013 22:57:05 -0400 Subject: [PATCH] [base] Enable new algorithm for BBox_Cubic_Check. * src/base/ftbbox.c: Enable new BBox_Cubic_Check algorithm, remove the old one. Improve comments. --- ChangeLog | 7 ++ src/base/ftbbox.c | 216 ++++++---------------------------------------- 2 files changed, 32 insertions(+), 191 deletions(-) diff --git a/ChangeLog b/ChangeLog index eb13f321d..051ef3bda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2013-08-19 Alexei Podtelezhnikov + + [base] Enable new algorithm for BBox_Cubic_Check. + + * src/base/ftbbox.c: Enable new BBox_Cubic_Check algorithm, remove the + old one. Improve comments. + 2013-08-18 Werner Lemberg * builds/unix/unix-def.in (freetype2.pc): Don't set executable bit. diff --git a/src/base/ftbbox.c b/src/base/ftbbox.c index c4bd02740..ebbfb1a8f 100644 --- a/src/base/ftbbox.c +++ b/src/base/ftbbox.c @@ -109,9 +109,9 @@ FT_Pos* max ) { /* This function is only called when a control off-point is outside */ - /* the bbox. This also means there must be a local extremum within */ - /* the segment with the value of (y1*y3 - y2*y2)/(y1 - 2*y2 + y3). */ - /* Offsetting from the closest point to the extermum, y2, we get */ + /* the bbox that contains all on-points. It finds a local extremum */ + /* within the segment, equal to (y1*y3 - y2*y2)/(y1 - 2*y2 + y3). */ + /* Or, offsetting from y2, we get */ y1 -= y2; y3 -= y2; @@ -185,8 +185,8 @@ /* */ /* */ /* Finds the extrema of a 1-dimensional cubic Bezier curve and */ - /* updates a bounding range. This version uses splitting because we */ - /* don't want to use square roots and extra accuracy. */ + /* updates a bounding range. This version uses iterative splitting */ + /* because it is faster than the exact solution with square roots. */ /* */ /* */ /* p1 :: The start coordinate. */ @@ -203,17 +203,15 @@ /* max :: The address of the current maximum. */ /* */ -#if 0 - static FT_Pos - update_max( FT_Pos q1, - FT_Pos q2, - FT_Pos q3, - FT_Pos q4, - FT_Pos max ) + update_cubic_max( FT_Pos q1, + FT_Pos q2, + FT_Pos q3, + FT_Pos q4, + FT_Pos max ) { - /* for a conic segment to possibly reach new maximum */ - /* one of its off-points must be above the current value */ + /* for a cubic segment to possibly reach new maximum, at least */ + /* one of its off-points must stay above the current value */ while ( q2 > max || q3 > max ) { /* determine which half contains the maximum and split */ @@ -267,13 +265,15 @@ FT_Pos nmin, nmax; FT_Int shift; - /* This implementation relies on iterative bisection of the segment. */ - /* The fixed-point arithmentic of bisection is inherently stable but */ - /* may loose accuracy in the two lowest bits. To compensate, we */ - /* upscale the segment if there is room. Large values may need to be */ - /* downscaled to avoid overflows during bisection bisection. This */ - /* function is only called when a control off-point is outside the */ - /* the bbox and, thus, has the top absolute value among arguments. */ + /* This function is only called when a control off-point is outside */ + /* the bbox that contains all on-points. It finds a local extremum */ + /* within the segment using iterative bisection of the segment. */ + /* The fixed-point arithmentic of bisection is inherently stable */ + /* but may loose accuracy in the two lowest bits. To compensate, */ + /* we upscale the segment if there is room. Large values may need */ + /* to be downscaled to avoid overflows during bisection bisection. */ + /* The control off-point outside the bbox is likely to have the top */ + /* absolute value among arguments. */ shift = 27 - FT_MSB( FT_ABS( p2 ) | FT_ABS( p3 ) ); @@ -282,7 +282,7 @@ /* upscaling too much just wastes time */ if ( shift > 2 ) shift = 2; - + p1 <<= shift; p2 <<= shift; p3 <<= shift; @@ -290,7 +290,7 @@ nmin = *min << shift; nmax = *max << shift; } - else + else { p1 >>= -shift; p2 >>= -shift; @@ -300,10 +300,10 @@ nmax = *max >> -shift; } - nmax = update_max( p1, p2, p3, p4, nmax ); + nmax = update_cubic_max( p1, p2, p3, p4, nmax ); /* now flip the signs to update the minimum */ - nmin = -update_max( -p1, -p2, -p3, -p4, -nmin ); + nmin = -update_cubic_max( -p1, -p2, -p3, -p4, -nmin ); if ( shift > 0 ) { @@ -322,172 +322,6 @@ *max = nmax; } -#else - - static void - test_cubic_extrema( FT_Pos y1, - FT_Pos y2, - FT_Pos y3, - FT_Pos y4, - FT_Fixed u, - FT_Pos* min, - FT_Pos* max ) - { - /* FT_Pos a = y4 - 3*y3 + 3*y2 - y1; */ - FT_Pos b = y3 - 2*y2 + y1; - FT_Pos c = y2 - y1; - FT_Pos d = y1; - FT_Pos y; - FT_Fixed uu; - - FT_UNUSED ( y4 ); - - - /* The polynomial is */ - /* */ - /* P(x) = a*x^3 + 3b*x^2 + 3c*x + d , */ - /* */ - /* dP/dx = 3a*x^2 + 6b*x + 3c . */ - /* */ - /* However, we also have */ - /* */ - /* dP/dx(u) = 0 , */ - /* */ - /* which implies by subtraction that */ - /* */ - /* P(u) = b*u^2 + 2c*u + d . */ - - if ( u > 0 && u < 0x10000L ) - { - uu = FT_MulFix( u, u ); - y = d + FT_MulFix( c, 2*u ) + FT_MulFix( b, uu ); - - if ( y < *min ) *min = y; - if ( y > *max ) *max = y; - } - } - - - static void - BBox_Cubic_Check( FT_Pos y1, - FT_Pos y2, - FT_Pos y3, - FT_Pos y4, - FT_Pos* min, - FT_Pos* max ) - { - /* always compare first and last points */ - if ( y1 < *min ) *min = y1; - else if ( y1 > *max ) *max = y1; - - if ( y4 < *min ) *min = y4; - else if ( y4 > *max ) *max = y4; - - /* now, try to see if there are split points here */ - if ( y1 <= y4 ) - { - /* flat or ascending arc test */ - if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 ) - return; - } - else /* y1 > y4 */ - { - /* descending arc test */ - if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 ) - return; - } - - /* There are some split points. Find them. */ - /* We already made sure that a, b, and c below cannot be all zero. */ - { - FT_Pos a = y4 - 3*y3 + 3*y2 - y1; - FT_Pos b = y3 - 2*y2 + y1; - FT_Pos c = y2 - y1; - FT_Pos d; - FT_Fixed t; - FT_Int shift; - - - /* We need to solve `ax^2+2bx+c' here, without floating points! */ - /* The trick is to normalize to a different representation in order */ - /* to use our 16.16 fixed-point routines. */ - /* */ - /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */ - /* These values must fit into a single 16.16 value. */ - /* */ - /* We normalize a, b, and c to `8.16' fixed-point values to ensure */ - /* that their product is held in a `16.16' value including the sign. */ - /* Necessarily, we need to shift `a', `b', and `c' so that the most */ - /* significant bit of their absolute values is at position 22. */ - /* */ - /* This also means that we are using 23 bits of precision to compute */ - /* the zeros, independently of the range of the original polynomial */ - /* coefficients. */ - /* */ - /* This algorithm should ensure reasonably accurate values for the */ - /* zeros. Note that they are only expressed with 16 bits when */ - /* computing the extrema (the zeros need to be in 0..1 exclusive */ - /* to be considered part of the arc). */ - - shift = FT_MSB( FT_ABS( a ) | FT_ABS( b ) | FT_ABS( c ) ); - - if ( shift > 22 ) - { - shift -= 22; - - /* this loses some bits of precision, but we use 23 of them */ - /* for the computation anyway */ - a >>= shift; - b >>= shift; - c >>= shift; - } - else - { - shift = 22 - shift; - - a <<= shift; - b <<= shift; - c <<= shift; - } - - /* handle a == 0 */ - if ( a == 0 ) - { - if ( b != 0 ) - { - t = - FT_DivFix( c, b ) / 2; - test_cubic_extrema( y1, y2, y3, y4, t, min, max ); - } - } - else - { - /* solve the equation now */ - d = FT_MulFix( b, b ) - FT_MulFix( a, c ); - if ( d < 0 ) - return; - - if ( d == 0 ) - { - /* there is a single split point at -b/a */ - t = - FT_DivFix( b, a ); - test_cubic_extrema( y1, y2, y3, y4, t, min, max ); - } - else - { - /* there are two solutions; we need to filter them */ - d = FT_SqrtFixed( (FT_Int32)d ); - t = - FT_DivFix( b - d, a ); - test_cubic_extrema( y1, y2, y3, y4, t, min, max ); - - t = - FT_DivFix( b + d, a ); - test_cubic_extrema( y1, y2, y3, y4, t, min, max ); - } - } - } - } - -#endif - /*************************************************************************/ /* */