gl_engine: add offset support in dashed strokes

Offset is correctly applied to dashed strokes,
properly shifting the dash pattern.

@Issue: https://github.com/thorvg/thorvg/issues/3217
@Issue: https://github.com/thorvg/thorvg/issues/3223
@Issue: https://github.com/thorvg/thorvg/issues/3191
This commit is contained in:
Mira Grudzinska 2025-02-13 23:54:24 +01:00 committed by Hermet Park
parent 95cd0863eb
commit acbc3b3ba9
2 changed files with 33 additions and 10 deletions

View file

@ -1564,10 +1564,11 @@ void Stroker::stroke(const RenderShape *rshape)
} }
const float *dash_pattern = nullptr; const float *dash_pattern = nullptr;
auto dashCnt = rshape->strokeDash(&dash_pattern, nullptr); auto dash_offset = 0.0f;
auto dashCnt = rshape->strokeDash(&dash_pattern, &dash_offset);
if (dashCnt == 0) doStroke(cmds, cmdCnt, pts, ptsCnt); if (dashCnt == 0) doStroke(cmds, cmdCnt, pts, ptsCnt);
else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern); else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern, dash_offset);
free(trimmedCmds); free(trimmedCmds);
free(trimmedPts); free(trimmedPts);
@ -1629,7 +1630,7 @@ void Stroker::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point
} }
void Stroker::doDashStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count, uint32_t dash_count, const float *dash_pattern) void Stroker::doDashStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count, uint32_t dash_count, const float *dash_pattern, float dash_offset)
{ {
Array<PathCommand> dash_cmds{}; Array<PathCommand> dash_cmds{};
Array<Point> dash_pts{}; Array<Point> dash_pts{};
@ -1637,7 +1638,7 @@ void Stroker::doDashStroke(const PathCommand *cmds, uint32_t cmd_count, const Po
dash_cmds.reserve(20 * cmd_count); dash_cmds.reserve(20 * cmd_count);
dash_pts.reserve(20 * pts_count); dash_pts.reserve(20 * pts_count);
DashStroke dash(&dash_cmds, &dash_pts, dash_count, dash_pattern); DashStroke dash(&dash_cmds, &dash_pts, dash_count, dash_pattern, dash_offset);
dash.doStroke(cmds, cmd_count, pts, pts_count); dash.doStroke(cmds, cmd_count, pts, pts_count);
this->doStroke(dash_cmds.data, dash_cmds.count, dash_pts.data, dash_pts.count); this->doStroke(dash_cmds.data, dash_cmds.count, dash_pts.data, dash_pts.count);
@ -1974,11 +1975,12 @@ void Stroker::strokeRound(const Point& p, const Point& outDir)
} }
DashStroke::DashStroke(Array<PathCommand> *cmds, Array<Point> *pts, uint32_t dash_count, const float *dash_pattern) DashStroke::DashStroke(Array<PathCommand> *cmds, Array<Point> *pts, uint32_t dash_count, const float *dash_pattern, float dash_offset)
: mCmds(cmds), : mCmds(cmds),
mPts(pts), mPts(pts),
mDashCount(dash_count), mDashCount(dash_count),
mDashPattern(dash_pattern), mDashPattern(dash_pattern),
mDashOffset(dash_offset),
mCurrLen(), mCurrLen(),
mCurrIdx(), mCurrIdx(),
mCurOpGap(false), mCurOpGap(false),
@ -1990,6 +1992,26 @@ DashStroke::DashStroke(Array<PathCommand> *cmds, Array<Point> *pts, uint32_t das
void DashStroke::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count) void DashStroke::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Point *pts, uint32_t pts_count)
{ {
int32_t idx = 0;
auto offset = mDashOffset;
bool gap = false;
if (!tvg::zero(mDashOffset)) {
auto len = 0.0f;
for (auto i = 0; i < mDashCount; ++i) len += mDashPattern[i];
if (mDashCount % 2) len *= 2;
offset = fmodf(offset, len);
if (offset < 0) offset += len;
for (uint32_t i = 0; i < mDashCount * (mDashCount % 2 + 1); ++i, ++idx) {
auto curPattern = mDashPattern[i % mDashCount];
if (offset < curPattern) break;
offset -= curPattern;
gap = !gap;
}
idx = idx % mDashCount;
}
for (uint32_t i = 0; i < cmd_count; i++) { for (uint32_t i = 0; i < cmd_count; i++) {
switch (*cmds) { switch (*cmds) {
case PathCommand::Close: { case PathCommand::Close: {
@ -1998,9 +2020,9 @@ void DashStroke::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Poi
} }
case PathCommand::MoveTo: { case PathCommand::MoveTo: {
// reset the dash state // reset the dash state
mCurrIdx = 0; mCurrIdx = idx;
mCurrLen = mDashPattern[0]; mCurrLen = mDashPattern[idx] - offset;
mCurOpGap = false; mCurOpGap = gap;
mPtStart = mPtCur = *pts; mPtStart = mPtCur = *pts;
pts++; pts++;
break; break;

View file

@ -87,7 +87,7 @@ public:
private: private:
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, uint32_t dash_count, const float* dash_pattern); void doDashStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count, uint32_t dash_count, const float* dash_pattern, float dash_offset);
float strokeRadius() const float strokeRadius() const
{ {
@ -122,7 +122,7 @@ private:
class DashStroke class DashStroke
{ {
public: public:
DashStroke(Array<PathCommand>* cmds, Array<Point>* pts, uint32_t dash_count, const float* dash_pattern); DashStroke(Array<PathCommand>* cmds, Array<Point>* pts, uint32_t dash_count, const float* dash_pattern, float dash_offset);
~DashStroke() = default; ~DashStroke() = default;
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);
@ -137,6 +137,7 @@ private:
Array<Point>* mPts; Array<Point>* mPts;
uint32_t mDashCount; uint32_t mDashCount;
const float* mDashPattern; const float* mDashPattern;
float mDashOffset;
float mCurrLen; float mCurrLen;
int32_t mCurrIdx; int32_t mCurrIdx;
bool mCurOpGap; bool mCurOpGap;