[dense] Add drawing functions to rasterizer

* src/dense/ftdense.c: (dense_render_line, dense_render_quadratic,
dense_render_cubic, dense_render_glyph, dense_raster_render, Lerp):
New Functions
This commit is contained in:
Anurag Thakur 2022-11-19 10:42:07 +05:30
parent f2ff1dc37e
commit 5e681678f6

View File

@ -32,6 +32,15 @@ typedef struct dense_TRaster_
} dense_TRaster, *dense_PRaster;
/* Linear interpolation between P0 and P1 */
static FT_Vector
Lerp( float T, FT_Vector P0, FT_Vector P1 )
{
FT_Vector p;
p.x = P0.x + T * ( P1.x - P0.x );
p.y = P0.y + T * ( P1.y - P0.y );
return p;
}
static int
dense_move_to( const FT_Vector* to, dense_worker* worker )
@ -54,11 +63,112 @@ dense_line_to( const FT_Vector* to, dense_worker* worker )
}
void
dense_render_line( dense_worker* worker, TPos tox, TPos toy )
dense_render_line( dense_worker* worker, FT_Pos tox, FT_Pos toy )
{
return;
float from_x = worker->prev_x;
float from_y = worker->prev_y;
if ( from_y == toy )
return;
from_x /= 256.0;
from_y /= 256.0;
float to_x = tox / 256.0;
float to_y = toy / 256.0;
float dir;
if ( from_y < to_y )
dir = 1;
else
{
dir = -1;
FT_SWAP(from_x, to_x );
FT_SWAP(from_y, to_y );
}
// Clip to the height.
if ( from_y >= worker->m_h || to_y <= 0 )
return;
float dxdy = ( to_x - from_x ) / (float)( to_y - from_y );
if ( from_y < 0 )
{
from_x -= from_y * dxdy;
from_y = 0;
}
if ( to_y > worker->m_h )
{
to_x -= ( to_y - worker->m_h ) * dxdy;
to_y = (float)worker->m_h;
}
float x = from_x;
int y0 = (int)from_y;
int y_limit = (int)ceil( to_y );
float* m_a = worker->m_a;
for ( int y = y0; y < y_limit; y++ )
{
int linestart = y * worker->m_w;
float dy = fmin( y + 1.0f, to_y ) - fmax( (float)y, from_y );
float xnext = x + dxdy * dy;
float d = dy * dir;
float x0, x1;
if ( x < xnext )
{
x0 = x;
x1 = xnext;
}
else
{
x0 = xnext;
x1 = x;
}
/*
It's possible for x0 to be negative on the last scanline because of
floating-point inaccuracy That would cause an out-of-bounds array access at
index -1.
*/
float x0floor = x0 <= 0.0f ? 0.0f : (float)floor( x0 );
int x0i = (int)x0floor;
float x1ceil = (float)ceil( x1 );
int x1i = (int)x1ceil;
if ( x1i <= x0i + 1 )
{
float xmf = 0.5f * ( x + xnext ) - x0floor;
m_a[linestart + x0i] += d - d * xmf;
m_a[linestart + ( x0i + 1 )] += d * xmf;
}
else
{
float s = 1.0f / ( x1 - x0 );
float x0f = x0 - x0floor;
float a0 = 0.5f * s * ( 1.0f - x0f ) * ( 1.0f - x0f );
float x1f = x1 - x1ceil + 1.0f;
float am = 0.5f * s * x1f * x1f;
m_a[linestart + x0i] += d * a0;
if ( x1i == x0i + 2 )
m_a[linestart + ( x0i + 1 )] += d * ( 1.0f - a0 - am );
else
{
float a1 = s * ( 1.5f - x0f );
m_a[linestart + ( x0i + 1 )] += d * ( a1 - a0 );
for ( int xi = x0i + 2; xi < x1i - 1; xi++ )
m_a[linestart + xi] += d * s;
float a2 = a1 + ( x1i - x0i - 3 ) * s;
m_a[linestart + ( x1i - 1 )] += d * ( 1.0f - a2 - am );
}
m_a[linestart + x1i] += d * am;
}
x = xnext;
}
}
static int
dense_conic_to( const FT_Vector* control,
const FT_Vector* to,
@ -73,7 +183,56 @@ dense_render_quadratic( dense_worker* worker,
FT_Vector* control,
FT_Vector* to )
{
/*
Calculate devsq as the square of four times the
distance from the control point to the midpoint of the curve.
This is the place at which the curve is furthest from the
line joining the control points.
4 x point on curve = p0 + 2p1 + p2
4 x midpoint = 4p1
The division by four is omitted to save time.
*/
FT_Vector aP0 = { DOWNSCALE( worker->prev_x ), DOWNSCALE( worker->prev_y ) };
FT_Vector aP1 = { control->x, control->y };
FT_Vector aP2 = { to->x, to->y };
float devx = aP0.x - aP1.x - aP1.x + aP2.x;
float devy = aP0.y - aP1.y - aP1.y + aP2.y;
float devsq = devx * devx + devy * devy;
if ( devsq < 0.333f )
{
dense_line_to( &aP2, worker );
return;
}
/*
According to Raph Levien, the reason for the subdivision by n (instead of
recursive division by the Casteljau system) is that "I expect the flatness
computation to be semi-expensive (it's done once rather than on each potential
subdivision) and also because you'll often get fewer subdivisions. Taking a
circular arc as a simplifying assumption, where I get n, a recursive approach
would get 2^ceil(lg n), which, if I haven't made any horrible mistakes, is
expected to be 33% more in the limit".
*/
const float tol = 3.0f;
int n = (int)floor( sqrt( sqrt( tol * devsq ) ) )/8;
FT_Vector p = aP0;
float nrecip = 1.0f / ( n + 1.0f );
float t = 0.0f;
for ( int i = 0; i < n; i++ )
{
t += nrecip;
FT_Vector next = Lerp( t, Lerp( t, aP0, aP1 ), Lerp( t, aP1, aP2 ) );
dense_line_to(&next, worker );
p = next;
}
dense_line_to( &aP2, worker );
}
static int
@ -92,7 +251,43 @@ dense_render_cubic( dense_worker* worker,
FT_Vector* control_2,
FT_Vector* to )
{
return;
FT_Vector aP0 = { DOWNSCALE( worker->prev_x ), DOWNSCALE( worker->prev_y ) };
FT_Vector aP1 = { control_1->x, control_1->y };
FT_Vector aP2 = { control_2->x, control_2->y };
FT_Vector aP3 = { to->x, to->y };
float devx = aP0.x - aP1->x - aP1->x + aP2->x;
float devy = aP0.y - aP1->y - aP1->y + aP2->y;
float devsq0 = devx * devx + devy * devy;
devx = aP1->x - aP2->x - aP2->x + aP3->x;
devy = aP1->y - aP2->y - aP2->y + aP3->y;
float devsq1 = devx * devx + devy * devy;
float devsq = fmax( devsq0, devsq1 );
if ( devsq < 0.333f )
{
dense_render_line( worker, aP3->x, aP3->y );
return;
}
const float tol = 3.0f;
int n = (int)floor( sqrt( sqrt( tol * devsq ) ) ) / 8;
FT_Vector p = aP0;
float nrecip = 1.0f / ( n + 1.0f );
float t = 0.0f;
for ( int i = 0; i < n; i++ )
{
t += nrecip;
FT_Vector a = Lerp( t, Lerp( t, aP0, *aP1 ), Lerp( t, *aP1, *aP2 ) );
FT_Vector b = Lerp( t, Lerp( t, *aP1, *aP2 ), Lerp( t, *aP2, *aP3 ) );
FT_Vector next = Lerp( t, a, b );
dense_render_line( worker, next.x, next.y );
worker->prev_x = next.x;
worker->prev_y = next.y;
p = next;
}
dense_line_to( &aP3, worker );
}
static int
@ -152,13 +347,78 @@ dense_render_glyph( dense_worker* worker, const FT_Bitmap* target )
{
FT_Error error = FT_Outline_Decompose( &( worker->outline ),
&dense_decompose_funcs, worker );
// Render into bitmap
const float* source = worker->m_a;
unsigned char* dest = target->buffer;
unsigned char* dest_end = target->buffer + worker->m_w * worker->m_h;
float value = 0.0f;
while ( dest < dest_end )
{
value += *source++;
if ( value > 0.0f )
{
int n = (int)( fabs( value ) * 255.0f + 0.5f );
if ( n > 255 )
n = 255;
*dest = (unsigned char)n;
}
else
*dest = 0;
dest++;
}
free(worker->m_a);
return error;
}
static int
dense_raster_render( FT_Raster raster, const FT_Raster_Params* params )
{
return 0;
const FT_Outline* outline = (const FT_Outline*)params->source;
FT_Bitmap* target_map = params->target;
dense_worker worker[1];
if ( !raster )
return FT_THROW( Invalid_Argument );
if ( !outline )
return FT_THROW( Invalid_Outline );
worker->outline = *outline;
if ( !target_map )
return FT_THROW( Invalid_Argument );
/* nothing to do */
if ( !target_map->width || !target_map->rows )
return 0;
if ( !target_map->buffer )
return FT_THROW( Invalid_Argument );
worker->m_origin_x = 0;
worker->m_origin_y = 0;
worker->m_w = target_map->pitch;
worker->m_h = target_map->rows;
int size = worker->m_w * worker->m_h + 4;
worker->m_a = malloc( sizeof( float ) * size );
worker->m_a_size = size;
memset( worker->m_a, 0, ( sizeof( float ) * size ) );
/* exit if nothing to do */
if ( worker->m_w <= worker->m_origin_x || worker->m_h <= worker->m_origin_y )
{
return 0;
}
// Invert the pitch to account for different +ve y-axis direction in dense array
// (maybe temporary solution)
target_map->pitch *= -1;
return dense_render_glyph( worker, target_map );
}
FT_DEFINE_RASTER_FUNCS(