blob: f21da6da3ddd0033d4ce83f8e6be67aeeec8fec4 [file] [edit]
/***************************************************************************/
/* */
/* ftgrays.c */
/* */
/* A new `perfect' anti-aliasing renderer (body). */
/* */
/* Copyright 2000-2001, 2002 by */
/* David Turner, Robert Wilhelm, and Werner Lemberg. */
/* */
/* This file is part of the FreeType project, and may only be used, */
/* modified, and distributed under the terms of the FreeType project */
/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
/* this file you indicate that you have read the license and */
/* understand and accept it fully. */
/* */
/***************************************************************************/
/*************************************************************************/
/* */
/* This is a new anti-aliasing scan-converter for FreeType 2. The */
/* algorithm used here is _very_ different from the one in the standard */
/* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
/* coverage of the outline on each pixel cell. */
/* */
/* It is based on ideas that I initially found in Raph Levien's */
/* excellent LibArt graphics library (see http://www.levien.com/libart */
/* for more information, though the web pages do not tell anything */
/* about the renderer; you'll have to dive into the source code to */
/* understand how it works). */
/* */
/* Note, however, that this is a _very_ different implementation */
/* compared to Raph's. Coverage information is stored in a very */
/* different way, and I don't use sorted vector paths. Also, it doesn't */
/* use floating point values. */
/* */
/* This renderer has the following advantages: */
/* */
/* - It doesn't need an intermediate bitmap. Instead, one can supply a */
/* callback function that will be called by the renderer to draw gray */
/* spans on any target surface. You can thus do direct composition on */
/* any kind of bitmap, provided that you give the renderer the right */
/* callback. */
/* */
/* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
/* each pixel cell. */
/* */
/* - It performs a single pass on the outline (the `standard' FT2 */
/* renderer makes two passes). */
/* */
/* - It can easily be modified to render to _any_ number of gray levels */
/* cheaply. */
/* */
/* - For small (< 20) pixel sizes, it is faster than the standard */
/* renderer. */
/* */
/*************************************************************************/
/*
GPAC version modifications:
* removed all cubic/quadratic support (present in GPAC path objects)
* moved span data memory to dynamic allocation
* bypassed Y-sorting of cells by using an array of scanlines: a bit more consuming
in memory, but faster cell sorting (X-sorting only)
* deferred coverage push for 3D
* threading support
*/
#include "rast_soft.h"
#ifndef GPAC_DISABLE_EVG
void gray_record_cell(GF_EVGSurface *surf)
{
if (( surf->area | surf->cover) && (surf->ey<surf->max_ey)) {
long y = surf->ey - surf->min_ey;
if (y>=0) {
AACell *cell;
AAScanline *sl = &surf->scanlines[y];
if (sl->num >= sl->alloc) {
sl->cells = (AACell*)gf_realloc(sl->cells, sizeof(AACell)* (sl->alloc + AA_CELL_STEP_ALLOC));
sl->alloc += AA_CELL_STEP_ALLOC;
}
cell = &sl->cells[sl->num];
sl->num++;
/*clip cell */
if (surf->ex<surf->min_ex) cell->x = (TCoord) -1;
else if (surf->ex>surf->max_ex) cell->x = (TCoord) (surf->max_ex - surf->min_ex);
else cell->x = (TCoord)(surf->ex - surf->min_ex);
cell->area = surf->area;
cell->cover = surf->cover;
cell->idx1 = surf->idx1;
cell->idx2 = surf->idx2;
if (surf->first_scanline > (u32) y)
surf->first_scanline = y;
}
}
}
void gray_set_cell(GF_EVGSurface *surf, TCoord ex, TCoord ey )
{
if ((surf->ex != ex) || (surf->ey != ey)) {
gray_record_cell(surf);
surf->ex = ex;
surf->ey = ey;
surf->area = 0;
surf->cover = 0;
}
}
static GFINLINE void evg_translate_point(GF_Matrix2D *mx, EVG_Vector *pt, TPos *x, TPos *y)
{
Fixed _x, _y;
_x = pt->x;
_y = pt->y;
gf_mx2d_apply_coords(mx, &_x, &_y);
#ifdef GPAC_FIXED_POINT
*x = UPSCALE(_x);
*y = UPSCALE(_y);
#else
*x = (s32) (_x * ONE_PIXEL);
*y = (s32) (_y * ONE_PIXEL);
#endif
}
static GFINLINE int gray_move_to(EVG_Vector *to, GF_EVGSurface *surf)
{
TPos x, y;
TCoord ex, ey;
/* record current cell, if any */
gray_record_cell(surf);
evg_translate_point(surf->mx, to, &x, &y);
ex = TRUNC(x);
ey = TRUNC(y);
if ( ex < surf->min_ex ) ex = (TCoord)(surf->min_ex - 1);
surf->area = 0;
surf->cover = 0;
gray_set_cell(surf, ex, ey );
if (ey<0) ey=0;
surf->last_ey = SUBPIXELS( ey );
surf->x = x;
surf->y = y;
return 0;
}
/*************************************************************************/
/* */
/* Render a scanline as one or more cells. */
/* */
static void gray_render_scanline(GF_EVGSurface *surf, TCoord ey, TPos x1, TCoord y1, TPos x2, TCoord y2)
{
TCoord ex1, ex2, fx1, fx2, delta;
long p, first;
long dx;
int incr, mod;
dx = x2 - x1;
ex1 = TRUNC( x1 ); /* if (ex1 >= surf->max_ex) ex1 = surf->max_ex-1; */
ex2 = TRUNC( x2 ); /* if (ex2 >= surf->max_ex) ex2 = surf->max_ex-1; */
if (ex1<0) {
ex1=0;
fx1 = (TCoord) 0;
} else {
fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
}
if (ex2<0) {
ex2=0;
fx2 = (TCoord) 0;
} else {
fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
}
/* trivial case. Happens often */
if ( y1 == y2 ) {
gray_set_cell(surf, ex2, ey );
return;
}
/* everything is located in a single cell. That is easy! */
if ( ex1 == ex2 ) {
delta = y2 - y1;
surf->area += (TArea)( fx1 + fx2 ) * delta;
surf->cover += delta;
return;
}
/* ok, we'll have to render a run of adjacent cells on the same */
/* scanline... */
p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
first = ONE_PIXEL;
incr = 1;
if ( dx < 0 ) {
p = fx1 * ( y2 - y1 );
first = 0;
incr = -1;
dx = -dx;
}
delta = (TCoord)( p / dx );
mod = (TCoord)( p % dx );
if ( mod < 0 ) {
delta--;
mod += (TCoord)dx;
}
surf->area += (TArea)( fx1 + first ) * delta;
surf->cover += delta;
ex1 += incr;
gray_set_cell(surf, ex1, ey );
y1 += delta;
if ( ex1 != ex2 ) {
int lift, rem;
p = ONE_PIXEL * ( y2 - y1 + delta );
lift = (TCoord)( p / dx );
rem = (TCoord)( p % dx );
if ( rem < 0 ) {
lift--;
rem += (TCoord)dx;
}
mod -= (int) dx;
while ( ex1 != ex2 ) {
delta = lift;
mod += rem;
if ( mod >= 0 ) {
mod -= (TCoord)dx;
delta++;
}
surf->area += (TArea)ONE_PIXEL * delta;
surf->cover += delta;
y1 += delta;
ex1 += incr;
gray_set_cell(surf, ex1, ey );
}
}
delta = y2 - y1;
surf->area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;
surf->cover += delta;
}
/*************************************************************************/
/* */
/* Render a given line as a series of scanlines. */
void gray_render_line(GF_EVGSurface *surf, TPos to_x, TPos to_y)
{
TCoord min, max;
TCoord ey1, ey2, fy1, fy2;
TPos x, x2;
long dx, dy;
long p, first;
int delta, rem, mod, lift, incr;
ey1 = TRUNC( surf->last_ey );
ey2 = TRUNC( to_y ); /* if (ey2 >= surf->max_ey) ey2 = surf->max_ey-1; */
if (ey2<0) ey2=0;
fy1 = (TCoord)( surf->y - surf->last_ey );
fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
dx = to_x - surf->x;
dy = to_y - surf->y;
/* perform vertical clipping */
min = ey1;
max = ey2;
if ( ey1 > ey2 ) {
min = ey2;
max = ey1;
}
if ( min >= surf->max_ey || max < surf->min_ey ) goto End;
/* everything is on a single scanline */
if ( ey1 == ey2 ) {
gray_render_scanline(surf, ey1, surf->x, fy1, to_x, fy2 );
goto End;
}
/* vertical line - avoid calling gray_render_scanline */
incr = 1;
if (dx == 0 ) {
TCoord ex = TRUNC( surf->x );
TCoord tdiff;
if (ex<0) {
ex = 0;
tdiff=0;
} else {
tdiff = surf->x - SUBPIXELS( ex );
}
TCoord two_fx = (TCoord)( ( tdiff ) << 1 );
TPos area;
first = ONE_PIXEL;
if ( dy < 0 ) {
first = 0;
incr = -1;
}
delta = (int)( first - fy1 );
surf->area += (TArea)two_fx * delta;
surf->cover += delta;
ey1 += incr;
gray_set_cell(surf, ex, ey1 );
delta = (int)( first + first - ONE_PIXEL );
area = (TArea)two_fx * delta;
while ( ey1 != ey2 ) {
surf->area += area;
surf->cover += delta;
ey1 += incr;
gray_set_cell(surf, ex, ey1 );
}
delta = (int)( fy2 - ONE_PIXEL + first );
surf->area += (TArea)two_fx * delta;
surf->cover += delta;
goto End;
}
/* ok, we have to render several scanlines */
p = ( ONE_PIXEL - fy1 ) * dx;
first = ONE_PIXEL;
incr = 1;
if ( dy < 0 ) {
p = fy1 * dx;
first = 0;
incr = -1;
dy = -dy;
}
delta = (int)( p / dy );
mod = (int)( p % dy );
if ( mod < 0 ) {
delta--;
mod += (TCoord)dy;
}
x = surf->x + delta;
gray_render_scanline(surf, ey1, surf->x, fy1, x, (TCoord)first );
ey1 += incr;
gray_set_cell(surf, TRUNC( x ), ey1 );
if ( ey1 != ey2 ) {
p = ONE_PIXEL * dx;
lift = (int)( p / dy );
rem = (int)( p % dy );
if ( rem < 0 ) {
lift--;
rem += (int)dy;
}
mod -= (int)dy;
while ( ey1 != ey2 ) {
delta = lift;
mod += rem;
if ( mod >= 0 ) {
mod -= (int)dy;
delta++;
}
x2 = x + delta;
gray_render_scanline(surf, ey1, x, (TCoord)( ONE_PIXEL - first ), x2, (TCoord)first );
x = x2;
ey1 += incr;
gray_set_cell(surf, TRUNC( x ), ey1 );
}
}
gray_render_scanline(surf, ey1, x, (TCoord)( ONE_PIXEL - first ), to_x, fy2 );
End:
surf->x = to_x;
surf->y = to_y;
surf->last_ey = SUBPIXELS( ey2 );
}
static int EVG_Outline_Decompose(EVG_Outline *outline, GF_EVGSurface *surf)
{
EVG_Vector v_start;
int n; /* index of contour in outline */
int first; /* index of first point in contour */
TPos _x, _y;
first = 0;
for ( n = 0; n < outline->n_contours; n++ ) {
EVG_Vector *point;
EVG_Vector *limit;
int last; /* index of last point in contour */
last = outline->contours[n];
limit = outline->points + last;
v_start = outline->points[first];
point = outline->points + first;
gray_move_to(&v_start, surf);
while ( point < limit ) {
point++;
evg_translate_point(surf->mx, point, &_x, &_y);
gray_render_line(surf, _x, _y);
}
/* close the contour with a line segment */
evg_translate_point(surf->mx, &v_start, &_x, &_y);
gray_render_line(surf, _x, _y);
first = last + 1;
}
return 0;
}
#define SWAP_CELLS( a, b, temp ) { \
temp = *(a); \
*(a) = *(b); \
*(b) = temp; \
}
/* This is a non-recursive quicksort that directly process our cells */
/* array. It should be faster than calling the stdlib qsort(), and we */
/* can even tailor our insertion threshold... */
#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */
/* through a normal insertion sort */
void gray_quick_sort( AACell *cells, int count )
{
AACell *stack[80]; /* should be enough ;-) */
AACell **top; /* top of stack */
AACell *base, *limit;
AACell temp;
limit = cells + count;
base = cells;
top = stack;
for (;;) {
int len = (int)( limit - base );
AACell *i, *j;
if ( len > QSORT_THRESHOLD ) {
AACell *pivot;
/* we use base + len/2 as the pivot */
pivot = base + len / 2;
SWAP_CELLS( base, pivot, temp );
i = base + 1;
j = limit - 1;
/* now ensure that *i <= *base <= *j */
if(j->x < i->x)
SWAP_CELLS( i, j, temp );
if(base->x < i->x)
SWAP_CELLS( base, i, temp );
if(j->x < base->x)
SWAP_CELLS( base, j, temp );
for (;;) {
int x = base->x;
do i++;
while( i->x < x );
do j--;
while( x < j->x );
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 ) {
top[0] = base;
top[1] = j;
base = i;
} else {
top[0] = i;
top[1] = limit;
limit = j;
}
top += 2;
} else {
/* the sub-array is small, perform insertion sort */
j = base;
i = j + 1;
for ( ; i < limit; j = i, i++ ) {
for ( ; j[1].x < j->x; j-- ) {
SWAP_CELLS( j + 1, j, temp );
if ( j == base )
break;
}
}
if ( top > stack ) {
top -= 2;
base = top[0];
limit = top[1];
} else
break;
}
}
}
static void gray_hline(EVGRasterCtx *raster, TCoord x, TCoord y, TPos area, int acount, u32 fill_rule, u32 idx1, u32 idx2)
{
int coverage;
EVG_Span* span;
int count;
int odd_flag = 1;
x += (TCoord)raster->surf->min_ex;
if (x>=raster->surf->max_ex) return;
y += (TCoord)raster->surf->min_ey;
/* compute the coverage line's coverage, depending on the */
/* outline fill rule */
/* */
/* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
/* */
coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
/* use range 0..256 */
if ( coverage < 0 )
coverage = -coverage;
if (fill_rule) {
/* odd / even winding rule */
if ( coverage >= 256 ) {
coverage &= 511;
if ( coverage > 256 )
coverage = 512 - coverage;
else if ( coverage == 256 )
coverage = 255;
if (coverage!=255)
odd_flag = 0;
/* even only fill */
if (fill_rule==2) {
coverage = odd_flag ? 0 : (255-coverage);
} else {
coverage = 255;
}
}
else if (fill_rule==2) {
coverage = 255-coverage;
}
} else {
// normal non-zero winding rule
if ( coverage >= 256 )
coverage = 255;
}
if ((coverage < 0xFF) && (coverage > raster->surf->aa_level))
return;
if (!coverage)
return;
/* see if we can add this span to the current list */
count = raster->num_gray_spans;
span = raster->gray_spans + count - 1;
if ( (count > 0)
&& ((int)span->x + span->len == (int)x)
&& (span->coverage == coverage )
&& (span->odd_flag == odd_flag )
) {
span->len = (unsigned short)( span->len + acount );
return;
}
if ((u32) count >= raster->max_gray_spans) {
raster->surf->render_span(y, count, raster->gray_spans, raster->surf, raster);
raster->num_gray_spans = 0;
span = raster->gray_spans;
} else {
if ((u32) count==raster->alloc_gray_spans) {
raster->alloc_gray_spans*=2;
raster->gray_spans = gf_realloc(raster->gray_spans, sizeof(EVG_Span)*raster->alloc_gray_spans);
span = raster->gray_spans + count - 1;
}
span++;
}
/* add a gray span to the current list */
span->x = (short)x;
span->len = (unsigned short)acount;
span->coverage = (unsigned char)coverage;
span->odd_flag = (unsigned char)odd_flag;
span->idx1 = idx1;
span->idx2 = idx2;
raster->num_gray_spans++;
}
void gray_sweep_line(EVGRasterCtx *raster, AAScanline *sl, int y, u32 fill_rule)
{
TCoord cover;
AACell *start, *cur;
cur = sl->cells;
cover = 0;
raster->num_gray_spans = 0;
while (sl->num) {
TCoord x;
TArea area;
start = cur;
x = start->x;
area = start->area;
cover += start->cover;
/* accumulate all start cells */
while(--sl->num) {
++cur ;
if (cur->x != start->x )
break;
area += cur->area;
cover += cur->cover;
}
/* if the start cell has a non-null area, we must draw an */
/* individual gray pixel there */
if ( area && x >= 0 ) {
gray_hline( raster, x, y, cover * ( ONE_PIXEL * 2 ) - area, 1, fill_rule, start->idx1, start->idx2);
x++;
}
if ( x < 0 ) x = 0;
/* draw a gray span between the start cell and the current one */
if ( cur->x > x )
gray_hline( raster, x, y, cover * ( ONE_PIXEL * 2 ), cur->x - x, fill_rule, cur->idx1, cur->idx2);
}
raster->surf->render_span((int) (y + raster->surf->min_ey), raster->num_gray_spans, raster->gray_spans, raster->surf, raster );
}
#define LINES_PER_THREAD 6
static Bool th_fetch_lines(EVGRasterCtx *rctx)
{
gf_mx_p(rctx->surf->raster_mutex);
if (!rctx->surf->last_dispatch_line) {
rctx->first_line = rctx->last_line = 0;
gf_mx_v(rctx->surf->raster_mutex);
return GF_FALSE;
}
rctx->first_line = rctx->surf->last_dispatch_line;
rctx->surf->last_dispatch_line += LINES_PER_THREAD;
if (rctx->surf->last_dispatch_line >= rctx->surf->max_line_y) {
rctx->surf->last_dispatch_line = 0;
rctx->last_line = rctx->surf->max_line_y;
} else {
rctx->last_line = rctx->surf->last_dispatch_line;
}
gf_mx_v(rctx->surf->raster_mutex);
return GF_TRUE;
}
u32 th_sweep_lines(void *par)
{
EVGRasterCtx *rctx = par;
while (rctx->th_state == 1) {
u32 i;
u32 first_patch, last_patch;
//only for threads, wait for start raster event
if (rctx->th) {
//we are active, don't grab sema (final flush of sweep)
if (rctx->active) {
gf_sleep(0);
continue;
}
gf_sema_wait(rctx->surf->raster_sem);
if (!rctx->th_state) break;
rctx->active = GF_TRUE;
}
first_patch = 0xFFFFFFFF;
last_patch = 0;
while (1) {
/* sort each scanline and render it*/
for (i=rctx->first_line; i<rctx->last_line; i++) {
AAScanline *sl = &rctx->surf->scanlines[i];
if (sl->num) {
if (sl->num>1) gray_quick_sort(sl->cells, sl->num);
gray_sweep_line(rctx, sl, i, rctx->fill_rule);
sl->num = 0;
} else if (rctx->is_tri_raster) {
//if nothing on this line, we are done for this quad/triangle
break;
}
//only true if rctx->is_tri_raster is true
if (sl->pnum) {
if (first_patch > i) first_patch = i;
if (last_patch < i) last_patch = i;
}
}
if (!th_fetch_lines(rctx))
break;
}
gf_mx_p(rctx->surf->raster_mutex);
if (rctx->is_tri_raster) {
if (first_patch<rctx->surf->first_patch)
rctx->surf->first_patch = first_patch;
if (last_patch>rctx->surf->last_patch)
rctx->surf->last_patch = last_patch;
}
rctx->surf->pending_threads--;
gf_mx_v(rctx->surf->raster_mutex);
if (!rctx->th) break;
}
//we are done
rctx->th_state = 2;
return 0;
}
/* sort each scanline and render it*/
GF_Err evg_sweep_lines(GF_EVGSurface *surf, u32 size_y, u32 fill_rule, Bool is_tri_raster, GF_EVGFragmentParam *fparam)
{
u32 i;
if (fparam) {
surf->raster_ctx.frag_param = *fparam;
if (surf->frag_shader_init)
surf->frag_shader_init(surf->frag_shader_udta, &surf->raster_ctx.frag_param, 0, GF_FALSE);
#ifndef GPAC_DISABLE_THREADS
//shaders are not thread-safe yet
for (i=0; i<surf->nb_threads; i++) {
surf->th_raster_ctx[i].frag_param = *fparam;
if (surf->frag_shader_init)
surf->frag_shader_init(surf->frag_shader_udta, &surf->th_raster_ctx[i].frag_param, i+1, GF_FALSE);
}
#endif
}
#ifndef GPAC_DISABLE_THREADS
if (!surf->nb_threads) {
#endif
for (i=surf->first_scanline; i<size_y; i++) {
AAScanline *sl = &surf->scanlines[i];
if (sl->num) {
if (sl->num>1) gray_quick_sort(sl->cells, sl->num);
gray_sweep_line(&surf->raster_ctx, sl, i, fill_rule);
sl->num = 0;
} else if (is_tri_raster) {
//if nothing on this line, we are done for this quad/triangle
break;
}
if (sl->pnum) {
if (surf->first_patch > i) surf->first_patch = i;
if (surf->last_patch < i) surf->last_patch = i;
}
}
return GF_OK;
#ifndef GPAC_DISABLE_THREADS
}
surf->raster_ctx.fill_rule = fill_rule;
surf->raster_ctx.is_tri_raster = is_tri_raster ? 1 : 0;
surf->raster_ctx.first_line = surf->first_scanline;
surf->raster_ctx.last_line = surf->raster_ctx.first_line + LINES_PER_THREAD;
if (surf->raster_ctx.first_line%2) surf->raster_ctx.last_line++;
if (surf->raster_ctx.last_line >= size_y) {
surf->raster_ctx.last_line = size_y;
surf->last_dispatch_line = 0;
}
surf->pending_threads = 1;
surf->max_line_y = size_y;
surf->last_dispatch_line = surf->raster_ctx.last_line;
for (i=0; i<surf->nb_threads; i++) {
EVGRasterCtx *rctx = &surf->th_raster_ctx[i];
surf->pending_threads++;
if (!surf->last_dispatch_line) {
rctx->first_line = 0;
rctx->last_line = 0;
continue;
}
rctx->first_line = surf->last_dispatch_line;
rctx->last_line = surf->last_dispatch_line + LINES_PER_THREAD;
if (rctx->last_line >= size_y) {
rctx->last_line = size_y;
surf->last_dispatch_line = 0;
} else {
surf->last_dispatch_line += LINES_PER_THREAD;
}
rctx->fill_rule = fill_rule;
rctx->is_tri_raster = is_tri_raster ? 1 : 0;
}
//notify semaphore
gf_sema_notify(surf->raster_sem, surf->nb_threads);
//run using caller process
surf->raster_ctx.th_state = 1;
th_sweep_lines(&surf->raster_ctx);
while (surf->pending_threads) {
gf_sleep(0);
}
//move all threads to inactive so that they grab the sema
for (i=0; i<surf->nb_threads; i++) {
EVGRasterCtx *rctx = &surf->th_raster_ctx[i];
rctx->active = GF_FALSE;
}
if (fparam && surf->frag_shader_init) {
surf->frag_shader_init(surf->frag_shader_udta, &surf->raster_ctx.frag_param, 0, GF_TRUE);
for (i=0; i<surf->nb_threads; i++) {
surf->frag_shader_init(surf->frag_shader_udta, &surf->th_raster_ctx[i].frag_param, i+1, GF_TRUE);
}
}
return GF_OK;
#endif
}
GF_Err evg_raster_render(GF_EVGSurface *surf)
{
u32 fill_rule = 0;
u32 size_y;
EVG_Outline* outline = (EVG_Outline*)&surf->ftoutline;
/* return immediately if the outline is empty */
if ( outline->n_points == 0 || outline->n_contours <= 0 ) return GF_OK;
/* Set up state in the raster object */
surf->min_ex = surf->clip_xMin;
surf->min_ey = surf->clip_yMin;
surf->max_ex = surf->clip_xMax;
surf->max_ey = surf->clip_yMax;
size_y = (u32) (surf->max_ey - surf->min_ey);
if (surf->max_lines < size_y) {
surf->scanlines = (AAScanline*)gf_realloc(surf->scanlines, sizeof(AAScanline)*size_y);
if (!surf->scanlines) return GF_OUT_OF_MEM;
memset(&surf->scanlines[surf->max_lines], 0, sizeof(AAScanline)*(size_y-surf->max_lines) );
surf->max_lines = size_y;
}
surf->ex = (int) (surf->max_ex+1);
surf->ey = (int) (surf->max_ey+1);
surf->cover = 0;
surf->area = 0;
surf->first_scanline = surf->max_ey;
EVG_Outline_Decompose(outline, surf);
gray_record_cell(surf);
/*store odd/even rule*/
if (outline->flags & GF_PATH_FILL_ZERO_NONZERO) fill_rule = 1;
else if (outline->flags & GF_PATH_FILL_EVEN) fill_rule = 2;
if (surf->mask_mode == GF_EVGMASK_RECORD) {
surf->render_span = evg_fill_span_mask;
} else {
surf->render_span = (EVG_SpanFunc) surf->fill_spans;
}
if ((void *) surf->sten == (void *) &surf->shader_sten) {
GF_EVGFragmentParam fparam;
memset(&fparam, 0, sizeof(GF_EVGFragmentParam));
fparam.tx_width = surf->shader_sten.width;
fparam.tx_height = surf->shader_sten.height;
return evg_sweep_lines(surf, size_y, fill_rule, GF_FALSE, &fparam);
}
return evg_sweep_lines(surf, size_y, fill_rule, GF_FALSE, NULL);
}
/* END */
#endif //GPAC_DISABLE_EVG