ref: ea10fbf91f4369b868fa1d489ada1a063d5b0e52
parent: 82942cc2791584e08c147aed2e0957ed49662136
author: David Turner <[email protected]>
date: Tue Apr 4 09:17:04 EDT 2000
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..
--- a/demos/src/ftgrays.c
+++ b/demos/src/ftgrays.c
@@ -38,6 +38,8 @@
/* - 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 */
@@ -44,11 +46,13 @@
/* 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. */
/* */
+/* */
#include <ftimage.h>
@@ -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_
@@ -152,7 +175,12 @@
FT_Raster_Span_Func render_span;
void* render_span_data;
int span_y;
-
+
+ int band_size;
+ int band_shoot;
+ int conic_level;
+ int cubic_level;
+
void* memory;
} TRaster, *PRaster;
@@ -185,7 +213,7 @@
{
FT_Vector* vec = outline->points;
FT_Vector* limit = vec + outline->n_points;
-
+
if ( outline->n_points <= 0 )
{
ras.min_ex = ras.max_ex = 0;
@@ -192,16 +220,16 @@
ras.min_ey = ras.max_ey = 0;
return;
}
-
+
ras.min_ex = ras.max_ex = vec->x;
ras.min_ey = ras.max_ey = vec->y;
vec++;
-
+
for ( ; vec < limit; vec++ )
{
TPos x = vec->x;
TPos y = vec->y;
-
+
if ( x < ras.min_ex ) ras.min_ex = x;
if ( x > ras.max_ex ) ras.max_ex = x;
if ( y < ras.min_ey ) ras.min_ey = y;
@@ -208,7 +236,7 @@
if ( y > ras.max_ey ) ras.max_ey = y;
}
- /* truncate the bounding box to integer pixels */
+ /* truncate the bounding box to integer pixels */
ras.min_ex = ras.min_ex >> 6;
ras.min_ey = ras.min_ey >> 6;
ras.max_ex = ( ras.max_ex+63 ) >> 6;
@@ -226,13 +254,13 @@
PCell cell;
if (!ras.invalid && (ras.area | ras.cover))
- {
+ {
if ( ras.num_cells >= ras.max_cells )
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;
}
@@ -269,7 +297,7 @@
if (ex < ras.min_ex)
ex = ras.min_ex-1;
- /* if our position is new, then record the previous cell */
+ /* if our position is new, then record the previous cell */
if (ex != ras.ex || ey != ras.ey)
record = 1;
else
@@ -281,7 +309,7 @@
/* position, of changed the 'invalid' flag..) */
if ( (ras.invalid != invalid || record) && record_cell( RAS_VAR ) )
return 1;
-
+
if (clean)
{
ras.area = 0;
@@ -293,9 +321,9 @@
ras.ey = ey;
return 0;
}
-
+
/****************************************************************************/
/* */
/* START A NEW CONTOUR AT A GIVEN CELL */
@@ -305,7 +333,7 @@
{
if (ex < ras.min_ex)
ex = ras.min_ex-1;
-
+
ras.area = 0;
ras.cover = 0;
ras.ex = ex;
@@ -312,7 +340,7 @@
ras.ey = ey;
ras.last_ey = SUBPIXELS(ey);
ras.invalid = 0;
-
+
(void)set_cell( RAS_VAR_ ex, ey );
}
@@ -330,7 +358,7 @@
int incr, lift, mod, rem;
dx = x2-x1;
-
+
ex1 = TRUNC(x1); /* if (ex1 >= ras.max_ex) ex1 = ras.max_ex-1; */
ex2 = TRUNC(x2); /* if (ex2 >= ras.max_ex) ex2 = ras.max_ex-1; */
fx1 = x1 - SUBPIXELS(ex1);
@@ -339,8 +367,8 @@
/* trivial case. Happens often */
if (y1 == y2)
return set_cell( RAS_VAR_ ex2, ey );
-
+
/* everything is located in a single cell, that is easy ! */
/* */
if ( ex1 == ex2 )
@@ -350,7 +378,7 @@
ras.cover += delta;
return 0;
}
-
+
/* ok, we'll have to render a run of adjacent cells on the same */
/* scanline.. */
/* */
@@ -390,9 +418,9 @@
lift--;
rem += dx;
}
-
+
mod -= dx;
-
+
while (ex1 != ex2)
{
delta = lift;
@@ -409,11 +437,11 @@
if (set_cell( RAS_VAR_ ex1, ey )) goto Error;
}
}
-
+
delta = y2-y1;
ras.area += (fx2+ONE_PIXEL-first)*delta;
ras.cover += delta;
-
+
return 0;
Error:
return 1;
@@ -436,12 +464,26 @@
fy2 = to_y - SUBPIXELS(ey2);
dx = to_x - ras.x;
- dy = to_y - ras.y;
+ dy = to_y - ras.y;
/* we should do something about the trivial case where dx == 0, */
/* as it happens very often !! ... XXXXX */
- /* everything is on a single scanline */
+ /* 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 )
{
if (render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 )) goto Error;
@@ -448,9 +490,7 @@
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;
@@ -472,7 +512,7 @@
x = ras.x + delta;
if (render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first )) goto Error;
-
+
ey1 += incr;
if (set_cell( RAS_VAR_ TRUNC(x), ey1 )) goto Error;
@@ -487,7 +527,7 @@
rem += dy;
}
mod -= dy;
-
+
while (ey1 != ey2)
{
delta = lift;
@@ -504,9 +544,9 @@
if (set_cell( RAS_VAR_ TRUNC(x), ey1 )) goto Error;
}
}
-
+
if (render_scanline( RAS_VAR_ ey1, x, ONE_PIXEL-first, to_x, fy2 )) goto Error;
-
+
Fin:
ras.x = to_x;
ras.y = to_y;
@@ -521,7 +561,7 @@
void split_conic( FT_Vector* base )
{
TPos a, b;
-
+
base[4].x = base[2].x;
b = base[1].x;
a = base[3].x = ( base[2].x + b )/2;
@@ -535,6 +575,7 @@
base[2].y = ( a + b ) / 2;
}
+
static
int render_conic( RAS_ARG_ FT_Vector* control, FT_Vector* to )
{
@@ -542,13 +583,13 @@
int top, level;
int* levels;
FT_Vector* arc;
-
+
dx = DOWNSCALE(ras.x) + to->x - (control->x << 1); if (dx < 0) dx = -dx;
dy = DOWNSCALE(ras.y) + to->y - (control->y << 1); if (dy < 0) dy = -dy;
if (dx < dy) dx = dy;
-
+
level = 1;
- dx = dx/16;
+ dx = dx/ras.conic_level;
while ( dx > 0 )
{
dx >>= 1;
@@ -555,43 +596,73 @@
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;
+
+ 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;
- 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;
+ 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 @@
base[3].y = ( a + b ) / 2;
}
+
static
int render_cubic( RAS_ARG_ FT_Vector* control1,
FT_Vector* control2,
@@ -630,20 +702,20 @@
int top, level;
int* levels;
FT_Vector* arc;
-
+
dx = DOWNSCALE(ras.x) + to->x - (control1->x << 1); if (dx < 0) dx = -dx;
dy = DOWNSCALE(ras.y) + to->y - (control1->y << 1); if (dy < 0) dy = -dy;
if (dx < dy) dx = dy;
da = dx;
-
+
dx = DOWNSCALE(ras.x) + to->x - 3*(control1->x + control2->x); if (dx < 0) dx = -dx;
dy = DOWNSCALE(ras.y) + to->y - 3*(control1->x + control2->y); if (dy < 0) dy = -dy;
if (dx < dy) dx = dy;
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 @@
}
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
@@ -720,18 +826,18 @@
for ( j = i-gap; ; j -= gap )
{
PCell k = j+gap;
-
+
if ( LESS_THAN(j,k) )
break;
-
- SWAP_CELLS(j,k,temp);
-
+
+ SWAP_CELLS(j,k,temp);
+
if ( j < cells+gap )
break;
}
}
}
-
+
}
#endif
@@ -740,7 +846,7 @@
/* 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
@@ -751,7 +857,7 @@
PCell* top; /* top of stack */
PCell base, limit;
TCell temp;
-
+
limit = cells + count;
base = cells;
top = stack;
@@ -759,13 +865,13 @@
{
int len = limit-base;
PCell i, j, pivot;
-
+
if ( len > QSORT_THRESHOLD)
{
/* we use base+len/2 as the pivot */
pivot = base + len/2;
SWAP_CELLS( base, pivot, temp );
-
+
i = base + 1;
j = limit-1;
@@ -772,13 +878,13 @@
/* now ensure that *i <= *base <= *j */
if (LESS_THAN(j,i))
SWAP_CELLS( i, j, temp );
-
+
if (LESS_THAN(base,i))
SWAP_CELLS( base, i, temp );
-
+
if (LESS_THAN(j,base))
SWAP_CELLS( base, j, temp );
-
+
for (;;)
{
do i++; while (LESS_THAN(i,base));
@@ -785,12 +891,12 @@
do j--; while (LESS_THAN(base,j));
if (i > j)
break;
-
+
SWAP_CELLS( i,j, temp );
}
SWAP_CELLS( base, j, temp );
-
+
/* now, push the largest sub-array */
if ( j - base > limit -i )
{
@@ -804,7 +910,7 @@
top[1] = limit;
limit = j;
}
- top += 2;
+ top += 2;
}
else
{
@@ -840,7 +946,7 @@
int check_sort( PCell cells, int count )
{
PCell p, q;
-
+
for ( p = cells + count-2; p >= cells; p-- )
{
q = p+1;
@@ -858,10 +964,10 @@
FT_Raster raster )
{
TPos x, y;
-
+
/* record current cell, if any */
record_cell( (PRaster)raster );
-
+
/* start to a new position */
x = UPSCALE(to->x);
y = UPSCALE(to->y);
@@ -870,8 +976,8 @@
((PRaster)raster)->y = y;
return 0;
}
-
+
static
int Line_To( FT_Vector* to,
FT_Raster raster )
@@ -902,16 +1008,19 @@
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;
if (map->pitch >= 0)
p += (map->rows-1)*map->pitch;
-
+
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;
@@ -918,6 +1027,7 @@
for ( ; q < limit; q++ )
q[0] = (spans->coverage+1) >> 1;
}
+#endif
}
}
@@ -929,7 +1039,7 @@
{
PCell cell, limit;
int y = -1;
-
+
cell = ras.cells;
limit = cell + ras.num_cells;
for ( ; cell < limit; cell++ )
@@ -945,7 +1055,7 @@
fprintf(stderr, "\n" );
}
#endif
-
+
static
void grays_hline( RAS_ARG_ TScan x, TScan y, TPos area, int acount )
{
@@ -952,22 +1062,22 @@
FT_Span* span;
int count;
int coverage;
-
+
/* compute the coverage line's coverage, depending on the */
/* outline fill rule.. */
/* */
/* The coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
/* */
-
+
coverage = area >> (PIXEL_BITS*2+1-8); /* use range 0..256 */
if ( ras.outline.flags & ft_outline_even_odd_fill )
{
if (coverage < 0)
coverage = -coverage;
-
+
while (coverage >= 512)
coverage -= 512;
-
+
if (coverage > 256)
coverage = 0;
else if (coverage == 256)
@@ -978,11 +1088,13 @@
/* normal non-zero winding rule */
if (coverage < 0)
coverage = -coverage;
-
+
if (coverage >= 256)
coverage = 255;
}
-
+
+ y += ras.min_ey;
+
if (coverage)
{
/* see if we can add this span to the current list */
@@ -994,7 +1106,7 @@
span->len += acount;
return;
}
-
+
if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS)
{
if (ras.render_span)
@@ -1001,8 +1113,8 @@
ras.render_span( ras.span_y, count, ras.gray_spans,
ras.render_span_data );
/* ras.render_span( span->y, ras.gray_spans, count ); */
-
-#ifdef DEBUG_GRAYS
+
+#ifdef DEBUG_GRAYS
if (ras.span_y >= 0)
{
int n;
@@ -1014,19 +1126,19 @@
fprintf( stderr, "\n" );
}
#endif
-
+
ras.num_gray_spans = 0;
ras.span_y = y;
-
+
count = 0;
span = ras.gray_spans;
}
else
span++;
-
+
/* 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 @@
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;
@@ -1045,13 +1157,16 @@
cover = 0;
ras.span_y = -1;
ras.num_gray_spans = 0;
-
+
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,24 +1173,22 @@
++cur;
if (cur >= limit || cur->y != start->y || cur->x != start->x)
break;
- start->area += cur->area;
- start->cover += cur->cover;
+
+ area += cur->area;
+ cover += cur->cover;
}
-
- /* compute next cover */
- cover += start->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++;
}
-
+
if (x < 0)
x = 0;
-
+
if (cur < limit && start->y == cur->y)
{
/* draw a gray span between the start cell and the current one */
@@ -1089,7 +1202,7 @@
grays_hline( RAS_VAR_ x, y, cover*(ONE_PIXEL*2), ras.max_ex - x );
cover = 0;
}
-
+
if (cur >= limit)
break;
}
@@ -1099,9 +1212,9 @@
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;
for (n = 0; n < ras.num_gray_spans; n++, span++)
@@ -1110,9 +1223,15 @@
}
#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 @@
(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;
- /* Now decompose curve */
- if ( FT_Outline_Decompose( outline, &interface, &ras ) )
- return 1;
- /* XXX: the error condition is in ras.error */
+ /* 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;
+
+ 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;
}
@@ -1149,7 +1364,7 @@
{
FT_Outline* outline = (FT_Outline*)params->source;
FT_Bitmap* target_map = params->target;
-
+
if ( !raster || !raster->cells || !raster->max_cells )
return -1;
@@ -1175,20 +1390,6 @@
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 )
@@ -1196,9 +1397,8 @@
ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
ras.render_span_data = params->user;
}
-
- grays_sweep( (PRaster)raster, target_map );
- return 0;
+
+ return grays_convert_glyph( (PRaster)raster, outline );
}
@@ -1210,7 +1410,7 @@
int grays_raster_new( void* memory, FT_Raster *araster )
{
static FT_RasterRec_ the_raster;
- *araster = &the_raster;
+ *araster = &the_raster;
memset( &the_raster, sizeof(the_raster), 0 );
return 0;
}
@@ -1231,7 +1431,7 @@
{
FT_Error error;
PRaster raster;
-
+
*araster = 0;
if ( !ALLOC( raster, sizeof(TRaster) ))
{
@@ -1238,10 +1438,10 @@
raster->memory = memory;
*araster = (FT_Raster)raster;
}
-
+
return error;
}
-
+
static
void grays_raster_done( FT_Raster raster )
{
@@ -1248,7 +1448,7 @@
FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;
FREE( raster );
}
-
+
#endif
@@ -1259,8 +1459,12 @@
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;
}
@@ -1267,7 +1471,7 @@
FT_Raster_Funcs ft_grays_raster =
{
ft_glyph_format_outline,
-
+
(FT_Raster_New_Func) grays_raster_new,
(FT_Raster_Reset_Func) grays_raster_reset,
(FT_Raster_Set_Mode_Func) 0,