mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-09 06:04:03 +00:00
gl_engine: Fix calculation error in path triming
Basically rewrite the PathTrim code, correct the Line and Bezier split function calling. Also the trim situation where start is greater than end can be handled correctly.
This commit is contained in:
parent
babff908cd
commit
be538bacf7
2 changed files with 159 additions and 186 deletions
|
@ -1666,7 +1666,39 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
auto end = 0.0f;
|
auto end = 0.0f;
|
||||||
rshape->stroke->strokeTrim(begin, end);
|
rshape->stroke->strokeTrim(begin, end);
|
||||||
|
|
||||||
if (rshape->stroke->trim.simultaneous) {
|
if (begin == end) return;
|
||||||
|
|
||||||
|
if (begin > end) {
|
||||||
|
doTrimStroke(cmds, cmdCnt, pts, ptsCnt, rshape->stroke->trim.simultaneous, begin, 1.0f);
|
||||||
|
doTrimStroke(cmds, cmdCnt, pts, ptsCnt, rshape->stroke->trim.simultaneous, 0.0f, end);
|
||||||
|
} else {
|
||||||
|
doTrimStroke(cmds, cmdCnt, pts, ptsCnt, rshape->stroke->trim.simultaneous, begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float *dash_pattern = nullptr;
|
||||||
|
auto dashCnt = rshape->strokeDash(&dash_pattern, nullptr);
|
||||||
|
|
||||||
|
if (dashCnt == 0) doStroke(cmds, cmdCnt, pts, ptsCnt);
|
||||||
|
else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RenderRegion Stroker::bounds() const
|
||||||
|
{
|
||||||
|
return RenderRegion {
|
||||||
|
static_cast<int32_t>(floor(mLeftTop.x)),
|
||||||
|
static_cast<int32_t>(floor(mLeftTop.y)),
|
||||||
|
static_cast<int32_t>(ceil(mRightBottom.x - floor(mLeftTop.x))),
|
||||||
|
static_cast<int32_t>(ceil(mRightBottom.y - floor(mLeftTop.y))),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stroker::doTrimStroke(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt, bool simultaneous, float start, float end)
|
||||||
|
{
|
||||||
|
if (simultaneous) {
|
||||||
auto startCmds = cmds;
|
auto startCmds = cmds;
|
||||||
auto currCmds = cmds;
|
auto currCmds = cmds;
|
||||||
int ptsNum = 0;
|
int ptsNum = 0;
|
||||||
|
@ -1675,7 +1707,7 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
if (currCmds != startCmds) {
|
if (currCmds != startCmds) {
|
||||||
PathTrim trim{};
|
PathTrim trim{};
|
||||||
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, begin, end)) {
|
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, start, end)) {
|
||||||
const auto& sCmds = trim.cmds();
|
const auto& sCmds = trim.cmds();
|
||||||
const auto& sPts = trim.pts();
|
const auto& sPts = trim.pts();
|
||||||
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
||||||
|
@ -1699,7 +1731,7 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
case PathCommand::Close: {
|
case PathCommand::Close: {
|
||||||
PathTrim trim{};
|
PathTrim trim{};
|
||||||
currCmds++;
|
currCmds++;
|
||||||
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, begin, end)) {
|
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, start, end)) {
|
||||||
const auto& sCmds = trim.cmds();
|
const auto& sCmds = trim.cmds();
|
||||||
const auto& sPts = trim.pts();
|
const auto& sPts = trim.pts();
|
||||||
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
||||||
|
@ -1715,7 +1747,7 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
if (startCmds != currCmds && ptsNum > 0) {
|
if (startCmds != currCmds && ptsNum > 0) {
|
||||||
PathTrim trim{};
|
PathTrim trim{};
|
||||||
|
|
||||||
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, begin, end)) {
|
if (trim.trim(startCmds, currCmds - startCmds, pts, ptsNum, start, end)) {
|
||||||
const auto& sCmds = trim.cmds();
|
const auto& sCmds = trim.cmds();
|
||||||
const auto& sPts = trim.pts();
|
const auto& sPts = trim.pts();
|
||||||
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
||||||
|
@ -1726,30 +1758,12 @@ void Stroker::stroke(const RenderShape *rshape)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PathTrim trim{};
|
PathTrim trim{};
|
||||||
if (trim.trim(cmds, cmdCnt, pts, ptsCnt, begin, end)) {
|
if (trim.trim(cmds, cmdCnt, pts, ptsCnt, start, end)) {
|
||||||
const auto& sCmds = trim.cmds();
|
const auto& sCmds = trim.cmds();
|
||||||
const auto& sPts = trim.pts();
|
const auto& sPts = trim.pts();
|
||||||
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
doStroke(sCmds.data, sCmds.count, sPts.data, sPts.count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const float *dash_pattern = nullptr;
|
|
||||||
auto dashCnt = rshape->strokeDash(&dash_pattern, nullptr);
|
|
||||||
|
|
||||||
if (dashCnt == 0) doStroke(cmds, cmdCnt, pts, ptsCnt);
|
|
||||||
else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenderRegion Stroker::bounds() const
|
|
||||||
{
|
|
||||||
return RenderRegion {
|
|
||||||
static_cast<int32_t>(floor(mLeftTop.x)),
|
|
||||||
static_cast<int32_t>(floor(mLeftTop.y)),
|
|
||||||
static_cast<int32_t>(ceil(mRightBottom.x - floor(mLeftTop.x))),
|
|
||||||
static_cast<int32_t>(ceil(mRightBottom.y - floor(mLeftTop.y))),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
|
void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
|
||||||
|
@ -2361,159 +2375,117 @@ void PathTrim::trimPath(const PathCommand* cmds, uint32_t cmd_count, const Point
|
||||||
{
|
{
|
||||||
auto pos = 0.0f;
|
auto pos = 0.0f;
|
||||||
Point zero = {0.0f, 0.0f};
|
Point zero = {0.0f, 0.0f};
|
||||||
const Point* prev = nullptr;
|
Point prev = {};
|
||||||
const Point* begin = nullptr;
|
Point begin = {};
|
||||||
auto closed = true;
|
auto closed = true;
|
||||||
auto pushedMoveTo = false;
|
auto pushedMoveTo = false;
|
||||||
auto hasLineTo = false;
|
auto hasLineTo = false;
|
||||||
|
|
||||||
auto handle_line_to = [&](const Point* p1, const Point* p2) {
|
auto handle_line_to = [&](const Point* p1, const Point* p2) {
|
||||||
auto currLen = length(p1, p2);
|
auto currLen = length(p1, p2);
|
||||||
if (pos + currLen < start) {
|
if (pos + currLen <= start) {
|
||||||
pos += currLen;
|
pos += currLen;
|
||||||
|
prev = *p2;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pos >= start && pos + currLen <= end) {
|
|
||||||
// the entire edge is within the trim range
|
|
||||||
if (!pushedMoveTo) {
|
|
||||||
mCmds.push(PathCommand::MoveTo);
|
|
||||||
mPts.push(*p1);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
|
||||||
mCmds.push(PathCommand::LineTo);
|
|
||||||
mPts.push(*p2);
|
|
||||||
} else if (pos >= start) {
|
|
||||||
// split the edge and save the left part
|
|
||||||
Line l{ *p1, *p2 };
|
|
||||||
Line left, right;
|
|
||||||
l.split((pos - end) / currLen, left, right);
|
|
||||||
if (!pushedMoveTo) {
|
|
||||||
mCmds.push(PathCommand::MoveTo);
|
|
||||||
mPts.push(*p1);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
|
||||||
mCmds.push(PathCommand::LineTo);
|
|
||||||
mPts.push(left.pt2);
|
|
||||||
} else if (pos + currLen <= end) {
|
|
||||||
// split the edge and save the right part
|
|
||||||
Line l{ *p1, *p2 };
|
|
||||||
Line left, right;
|
|
||||||
l.split((pos + currLen - start) / currLen, left, right);
|
|
||||||
if (!pushedMoveTo) {
|
|
||||||
mCmds.push(PathCommand::MoveTo);
|
|
||||||
mPts.push(right.pt1);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
|
||||||
mCmds.push(PathCommand::LineTo);
|
|
||||||
mPts.push(right.pt2);
|
|
||||||
} else {
|
|
||||||
// only part of the edge is within the trim range
|
|
||||||
Line l{ *p1, *p2 };
|
|
||||||
Line left, right;
|
|
||||||
// find the start point
|
|
||||||
l.split((start - pos) / currLen, left, right);
|
|
||||||
auto startP = left.pt2;
|
|
||||||
|
|
||||||
// find the end point
|
if (pos >= end) {
|
||||||
l.split((end - pos) / currLen, left, right);
|
prev = *p2;
|
||||||
auto endP = right.pt1;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Line line{*p1, *p2};
|
||||||
|
|
||||||
|
if (pos < start) {
|
||||||
|
Line left, right;
|
||||||
|
line.split(start - pos, left, right);
|
||||||
|
|
||||||
|
pos += left.length();
|
||||||
|
line = right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos + currLen > end) {
|
||||||
|
Line left, right;
|
||||||
|
line.split(end - pos, left, right);
|
||||||
|
pos += left.length();
|
||||||
|
line = left;
|
||||||
|
}
|
||||||
|
|
||||||
if (!pushedMoveTo) {
|
if (!pushedMoveTo) {
|
||||||
mCmds.push(PathCommand::MoveTo);
|
mCmds.push(PathCommand::MoveTo);
|
||||||
mPts.push(startP);
|
mPts.push(line.pt1);
|
||||||
pushedMoveTo = true;
|
pushedMoveTo = true;
|
||||||
|
begin = line.pt1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pos += line.length();
|
||||||
|
|
||||||
mCmds.push(PathCommand::LineTo);
|
mCmds.push(PathCommand::LineTo);
|
||||||
mPts.push(endP);
|
mPts.push(line.pt2);
|
||||||
}
|
prev = line.pt2;
|
||||||
pos += currLen;
|
hasLineTo = true;
|
||||||
|
closed = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
for (uint32_t i = 0; i < cmd_count; i++) {
|
for (uint32_t i = 0; i < cmd_count; i++) {
|
||||||
if (pos - end > 0.001f) return; // we are done
|
if (pos - end > 0.001f) return; // we are done
|
||||||
|
|
||||||
|
if (pos >= end) return;
|
||||||
|
|
||||||
switch (cmds[i]) {
|
switch (cmds[i]) {
|
||||||
case PathCommand::MoveTo: {
|
case PathCommand::MoveTo: {
|
||||||
prev = pts;
|
prev = *pts;
|
||||||
begin = pts;
|
begin = *pts;
|
||||||
pts++;
|
pts++;
|
||||||
closed = false;
|
closed = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::LineTo: {
|
case PathCommand::LineTo: {
|
||||||
if (prev == nullptr) prev = begin = &zero;
|
handle_line_to(&prev, pts);
|
||||||
handle_line_to(prev, pts);
|
|
||||||
hasLineTo = true;
|
hasLineTo = true;
|
||||||
prev = pts;
|
|
||||||
pts++;
|
pts++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case PathCommand::CubicTo: {
|
case PathCommand::CubicTo: {
|
||||||
if (prev == nullptr) prev = begin = &zero;
|
Bezier b{ prev, pts[0], pts[1], pts[2]};
|
||||||
Bezier b{ *prev, pts[0], pts[1], pts[2]};
|
|
||||||
auto currLen = b.length();
|
|
||||||
|
|
||||||
if (pos + currLen < start || currLen < 0.001) {
|
auto currLen = b.length();
|
||||||
|
if (pos + currLen <= start) {
|
||||||
pos += currLen;
|
pos += currLen;
|
||||||
prev = pts + 2;
|
prev = pts[2];
|
||||||
pts += 3;
|
pts += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (pos >= start && pos + currLen <= end) {
|
|
||||||
// the entire edge is within the trim range
|
if (pos < start) {
|
||||||
if (!pushedMoveTo) {
|
|
||||||
mCmds.push(PathCommand::MoveTo);
|
|
||||||
mPts.push(*prev);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
|
||||||
mCmds.push(PathCommand::CubicTo);
|
|
||||||
mPts.push(pts[0]);
|
|
||||||
mPts.push(pts[1]);
|
|
||||||
mPts.push(pts[2]);
|
|
||||||
} else if (pos >= start) {
|
|
||||||
// split the edge and save the left part
|
|
||||||
Bezier left;
|
|
||||||
b.split((end - pos) / currLen, left);
|
|
||||||
if (!pushedMoveTo) {
|
|
||||||
mCmds.push(PathCommand::MoveTo);
|
|
||||||
mPts.push(*prev);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
|
||||||
mCmds.push(PathCommand::CubicTo);
|
|
||||||
mPts.push(left.ctrl1);
|
|
||||||
mPts.push(left.ctrl2);
|
|
||||||
mPts.push(left.end);
|
|
||||||
} else if (pos + currLen <= end) {
|
|
||||||
// split the edge and save the right part
|
|
||||||
Bezier left, right;
|
Bezier left, right;
|
||||||
b.split((start - pos) / currLen, left, right);
|
b.split(start - pos, left, right);
|
||||||
if (!pushedMoveTo) {
|
pos += left.length();
|
||||||
mCmds.push(PathCommand::MoveTo);
|
b = right;
|
||||||
mPts.push(right.start);
|
|
||||||
pushedMoveTo = true;
|
|
||||||
}
|
}
|
||||||
mCmds.push(PathCommand::CubicTo);
|
|
||||||
mPts.push(right.ctrl1);
|
if (pos + currLen > end) {
|
||||||
mPts.push(right.ctrl2);
|
|
||||||
mPts.push(right.end);
|
|
||||||
} else {
|
|
||||||
// only part of the edge is within the trim range
|
|
||||||
Bezier left, right;
|
Bezier left, right;
|
||||||
b.split((start - pos) / currLen, left, right);
|
b.split(end - pos, left, right);
|
||||||
right.split((end - start) / right.length(), left);
|
pos += left.length();
|
||||||
|
b = left;
|
||||||
|
}
|
||||||
|
|
||||||
if (!pushedMoveTo) {
|
if (!pushedMoveTo) {
|
||||||
mCmds.push(PathCommand::MoveTo);
|
mCmds.push(PathCommand::MoveTo);
|
||||||
mPts.push(left.start);
|
mPts.push(b.start);
|
||||||
pushedMoveTo = true;
|
pushedMoveTo = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pos += b.length();
|
||||||
mCmds.push(PathCommand::CubicTo);
|
mCmds.push(PathCommand::CubicTo);
|
||||||
mPts.push(left.ctrl1);
|
mPts.push(b.ctrl1);
|
||||||
mPts.push(left.ctrl2);
|
mPts.push(b.ctrl2);
|
||||||
mPts.push(left.end);
|
mPts.push(b.end);
|
||||||
}
|
prev = b.end;
|
||||||
hasLineTo = true;
|
hasLineTo = true;
|
||||||
prev = pts + 2;
|
closed = false;
|
||||||
pos += currLen;
|
|
||||||
pts += 3;
|
pts += 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2522,16 +2494,16 @@ void PathTrim::trimPath(const PathCommand* cmds, uint32_t cmd_count, const Point
|
||||||
if (!hasLineTo) {
|
if (!hasLineTo) {
|
||||||
closed = true;
|
closed = true;
|
||||||
pushedMoveTo = false;
|
pushedMoveTo = false;
|
||||||
prev = begin = nullptr;
|
prev = begin = zero;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (*prev == *begin) {
|
if (prev == begin) {
|
||||||
prev = begin = nullptr;
|
prev = begin = zero;
|
||||||
closed = true;
|
closed = true;
|
||||||
pushedMoveTo = false;
|
pushedMoveTo = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
auto currLen = length(prev, begin);
|
auto currLen = length(&prev, &begin);
|
||||||
if (currLen + pos < start) {
|
if (currLen + pos < start) {
|
||||||
pos += currLen;
|
pos += currLen;
|
||||||
break;
|
break;
|
||||||
|
@ -2541,10 +2513,10 @@ void PathTrim::trimPath(const PathCommand* cmds, uint32_t cmd_count, const Point
|
||||||
pos += currLen;
|
pos += currLen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
handle_line_to(prev, begin);
|
// handle_line_to(&prev, &begin);
|
||||||
closed = true;
|
closed = true;
|
||||||
pushedMoveTo = false;
|
pushedMoveTo = false;
|
||||||
prev = begin = nullptr;
|
prev = begin = zero;
|
||||||
pos += currLen;
|
pos += currLen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,6 +116,7 @@ public:
|
||||||
RenderRegion bounds() const;
|
RenderRegion bounds() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void doTrimStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count, bool simultaneous, float start, float end);
|
||||||
void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count);
|
void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count);
|
||||||
void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count,
|
void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count,
|
||||||
uint32_t dash_count, const float* dash_pattern);
|
uint32_t dash_count, const float* dash_pattern);
|
||||||
|
|
Loading…
Add table
Reference in a new issue