wg_engine: introduce simultaneous flag support for strokes trimming

https://github.com/thorvg/thorvg/issues/2435
This commit is contained in:
Sergii Liebodkin 2024-08-01 06:07:20 +03:00 committed by GitHub
parent 21a21a63ab
commit 25d1fc8bee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 76 additions and 19 deletions

View file

@ -107,7 +107,6 @@ void WgPolyline::appendCubic(WgPoint p1, WgPoint p2, WgPoint p3, size_t nsegs)
void WgPolyline::trim(WgPolyline* polyline, float trimBegin, float trimEnd) const void WgPolyline::trim(WgPolyline* polyline, float trimBegin, float trimEnd) const
{ {
assert(polyline); assert(polyline);
polyline->clear();
float begLen = len * trimBegin; float begLen = len * trimBegin;
float endLen = len * trimEnd; float endLen = len * trimEnd;
float currentLength = 0.0f; float currentLength = 0.0f;

View file

@ -291,31 +291,29 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha
releaseMeshes(context); releaseMeshes(context);
strokeFirst = rshape.stroke ? rshape.stroke->strokeFirst : false; strokeFirst = rshape.stroke ? rshape.stroke->strokeFirst : false;
static WgPolyline polyline; Array<WgPolyline*> polylines{};
polyline.clear();
// decode path // decode path
size_t pntIndex = 0; size_t pntIndex = 0;
for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) { for (uint32_t cmdIndex = 0; cmdIndex < rshape.path.cmds.count; cmdIndex++) {
PathCommand cmd = rshape.path.cmds[cmdIndex]; PathCommand cmd = rshape.path.cmds[cmdIndex];
if (cmd == PathCommand::MoveTo) { if (cmd == PathCommand::MoveTo) {
// proceed current polyline // proceed current polyline
updateMeshes(context, &polyline, rshape.stroke); polylines.push(new WgPolyline);
polyline.clear(); polylines.last()->appendPoint(rshape.path.pts[pntIndex]);
polyline.appendPoint(rshape.path.pts[pntIndex]);
pntIndex++; pntIndex++;
} else if (cmd == PathCommand::LineTo) { } else if (cmd == PathCommand::LineTo) {
polyline.appendPoint(rshape.path.pts[pntIndex]); polylines.last()->appendPoint(rshape.path.pts[pntIndex]);
pntIndex++; pntIndex++;
} else if (cmd == PathCommand::Close) { } else if (cmd == PathCommand::Close) {
polyline.close(); polylines.last()->close();
} else if (cmd == PathCommand::CubicTo) { } else if (cmd == PathCommand::CubicTo) {
assert(polyline.pts.count > 0); assert(polylines.last()->pts.count > 0);
WgPoint pt0 = polyline.pts.last().trans(rt); WgPoint pt0 = polylines.last()->pts.last().trans(rt);
WgPoint pt1 = WgPoint(rshape.path.pts[pntIndex + 0]).trans(rt); WgPoint pt1 = WgPoint(rshape.path.pts[pntIndex + 0]).trans(rt);
WgPoint pt2 = WgPoint(rshape.path.pts[pntIndex + 1]).trans(rt); WgPoint pt2 = WgPoint(rshape.path.pts[pntIndex + 1]).trans(rt);
WgPoint pt3 = WgPoint(rshape.path.pts[pntIndex + 2]).trans(rt); WgPoint pt3 = WgPoint(rshape.path.pts[pntIndex + 2]).trans(rt);
uint32_t nsegs = (uint32_t)(pt0.dist(pt1) + pt1.dist(pt2) + pt2.dist(pt3)); uint32_t nsegs = (uint32_t)(pt0.dist(pt1) + pt1.dist(pt2) + pt2.dist(pt3));
polyline.appendCubic( polylines.last()->appendCubic(
rshape.path.pts[pntIndex + 0], rshape.path.pts[pntIndex + 0],
rshape.path.pts[pntIndex + 1], rshape.path.pts[pntIndex + 1],
rshape.path.pts[pntIndex + 2], rshape.path.pts[pntIndex + 2],
@ -323,13 +321,38 @@ void WgRenderDataShape::updateMeshes(WgContext &context, const RenderShape &rsha
pntIndex += 3; pntIndex += 3;
} }
} }
// proceed last polyline // proceed shapes
updateMeshes(context, &polyline, rshape.stroke); float totalLen{};
for (uint32_t i = 0; i < polylines.count; i++) {
totalLen += polylines[i]->len;
updateShapes(context, polylines[i]);
}
// proceed strokes
if (rshape.stroke) {
float trimBegin{};
float trimEnd{};
if (!rshape.stroke->strokeTrim(trimBegin, trimEnd)) { trimBegin = 0.0f; trimEnd = 1.0f; }
if (rshape.stroke->trim.simultaneous) {
for (uint32_t i = 0; i < polylines.count; i++)
updateStrokes(context, polylines[i], rshape.stroke, trimBegin, trimEnd);
} else {
if (trimBegin <= trimEnd) {
updateStrokesList(context, polylines, rshape.stroke, totalLen, trimBegin, trimEnd);
} else {
updateStrokesList(context, polylines, rshape.stroke, totalLen, 0.0f, trimEnd);
updateStrokesList(context, polylines, rshape.stroke, totalLen, trimBegin, 1.0f);
}
}
}
// delete polylines
for (uint32_t i = 0; i < polylines.count; i++)
delete polylines[i];
// update shapes bbox
meshDataBBox.update(context, pMin, pMax); meshDataBBox.update(context, pMin, pMax);
} }
void WgRenderDataShape::updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke) void WgRenderDataShape::updateShapes(WgContext& context, const WgPolyline* polyline)
{ {
assert(polyline); assert(polyline);
// generate fill geometry // generate fill geometry
@ -340,20 +363,53 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const WgPolyline* polyl
meshGroupShapesBBox.append(context, pmin, pmax); meshGroupShapesBBox.append(context, pmin, pmax);
updateBBox(pmin, pmax); updateBBox(pmin, pmax);
} }
}
void WgRenderDataShape::updateStrokesList(WgContext& context, Array<WgPolyline*> polylines, const RenderStroke* rstroke, float totalLen, float trimBegin, float trimEnd)
{
float tp1 = totalLen * trimBegin; // trim point begin
float tp2 = totalLen * trimEnd; // trim point end
float pc = 0; // point current
for (uint32_t i = 0; i < polylines.count; i++) {
float pl = polylines[i]->len; // current polyline length
float trimBegin = ((pc <= tp1) && (pc + pl > tp1)) ? (tp1 - pc) / pl : 0.0f;
float trimEnd = ((pc <= tp2) && (pc + pl > tp2)) ? (tp2 - pc) / pl : 1.0f;
if ((pc + pl >= tp1) && (pc <= tp2))
updateStrokes(context, polylines[i], rstroke, trimBegin, trimEnd);
pc += pl;
// break if reached the tail
if (pc > tp2) break;
}
}
void WgRenderDataShape::updateStrokes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke, float trimBegin, float trimEnd)
{
assert(polyline);
// generate strokes geometry // generate strokes geometry
if ((polyline->pts.count >= 1) && rstroke && (rstroke->width > 0.0f)) { if ((polyline->pts.count >= 1) && rstroke && (rstroke->width > 0.0f)) {
static WgGeometryData geometryData; geometryData.clear(); static WgGeometryData geometryData; geometryData.clear();
static WgPolyline trimmed; static WgPolyline trimmed;
// trim -> split -> stroke // trim -> split -> stroke
float trimBegin = rstroke->trim.begin < rstroke->trim.end ? rstroke->trim.begin : rstroke->trim.end;
float trimEnd = rstroke->trim.begin < rstroke->trim.end ? rstroke->trim.end : rstroke->trim.begin;
if (trimBegin == trimEnd) return; if (trimBegin == trimEnd) return;
if ((rstroke->dashPattern) && ((trimBegin != 0.0f) || (trimEnd != 1.0f))) { if ((rstroke->dashPattern) && ((trimBegin != 0.0f) || (trimEnd != 1.0f))) {
trimmed.clear();
if (trimBegin < trimEnd)
polyline->trim(&trimmed, trimBegin, trimEnd); polyline->trim(&trimmed, trimBegin, trimEnd);
else {
polyline->trim(&trimmed, trimBegin, 1.0f);
polyline->trim(&trimmed, 0.0f, trimEnd);
}
geometryData.appendStrokeDashed(&trimmed, rstroke); geometryData.appendStrokeDashed(&trimmed, rstroke);
} else // trim -> stroke } else // trim -> stroke
if ((trimBegin != 0.0f) || (trimEnd != 1.0f)) { if ((trimBegin != 0.0f) || (trimEnd != 1.0f)) {
trimmed.clear();
if (trimBegin < trimEnd)
polyline->trim(&trimmed, trimBegin, trimEnd); polyline->trim(&trimmed, trimBegin, trimEnd);
else {
polyline->trim(&trimmed, trimBegin, 1.0f);
polyline->trim(&trimmed, 0.0f, trimEnd);
}
geometryData.appendStroke(&trimmed, rstroke); geometryData.appendStroke(&trimmed, rstroke);
} else // split -> stroke } else // split -> stroke
if (rstroke->dashPattern) { if (rstroke->dashPattern) {

View file

@ -113,7 +113,9 @@ struct WgRenderDataShape: public WgRenderDataPaint
void updateBBox(WgPoint pmin, WgPoint pmax); void updateBBox(WgPoint pmin, WgPoint pmax);
void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& rt); void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& rt);
void updateMeshes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke); void updateShapes(WgContext& context, const WgPolyline* polyline);
void updateStrokesList(WgContext& context, Array<WgPolyline*> polylines, const RenderStroke* rstroke, float totalLen, float trimBegin, float trimEnd);
void updateStrokes(WgContext& context, const WgPolyline* polyline, const RenderStroke* rstroke, float trimBegin, float trimEnd);
void releaseMeshes(WgContext& context); void releaseMeshes(WgContext& context);
void release(WgContext& context) override; void release(WgContext& context) override;
Type type() override { return Type::Shape; }; Type type() override { return Type::Shape; };