diff --git a/[GSoC]ChangeLog b/[GSoC]ChangeLog index dc64a3813..12194c69d 100644 --- a/[GSoC]ChangeLog +++ b/[GSoC]ChangeLog @@ -1,3 +1,15 @@ +2020-07-08 Anuj Verma + + [sdf] Added functions to subdivide a conic curve. + + * src/sdf/ftsdf.c (split_conic, split_sdf_conic): + These functions can be used to subdivide a + conic bezier curve into line segments which can + then be used to generate the SDF. + + * src/sdf/ftsdf.c (split_sdf_shape): Added function + to split a outline into a line segments. + 2020-07-06 Anuj Verma * [GSoC]ChangLog: Fixed typos. diff --git a/src/sdf/ftsdf.c b/src/sdf/ftsdf.c index b438c5730..0fa0bccf3 100644 --- a/src/sdf/ftsdf.c +++ b/src/sdf/ftsdf.c @@ -647,6 +647,197 @@ return cbox; } + /* The function is exactly same as the one */ + /* in the smooth renderer. It splits a conic */ + /* into two conic exactly half way at t = 0.5 */ + static void + split_conic( FT_26D6_Vec* base ) + { + FT_26D6 a, b; + + + base[4].x = base[2].x; + a = base[0].x + base[1].x; + b = base[1].x + base[2].x; + base[3].x = b / 2; + base[2].x = ( a + b ) / 4; + base[1].x = a / 2; + + base[4].y = base[2].y; + a = base[0].y + base[1].y; + b = base[1].y + base[2].y; + base[3].y = b / 2; + base[2].y = ( a + b ) / 4; + base[1].y = a / 2; + } + + /* the function splits a conic bezier curve */ + /* into a number of lines and adds them to */ + /* a list `out'. The function uses recursion */ + /* that is why a `max_splits' param is required */ + /* for stopping. */ + static FT_Error + split_sdf_conic( FT_Memory memory, + FT_26D6_Vec* control_points, + FT_Int max_splits, + FT_List out ) + { + FT_Error error = FT_Err_Ok; + FT_26D6_Vec cpos[5]; + SDF_Edge* left,* right; + FT_ListNode n1, n2; + + + if ( !memory || !out ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + /* split the conic */ + cpos[0] = control_points[0]; + cpos[1] = control_points[1]; + cpos[2] = control_points[2]; + + split_conic( cpos ); + + /* If max number of splits is done */ + /* then stop and add the lines to */ + /* the list. */ + if ( max_splits <= 2 ) + goto Append; + + /* If not max splits then keep splitting */ + FT_CALL( split_sdf_conic( memory, &cpos[0], max_splits / 2, out ) ); + FT_CALL( split_sdf_conic( memory, &cpos[2], max_splits / 2, out ) ); + + /* [NOTE]: This is not an efficient way of */ + /* splitting the curve. Check the deviation */ + /* instead and stop if the deviation is less */ + /* than a pixel. */ + + goto Exit; + + Append: + + /* Allocation and add the lines to the list. */ + + 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; + + right->start_pos = cpos[2]; + 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 ); + + Exit: + return error; + } + + /* This function subdivide and entire shape */ + /* into line segment such that the it does */ + /* look visually different than the original */ + /* curve. */ + 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; + + + if ( !shape ) + { + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + contours = shape->contours; + + /* for each contour */ + while ( contours.head ) + { + edges = ((SDF_Contour*)contours.head->data)->edges; + + /* for each edge */ + while ( edges.head ) + { + SDF_Edge* edge = (SDF_Edge*)edges.head->data; + FT_ListNode node; + SDF_Edge* temp; + + switch ( edge->edge_type ) + { + case SDF_EDGE_LINE: + { + /* 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; + break; + } + case SDF_EDGE_CONIC: + { + /* Subdivide the curve and add to the list. */ + FT_26D6_Vec ctrls[3]; + + + ctrls[0] = edge->start_pos; + ctrls[1] = edge->control_a; + ctrls[2] = edge->end_pos; + error = split_sdf_conic( memory, ctrls, 16, &new_edges ); + break; + } + case SDF_EDGE_CUBIC: + { + /* [TODO] */ + } + default: + error = FT_THROW( Invalid_Argument ); + goto Exit; + } + + edges.head = edges.head->next; + } + + /* 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; + + contours.head = contours.head->next; + } + + Exit: + return error; + } + /************************************************************************** * * for debugging