From c7822cca53f9d7174b98d3ada558bd7ce9b2650c Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Thu, 30 May 2024 21:10:37 +0200 Subject: [PATCH] common: strokeTrim api introduced New api sets/gets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible. @issue: https://github.com/thorvg/thorvg/issues/2190 --- inc/thorvg.h | 31 ++++++++++++++++++-- src/loaders/lottie/tvgLottieBuilder.cpp | 2 +- src/loaders/lottie/tvgLottieModel.cpp | 30 ++++--------------- src/renderer/sw_engine/tvgSwShape.cpp | 7 +++-- src/renderer/tvgRender.h | 2 +- src/renderer/tvgShape.cpp | 15 ++++++++++ src/renderer/tvgShape.h | 39 +++++++++++++++++++++---- 7 files changed, 88 insertions(+), 38 deletions(-) diff --git a/inc/thorvg.h b/inc/thorvg.h index f7396050..a3d757a3 100644 --- a/inc/thorvg.h +++ b/inc/thorvg.h @@ -1044,7 +1044,6 @@ public: */ Result stroke(StrokeJoin join) noexcept; - /** * @brief Sets the stroke miterlimit. * @@ -1056,6 +1055,22 @@ public: */ Result strokeMiterlimit(float miterlimit) noexcept; + /** + * @brief Sets the trim of the stroke along the defined path segment, allowing control over which part of the stroke is visible. + * + * The values of the arguments @p begin, @p end, and @p offset are in the range of 0.0 to 1.0, representing the beginning of the path and the end, respectively. + * + * @param[in] begin Specifies the start of the segment to display along the path. + * @param[in] end Specifies the end of the segment to display along the path. + * @param[in] simultaneous Determines how to trim multiple paths within a single shape. If set to @c true (default), trimming is applied simultaneously to all paths; + * Otherwise, all paths are treated as a single entity with a combined length equal to the sum of their individual lengths and are trimmed as such. + * + * @retval Result::Success when succeed. + * + * @note Experimental API + */ + Result strokeTrim(float begin, float end, bool simultaneous = true) noexcept; + /** * @brief Sets the solid color for all of the figures from the path. * @@ -1095,7 +1110,6 @@ public: */ Result fill(FillRule r) noexcept; - /** * @brief Sets the rendering order of the stroke and the fill. * @@ -1107,7 +1121,6 @@ public: */ Result order(bool strokeFirst) noexcept; - /** * @brief Gets the commands data of the path. * @@ -1210,6 +1223,18 @@ public: */ float strokeMiterlimit() const noexcept; + /** + * @brief Gets the trim of the stroke along the defined path segment. + * + * @param[out] begin The starting point of the segment to display along the path. + * @param[out] end Specifies the end of the segment to display along the path. + * + * @retval @c true if trimming is applied simultaneously to all paths of the shape, @c false otherwise. + * + * @note Experimental API + */ + bool strokeTrim(float* begin, float* end) const noexcept; + /** * @brief Creates a new Shape object. * diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 257e1004..bf247f5f 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -916,7 +916,7 @@ static void _updateTrimpath(TVG_UNUSED LottieGroup* parent, LottieObject** child end = (length * end) + pbegin; } - P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Individual ? true : false); + P(ctx->propagator)->strokeTrim(begin, end, trimpath->type == LottieTrimpath::Type::Simultaneous); } diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 845fc4d4..b27ce733 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -121,11 +121,11 @@ LottieImage::~LottieImage() void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpressions* exps) { - auto s = this->start(frameNo, exps) * 0.01f; - auto e = this->end(frameNo, exps) * 0.01f; + start = this->start(frameNo, exps) * 0.01f; + end = this->end(frameNo, exps) * 0.01f; auto o = fmodf(this->offset(frameNo, exps), 360.0f) / 360.0f; //0 ~ 1 - auto diff = fabs(s - e); + auto diff = fabs(start - end); if (mathZero(diff)) { start = 0.0f; end = 0.0f; @@ -137,28 +137,8 @@ void LottieTrimpath::segment(float frameNo, float& start, float& end, LottieExpr return; } - s += o; - e += o; - - auto loop = true; - - //no loop - if (s > 1.0f && e > 1.0f) loop = false; - if (s < 0.0f && e < 0.0f) loop = false; - if (s >= 0.0f && s <= 1.0f && e >= 0.0f && e <= 1.0f) loop = false; - - if (s > 1.0f) s -= 1.0f; - if (s < 0.0f) s += 1.0f; - if (e > 1.0f) e -= 1.0f; - if (e < 0.0f) e += 1.0f; - - if (loop) { - start = s > e ? s : e; - end = s < e ? s : e; - } else { - start = s < e ? s : e; - end = s > e ? s : e; - } + start += o; + end += o; } diff --git a/src/renderer/sw_engine/tvgSwShape.cpp b/src/renderer/sw_engine/tvgSwShape.cpp index 0e1177c2..d40c789e 100644 --- a/src/renderer/sw_engine/tvgSwShape.cpp +++ b/src/renderer/sw_engine/tvgSwShape.cpp @@ -331,8 +331,9 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans break; } case PathCommand::MoveTo: { - if (rshape->stroke->trim.individual) _dashMoveTo(dash, pts); - else _dashMoveTo(dash, offIdx, offset, pts); + if (rshape->stroke->trim.simultaneous) _dashMoveTo(dash, offIdx, offset, pts); + else _dashMoveTo(dash, pts); + ++pts; break; } @@ -371,7 +372,7 @@ static float _outlineLength(const RenderShape* rshape) const Point* close = nullptr; auto length = 0.0f; auto slength = -1.0f; - auto simultaneous = !rshape->stroke->trim.individual; + auto simultaneous = rshape->stroke->trim.simultaneous; //Compute the whole length while (cmdCnt-- > 0) { diff --git a/src/renderer/tvgRender.h b/src/renderer/tvgRender.h index 6ea516c2..8f28d37d 100644 --- a/src/renderer/tvgRender.h +++ b/src/renderer/tvgRender.h @@ -142,7 +142,7 @@ struct RenderStroke struct { float begin = 0.0f; float end = 1.0f; - bool individual = false; + bool simultaneous = true; } trim; ~RenderStroke() diff --git a/src/renderer/tvgShape.cpp b/src/renderer/tvgShape.cpp index 4cc8f649..8305d066 100644 --- a/src/renderer/tvgShape.cpp +++ b/src/renderer/tvgShape.cpp @@ -362,6 +362,7 @@ Result Shape::stroke(StrokeJoin join) noexcept return Result::Success; } + Result Shape::strokeMiterlimit(float miterlimit) noexcept { // https://www.w3.org/TR/SVG2/painting.html#LineJoin @@ -385,12 +386,26 @@ StrokeJoin Shape::strokeJoin() const noexcept return pImpl->rs.strokeJoin(); } + float Shape::strokeMiterlimit() const noexcept { return pImpl->rs.strokeMiterlimit(); } +Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept +{ + pImpl->strokeTrim(begin, end, simultaneous); + return Result::Success; +} + + +bool Shape::strokeTrim(float* begin, float* end) const noexcept +{ + return pImpl->strokeTrim(begin, end); +} + + Result Shape::fill(FillRule r) noexcept { pImpl->rs.rule = r; diff --git a/src/renderer/tvgShape.h b/src/renderer/tvgShape.h index 55335214..c57550f2 100644 --- a/src/renderer/tvgShape.h +++ b/src/renderer/tvgShape.h @@ -216,21 +216,50 @@ struct Shape::Impl return true; } - bool strokeTrim(float begin, float end, bool individual) + void strokeTrim(float begin, float end, bool simultaneous) { if (!rs.stroke) { - if (begin == 0.0f && end == 1.0f) return true; + if (begin == 0.0f && end == 1.0f) return; rs.stroke = new RenderStroke(); } - if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end)) return true; + if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) && + rs.stroke->trim.simultaneous == simultaneous) return; + + auto loop = true; + + if (begin > 1.0f && end > 1.0f) loop = false; + if (begin < 0.0f && end < 0.0f) loop = false; + if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false; + + if (begin > 1.0f) begin -= 1.0f; + if (begin < 0.0f) begin += 1.0f; + if (end > 1.0f) end -= 1.0f; + if (end < 0.0f) end += 1.0f; + + if ((loop && begin < end) || (!loop && begin > end)) { + auto tmp = begin; + begin = end; + end = tmp; + } rs.stroke->trim.begin = begin; rs.stroke->trim.end = end; - rs.stroke->trim.individual = individual; + rs.stroke->trim.simultaneous = simultaneous; flag |= RenderUpdateFlag::Stroke; + } - return true; + bool strokeTrim(float* begin, float* end) + { + if (rs.stroke) { + if (begin) *begin = rs.stroke->trim.begin; + if (end) *end = rs.stroke->trim.end; + return rs.stroke->trim.simultaneous; + } else { + if (begin) *begin = 0.0f; + if (end) *end = 1.0f; + return false; + } } bool strokeCap(StrokeCap cap)