From: Subhransu Mohanty Date: Tue, 30 Jun 2020 03:30:25 +0000 (+0900) Subject: freetype: cherry pick optimization patches from freetype. X-Git-Tag: submit/tizen/20200713.050659~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=25928ef55f75e694d261ba41c13d9ad6d9189ad0;p=platform%2Fcore%2Fuifw%2Flottie-player.git freetype: cherry pick optimization patches from freetype. --- diff --git a/src/vector/freetype/v_ft_raster.cpp b/src/vector/freetype/v_ft_raster.cpp index 51cb1f2..e48ad1b 100644 --- a/src/vector/freetype/v_ft_raster.cpp +++ b/src/vector/freetype/v_ft_raster.cpp @@ -749,100 +749,60 @@ static void gray_split_cubic(SW_FT_Vector* base) base[3].y = ( a + c ) >> 3; } -static void gray_render_cubic(RAS_ARG_ const SW_FT_Vector* control1, + +static void +gray_render_cubic(RAS_ARG_ const SW_FT_Vector* control1, const SW_FT_Vector* control2, const SW_FT_Vector* to) { - SW_FT_Vector* arc; - TPos min, max, y; - - arc = ras.bez_stack; - arc[0].x = UPSCALE(to->x); - arc[0].y = UPSCALE(to->y); - arc[1].x = UPSCALE(control2->x); - arc[1].y = UPSCALE(control2->y); - arc[2].x = UPSCALE(control1->x); - arc[2].y = UPSCALE(control1->y); + SW_FT_Vector* arc = ras.bez_stack; + + arc[0].x = UPSCALE( to->x ); + arc[0].y = UPSCALE( to->y ); + arc[1].x = UPSCALE( control2->x ); + arc[1].y = UPSCALE( control2->y ); + arc[2].x = UPSCALE( control1->x ); + arc[2].y = UPSCALE( control1->y ); arc[3].x = ras.x; arc[3].y = ras.y; - /* Short-cut the arc that crosses the current band. */ - min = max = arc[0].y; - - y = arc[1].y; - if (y < min) min = y; - if (y > max) max = y; - - y = arc[2].y; - if (y < min) min = y; - if (y > max) max = y; - - y = arc[3].y; - if (y < min) min = y; - if (y > max) max = y; - - if (TRUNC(min) >= ras.max_ey || TRUNC(max) < ras.min_ey) goto Draw; - - for (;;) { - /* Decide whether to split or draw. See `Rapid Termination */ - /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ - /* F. Hain, at */ - /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf - */ - - { - TPos dx, dy, dx_, dy_; - TPos dx1, dy1, dx2, dy2; - TPos L, s, s_limit; - - /* dx and dy are x and y components of the P0-P3 chord vector. */ - dx = dx_ = arc[3].x - arc[0].x; - dy = dy_ = arc[3].y - arc[0].y; - - L = SW_FT_HYPOT(dx_, dy_); - - /* Avoid possible arithmetic overflow below by splitting. */ - if (L > 32767) goto Split; - - /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ - s_limit = L * (TPos)(ONE_PIXEL / 6); - - /* s is L * the perpendicular distance from P1 to the line P0-P3. */ - dx1 = arc[1].x - arc[0].x; - dy1 = arc[1].y - arc[0].y; - s = SW_FT_ABS(dy * dx1 - dx * dy1); - - if (s > s_limit) goto Split; - - /* s is L * the perpendicular distance from P2 to the line P0-P3. */ - dx2 = arc[2].x - arc[0].x; - dy2 = arc[2].y - arc[0].y; - s = SW_FT_ABS(dy * dx2 - dx * dy2); - - if (s > s_limit) goto Split; + /* short-cut the arc that crosses the current band */ + if ( ( TRUNC( arc[0].y ) >= ras.max_ey && + TRUNC( arc[1].y ) >= ras.max_ey && + TRUNC( arc[2].y ) >= ras.max_ey && + TRUNC( arc[3].y ) >= ras.max_ey ) || + ( TRUNC( arc[0].y ) < ras.min_ey && + TRUNC( arc[1].y ) < ras.min_ey && + TRUNC( arc[2].y ) < ras.min_ey && + TRUNC( arc[3].y ) < ras.min_ey ) ) + { + ras.x = arc[0].x; + ras.y = arc[0].y; + return; + } - /* Split super curvy segments where the off points are so far - from the chord that the angles P0-P1-P3 or P0-P2-P3 become - acute as detected by appropriate dot products. */ - if (dx1 * (dx1 - dx) + dy1 * (dy1 - dy) > 0 || - dx2 * (dx2 - dx) + dy2 * (dy2 - dy) > 0) - goto Split; + for (;;) + { + /* with each split, control points quickly converge towards */ + /* chord trisection points and the vanishing distances below */ + /* indicate when the segment is flat enough to draw */ + if ( SW_FT_ABS( 2 * arc[0].x - 3 * arc[1].x + arc[3].x ) > ONE_PIXEL / 2 || + SW_FT_ABS( 2 * arc[0].y - 3 * arc[1].y + arc[3].y ) > ONE_PIXEL / 2 || + SW_FT_ABS( arc[0].x - 3 * arc[2].x + 2 * arc[3].x ) > ONE_PIXEL / 2 || + SW_FT_ABS( arc[0].y - 3 * arc[2].y + 2 * arc[3].y ) > ONE_PIXEL / 2 ) + goto Split; + + gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); + + if ( arc == ras.bez_stack ) + return; - /* No reason to split. */ - goto Draw; - } + arc -= 3; + continue; Split: - gray_split_cubic(arc); - arc += 3; - continue; - - Draw: - gray_render_line(RAS_VAR_ arc[0].x, arc[0].y); - - if (arc == ras.bez_stack) return; - - arc -= 3; + gray_split_cubic( arc ); + arc += 3; } } diff --git a/src/vector/freetype/v_ft_stroker.cpp b/src/vector/freetype/v_ft_stroker.cpp index c7307eb..3160f84 100644 --- a/src/vector/freetype/v_ft_stroker.cpp +++ b/src/vector/freetype/v_ft_stroker.cpp @@ -481,65 +481,60 @@ static SW_FT_Error ft_stroke_border_cubicto(SW_FT_StrokeBorder border, #define SW_FT_ARC_CUBIC_ANGLE (SW_FT_ANGLE_PI / 2) -static SW_FT_Error ft_stroke_border_arcto(SW_FT_StrokeBorder border, - SW_FT_Vector* center, - SW_FT_Fixed radius, - SW_FT_Angle angle_start, - SW_FT_Angle angle_diff) + +static SW_FT_Error +ft_stroke_border_arcto( SW_FT_StrokeBorder border, + SW_FT_Vector* center, + SW_FT_Fixed radius, + SW_FT_Angle angle_start, + SW_FT_Angle angle_diff ) { - SW_FT_Angle total, angle, step, rotate, next, theta; - SW_FT_Vector a, b, a2, b2; - SW_FT_Fixed length; - SW_FT_Error error = 0; - - /* compute start point */ - SW_FT_Vector_From_Polar(&a, radius, angle_start); - a.x += center->x; - a.y += center->y; - - total = angle_diff; - angle = angle_start; - rotate = (angle_diff >= 0) ? SW_FT_ANGLE_PI2 : -SW_FT_ANGLE_PI2; - - while (total != 0) { - step = total; - if (step > SW_FT_ARC_CUBIC_ANGLE) - step = SW_FT_ARC_CUBIC_ANGLE; - - else if (step < -SW_FT_ARC_CUBIC_ANGLE) - step = -SW_FT_ARC_CUBIC_ANGLE; - - next = angle + step; - theta = step; - if (theta < 0) theta = -theta; - - theta >>= 1; - - /* compute end point */ - SW_FT_Vector_From_Polar(&b, radius, next); - b.x += center->x; - b.y += center->y; - - /* compute first and second control points */ - length = SW_FT_MulDiv(radius, SW_FT_Sin(theta) * 4, - (0x10000L + SW_FT_Cos(theta)) * 3); - - SW_FT_Vector_From_Polar(&a2, length, angle + rotate); - a2.x += a.x; - a2.y += a.y; - - SW_FT_Vector_From_Polar(&b2, length, next - rotate); - b2.x += b.x; - b2.y += b.y; - - /* add cubic arc */ - error = ft_stroke_border_cubicto(border, &a2, &b2, &b); - if (error) break; - - /* process the rest of the arc ?? */ - a = b; - total -= step; - angle = next; + SW_FT_Fixed coef; + SW_FT_Vector a0, a1, a2, a3; + SW_FT_Int i, arcs = 1; + SW_FT_Error error = 0; + + + /* number of cubic arcs to draw */ + while ( angle_diff > SW_FT_ARC_CUBIC_ANGLE * arcs || + -angle_diff > SW_FT_ARC_CUBIC_ANGLE * arcs ) + arcs++; + + /* control tangents */ + coef = SW_FT_Tan( angle_diff / ( 4 * arcs ) ); + coef += coef / 3; + + /* compute start and first control point */ + SW_FT_Vector_From_Polar( &a0, radius, angle_start ); + a1.x = SW_FT_MulFix( -a0.y, coef ); + a1.y = SW_FT_MulFix( a0.x, coef ); + + a0.x += center->x; + a0.y += center->y; + a1.x += a0.x; + a1.y += a0.y; + + for ( i = 1; i <= arcs; i++ ) + { + /* compute end and second control point */ + SW_FT_Vector_From_Polar( &a3, radius, + angle_start + i * angle_diff / arcs ); + a2.x = SW_FT_MulFix( a3.y, coef ); + a2.y = SW_FT_MulFix( -a3.x, coef ); + + a3.x += center->x; + a3.y += center->y; + a2.x += a3.x; + a2.y += a3.y; + + /* add cubic arc */ + error = ft_stroke_border_cubicto( border, &a1, &a2, &a3 ); + if ( error ) + break; + + /* a0 = a3; */ + a1.x = a3.x - a2.x + a3.x; + a1.y = a3.y - a2.y + a3.y; } return error; @@ -782,61 +777,56 @@ static SW_FT_Error ft_stroker_arcto(SW_FT_Stroker stroker, SW_FT_Int side) } /* add a cap at the end of an opened path */ -static SW_FT_Error ft_stroker_cap(SW_FT_Stroker stroker, SW_FT_Angle angle, - SW_FT_Int side) +static SW_FT_Error +ft_stroker_cap(SW_FT_Stroker stroker, + SW_FT_Angle angle, + SW_FT_Int side) { SW_FT_Error error = 0; - if (stroker->line_cap == SW_FT_STROKER_LINECAP_ROUND) { + if (stroker->line_cap == SW_FT_STROKER_LINECAP_ROUND) + { /* add a round cap */ stroker->angle_in = angle; stroker->angle_out = angle + SW_FT_ANGLE_PI; error = ft_stroker_arcto(stroker, side); - } else if (stroker->line_cap == SW_FT_STROKER_LINECAP_SQUARE) { - /* add a square cap */ - SW_FT_Vector delta, delta2; - SW_FT_Angle rotate = SW_FT_SIDE_TO_ROTATE(side); - SW_FT_Fixed radius = stroker->radius; - SW_FT_StrokeBorder border = stroker->borders + side; - - SW_FT_Vector_From_Polar(&delta2, radius, angle + rotate); - SW_FT_Vector_From_Polar(&delta, radius, angle); - - delta.x += stroker->center.x + delta2.x; - delta.y += stroker->center.y + delta2.y; - - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; - - SW_FT_Vector_From_Polar(&delta2, radius, angle - rotate); - SW_FT_Vector_From_Polar(&delta, radius, angle); - - delta.x += delta2.x + stroker->center.x; - delta.y += delta2.y + stroker->center.y; - - error = ft_stroke_border_lineto(border, &delta, FALSE); - } else if (stroker->line_cap == SW_FT_STROKER_LINECAP_BUTT) { - /* add a butt ending */ - SW_FT_Vector delta; - SW_FT_Angle rotate = SW_FT_SIDE_TO_ROTATE(side); - SW_FT_Fixed radius = stroker->radius; - SW_FT_StrokeBorder border = stroker->borders + side; + } + else + { + /* add a square or butt cap */ + SW_FT_Vector middle, delta; + SW_FT_Fixed radius = stroker->radius; + SW_FT_StrokeBorder border = stroker->borders + side; - SW_FT_Vector_From_Polar(&delta, radius, angle + rotate); + /* compute middle point and first angle point */ + SW_FT_Vector_From_Polar( &middle, radius, angle ); + delta.x = side ? middle.y : -middle.y; + delta.y = side ? -middle.x : middle.x; - delta.x += stroker->center.x; - delta.y += stroker->center.y; + if ( stroker->line_cap == SW_FT_STROKER_LINECAP_SQUARE ) + { + middle.x += stroker->center.x; + middle.y += stroker->center.y; + } + else /* SW_FT_STROKER_LINECAP_BUTT */ + { + middle.x = stroker->center.x; + middle.y = stroker->center.y; + } - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; + delta.x += middle.x; + delta.y += middle.y; - SW_FT_Vector_From_Polar(&delta, radius, angle - rotate); + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; - delta.x += stroker->center.x; - delta.y += stroker->center.y; + /* compute second angle point */ + delta.x = middle.x - delta.x + middle.x; + delta.y = middle.y - delta.y + middle.y; - error = ft_stroke_border_lineto(border, &delta, FALSE); + error = ft_stroke_border_lineto( border, &delta, FALSE ); } Exit: @@ -849,8 +839,8 @@ static SW_FT_Error ft_stroker_inside(SW_FT_Stroker stroker, SW_FT_Int side, { SW_FT_StrokeBorder border = stroker->borders + side; SW_FT_Angle phi, theta, rotate; - SW_FT_Fixed length, thcos; - SW_FT_Vector delta; + SW_FT_Fixed length; + SW_FT_Vector sigma, delta; SW_FT_Error error = 0; SW_FT_Bool intersect; /* use intersection of lines? */ @@ -860,15 +850,21 @@ static SW_FT_Error ft_stroker_inside(SW_FT_Stroker stroker, SW_FT_Int side, /* Only intersect borders if between two lineto's and both */ /* lines are long enough (line_length is zero for curves). */ - if (!border->movable || line_length == 0) + if (!border->movable || line_length == 0 || + theta > 0x59C000 || theta < -0x59C000 ) intersect = FALSE; else { - /* compute minimum required length of lines */ - SW_FT_Fixed min_length = - ft_pos_abs(SW_FT_MulFix(stroker->radius, SW_FT_Tan(theta))); + /* compute minimum required length of lines */ + SW_FT_Fixed min_length; - intersect = SW_FT_BOOL(stroker->line_length >= min_length && - line_length >= min_length); + + SW_FT_Vector_Unit( &sigma, theta ); + min_length = + ft_pos_abs( SW_FT_MulDiv( stroker->radius, sigma.y, sigma.x ) ); + + intersect = SW_FT_BOOL( min_length && + stroker->line_length >= min_length && + line_length >= min_length ); } if (!intersect) { @@ -880,15 +876,13 @@ static SW_FT_Error ft_stroker_inside(SW_FT_Stroker stroker, SW_FT_Int side, border->movable = FALSE; } else { /* compute median angle */ - phi = stroker->angle_in + theta; + phi = stroker->angle_in + theta + rotate; - thcos = SW_FT_Cos(theta); + length = SW_FT_DivFix( stroker->radius, sigma.x ); - length = SW_FT_DivFix(stroker->radius, thcos); - - SW_FT_Vector_From_Polar(&delta, length, phi + rotate); - delta.x += stroker->center.x; - delta.y += stroker->center.y; + SW_FT_Vector_From_Polar( &delta, length, phi ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; } error = ft_stroke_border_lineto(border, &delta, FALSE); @@ -896,137 +890,156 @@ static SW_FT_Error ft_stroker_inside(SW_FT_Stroker stroker, SW_FT_Int side, return error; } -/* process an outside corner, i.e. compute bevel/miter/round */ -static SW_FT_Error ft_stroker_outside(SW_FT_Stroker stroker, SW_FT_Int side, - SW_FT_Fixed line_length) + /* process an outside corner, i.e. compute bevel/miter/round */ +static SW_FT_Error +ft_stroker_outside( SW_FT_Stroker stroker, + SW_FT_Int side, + SW_FT_Fixed line_length ) { - SW_FT_StrokeBorder border = stroker->borders + side; - SW_FT_Error error; - SW_FT_Angle rotate; + SW_FT_StrokeBorder border = stroker->borders + side; + SW_FT_Error error; + SW_FT_Angle rotate; - if (stroker->line_join == SW_FT_STROKER_LINEJOIN_ROUND) - error = ft_stroker_arcto(stroker, side); - else { - /* this is a mitered (pointed) or beveled (truncated) corner */ - SW_FT_Fixed sigma = 0, radius = stroker->radius; - SW_FT_Angle theta = 0, phi = 0; - SW_FT_Fixed thcos = 0; - SW_FT_Bool bevel, fixed_bevel; - rotate = SW_FT_SIDE_TO_ROTATE(side); + if ( stroker->line_join == SW_FT_STROKER_LINEJOIN_ROUND ) + error = ft_stroker_arcto( stroker, side ); + else + { + /* this is a mitered (pointed) or beveled (truncated) corner */ + SW_FT_Fixed radius = stroker->radius; + SW_FT_Vector sigma; + SW_FT_Angle theta = 0, phi = 0; + SW_FT_Bool bevel, fixed_bevel; - bevel = SW_FT_BOOL(stroker->line_join == SW_FT_STROKER_LINEJOIN_BEVEL); - fixed_bevel = SW_FT_BOOL(stroker->line_join != - SW_FT_STROKER_LINEJOIN_MITER_VARIABLE); + rotate = SW_FT_SIDE_TO_ROTATE( side ); - if (!bevel) { - theta = SW_FT_Angle_Diff(stroker->angle_in, stroker->angle_out); + bevel = + SW_FT_BOOL( stroker->line_join == SW_FT_STROKER_LINEJOIN_BEVEL ); - if (theta == SW_FT_ANGLE_PI) { - theta = rotate; - phi = stroker->angle_in; - } else { - theta /= 2; - phi = stroker->angle_in + theta + rotate; - } + fixed_bevel = + SW_FT_BOOL( stroker->line_join != SW_FT_STROKER_LINEJOIN_MITER_VARIABLE ); - thcos = SW_FT_Cos(theta); - sigma = SW_FT_MulFix(stroker->miter_limit, thcos); + /* check miter limit first */ + if ( !bevel ) + { + theta = SW_FT_Angle_Diff( stroker->angle_in, stroker->angle_out ) / 2; - /* is miter limit exceeded? */ - if (sigma < 0x10000L) { - /* don't create variable bevels for very small deviations; */ - /* SW_FT_Sin(x) = 0 for x <= 57 */ - if (fixed_bevel || ft_pos_abs(theta) > 57) bevel = TRUE; - } + if ( theta == SW_FT_ANGLE_PI2 ) + theta = -rotate; + + phi = stroker->angle_in + theta + rotate; + + SW_FT_Vector_From_Polar( &sigma, stroker->miter_limit, theta ); + + /* is miter limit exceeded? */ + if ( sigma.x < 0x10000L ) + { + /* don't create variable bevels for very small deviations; */ + /* FT_Sin(x) = 0 for x <= 57 */ + if ( fixed_bevel || ft_pos_abs( theta ) > 57 ) + bevel = TRUE; } + } - if (bevel) /* this is a bevel (broken angle) */ + if ( bevel ) /* this is a bevel (broken angle) */ + { + if ( fixed_bevel ) { - if (fixed_bevel) { - /* the outer corners are simply joined together */ - SW_FT_Vector delta; - - /* add bevel */ - SW_FT_Vector_From_Polar(&delta, radius, - stroker->angle_out + rotate); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - - border->movable = FALSE; - error = ft_stroke_border_lineto(border, &delta, FALSE); - } else /* variable bevel */ - { - /* the miter is truncated */ - SW_FT_Vector middle, delta; - SW_FT_Fixed length; + /* the outer corners are simply joined together */ + SW_FT_Vector delta; - /* compute middle point */ - SW_FT_Vector_From_Polar( - &middle, SW_FT_MulFix(radius, stroker->miter_limit), phi); - middle.x += stroker->center.x; - middle.y += stroker->center.y; - /* compute first angle point */ - length = SW_FT_MulDiv(radius, 0x10000L - sigma, - ft_pos_abs(SW_FT_Sin(theta))); + /* add bevel */ + SW_FT_Vector_From_Polar( &delta, + radius, + stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; - SW_FT_Vector_From_Polar(&delta, length, phi + rotate); - delta.x += middle.x; - delta.y += middle.y; + border->movable = FALSE; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + else /* variable bevel or clipped miter */ + { + /* the miter is truncated */ + SW_FT_Vector middle, delta; + SW_FT_Fixed coef; - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; - /* compute second angle point */ - SW_FT_Vector_From_Polar(&delta, length, phi - rotate); - delta.x += middle.x; - delta.y += middle.y; + /* compute middle point and first angle point */ + SW_FT_Vector_From_Polar( &middle, + SW_FT_MulFix( radius, stroker->miter_limit ), + phi ); - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; + coef = SW_FT_DivFix( 0x10000L - sigma.x, sigma.y ); + delta.x = SW_FT_MulFix( middle.y, coef ); + delta.y = SW_FT_MulFix( -middle.x, coef ); - /* finally, add an end point; only needed if not lineto */ - /* (line_length is zero for curves) */ - if (line_length == 0) { - SW_FT_Vector_From_Polar(&delta, radius, - stroker->angle_out + rotate); + middle.x += stroker->center.x; + middle.y += stroker->center.y; + delta.x += middle.x; + delta.y += middle.y; - delta.x += stroker->center.x; - delta.y += stroker->center.y; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; - error = ft_stroke_border_lineto(border, &delta, FALSE); - } - } - } else /* this is a miter (intersection) */ - { - SW_FT_Fixed length; - SW_FT_Vector delta; + /* compute second angle point */ + delta.x = middle.x - delta.x + middle.x; + delta.y = middle.y - delta.y + middle.y; - length = SW_FT_DivFix(stroker->radius, thcos); + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* finally, add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + SW_FT_Vector_From_Polar( &delta, + radius, + stroker->angle_out + rotate ); - SW_FT_Vector_From_Polar(&delta, length, phi); delta.x += stroker->center.x; delta.y += stroker->center.y; - error = ft_stroke_border_lineto(border, &delta, FALSE); - if (error) goto Exit; + error = ft_stroke_border_lineto( border, &delta, FALSE ); + } + } + } + else /* this is a miter (intersection) */ + { + SW_FT_Fixed length; + SW_FT_Vector delta; - /* now add an end point; only needed if not lineto */ - /* (line_length is zero for curves) */ - if (line_length == 0) { - SW_FT_Vector_From_Polar(&delta, stroker->radius, - stroker->angle_out + rotate); - delta.x += stroker->center.x; - delta.y += stroker->center.y; - error = ft_stroke_border_lineto(border, &delta, FALSE); - } + length = SW_FT_MulDiv( stroker->radius, stroker->miter_limit, sigma.x ); + + SW_FT_Vector_From_Polar( &delta, length, phi ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); + if ( error ) + goto Exit; + + /* now add an end point; only needed if not lineto */ + /* (line_length is zero for curves) */ + if ( line_length == 0 ) + { + SW_FT_Vector_From_Polar( &delta, + stroker->radius, + stroker->angle_out + rotate ); + delta.x += stroker->center.x; + delta.y += stroker->center.y; + + error = ft_stroke_border_lineto( border, &delta, FALSE ); } + } } -Exit: + Exit: return error; }