sw_engine/stroke: enhanced the quality of the dash line corners.

Previously, the engine didn't properly cover the dash line corner styles
because it considered a new line to start at the corner.

This update modifies the logic to recognize curved lines
as a single line, including the corners.

There may still be some quality issues,
but it's an improvement over the previous version.

@Issue: https://github.com/thorvg/thorvg/issues/121
This commit is contained in:
Hermet Park 2023-10-05 00:57:39 +09:00 committed by Hermet Park
parent 7ccd287e59
commit 0cc6cfffef
3 changed files with 43 additions and 17 deletions

View file

@ -205,6 +205,7 @@ struct SwDashStroke
float* pattern = nullptr;
uint32_t cnt = 0;
bool curOpGap = false;
bool move = true;
};
struct SwShape

View file

@ -121,7 +121,10 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
if (dash.move) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
dash.move = false;
}
_outlineLineTo(*dash.outline, to, transform);
}
} else {
@ -131,7 +134,10 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
len -= dash.curLen;
_lineSplitAt(cur, dash.curLen, left, right);
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &left.pt1, transform);
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
_outlineMoveTo(*dash.outline, &left.pt1, transform);
dash.move = false;
}
_outlineLineTo(*dash.outline, &left.pt2, transform);
}
} else {
@ -142,11 +148,15 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
dash.curOpGap = !dash.curOpGap;
cur = right;
dash.ptCur = cur.pt1;
dash.move = true;
}
//leftovers
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
if (dash.move) {
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
dash.move = false;
}
_outlineLineTo(*dash.outline, &cur.pt2, transform);
}
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
@ -168,21 +178,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
if (len < dash.curLen) {
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
if (dash.move) {
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
dash.move = false;
}
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
}
} else {
bool begin = true; //starting with move_to
while (len > dash.curLen) {
Bezier left, right;
if (dash.curLen > 0) {
len -= dash.curLen;
bezSplitAt(cur, dash.curLen, left, right);
if (!dash.curOpGap) {
// leftovers from a previous command don't require moveTo
if (begin || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLT_EPSILON) {
_outlineMoveTo(*dash.outline, &left.start, transform);
begin = false;
dash.move = false;
}
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
}
@ -194,11 +205,15 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
dash.curOpGap = !dash.curOpGap;
cur = right;
dash.ptCur = right.start;
dash.move = true;
}
//leftovers
dash.curLen -= len;
if (!dash.curOpGap) {
_outlineMoveTo(*dash.outline, &cur.start, transform);
if (dash.move) {
_outlineMoveTo(*dash.outline, &cur.start, transform);
dash.move = false;
}
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
}
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
@ -212,6 +227,22 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
}
static void _dashClose(SwDashStroke& dash, const Matrix* transform)
{
_dashLineTo(dash, &dash.ptStart, transform);
}
static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts, const Matrix* transform)
{
dash.curIdx = offIdx % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx] - offset;
dash.curOpGap = offIdx % 2;
dash.ptStart = dash.ptCur = *pts;
dash.move = true;
}
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid)
{
const PathCommand* cmds = rshape->path.cmds.data;
@ -261,7 +292,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
trimmed = true;
//just a dasy style.
} else {
if (dash.cnt == 0) return nullptr;
}
@ -303,15 +333,11 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
while (cmdCnt-- > 0) {
switch (*cmds) {
case PathCommand::Close: {
_dashLineTo(dash, &dash.ptStart, transform);
_dashClose(dash, transform);
break;
}
case PathCommand::MoveTo: {
//reset the dash
dash.curIdx = offIdx % dash.cnt;
dash.curLen = dash.pattern[dash.curIdx] - offset;
dash.curOpGap = offIdx % 2;
dash.ptStart = dash.ptCur = *pts;
_dashMoveTo(dash, offIdx, offset, pts, transform);
++pts;
break;
}

View file

@ -689,7 +689,6 @@ static void _endSubPath(SwStroke& stroke)
//No specific corner processing is required if the turn is 0
if (turn != 0) {
//when we turn to the right, the inside is 0
int32_t inside = 0;