[smooth] Faster alternative line renderer.

This implementation renders the entire line segment at once without
subdividing it into scanlines.  The main speed improvement comes from
reducing the number of divisions to just two per line segment, which
is a bare minimum to calculate cell coverage in a smooth rasterizer.
Notably, the progression from cell to cell does not itself require any
divisions at all.  The speed improvement is more noticeable at larger
sizes.

* src/smooth/ftgrays.c (gray_render_line): New implementation.
This commit is contained in:
Alexei Podtelezhnikov 2015-10-06 22:39:54 -04:00
parent 066a49139b
commit 6eb6158dd7
2 changed files with 165 additions and 2 deletions

View File

@ -1,3 +1,17 @@
2015-10-01 Alexei Podtelezhnikov <apodtele@gmail.com>
[smooth] Faster alternative line renderer.
This implementation renders the entire line segment at once without
subdividing it into scanlines. The main speed improvement comes from
reducing the number of divisions to just two per line segment, which
is a bare minimum to calculate cell coverage in a smooth rasterizer.
Notably, the progression from cell to cell does not itself require any
divisions at all. The speed improvement is more noticeable at larger
sizes.
* src/smooth/ftgrays.c (gray_render_line): New implementation.
2015-10-06 Werner Lemberg <wl@gnu.org>
[cff] Return correct PS names from pure CFF (#46130).

View File

@ -135,8 +135,10 @@
#include <string.h>
#include <setjmp.h>
#include <limits.h>
#define FT_UINT_MAX UINT_MAX
#define FT_INT_MAX INT_MAX
#define FT_CHAR_BIT CHAR_BIT
#define FT_UINT_MAX UINT_MAX
#define FT_INT_MAX INT_MAX
#define FT_ULONG_MAX ULONG_MAX
#define ft_memset memset
@ -366,6 +368,15 @@ typedef ptrdiff_t FT_PtrDist;
#endif /* __arm__ */
/* These macros speed up repetitive divisions by replacing them */
/* with multiplications and right shifts. */
#define FT_UDIVPREP( b ) \
long b ## _r = (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b )
#define FT_UDIV( a, b ) \
( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \
( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) )
/*************************************************************************/
/* */
/* TYPE DEFINITIONS */
@ -678,6 +689,7 @@ typedef ptrdiff_t FT_PtrDist;
gray_set_cell( RAS_VAR_ ex, ey );
}
#if 0
/*************************************************************************/
/* */
@ -910,6 +922,143 @@ typedef ptrdiff_t FT_PtrDist;
ras.y = to_y;
}
#else
/*************************************************************************/
/* */
/* Render a straight line across multiple cells in any direction. */
/* */
static void
gray_render_line( RAS_ARG_ TPos to_x,
TPos to_y )
{
TPos dx, dy, fx1, fy1, fx2, fy2;
TCoord ex1, ex2, ey1, ey2;
ex1 = TRUNC( ras.x );
ex2 = TRUNC( to_x );
ey1 = TRUNC( ras.y );
ey2 = TRUNC( to_y );
/* perform vertical clipping */
if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
( ey1 < ras.min_ey && ey2 < ras.min_ey ) )
goto End;
dx = to_x - ras.x;
dy = to_y - ras.y;
fx1 = ras.x - SUBPIXELS( ex1 );
fy1 = ras.y - SUBPIXELS( ey1 );
if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */
;
else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */
{
ex1 = ex2;
gray_set_cell( RAS_VAR_ ex1, ey1 );
}
else if ( dx == 0 )
{
if ( dy > 0 ) /* vertical line up */
do
{
fy2 = ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * fx1 * 2;
fy1 = 0;
ey1++;
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ey1 != ey2 );
else /* vertical line down */
do
{
fy2 = 0;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * fx1 * 2;
fy1 = ONE_PIXEL;
ey1--;
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ey1 != ey2 );
}
else /* any other line */
{
TArea prod = dx * fy1 - dy * fx1;
FT_UDIVPREP( dx );
FT_UDIVPREP( dy );
/* The fundamental value `prod' determines which side and the */
/* exact coordinate where the line exits current cell. It is */
/* also easily updated when moving from one cell to the next. */
do
{
if ( prod <= 0 &&
prod - dx * ONE_PIXEL > 0 ) /* left */
{
fx2 = 0;
fy2 = (TPos)FT_UDIV( -prod, -dx );
prod -= dy * ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = ONE_PIXEL;
fy1 = fy2;
ex1--;
}
else if ( prod - dx * ONE_PIXEL <= 0 &&
prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */
{
prod -= dx * ONE_PIXEL;
fx2 = (TPos)FT_UDIV( -prod, dy );
fy2 = ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = fx2;
fy1 = 0;
ey1++;
}
else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 &&
prod + dy * ONE_PIXEL >= 0 ) /* right */
{
prod += dy * ONE_PIXEL;
fx2 = ONE_PIXEL;
fy2 = (TPos)FT_UDIV( prod, dx );
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = 0;
fy1 = fy2;
ex1++;
}
else /* ( prod + dy * ONE_PIXEL < 0 &&
prod > 0 ) down */
{
fx2 = (TPos)FT_UDIV( prod, -dy );
fy2 = 0;
prod += dx * ONE_PIXEL;
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
fx1 = fx2;
fy1 = ONE_PIXEL;
ey1--;
}
gray_set_cell( RAS_VAR_ ex1, ey1 );
} while ( ex1 != ex2 || ey1 != ey2 );
}
fx2 = to_x - SUBPIXELS( ex2 );
fy2 = to_y - SUBPIXELS( ey2 );
ras.cover += ( fy2 - fy1 );
ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
End:
ras.x = to_x;
ras.y = to_y;
}
#endif
static void
gray_split_conic( FT_Vector* base )