Consider the smooth renderer completed ! It will probably

be moved to "freetype2/src/base" soon and become part
of the library.

changes include:

  - fixed a bug which prevented large spans being drawn correctly.
  - now renders large glyphs that overflow the render pool.
  - various performance enhancements. the renderer now
    renders large glyphs with much greater  speed :-)
  - also faster with small glyphs ..

future plans:
  - fix a vertical-dropout bug in the standard raster (b&w)
  - write a demonstration program to demonstrate direct
    composition..
This commit is contained in:
David Turner 2000-04-04 13:17:04 +00:00
parent 82942cc279
commit ea10fbf91f
1 changed files with 374 additions and 170 deletions

View File

@ -38,15 +38,19 @@
/* - can easily be modified to render to _any_ number of gray levels */
/* cheaply.. */
/* */
/* - faster than the standard renderer for small (< 20) pixel sizes */
/* */
/* It has the following disadvantages (for now): */
/* */
/* - need more memory than the standard scan-converter to render */
/* a single outline. Note that this may be changed in a near */
/* future (we might be able to pack the data in the TCell structure) */
/* */
/* - apparently, glyphs rendered with this module are a bit more */
/* "fuzzy" than those produced with the standard renderer. I hope */
/* to fix this using a gamma table somewhere.. */
/* - a bit slower than the standard renderer for large glyphs (whose */
/* size is typically > 100 pixels), but faster for smaller sizes.. */
/* */
/* - apparently, glyphs rendered with this module are very slightly */
/* more "fuzzy" than those produced with the standard renderer. */
/* */
/* */
@ -61,8 +65,10 @@
#include <freetype.h> /* to link to FT_Outline_Decompose */
#endif
/* define this to dump debugging information */
#define xxxDEBUG_GRAYS
/* as usual, for the speed hungry :-) */
#ifndef FT_STATIC_RASTER
#define RAS_ARG PRaster raster
@ -84,7 +90,9 @@
#endif
/* must be at least 6 bits !! */
#define PIXEL_BITS 8
#define ONE_PIXEL (1L << PIXEL_BITS)
#define PIXEL_MASK (-1L << PIXEL_BITS)
#define TRUNC(x) ((x) >> PIXEL_BITS)
@ -96,6 +104,10 @@
#define UPSCALE(x) (PIXEL_BITS >= 6 ? (x) << (PIXEL_BITS-6) : (x) >> (6-PIXEL_BITS))
#define DOWNSCALE(x) (PIXEL_BITS >= 6 ? (x) >> (PIXEL_BITS-6) : (x) << (6-PIXEL_BITS))
/* define if you want to use more compact storage, this increases the number */
/* of cells available in the render pool but slows down the rendering a bit */
/* useful when you have a really tiny render pool */
#define xxxGRAYS_COMPACT
@ -104,21 +116,32 @@
/* TYPE DEFINITIONS */
/* */
typedef int TScan;
typedef long TPos;
typedef float TDist;
typedef int TScan; /* integer scanline/pixel coordinate */
typedef long TPos; /* sub-pixel coordinate */
/* maximum number of gray spans in a call to the span callback */
#define FT_MAX_GRAY_SPANS 32
#ifdef GRAYS_COMPACT
typedef struct TCell_
{
short x : 14;
short y : 14;
int cover : PIXEL_BITS+2;
int area : PIXEL_BITS*2+2;
} TCell, *PCell;
#else
typedef struct TCell_
{
TScan x;
TScan y;
int area;
int cover;
int area;
} TCell, *PCell;
#endif
typedef struct TRaster_
@ -153,6 +176,11 @@ typedef struct TRaster_
void* render_span_data;
int span_y;
int band_size;
int band_shoot;
int conic_level;
int cubic_level;
void* memory;
} TRaster, *PRaster;
@ -231,8 +259,8 @@ int record_cell( RAS_ARG )
return 1;
cell = ras.cells + ras.num_cells++;
cell->x = ras.ex - ras.min_ex;
cell->y = ras.ey - ras.min_ey;
cell->x = (ras.ex - ras.min_ex);
cell->y = (ras.ey - ras.min_ey);
cell->area = ras.area;
cell->cover = ras.cover;
}
@ -441,6 +469,20 @@ int render_line( RAS_ARG_ TPos to_x, TPos to_y )
/* we should do something about the trivial case where dx == 0, */
/* as it happens very often !! ... XXXXX */
/* perform vertical clipping */
{
TScan min, max;
min = ey1;
max = ey2;
if (ey1 > ey2)
{
min = ey2;
max = ey1;
}
if (min >= ras.max_ey || max < ras.min_ey)
goto Fin;
}
/* everything is on a single scanline */
if ( ey1 == ey2 )
{
@ -448,9 +490,7 @@ int render_line( RAS_ARG_ TPos to_x, TPos to_y )
goto Fin;
}
/* ok, we'll have to render a run of adjacent cells on the same */
/* scanline.. */
/* */
/* ok, we'll have to render several scanlines */
p = (ONE_PIXEL-fy1)*dx;
first = ONE_PIXEL;
incr = 1;
@ -535,6 +575,7 @@ void split_conic( FT_Vector* base )
base[2].y = ( a + b ) / 2;
}
static
int render_conic( RAS_ARG_ FT_Vector* control, FT_Vector* to )
{
@ -548,50 +589,80 @@ int render_conic( RAS_ARG_ FT_Vector* control, FT_Vector* to )
if (dx < dy) dx = dy;
level = 1;
dx = dx/16;
dx = dx/ras.conic_level;
while ( dx > 0 )
{
dx >>= 1;
level++;
}
/* a shortcut to speed things up */
if (level <= 1)
return render_line( RAS_VAR_ UPSCALE(to->x), UPSCALE(to->y) );
{
/* we compute the mid-point directly in order to avoid */
/* calling 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;
return render_line( RAS_VAR_ mid_x, mid_y ) ||
render_line( RAS_VAR_ to_x, to_y );
}
arc = ras.bez_stack;
arc[0] = *to;
arc[1] = *control;
arc[2].x = ras.x;
arc[2].y = ras.y;
arc[0].x = UPSCALE(arc[0].x);
arc[0].y = UPSCALE(arc[0].y);
arc[1].x = UPSCALE(arc[1].x);
arc[1].y = UPSCALE(arc[1].y);
levels = ras.lev_stack;
top = 0;
levels[0] = level;
for (;;)
arc[0].x = UPSCALE(to->x);
arc[0].y = UPSCALE(to->y);
arc[1].x = UPSCALE(control->x);
arc[1].y = UPSCALE(control->y);
arc[2].x = ras.x;
arc[2].y = ras.y;
while (top >= 0)
{
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;
if ( TRUNC(min) >= ras.max_ey || TRUNC(max) < 0 )
goto Draw;
split_conic(arc);
arc += 2;
top ++;
levels[top] = levels[top-1] = level-1;
continue;
}
else
Draw:
{
if (render_line( RAS_VAR_ arc[0].x, arc[0].y )) return 1;
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;
if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
render_line( RAS_VAR_ to_x, to_y ) ) return 1;
top--;
arc-=2;
if (top < 0)
return 0;
arc -= 2;
}
}
return 0;
}
@ -621,6 +692,7 @@ void split_cubic( FT_Vector* base )
base[3].y = ( a + b ) / 2;
}
static
int render_cubic( RAS_ARG_ FT_Vector* control1,
FT_Vector* control2,
@ -642,8 +714,8 @@ int render_cubic( RAS_ARG_ FT_Vector* control1,
db = dx;
level = 1;
da = da/16;
db = db/32;
da = da/ras.cubic_level;
db = db/ras.conic_level;
while ( da > 0 || db > 0 )
{
da >>= 1;
@ -652,50 +724,84 @@ int render_cubic( RAS_ARG_ FT_Vector* control1,
}
if (level <= 1)
return render_line( RAS_VAR_ UPSCALE(to->x), UPSCALE(to->y) );
{
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 + 3*UPSCALE(control1->x+control2->x))/8;
mid_y = (ras.y + to_y + 3*UPSCALE(control1->y+control2->y))/8;
return render_line( RAS_VAR_ mid_x, mid_y ) ||
render_line( RAS_VAR_ to_x, to_y );
}
arc = ras.bez_stack;
arc[0] = *to;
arc[1] = *control2;
arc[2] = *control1;
arc[0].x = UPSCALE(to->x);
arc[0].y = UPSCALE(to->y);
arc[1].x = UPSCALE(control2->x);
arc[1].y = UPSCALE(control2->y);
arc[2].x = UPSCALE(control1->x);
arc[2].y = UPSCALE(control1->y);
arc[3].x = ras.x;
arc[3].y = ras.y;
arc[0].x = UPSCALE(arc[0].x);
arc[0].y = UPSCALE(arc[0].y);
arc[1].x = UPSCALE(arc[1].x);
arc[1].y = UPSCALE(arc[1].y);
arc[2].x = UPSCALE(arc[2].x);
arc[2].y = UPSCALE(arc[2].y);
levels = ras.lev_stack;
top = 0;
levels[0] = level;
for (;;)
while (top >= 0)
{
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;
split_cubic(arc);
arc += 3;
top ++;
levels[top] = levels[top-1] = level-1;
continue;
}
else
Draw:
{
if (render_line( RAS_VAR_ arc[0].x, arc[0].y )) return 1;
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 + 3*(arc[1].x+arc[2].x))/8;
mid_y = (ras.y + to_y + 3*(arc[1].y+arc[2].y))/8;
if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
render_line( RAS_VAR_ to_x, to_y ) ) return 1;
top --;
arc -= 3;
if (top < 0)
return 0;
}
}
return 0;
}
/* a macro comparing two cell pointers. returns true if a <= b */
#if 1
#define PACK(a) ( ((long)(a)->y << 16) | (a)->x )
#define LESS_THAN(a,b) ( PACK(a) < PACK(b) )
#else
#define LESS_THAN(a,b) ( (a)->y<(b)->y || ((a)->y==(b)->y && (a)->x < (b)->x) )
#endif
#define SWAP_CELLS(a,b,temp) { temp = *(a); *(a) = *(b); *(b) = temp; }
#define DEBUG_SORT
#define QUICK_SORT
@ -740,7 +846,7 @@ void shell_sort ( PCell cells,
/* it should be faster than calling the stdlib qsort(), and we can even */
/* tailor our insertion threshold... */
#define QSORT_THRESHOLD 7 /* below this size, a sub-array will be sorted */
#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */
/* through a normal insertion sort.. */
static
@ -902,7 +1008,7 @@ int check_sort( PCell cells, int count )
static
void grays_render_span( int y, int count, FT_Span* spans, PRaster raster )
{
unsigned char *p, *q, *limit;
unsigned char *p;
FT_Bitmap* map = &raster->target;
/* first of all, compute the scanline offset */
p = (unsigned char*)map->buffer - y*map->pitch;
@ -912,12 +1018,16 @@ int check_sort( PCell cells, int count )
for ( ; count > 0; count--, spans++ )
{
if (spans->coverage)
#if 1
memset( p + spans->x, (spans->coverage+1) >> 1, spans->len );
#else
{
q = p + spans->x;
limit = q + spans->len;
for ( ; q < limit; q++ )
q[0] = (spans->coverage+1) >> 1;
}
#endif
}
}
@ -983,6 +1093,8 @@ int check_sort( PCell cells, int count )
coverage = 255;
}
y += ras.min_ey;
if (coverage)
{
/* see if we can add this span to the current list */
@ -1026,7 +1138,7 @@ int check_sort( PCell cells, int count )
/* add a gray span to the current list */
span->x = (short)x;
span->len = (unsigned char)acount;
span->len = (unsigned short)acount;
span->coverage = (unsigned char)coverage;
ras.num_gray_spans++;
}
@ -1036,7 +1148,7 @@ int check_sort( PCell cells, int count )
static
void grays_sweep( RAS_ARG_ FT_Bitmap* target )
{
TScan x, y, cover;
TScan x, y, cover, area;
PCell start, cur, limit;
cur = ras.cells;
@ -1048,9 +1160,12 @@ int check_sort( PCell cells, int count )
for (;;)
{
start = cur;
y = start->y;
x = start->x;
start = cur;
y = start->y;
x = start->x;
area = start->area;
cover += start->cover;
/* accumulate all start cells */
for (;;)
@ -1058,18 +1173,16 @@ int check_sort( PCell cells, int count )
++cur;
if (cur >= limit || cur->y != start->y || cur->x != start->x)
break;
start->area += cur->area;
start->cover += cur->cover;
}
/* compute next cover */
cover += start->cover;
area += cur->area;
cover += cur->cover;
}
/* if the start cell has a non-null area, we must draw an */
/* individual gray pixel there.. */
if (start->area && x >= 0)
if (area && x >= 0)
{
grays_hline( RAS_VAR_ x, y, cover*(ONE_PIXEL*2)-start->area, 1 );
grays_hline( RAS_VAR_ x, y, cover*(ONE_PIXEL*2)-area, 1 );
x++;
}
@ -1099,8 +1212,8 @@ int check_sort( PCell cells, int count )
ras.gray_spans, ras.render_span_data );
#ifdef DEBUG_GRAYS
{
int n;
FT_GraySpan* span;
int n;
FT_Span* span;
fprintf( stderr, "y=%3d ", ras.span_y );
span = ras.gray_spans;
@ -1111,8 +1224,14 @@ int check_sort( PCell cells, int count )
#endif
}
typedef struct TBand_
{
FT_Pos min, max;
} TBand;
static
int Convert_Glyph( RAS_ARG_ FT_Outline* outline )
int grays_convert_glyph( RAS_ARG_ FT_Outline* outline )
{
static
FT_Outline_Funcs interface =
@ -1123,23 +1242,119 @@ int check_sort( PCell cells, int count )
(FT_Outline_CubicTo_Func)Cubic_To
};
TBand bands[40], *band;
int n, num_bands;
TPos min, max, max_y;
/* Set up state in the raster object */
compute_cbox( RAS_VAR_ outline );
/* clip to target bitmap, exit if nothing to do */
if ( ras.max_ex <= 0 || ras.min_ex >= ras.target.width ||
ras.max_ey <= 0 || ras.min_ey >= ras.target.rows )
return 0;
if (ras.min_ex < 0) ras.min_ex = 0;
if (ras.min_ey < 0) ras.min_ey = 0;
if (ras.max_ex > ras.target.width) ras.max_ex = ras.target.width;
if (ras.max_ey > ras.target.rows) ras.max_ey = ras.target.rows;
ras.num_cells = 0;
/* simple heuristic used to speed-up the bezier decomposition */
/* see the code in render_conic and render_cubic for more details */
ras.conic_level = 32;
ras.cubic_level = 16;
{
int level = 0;
if (ras.max_ex > 24 || ras.max_ey > 24)
level++;
if (ras.max_ex > 120 || ras.max_ey > 120)
level+=2;
/* Now decompose curve */
if ( FT_Outline_Decompose( outline, &interface, &ras ) )
return 1;
/* XXX: the error condition is in ras.error */
ras.conic_level <<= level;
ras.cubic_level <<= level;
}
/* record the last cell */
return record_cell( RAS_VAR );
/* setup vertical bands */
num_bands = (ras.max_ey - ras.min_ey)/ras.band_size;
if (num_bands == 0) num_bands = 1;
if (num_bands >= 39) num_bands = 39;
ras.band_shoot = 0;
min = ras.min_ey;
max_y = ras.max_ey;
for ( n = 0; n < num_bands; n++, min = max )
{
max = min + ras.band_size;
if (n == num_bands-1 || max > max_y)
max = max_y;
bands[0].min = min;
bands[0].max = max;
band = bands;
while (band >= bands)
{
FT_Pos bottom, top, middle;
int error;
ras.num_cells = 0;
ras.invalid = 1;
ras.min_ey = band->min;
ras.max_ey = band->max;
error = FT_Outline_Decompose( outline, &interface, &ras ) ||
record_cell( RAS_VAR );
if (!error)
{
#ifdef SHELL_SORT
shell_sort( ras.cells, ras.num_cells );
#else
quick_sort( ras.cells, ras.num_cells );
#endif
#ifdef DEBUG_GRAYS
check_sort( ras.cells, ras.num_cells );
dump_cells( RAS_VAR );
#endif
grays_sweep( RAS_VAR_ &ras.target );
band--;
continue;
}
/* render pool overflow, we will reduce the render band by half */
bottom = band->min;
top = band->max;
middle = bottom + ((top-bottom) >> 1);
/* waoow !! this is too complex for a single scanline, something */
/* must be really rotten here !! */
if (middle == bottom)
{
#ifdef DEBUG_GRAYS
fprintf( stderr, "Rotten glyph !!\n" );
#endif
return 1;
}
if (bottom-top >= ras.band_size)
ras.band_shoot++;
band[1].min = bottom;
band[1].max = middle;
band[0].min = middle;
band[0].max = top;
band++;
}
}
if (ras.band_shoot > 8 && ras.band_size > 16)
ras.band_size = ras.band_size/2;
return 0;
}
@ -1175,20 +1390,6 @@ int check_sort( PCell cells, int count )
ras.num_cells = 0;
ras.invalid = 1;
if (Convert_Glyph( (PRaster)raster, outline ))
return 1;
#ifdef SHELL_SORT
shell_sort( ras.cells, ras.num_cells );
#else
quick_sort( ras.cells, ras.num_cells );
#endif
#ifdef DEBUG_GRAYS
check_sort( ras.cells, ras.num_cells );
dump_cells( RAS_VAR );
#endif
ras.render_span = (FT_Raster_Span_Func)grays_render_span;
ras.render_span_data = &ras;
if ( params->flags & ft_raster_flag_direct )
@ -1197,8 +1398,7 @@ int check_sort( PCell cells, int count )
ras.render_span_data = params->user;
}
grays_sweep( (PRaster)raster, target_map );
return 0;
return grays_convert_glyph( (PRaster)raster, outline );
}
@ -1259,8 +1459,12 @@ int check_sort( PCell cells, int count )
const char* pool_base,
long pool_size )
{
PRaster rast = (PRaster)raster;
if (raster && pool_base && pool_size >= 4096)
init_cells( (PRaster)raster, (char*)pool_base, pool_size );
init_cells( rast, (char*)pool_base, pool_size );
rast->band_size = (pool_size / sizeof(TCell))/8;
}