From acbc3b3ba97db716e4322a6a38f29e25b4c0492e Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 13 Feb 2025 23:54:24 +0100 Subject: [PATCH] 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 --- src/renderer/gl_engine/tvgGlTessellator.cpp | 38 ++++++++++++++++----- src/renderer/gl_engine/tvgGlTessellator.h | 5 +-- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/renderer/gl_engine/tvgGlTessellator.cpp b/src/renderer/gl_engine/tvgGlTessellator.cpp index 200671f1..7bd23e7b 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.cpp +++ b/src/renderer/gl_engine/tvgGlTessellator.cpp @@ -1564,10 +1564,11 @@ void Stroker::stroke(const RenderShape *rshape) } 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); - else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern); + else doDashStroke(cmds, cmdCnt, pts, ptsCnt, dashCnt, dash_pattern, dash_offset); free(trimmedCmds); 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 dash_cmds{}; Array 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_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); 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 *cmds, Array *pts, uint32_t dash_count, const float *dash_pattern) +DashStroke::DashStroke(Array *cmds, Array *pts, uint32_t dash_count, const float *dash_pattern, float dash_offset) : mCmds(cmds), mPts(pts), mDashCount(dash_count), mDashPattern(dash_pattern), + mDashOffset(dash_offset), mCurrLen(), mCurrIdx(), mCurOpGap(false), @@ -1990,6 +1992,26 @@ DashStroke::DashStroke(Array *cmds, Array *pts, uint32_t das 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++) { switch (*cmds) { case PathCommand::Close: { @@ -1998,9 +2020,9 @@ void DashStroke::doStroke(const PathCommand *cmds, uint32_t cmd_count, const Poi } case PathCommand::MoveTo: { // reset the dash state - mCurrIdx = 0; - mCurrLen = mDashPattern[0]; - mCurOpGap = false; + mCurrIdx = idx; + mCurrLen = mDashPattern[idx] - offset; + mCurOpGap = gap; mPtStart = mPtCur = *pts; pts++; break; diff --git a/src/renderer/gl_engine/tvgGlTessellator.h b/src/renderer/gl_engine/tvgGlTessellator.h index 0c5c8817..0d497974 100644 --- a/src/renderer/gl_engine/tvgGlTessellator.h +++ b/src/renderer/gl_engine/tvgGlTessellator.h @@ -87,7 +87,7 @@ public: private: 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 { @@ -122,7 +122,7 @@ private: class DashStroke { public: - DashStroke(Array* cmds, Array* pts, uint32_t dash_count, const float* dash_pattern); + DashStroke(Array* cmds, Array* pts, uint32_t dash_count, const float* dash_pattern, float dash_offset); ~DashStroke() = default; void doStroke(const PathCommand* cmds, uint32_t cmd_count, const Point* pts, uint32_t pts_count); @@ -137,6 +137,7 @@ private: Array* mPts; uint32_t mDashCount; const float* mDashPattern; + float mDashOffset; float mCurrLen; int32_t mCurrIdx; bool mCurOpGap;