mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 21:53:41 +00:00
sw_engine: fix trimming
For the 'simultaneous' trimming option, the maximum stroke length from all subpaths was determined and used to scale the trim of each subpath. As a result, if the subpaths had different lengths, this scaling was incorrect. Now, the length is determined separately for each subpath and is used to scale the trimming of its stroke. @Issue: https://github.com/thorvg/thorvg/issues/2335
This commit is contained in:
parent
97a1aa136a
commit
07e386007c
1 changed files with 100 additions and 94 deletions
|
@ -245,7 +245,86 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, float length, SwMpool* mpool, unsigned tid)
|
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
|
||||||
|
{
|
||||||
|
auto begin = length * rshape->stroke->trim.begin;
|
||||||
|
auto end = length * rshape->stroke->trim.end;
|
||||||
|
|
||||||
|
//default
|
||||||
|
if (end > begin) {
|
||||||
|
if (begin > 0.0f) dash->cnt = 4;
|
||||||
|
else dash->cnt = 2;
|
||||||
|
//looping
|
||||||
|
} else dash->cnt = 3;
|
||||||
|
|
||||||
|
if (dash->cnt == 2) {
|
||||||
|
dash->pattern[0] = end - begin;
|
||||||
|
dash->pattern[1] = length - (end - begin);
|
||||||
|
} else if (dash->cnt == 3) {
|
||||||
|
dash->pattern[0] = end;
|
||||||
|
dash->pattern[1] = (begin - end);
|
||||||
|
dash->pattern[2] = length - begin;
|
||||||
|
} else {
|
||||||
|
dash->pattern[0] = 0; //zero dash to start with a space.
|
||||||
|
dash->pattern[1] = begin;
|
||||||
|
dash->pattern[2] = end - begin;
|
||||||
|
dash->pattern[3] = length - end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
|
||||||
|
{
|
||||||
|
const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
|
||||||
|
auto cmdCnt = rshape->path.cmds.count - shiftCmds;
|
||||||
|
const Point* pts = rshape->path.pts.data + shiftPts;
|
||||||
|
auto ptsCnt = rshape->path.pts.count - shiftPts;
|
||||||
|
|
||||||
|
//No actual shape data
|
||||||
|
if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
|
||||||
|
|
||||||
|
const Point* close = nullptr;
|
||||||
|
auto length = 0.0f;
|
||||||
|
|
||||||
|
//must begin with moveTo
|
||||||
|
if (cmds[0] == PathCommand::MoveTo) {
|
||||||
|
close = pts;
|
||||||
|
cmds++;
|
||||||
|
pts++;
|
||||||
|
cmdCnt--;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cmdCnt-- > 0) {
|
||||||
|
switch (*cmds) {
|
||||||
|
case PathCommand::Close: {
|
||||||
|
length += mathLength(pts - 1, close);
|
||||||
|
if (subpath) return length;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::MoveTo: {
|
||||||
|
if (subpath) return length;
|
||||||
|
close = pts;
|
||||||
|
++pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::LineTo: {
|
||||||
|
length += mathLength(pts - 1, pts);
|
||||||
|
++pts;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PathCommand::CubicTo: {
|
||||||
|
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
|
||||||
|
pts += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
++cmds;
|
||||||
|
}
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
|
||||||
{
|
{
|
||||||
const PathCommand* cmds = rshape->path.cmds.data;
|
const PathCommand* cmds = rshape->path.cmds.data;
|
||||||
auto cmdCnt = rshape->path.cmds.count;
|
auto cmdCnt = rshape->path.cmds.count;
|
||||||
|
@ -255,49 +334,23 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
//No actual shape data
|
//No actual shape data
|
||||||
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
||||||
|
|
||||||
|
auto startPts = pts;
|
||||||
|
auto startCmds = cmds;
|
||||||
|
|
||||||
SwDashStroke dash;
|
SwDashStroke dash;
|
||||||
auto offset = 0.0f;
|
auto offset = 0.0f;
|
||||||
auto trimmed = false;
|
|
||||||
|
|
||||||
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
||||||
|
auto simultaneous = rshape->stroke->trim.simultaneous;
|
||||||
|
|
||||||
//dash by trimming.
|
if (dash.cnt == 0) {
|
||||||
if (length > 0.0f && dash.cnt == 0) {
|
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
|
||||||
auto begin = length * rshape->stroke->trim.begin;
|
else return nullptr;
|
||||||
auto end = length * rshape->stroke->trim.end;
|
|
||||||
|
|
||||||
//TODO: mix trimming + dash style
|
|
||||||
|
|
||||||
//default
|
|
||||||
if (end > begin) {
|
|
||||||
if (begin > 0.0f) dash.cnt += 4;
|
|
||||||
else dash.cnt += 2;
|
|
||||||
//looping
|
|
||||||
} else dash.cnt += 3;
|
|
||||||
|
|
||||||
dash.pattern = (float*)malloc(sizeof(float) * dash.cnt);
|
|
||||||
|
|
||||||
if (dash.cnt == 2) {
|
|
||||||
dash.pattern[0] = end - begin;
|
|
||||||
dash.pattern[1] = length - (end - begin);
|
|
||||||
} else if (dash.cnt == 3) {
|
|
||||||
dash.pattern[0] = end;
|
|
||||||
dash.pattern[1] = (begin - end);
|
|
||||||
dash.pattern[2] = length - begin;
|
|
||||||
} else {
|
} else {
|
||||||
dash.pattern[0] = 0; //zero dash to start with a space.
|
//TODO: handle dash + trim - for now trimming ignoring is forced
|
||||||
dash.pattern[1] = begin;
|
trimmed = false;
|
||||||
dash.pattern[2] = end - begin;
|
|
||||||
dash.pattern[3] = length - end;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
trimmed = true;
|
//offset
|
||||||
//just a dasy style.
|
|
||||||
} else {
|
|
||||||
if (dash.cnt == 0) return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//offset?
|
|
||||||
auto patternLength = 0.0f;
|
auto patternLength = 0.0f;
|
||||||
uint32_t offIdx = 0;
|
uint32_t offIdx = 0;
|
||||||
if (!mathZero(offset)) {
|
if (!mathZero(offset)) {
|
||||||
|
@ -319,6 +372,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
|
|
||||||
//must begin with moveTo
|
//must begin with moveTo
|
||||||
if (cmds[0] == PathCommand::MoveTo) {
|
if (cmds[0] == PathCommand::MoveTo) {
|
||||||
|
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
|
||||||
_dashMoveTo(dash, offIdx, offset, pts);
|
_dashMoveTo(dash, offIdx, offset, pts);
|
||||||
cmds++;
|
cmds++;
|
||||||
pts++;
|
pts++;
|
||||||
|
@ -331,9 +385,12 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
if (rshape->stroke->trim.simultaneous) _dashMoveTo(dash, offIdx, offset, pts);
|
if (trimmed) {
|
||||||
else _dashMoveTo(dash, pts);
|
if (simultaneous) {
|
||||||
|
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
|
||||||
|
_dashMoveTo(dash, offIdx, offset, pts);
|
||||||
|
} else _dashMoveTo(dash, pts);
|
||||||
|
} else _dashMoveTo(dash, offIdx, offset, pts);
|
||||||
++pts;
|
++pts;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -359,56 +416,6 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static float _outlineLength(const RenderShape* rshape)
|
|
||||||
{
|
|
||||||
const PathCommand* cmds = rshape->path.cmds.data;
|
|
||||||
auto cmdCnt = rshape->path.cmds.count;
|
|
||||||
const Point* pts = rshape->path.pts.data;
|
|
||||||
auto ptsCnt = rshape->path.pts.count;
|
|
||||||
|
|
||||||
//No actual shape data
|
|
||||||
if (cmdCnt == 0 || ptsCnt == 0) return 0.0f;
|
|
||||||
|
|
||||||
const Point* close = nullptr;
|
|
||||||
auto length = 0.0f;
|
|
||||||
auto slength = -1.0f;
|
|
||||||
auto simultaneous = rshape->stroke->trim.simultaneous;
|
|
||||||
|
|
||||||
//Compute the whole length
|
|
||||||
while (cmdCnt-- > 0) {
|
|
||||||
switch (*cmds) {
|
|
||||||
case PathCommand::Close: {
|
|
||||||
length += mathLength(pts - 1, close);
|
|
||||||
//retrieve the max length of the shape if the simultaneous mode.
|
|
||||||
if (simultaneous) {
|
|
||||||
if (slength < length) slength = length;
|
|
||||||
length = 0.0f;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PathCommand::MoveTo: {
|
|
||||||
close = pts;
|
|
||||||
++pts;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PathCommand::LineTo: {
|
|
||||||
length += mathLength(pts - 1, pts);
|
|
||||||
++pts;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PathCommand::CubicTo: {
|
|
||||||
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
|
|
||||||
pts += 3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++cmds;
|
|
||||||
}
|
|
||||||
if (simultaneous && slength > length) return slength;
|
|
||||||
else return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static bool _axisAlignedRect(const SwOutline* outline)
|
static bool _axisAlignedRect(const SwOutline* outline)
|
||||||
{
|
{
|
||||||
//Fast Track: axis-aligned rectangle?
|
//Fast Track: axis-aligned rectangle?
|
||||||
|
@ -585,11 +592,10 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix*
|
||||||
auto dashStroking = false;
|
auto dashStroking = false;
|
||||||
auto ret = true;
|
auto ret = true;
|
||||||
|
|
||||||
auto length = rshape->strokeTrim() ? _outlineLength(rshape) : 0.0f;
|
|
||||||
|
|
||||||
//Dash style (+trimming)
|
//Dash style (+trimming)
|
||||||
if (rshape->stroke->dashCnt > 0 || length > 0) {
|
auto trimmed = rshape->strokeTrim();
|
||||||
shapeOutline = _genDashOutline(rshape, transform, length, mpool, tid);
|
if (rshape->stroke->dashCnt > 0 || trimmed) {
|
||||||
|
shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
|
||||||
if (!shapeOutline) return false;
|
if (!shapeOutline) return false;
|
||||||
dashStroking = true;
|
dashStroking = true;
|
||||||
//Normal style
|
//Normal style
|
||||||
|
|
Loading…
Add table
Reference in a new issue