wg_engine: apply common trimming logic

The trim-related implementation has been removed
and replaced with the one available in common.

@Issue: https://github.com/thorvg/thorvg/issues/2854
This commit is contained in:
Mira Grudzinska 2025-01-14 23:59:33 +01:00 committed by Hermet Park
parent 8ed4c2f302
commit cb8cadfbba
3 changed files with 55 additions and 108 deletions

View file

@ -161,56 +161,51 @@ struct WgVertexBuffer
append(bezier.at((float)i / nsegs));
}
// trim source buffer
void trim(const WgVertexBuffer& buff, float beg, float end)
{
// empty buffer guard
if (buff.vcount == 0) return;
// initialize
float len_beg = buff.total() * beg;
float len_end = buff.total() * end;
// find points
size_t index_beg = buff.getIndexByLength(len_beg);
size_t index_end = buff.getIndexByLength(len_end);
float len_total_beg = buff.vleng[index_beg];
float len_total_end = buff.vleng[index_end];
float len_seg_beg = buff.vdist[index_beg];
float len_seg_end = buff.vdist[index_end];
// append points
float t_beg = len_seg_beg > 0.0f ? 1.0f - (len_total_beg - len_beg) / len_seg_beg : 0.0f;
float t_end = len_seg_end > 0.0f ? 1.0f - (len_total_end - len_end) / len_seg_end : 0.0f;
//t_beg == 1 handled in appendRange
if (index_beg > 0 && t_beg != 1.0f) append(lerp(buff.vbuff[index_beg-1], buff.vbuff[index_beg], t_beg));
appendRange(buff, index_beg, index_end);
//t_end == 0 handled in appendRange
if (index_end > 0 && t_end != 0.0f) append(lerp(buff.vbuff[index_end-1], buff.vbuff[index_end], t_end));
}
// decode path with callback for external prcesses
void decodePath(const RenderShape& rshape, bool update_dist, onPolylineFn onPolyline)
void decodePath(const RenderShape& rshape, bool update_dist, onPolylineFn onPolyline, bool trim = false)
{
// decode path
reset(tscale);
PathCommand *cmds, *trimmedCmds = nullptr;
Point *pts, *trimmedPts = nullptr;
uint32_t cmdCnt{};
if (trim) {
RenderPath trimmedPath;
if (!rshape.stroke->trim.trim(rshape.path, trimmedPath)) return;
cmds = trimmedCmds = trimmedPath.cmds.data;
cmdCnt = trimmedPath.cmds.count;
pts = trimmedPts = trimmedPath.pts.data;
trimmedPath.cmds.data = nullptr;
trimmedPath.pts.data = nullptr;
} else {
cmds = rshape.path.cmds.data;
cmdCnt = rshape.path.cmds.count;
pts = rshape.path.pts.data;
}
size_t pntIndex = 0;
ARRAY_FOREACH(p, rshape.path.cmds) {
auto cmd = *p;
for (uint32_t i = 0; i < cmdCnt; i++) {
auto& cmd = cmds[i];
if (cmd == PathCommand::MoveTo) {
// after path decoding we need to update distances and total length
if (update_dist) updateDistances();
if ((onPolyline) && (vcount > 0)) onPolyline(*this);
reset(tscale);
append(rshape.path.pts[pntIndex]);
append(pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::LineTo) {
append(rshape.path.pts[pntIndex]);
append(pts[pntIndex]);
pntIndex++;
} else if (cmd == PathCommand::Close) {
close();
// proceed path if close command is not the last command and next command is LineTo or CubicTo
if (((p + 1) < rshape.path.cmds.end()) &&
((*(p + 1) == PathCommand::LineTo) ||
(*(p + 1) == PathCommand::CubicTo))) {
if (i + 1 < cmdCnt &&
(cmds[i + 1] == PathCommand::LineTo ||
cmds[i + 1] == PathCommand::CubicTo)) {
// proceed current path
if (update_dist) updateDistances();
if ((vcount > 0) && (onPolyline)) onPolyline(*this);
@ -221,10 +216,14 @@ struct WgVertexBuffer
}
} else if (cmd == PathCommand::CubicTo) {
// append tesselated cubic spline with tscale param
appendCubic(vbuff[vcount - 1], rshape.path.pts[pntIndex + 0], rshape.path.pts[pntIndex + 1], rshape.path.pts[pntIndex + 2]);
appendCubic(vbuff[vcount - 1], pts[pntIndex + 0], pts[pntIndex + 1], pts[pntIndex + 2]);
pntIndex += 3;
}
}
free(trimmedCmds);
free(trimmedPts);
// after path decoding we need to update distances and total length
if (update_dist) updateDistances();
if ((vcount > 0) && (onPolyline)) onPolyline(*this);

View file

@ -385,61 +385,23 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
// path decoded vertex buffer
WgVertexBuffer pbuff;
pbuff.reset(scale);
// append shape without strokes
if (!rshape.stroke) {
pbuff.decodePath(rshape, false, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff);
});
// append shape with strokes
} else {
float tbeg{}, tend{};
if (!rshape.stroke->trim.get(tbeg, tend)) { tbeg = 0.0f; tend = 1.0f; }
bool loop = tbeg > tend;
if (tbeg == tend) {
pbuff.decodePath(rshape, false, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff);
});
} else if (rshape.stroke->trim.simultaneous) {
if (rshape.strokeTrim()) {
WgVertexBuffer trimbuff;
trimbuff.reset(scale);
pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff);
if (loop) {
proceedStrokes(context, rshape.stroke, tbeg, 1.0f, path_buff);
proceedStrokes(context, rshape.stroke, 0.0f, tend, path_buff);
} else {
proceedStrokes(context, rshape.stroke, tbeg, tend, path_buff);
}
});
trimbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff);
proceedStrokes(context, rshape.stroke, path_buff);
}, true);
} else {
float totalLen = 0.0f;
pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
appendShape(context, path_buff);
totalLen += path_buff.total();
if (rshape.stroke) proceedStrokes(context, rshape.stroke, path_buff);
});
float len_beg = totalLen * tbeg; // trim length begin
float len_end = totalLen * tend; // trim length end
float len_acc = 0.0; // accumulated length
// append strokes
pbuff.decodePath(rshape, true, [&](const WgVertexBuffer& path_buff) {
float len_path = path_buff.total(); // current path length
if (loop) {
if (len_acc + len_path >= len_beg) {
auto tbeg = len_acc <= len_beg ? (len_beg - len_acc) / len_path : 0.0f;
proceedStrokes(context, rshape.stroke, tbeg, 1.0f, path_buff);
}
if (len_acc < len_end) {
auto tend = len_acc + len_path >= len_end ? (len_end - len_acc) / len_path : 1.0f;
proceedStrokes(context, rshape.stroke, 0.0f, tend, path_buff);
}
} else {
if (len_acc + len_path >= len_beg && len_acc <= len_end) {
auto tbeg = len_acc <= len_beg ? (len_beg - len_acc) / len_path : 0.0f;
auto tend = len_acc + len_path >= len_end ? (len_end - len_acc) / len_path : 1.0f;
proceedStrokes(context, rshape.stroke, tbeg, tend, path_buff);
}
}
len_acc += len_path;
});
}
}
// update shapes bbox (with empty path handling)
if ((this->meshGroupShapesBBox.meshes.count > 0 ) ||
@ -450,29 +412,15 @@ void WgRenderDataShape::updateMeshes(WgContext& context, const RenderShape &rsha
}
void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* rstroke, float tbeg, float tend, const WgVertexBuffer& buff)
void WgRenderDataShape::proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff)
{
assert(rstroke);
static WgVertexBufferInd strokesGenerator;
strokesGenerator.reset(buff.tscale);
// trim -> dash -> stroke
if ((tbeg != 0.0f) || (tend != 1.0f)) {
if (tbeg == tend) return;
WgVertexBuffer trimed_buff;
trimed_buff.reset(buff.tscale);
trimed_buff.trim(buff, tbeg, tend);
trimed_buff.updateDistances();
// trim ->dash -> stroke
if (rstroke->dashPattern) strokesGenerator.appendStrokesDashed(trimed_buff, rstroke);
// trim -> stroke
else strokesGenerator.appendStrokes(trimed_buff, rstroke);
} else
// dash -> stroke
if (rstroke->dashPattern) {
strokesGenerator.appendStrokesDashed(buff, rstroke);
// stroke
} else
strokesGenerator.appendStrokes(buff, rstroke);
if (rstroke->dashPattern) strokesGenerator.appendStrokesDashed(buff, rstroke);
else strokesGenerator.appendStrokes(buff, rstroke);
appendStroke(context, strokesGenerator);
}

View file

@ -130,7 +130,7 @@ struct WgRenderDataShape: public WgRenderDataPaint
void updateBBox(Point pmin, Point pmax);
void updateAABB(const Matrix& tr);
void updateMeshes(WgContext& context, const RenderShape& rshape, const Matrix& tr);
void proceedStrokes(WgContext& context, const RenderStroke* rstroke, float tbeg, float tend, const WgVertexBuffer& buff);
void proceedStrokes(WgContext& context, const RenderStroke* rstroke, const WgVertexBuffer& buff);
void releaseMeshes(WgContext& context);
void release(WgContext& context) override;
Type type() override { return Type::Shape; };