[sdf] Remove use of `FT_List'.

Simply use a `next' pointer inside `SDF_Edge' and
`SDF_Contour' to create a linked list. This reduces
the number of allocations due to `FT_ListNode', also
we don't need a doubly linked list.

* src/sdf/ftsdf.c (SDF_Edge, SDF_Contour): Remove the
  `FT_List' and use a `next' pointer to create the
  linked list.

* src/sdf/ftsdf.c(sdf_edge_destructor, sdf_contour_destructor):
  Removed, not needed any more.

* src/sdf/ftsdf.c (*): Remove the use of `FT_List_'
  functions wherever necessary and sync with the new
  list.
This commit is contained in:
Anuj Verma 2020-07-10 09:15:58 +05:30 committed by anujverma
parent 8bf9da21e0
commit d9eeac807e
2 changed files with 139 additions and 163 deletions

View File

@ -1,8 +1,28 @@
2020-07-10 Anuj Verma <anujv@iitbhilai.ac.in>
[sdf] Remove use of `FT_List'.
Simply use a `next' pointer inside `SDF_Edge' and
`SDF_Contour' to create a linked list. This reduces
the number of allocations due to `FT_ListNode', also
we don't need a doubly linked list.
* src/sdf/ftsdf.c (SDF_Edge, SDF_Contour): Remove the
`FT_List' and use a `next' pointer to create the
linked list.
* src/sdf/ftsdf.c(sdf_edge_destructor, sdf_contour_destructor):
Removed, not needed any more.
* src/sdf/ftsdf.c (*): Remove the use of `FT_List_'
functions wherever necessary and sync with the new
list.
2020-07-09 Anuj Verma <anujv@iitbhilai.ac.in>
[sdf] Added subdivision optimization.
* src/sdf/ftsdfrend.c (sdf_generate_subdivision): The
* src/sdf/ftsdf.c (sdf_generate_subdivision): The
function generate SDF just like the `sdf_generate'
function, but subdivide the curve into a number of
lines and then use the `sdf_generate_bounding_box'

View File

@ -1,7 +1,6 @@
#include <freetype/internal/ftobjs.h>
#include <freetype/internal/ftdebug.h>
#include <freetype/ftlist.h>
#include <freetype/fttrigon.h>
#include "ftsdf.h"
@ -18,7 +17,7 @@
/* a chance of overflow and artifacts. You can safely use it upto a */
/* pixel size of 128. */
#ifndef USE_SQUARED_DISTANCES
# define USE_SQUARED_DISTANCES 1
# define USE_SQUARED_DISTANCES 0
#endif
/* If it is defined to 1 then the rasterizer will use Newton-Raphson's */
@ -134,14 +133,18 @@
SDF_Edge_Type edge_type; /* edge identifier */
struct SDF_Edge_* next; /* to create linked list */
} SDF_Edge;
/* A contour represent a set of edges which make a closed */
/* loop. */
typedef struct SDF_Contour_
{
FT_26D6_Vec last_pos; /* end position of the last edge */
FT_ListRec edges; /* list of all edges in the contour */
FT_26D6_Vec last_pos; /* end position of the last edge */
SDF_Edge* edges; /* list of all edges in the contour */
struct SDF_Contour_* next; /* to create linked list */
} SDF_Contour;
@ -149,8 +152,8 @@
/* glyph outline. */
typedef struct SDF_Shape_
{
FT_Memory memory; /* used internally to allocate memory */
FT_ListRec contours; /* list of all contours in the outline */
FT_Memory memory; /* used internally to allocate memory */
SDF_Contour* contours; /* list of all contours in the outline */
} SDF_Shape;
@ -188,13 +191,13 @@
static
const SDF_Edge null_edge = { { 0, 0 }, { 0, 0 },
{ 0, 0 }, { 0, 0 },
SDF_EDGE_UNDEFINED };
SDF_EDGE_UNDEFINED, NULL };
static
const SDF_Contour null_contour = { { 0, 0 }, { NULL, NULL } };
const SDF_Contour null_contour = { { 0, 0 }, NULL, NULL };
static
const SDF_Shape null_shape = { NULL, { NULL, NULL } };
const SDF_Shape null_shape = { NULL, NULL };
static
const SDF_Signed_Distance max_sdf = { INT_MAX, 0, 0 };
@ -236,18 +239,6 @@
FT_FREE( *edge );
}
/* Used in `FT_List_Finalize'. */
static void
sdf_edge_destructor( FT_Memory memory,
void* data,
void* user )
{
SDF_Edge* edge = (SDF_Edge*)data;
sdf_edge_done( memory, &edge );
}
/* Creates a new `SDF_Contour' on the heap and assigns */
/* the `contour' pointer to the newly allocated memory. */
static FT_Error
@ -280,28 +271,26 @@
sdf_contour_done( FT_Memory memory,
SDF_Contour** contour )
{
SDF_Edge* edges;
SDF_Edge* temp;
if ( !memory || !contour || !*contour )
return;
/* */
FT_List_Finalize( &(*contour)->edges, sdf_edge_destructor,
memory, NULL );
edges = (*contour)->edges;
/* release all the edges */
while ( edges )
{
temp = edges;
edges = edges->next;
sdf_edge_done( memory, &temp );
}
FT_FREE( *contour );
}
/* Used in `FT_List_Finalize'. */
static void
sdf_contour_destructor( FT_Memory memory,
void* data,
void* user )
{
SDF_Contour* contour = (SDF_Contour*)data;
sdf_contour_done( memory, &contour );
}
/* Creates a new `SDF_Shape' on the heap and assigns */
/* the `shape' pointer to the newly allocated memory. */
static FT_Error
@ -334,20 +323,28 @@
static void
sdf_shape_done( SDF_Shape** shape )
{
FT_Memory memory;
FT_Memory memory;
SDF_Contour* contours;
SDF_Contour* temp;
if ( !shape || !*shape )
return;
memory = (*shape)->memory;
contours = (*shape)->contours;
if ( !memory )
return;
/* release the list of contours */
FT_List_Finalize( &(*shape)->contours, sdf_contour_destructor,
memory, NULL );
/* release all the contours */
while ( contours )
{
temp = contours;
contours = contours->next;
sdf_contour_done( memory, &temp );
}
/* release the allocated shape struct */
FT_FREE( *shape );
@ -368,7 +365,6 @@
{
SDF_Shape* shape = ( SDF_Shape* )user;
SDF_Contour* contour = NULL;
FT_ListNode node = NULL;
FT_Error error = FT_Err_Ok;
FT_Memory memory = shape->memory;
@ -380,17 +376,11 @@
goto Exit;
}
error = sdf_contour_new( memory, &contour );
if ( error != FT_Err_Ok )
goto Exit;
if ( FT_QNEW( node ) )
goto Exit;
FT_CALL( sdf_contour_new( memory, &contour ) );
contour->last_pos = *to;
node->data = contour;
FT_List_Add( &shape->contours, node );
contour->next = shape->contours;
shape->contours = contour;
Exit:
return error;
@ -403,7 +393,6 @@
SDF_Shape* shape = ( SDF_Shape* )user;
SDF_Edge* edge = NULL;
SDF_Contour* contour = NULL;
FT_ListNode node = NULL;
FT_Error error = FT_Err_Ok;
FT_Memory memory = shape->memory;
@ -415,28 +404,22 @@
goto Exit;
}
contour = ( SDF_Contour* )shape->contours.tail->data;
contour = shape->contours;
if ( contour->last_pos.x == to->x &&
contour->last_pos.y == to->y )
goto Exit;
error = sdf_edge_new( memory, &edge );
if ( error != FT_Err_Ok )
goto Exit;
if ( FT_QNEW( node ) )
goto Exit;
FT_CALL( sdf_edge_new( memory, &edge ) );
edge->edge_type = SDF_EDGE_LINE;
edge->start_pos = contour->last_pos;
edge->end_pos = *to;
edge->next = contour->edges;
contour->edges = edge;
contour->last_pos = *to;
node->data = edge;
FT_List_Add( &contour->edges, node );
Exit:
return error;
}
@ -449,7 +432,6 @@
SDF_Shape* shape = ( SDF_Shape* )user;
SDF_Edge* edge = NULL;
SDF_Contour* contour = NULL;
FT_ListNode node = NULL;
FT_Error error = FT_Err_Ok;
FT_Memory memory = shape->memory;
@ -461,25 +443,19 @@
goto Exit;
}
contour = ( SDF_Contour* )shape->contours.tail->data;
contour = shape->contours;
error = sdf_edge_new( memory, &edge );
if ( error != FT_Err_Ok )
goto Exit;
if ( FT_QNEW( node ) )
goto Exit;
FT_CALL( sdf_edge_new( memory, &edge ) );
edge->edge_type = SDF_EDGE_CONIC;
edge->start_pos = contour->last_pos;
edge->control_a = *control_1;
edge->end_pos = *to;
edge->next = contour->edges;
contour->edges = edge;
contour->last_pos = *to;
node->data = edge;
FT_List_Add( &contour->edges, node );
Exit:
return error;
}
@ -493,7 +469,6 @@
SDF_Shape* shape = ( SDF_Shape* )user;
SDF_Edge* edge = NULL;
SDF_Contour* contour = NULL;
FT_ListNode node = NULL;
FT_Error error = FT_Err_Ok;
FT_Memory memory = shape->memory;
@ -505,14 +480,9 @@
goto Exit;
}
contour = ( SDF_Contour* )shape->contours.tail->data;
contour = shape->contours;
error = sdf_edge_new( memory, &edge );
if ( error != FT_Err_Ok )
goto Exit;
if ( FT_QNEW( node ) )
goto Exit;
FT_CALL( sdf_edge_new( memory, &edge ) );
edge->edge_type = SDF_EDGE_CUBIC;
edge->start_pos = contour->last_pos;
@ -520,11 +490,10 @@
edge->control_b = *control_2;
edge->end_pos = *to;
edge->next = contour->edges;
contour->edges = edge;
contour->last_pos = *to;
node->data = edge;
FT_List_Add( &contour->edges, node );
Exit:
return error;
}
@ -721,12 +690,11 @@
split_sdf_conic( FT_Memory memory,
FT_26D6_Vec* control_points,
FT_Int max_splits,
FT_List out )
SDF_Edge** out )
{
FT_Error error = FT_Err_Ok;
FT_26D6_Vec cpos[5];
SDF_Edge* left,* right;
FT_ListNode n1, n2;
if ( !memory || !out )
@ -766,9 +734,6 @@
FT_CALL( sdf_edge_new( memory, &left) );
FT_CALL( sdf_edge_new( memory, &right) );
if ( FT_QNEW( n1 ) || FT_QNEW( n2 ) )
goto Exit;
left->start_pos = cpos[0];
left->end_pos = cpos[2];
left->edge_type = SDF_EDGE_LINE;
@ -777,11 +742,9 @@
right->end_pos = cpos[4];
right->edge_type = SDF_EDGE_LINE;
n1->data = left;
n2->data = right;
FT_List_Add( out, n1 );
FT_List_Add( out, n2 );
left->next = right;
right->next = (*out);
*out = left;
Exit:
return error;
@ -796,12 +759,11 @@
split_sdf_cubic( FT_Memory memory,
FT_26D6_Vec* control_points,
FT_Int max_splits,
FT_List out )
SDF_Edge** out )
{
FT_Error error = FT_Err_Ok;
FT_26D6_Vec cpos[7];
SDF_Edge* left,* right;
FT_ListNode n1, n2;
if ( !memory || !out )
@ -842,9 +804,6 @@
FT_CALL( sdf_edge_new( memory, &left) );
FT_CALL( sdf_edge_new( memory, &right) );
if ( FT_QNEW( n1 ) || FT_QNEW( n2 ) )
goto Exit;
left->start_pos = cpos[0];
left->end_pos = cpos[3];
left->edge_type = SDF_EDGE_LINE;
@ -853,11 +812,9 @@
right->end_pos = cpos[6];
right->edge_type = SDF_EDGE_LINE;
n1->data = left;
n2->data = right;
FT_List_Add( out, n1 );
FT_List_Add( out, n2 );
left->next = right;
right->next = (*out);
*out = left;
Exit:
return error;
@ -870,11 +827,12 @@
static FT_Error
split_sdf_shape( SDF_Shape* shape )
{
FT_Error error = FT_Err_Ok;
FT_ListRec contours;
FT_ListRec edges;
FT_ListRec new_edges = { NULL, NULL };
FT_Memory memory = shape->memory;
FT_Error error = FT_Err_Ok;
FT_Memory memory = shape->memory;
SDF_Contour* contours;
SDF_Contour* new_contours = NULL;
if ( !shape )
@ -886,15 +844,17 @@
contours = shape->contours;
/* for each contour */
while ( contours.head )
while ( contours )
{
edges = ((SDF_Contour*)contours.head->data)->edges;
SDF_Edge* edges = contours->edges;
SDF_Edge* new_edges = NULL;
SDF_Contour* tempc;
/* for each edge */
while ( edges.head )
while ( edges )
{
SDF_Edge* edge = (SDF_Edge*)edges.head->data;
FT_ListNode node;
SDF_Edge* edge = edges;
SDF_Edge* temp;
switch ( edge->edge_type )
@ -903,18 +863,12 @@
{
/* Just create a duplicate edge in case */
/* it is a line. We can use the same edge */
/* but then `FT_List_Finalize' will have */
/* to be changed. */
FT_CALL( sdf_edge_new( memory, &temp ) );
if ( FT_QNEW( node ) )
goto Exit;
ft_memcpy( temp, edge, sizeof( *edge ) );
node->data = temp;
FT_List_Add( &new_edges, node );
node = NULL;
temp->next = new_edges;
new_edges = temp;
break;
}
case SDF_EDGE_CONIC:
@ -947,21 +901,25 @@
goto Exit;
}
edges.head = edges.head->next;
edges = edges->next;
}
edges = ((SDF_Contour*)contours.head->data)->edges;
/* add to the contours list */
FT_CALL( sdf_contour_new( memory, &tempc ) );
tempc->next = new_contours;
tempc->edges = new_edges;
new_contours = tempc;
new_edges = NULL;
/* Deallocate the previous list of edges and */
/* assign the newly created list to the contour. */
FT_List_Finalize( &edges, sdf_edge_destructor, memory, NULL );
((SDF_Contour*)contours.head->data)->edges = new_edges;
new_edges.head = NULL;
new_edges.tail = NULL;
/* deallocate the contour */
tempc = contours;
contours = contours->next;
contours.head = contours.head->next;
sdf_contour_done( memory, &tempc );
}
shape->contours = new_contours;
Exit:
return error;
}
@ -982,8 +940,8 @@
FT_UInt total_lines = 0;
FT_UInt total_conic = 0;
FT_UInt total_cubic = 0;
FT_ListRec contour_list;
SDF_Contour* contour_list;
if ( !shape )
{
@ -996,19 +954,19 @@
FT_TRACE5(( "-------------------------------------------------\n" ));
FT_TRACE5(( "[sdf] sdf_shape_dump:\n" ));
while ( contour_list.head != NULL )
while ( contour_list )
{
FT_UInt num_edges = 0;
FT_ListRec edge_list;
SDF_Contour* contour = (SDF_Contour*)contour_list.head->data;
SDF_Edge* edge_list;
SDF_Contour* contour = contour_list;
edge_list = contour->edges;
FT_TRACE5(( "Contour %d\n", num_contours ));
while ( edge_list.head != NULL )
while ( edge_list )
{
SDF_Edge* edge = (SDF_Edge*)edge_list.head->data;
SDF_Edge* edge = edge_list;
FT_TRACE5(( " Edge %d\n", num_edges ));
@ -1053,11 +1011,11 @@
num_edges++;
total_edges++;
edge_list.head = edge_list.head->next;
edge_list = edge_list->next;
}
num_contours++;
contour_list.head = contour_list.head->next;
contour_list = contour_list->next;
}
FT_TRACE5(( "\n" ));
@ -2353,7 +2311,7 @@
{
FT_Error error = FT_Err_Ok;
SDF_Signed_Distance min_dist = max_sdf;
FT_ListRec edge_list;
SDF_Edge* edge_list;
if ( !contour || !out )
@ -2365,13 +2323,13 @@
edge_list = contour->edges;
/* iterate through all the edges manually */
while ( edge_list.head ) {
while ( edge_list ) {
SDF_Signed_Distance current_dist = max_sdf;
FT_16D16 diff;
FT_CALL( sdf_edge_get_min_distance(
(SDF_Edge*)edge_list.head->data,
edge_list,
point, &current_dist ) );
if ( current_dist.distance >= 0 )
@ -2389,7 +2347,7 @@
FT_TRACE0(( "sdf_contour_get_min_distance: Overflowed.\n" ));
}
edge_list.head = edge_list.head->next;
edge_list = edge_list->next;
}
*out = min_dist;
@ -2472,7 +2430,7 @@
/* from this point to the entire shape. */
FT_26D6_Vec grid_point = zero_vector;
SDF_Signed_Distance min_dist = max_sdf;
FT_ListRec contour_list;
SDF_Contour* contour_list;
FT_UInt index;
FT_Short value;
@ -2490,18 +2448,18 @@
index = ( rows - y - 1 ) * width + x;
/* iterate through all the contours manually */
while ( contour_list.head ) {
while ( contour_list ) {
SDF_Signed_Distance current_dist = max_sdf;
FT_CALL( sdf_contour_get_min_distance(
(SDF_Contour*)contour_list.head->data,
contour_list,
grid_point, &current_dist ) );
if ( current_dist.distance < min_dist.distance )
min_dist = current_dist;
contour_list.head = contour_list.head->next;
contour_list = contour_list->next;
}
/* [OPTIMIZATION]: if (min_dist > sp_sq) then simply clamp */
@ -2558,14 +2516,14 @@
FT_UInt spread,
const FT_Bitmap* bitmap )
{
FT_Error error = FT_Err_Ok;
FT_Memory memory;
FT_Error error = FT_Err_Ok;
FT_Memory memory;
FT_UInt width, rows, i, j;
FT_ListRec contours; /* list of all contours */
FT_UInt sp_sq; /* max value to check */
FT_UInt width, rows, i, j;
FT_UInt sp_sq; /* max value to check */
FT_Short* buffer; /* the bitmap buffer */
SDF_Contour* contours; /* list of all contours */
FT_Short* buffer; /* the bitmap buffer */
/* This buffer has the same size in indices as the */
/* bitmap buffer. When we check a pixel position for */
@ -2613,20 +2571,18 @@
}
/* loop through all the contours */
while ( contours.head ) {
SDF_Contour* current_contour = (SDF_Contour*)contours.head->data;
FT_ListRec edges = current_contour->edges;
while ( contours ) {
SDF_Edge* edges = contours->edges;
/* loop through all the edges */
while ( edges.head )
while ( edges )
{
SDF_Edge* current_edge = (SDF_Edge*)edges.head->data;
FT_CBox cbox;
FT_Int x, y;
/* get the control box and increase by `spread' */
cbox = get_control_box( *current_edge );
cbox = get_control_box( *edges );
cbox.xMin = ( cbox.xMin - 63 ) / 64 - ( FT_Pos )spread;
cbox.xMax = ( cbox.xMax + 63 ) / 64 + ( FT_Pos )spread;
cbox.yMin = ( cbox.yMin - 63 ) / 64 - ( FT_Pos )spread;
@ -2656,7 +2612,7 @@
index = ( rows - y - 1 ) * width + x;
FT_CALL( sdf_edge_get_min_distance( current_edge,
FT_CALL( sdf_edge_get_min_distance( edges,
grid_point,
&dist ) );
@ -2681,10 +2637,10 @@
}
}
edges.head = edges.head->next;
edges = edges->next;
}
contours.head = contours.head->next;
contours = contours->next;
}
/* final pass */
@ -2872,7 +2828,7 @@
FT_CALL( sdf_outline_decompose( outline, shape ) );
FT_CALL( sdf_generate_subdivision( shape, sdf_params->spread,
FT_CALL( sdf_generate_subdivision( shape, sdf_params->spread,
sdf_params->root.target ) );
Exit: