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
parent 12260198d1
commit 3d06d5552f
3 changed files with 43 additions and 17 deletions

View file

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

View file

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

View file

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