freetype2/src/dense/ftdense.c

574 lines
15 KiB
C

/** The rasterizer for the 'dense' renderer */
#include <stdio.h>
#undef FT_COMPONENT
#define FT_COMPONENT dense
#include "ftdense.h"
#include <freetype/ftoutln.h>
#include <freetype/internal/ftcalc.h>
#include <freetype/internal/ftdebug.h>
#include <freetype/internal/ftobjs.h>
#include <math.h>
#include "ftdenseerrs.h"
/* @QUES: Please explain all these defines. Why is PIXEL_BITS 8??*/
#define PIXEL_BITS 8
#define ONE_PIXEL ( 1 << PIXEL_BITS )
#define TRUNC( x ) ( int )( ( x ) >> PIXEL_BITS )
#define UPSCALE( x ) ( ( x ) * ( ONE_PIXEL >> 6 ) )
#define DOWNSCALE( x ) ( ( x ) >> ( PIXEL_BITS - 6 ) )
typedef struct dense_TRaster_
{
void* memory;
} dense_TRaster, *dense_PRaster;
static RasterFP_Point
Lerp( float aT, RasterFP_Point aP0, RasterFP_Point aP1 )
{
RasterFP_Point p;
p.m_x = aP0.m_x + aT * ( aP1.m_x - aP0.m_x );
p.m_y = aP0.m_y + aT * ( aP1.m_y - aP0.m_y );
return p;
}
static int
dense_move_to( const FT_Vector* to, RasterFP* aRasterFP )
{
TPos x, y;
x = UPSCALE( to->x );
y = UPSCALE( to->y );
aRasterFP->prev_x = x;
aRasterFP->prev_y = y;
//printf( "last point is {%f, %f}", lp.m_x, lp.m_y );
return 0;
}
static int
dense_line_to( const FT_Vector* to, RasterFP* aRasterFP )
{
printf("dense_line_to: %d, %d\n", to->x, to->y);
RasterFP_Point tp = {UPSCALE(to->x), UPSCALE(to->y)};
RasterFP_DrawLine( aRasterFP, tp.m_x, tp.m_y );
dense_move_to( to, aRasterFP );
return 0;
}
void swap(long int *a, long int *b){
long int temp = *a;
*a = *b;
*b = temp;
}
void swapold(unsigned char *a, unsigned char *b){
unsigned char temp = *a;
*a = *b;
*b = temp;
}
void
RasterFP_DrawLine( RasterFP* aRasterFP, TPos to_x, TPos to_y )
{
TPos from_x = aRasterFP->prev_x;
TPos from_y = aRasterFP->prev_y;
if ( from_y == to_y )
return;
/* @QUES: What is this code that I commented out, supposed to do?*/
// aP0.m_x -= aRasterFP->m_origin_x;
// aP0.m_y -= aRasterFP->m_origin_y;
// aP1.m_x -= aRasterFP->m_origin_x;
// aP1.m_y -= aRasterFP->m_origin_y;
from_x = TRUNC((int)from_x );
from_y = TRUNC((int)from_y );
to_x = TRUNC((int)to_x );
to_y = TRUNC((int)to_y );
float dir;
if ( from_y < to_y )
dir = 1;
else
{
dir = -1;
swap(&from_x, &to_x);
swap(&from_y, &to_y);
}
// Clip to the height.
if ( from_y >= aRasterFP->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 > aRasterFP->m_h )
{
to_x -= ( to_y - aRasterFP->m_h ) * dxdy;
to_y = (float)aRasterFP->m_h;
}
/**
Handle parts of the line outside the clip rectangle by
snapping them to the left or right edge, or, if they intersect the clip area,
by recursive calls.
*/
/* @QUES: This code isn't present in font-rs. It was added later by graham asher
I have a very strong feeling that this isn't necessary.
Since probably the outline is already fitted in the bounding box. I tested
this code a little, removing it doesn't seem to make any difference*/
RasterFP_Point intersect = { 0, 0 };
int recursive = 0;
if ( from_x >= aRasterFP->m_w && to_x >= aRasterFP->m_w )
{
from_x = to_x = (float)aRasterFP->m_w;
dxdy = 0;
}
else if ( from_x <= 0 && to_x <= 0 )
{
from_x = to_x = 0;
dxdy = 0;
}
else if ( ( from_x < 0 ) != ( to_x < 0 ) )
{
intersect.m_x = 0;
intersect.m_y = to_y - to_x / dxdy;
recursive = 1;
}
else if ( ( from_x > aRasterFP->m_w ) != ( to_x > aRasterFP->m_w ) )
{
intersect.m_x = (float)aRasterFP->m_w;
intersect.m_y = from_y + ( aRasterFP->m_w - from_x ) / dxdy;
recursive = 1;
}
if ( recursive )
{
from_x += aRasterFP->m_origin_x;
from_y += aRasterFP->m_origin_y;
to_x += aRasterFP->m_origin_x;
to_y += aRasterFP->m_origin_y;
intersect.m_x += aRasterFP->m_origin_x;
intersect.m_y += aRasterFP->m_origin_y;
if ( dir == 1 )
{
RasterFP_DrawLine( aRasterFP, intersect.m_x, intersect.m_y );
RasterFP_DrawLine( aRasterFP, to_x, to_y );
}
else
{
RasterFP_DrawLine( aRasterFP, intersect.m_x, intersect.m_y );
RasterFP_DrawLine( aRasterFP, from_x, from_y );
}
return;
}
/* @QUES: I am still trying to understand this code */
float x = from_x;
int y0 = (int)from_y;
int y_limit = (int)ceil( to_y );
float* m_a = aRasterFP->m_a;
for ( int y = y0; y < y_limit; y++ )
{
int linestart = y * aRasterFP->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,
RasterFP* aRasterFP )
{
// RasterFP_Point controlP = { UPSCALE(control->x), UPSCALE(control->y) };
// RasterFP_Point toP = { UPSCALE(to->x), UPSCALE(to->y) };
// RasterFP_Point lP = { aRasterFP->prev_x, aRasterFP->prev_y };
RasterFP_DrawQuadratic( aRasterFP, control, to );
return 0;
}
void
RasterFP_DrawQuadratic( RasterFP* aRasterFP,
const FT_Vector* control,
const FT_Vector* to )
{
// assert( aRasterFP );
/*
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.
*/
RasterFP_Point aP0 = {aRasterFP->prev_x, aRasterFP->prev_y};
RasterFP_Point aP1 = {UPSCALE(control->x), UPSCALE(control->y)};
RasterFP_Point aP2 = {UPSCALE(to->x), UPSCALE(to->y)};
float devx = aP0.m_x - aP1.m_x - aP1.m_x + aP2.m_x;
float devy = aP0.m_y - aP1.m_y - aP1.m_y + aP2.m_y;
float devsq = devx * devx + devy * devy;
if ( devsq < 0.333f )
{
RasterFP_DrawLine(aRasterFP, aP2.m_x, aP2.m_y );
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 ) ) );
// RasterFP_Point p = aP0;
float nrecip = 1.0f / ( n + 1.0f );
float t = 0.0f;
for ( int i = 0; i < n; i++ )
{
t += nrecip;
RasterFP_Point next = Lerp( t, Lerp( t, aP0, aP1 ), Lerp( t, aP1, aP2 ) );
RasterFP_DrawLine(aRasterFP , next.m_x, next.m_y);
//p = next;
}
RasterFP_DrawLine( aRasterFP, aP2.m_x, aP2.m_y);
}
static int
dense_cubic_to( const FT_Vector* control1,
const FT_Vector* control2,
const FT_Vector* to,
RasterFP* aRasterFP )
{
RasterFP_Point ap1 = { UPSCALE(control1->x), UPSCALE(control1->y) };
RasterFP_Point ap2 = { UPSCALE(control2->x), UPSCALE(control2->y) };
RasterFP_Point ap3 = { UPSCALE(to->x), UPSCALE(to->y) };
RasterFP_Point lP = {aRasterFP->prev_x, aRasterFP->prev_y};
RasterFP_DrawCubic( aRasterFP, lP, ap1, ap2, ap3 );
return 0;
}
void
RasterFP_DrawCubic( RasterFP* aRasterFP,
RasterFP_Point aP0,
RasterFP_Point aP1,
RasterFP_Point aP2,
RasterFP_Point aP3 )
{
// assert( aRasterFP );
float devx = aP0.m_x - aP1.m_x - aP1.m_x + aP2.m_x;
float devy = aP0.m_y - aP1.m_y - aP1.m_y + aP2.m_y;
float devsq0 = devx * devx + devy * devy;
devx = aP1.m_x - aP2.m_x - aP2.m_x + aP3.m_x;
devy = aP1.m_y - aP2.m_y - aP2.m_y + aP3.m_y;
float devsq1 = devx * devx + devy * devy;
float devsq = fmax( devsq0, devsq1 );
if ( devsq < 0.333f )
{
RasterFP_DrawLine( aRasterFP, aP3.m_x , aP3.m_y);
return;
}
const float tol = 3.0f;
int n = (int)floor( sqrt( sqrt( tol * devsq ) ) );
RasterFP_Point p = aP0;
float nrecip = 1.0f / ( n + 1.0f );
float t = 0.0f;
for ( int i = 0; i < n; i++ )
{
t += nrecip;
RasterFP_Point a = Lerp( t, Lerp( t, aP0, aP1 ), Lerp( t, aP1, aP2 ) );
RasterFP_Point b = Lerp( t, Lerp( t, aP1, aP2 ), Lerp( t, aP2, aP3 ) );
RasterFP_Point next = Lerp( t, a, b );
RasterFP_DrawLine( aRasterFP, next.m_x, next.m_y );
p = next;
}
RasterFP_DrawLine( aRasterFP, aP3.m_x, aP3.m_y );
}
/* @QUES: This is the first method called on the module. I suspect
this only initializes the memory for it*/
static int
dense_raster_new( FT_Memory memory, dense_PRaster* araster )
{
FT_Error error;
dense_PRaster raster;
if ( !FT_NEW( raster ) )
raster->memory = memory;
*araster = raster;
printf("dense_raster_new\n");
return error;
}
/* @QUES: This is a simple method, only frees memory*/
static void
dense_raster_done( FT_Raster raster )
{
printf( "dense_raster_done\n" );
FT_Memory memory = (FT_Memory)( (dense_PRaster)raster )->memory;
FT_FREE( raster );
}
static void
dense_raster_reset( FT_Raster raster,
unsigned char* pool_base,
unsigned long pool_size )
{
FT_UNUSED( raster );
FT_UNUSED( pool_base );
FT_UNUSED( pool_size );
printf("dense_raster_reset\n");
}
/* @QUES: This methodisnt't called in normal ftlint execution*/
static int
dense_raster_set_mode( FT_Raster raster, unsigned long mode, void* args )
{
FT_UNUSED( raster );
FT_UNUSED( mode );
FT_UNUSED( args );
printf("dense_raster_set_mode\n");
return 0; /* nothing to do */
}
FT_DEFINE_OUTLINE_FUNCS( dense_decompose_funcs,
(FT_Outline_MoveTo_Func)dense_move_to, /* move_to */
(FT_Outline_LineTo_Func)dense_line_to, /* line_to */
(FT_Outline_ConicTo_Func)dense_conic_to, /* conic_to */
(FT_Outline_CubicTo_Func)dense_cubic_to, /* cubic_to */
0, /* shift */
0 /* delta */
)
/* @QUES: So, this calls FT_Outline_Decompose, that calls the move to,
line to, conic to, cubic to interface methods. The aRasterFP structure stores the
well, stuff in its m_a and finally renders it to the target->buffer*/
static int
dense_render_glyph( RasterFP* aRasterFP, const FT_Bitmap* target )
{
FT_Error error = FT_Outline_Decompose( &( aRasterFP->outline ),
&dense_decompose_funcs, aRasterFP );
// Render into bitmap
const float* source = aRasterFP->m_a;
//printf( "Outputting bitmap\n" );
// for ( int i = 0; i < aRasterFP->m_h; i++ )
// {
// printf( "\n" );
// for ( int j = 0; j < aRasterFP->m_w; j++ )
// {
// float strength = *( source + ( i * aRasterFP->m_w + j ) );
// if ( strength > 0.90 )
// {
// printf( "@|" );
// }
// else if ( strength > 0.70 )
// {
// printf( "#|" );
// }
// else if ( strength > 0.45 )
// {
// printf( "+|" );
// }
// else if ( strength > 0.20 )
// {
// printf( "*|" );
// }
// else
// {
// printf( ".|" );
// }
// }
// }
// printf( "\n" );
unsigned char* dest = target->buffer;
unsigned char* dest_end = target->buffer + aRasterFP->m_w * aRasterFP->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++;
}
for (int col = 0; col < aRasterFP->m_w; col++)
{
for (int row = 0; row < aRasterFP->m_h/2; row++)
{
//printf("Swapping position: %d, %d with %d, %d with rows = %d, cols = %d",row,col, aRasterFP->m_h-row, col, aRasterFP->m_h, aRasterFP->m_w);
swapold(target->buffer + aRasterFP->m_w*row + col, target->buffer + (aRasterFP->m_h-row-1)*aRasterFP->m_w + col);
}
}
return error;
}
/* @QUES: The main rasterizing method, params.->target->buffer gets the
rendered pixels*/
static int
dense_raster_render( FT_Raster raster, const FT_Raster_Params* params )
{
printf( "dense_raster_render\n" );
const FT_Outline* outline = (const FT_Outline*)params->source;
const FT_Bitmap* target_map = params->target;
RasterFP* aRasterFP = malloc( sizeof( RasterFP ) );
if ( !raster )
return FT_THROW( Invalid_Argument );
if ( !outline )
return FT_THROW( Invalid_Outline );
aRasterFP->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 );
aRasterFP->m_origin_x = 0;
aRasterFP->m_origin_y = 0;
/* @QUES: Why are my bitmaps upsied down 😭*/
aRasterFP->m_w = target_map->pitch;
aRasterFP->m_h = target_map->rows;
int size = aRasterFP->m_w * aRasterFP->m_h + 4;
aRasterFP->m_a = malloc( sizeof( float ) * size );
aRasterFP->m_a_size = size;
memset( aRasterFP->m_a, 0, ( sizeof( float ) * size ) );
/* exit if nothing to do */
if ( aRasterFP->m_w <= aRasterFP->m_origin_x ||
aRasterFP->m_h <= aRasterFP->m_origin_y )
{
return 0;
}
return dense_render_glyph( aRasterFP, target_map );
}
FT_DEFINE_RASTER_FUNCS(
ft_dense_raster,
FT_GLYPH_FORMAT_OUTLINE,
(FT_Raster_New_Func)dense_raster_new, /* raster_new */
(FT_Raster_Reset_Func)dense_raster_reset, /* raster_reset */
(FT_Raster_Set_Mode_Func)dense_raster_set_mode, /* raster_set_mode */
(FT_Raster_Render_Func)dense_raster_render, /* raster_render */
(FT_Raster_Done_Func)dense_raster_done /* raster_done */
)
/* END */