From 391777091c973a70f7424469d2ca8920a7f677b6 Mon Sep 17 00:00:00 2001 From: Mira Grudzinska Date: Tue, 11 Feb 2025 22:28:40 +0100 Subject: [PATCH] lottie: ++ dashed stroke support Until now lottie loader supported only a single dash-gap pair, and only the case where only the dash was provided was handled correctly. When both values were provided, the gap was incorrectly increased by the dash value. If more values were supplied, only the last pair was considered. @Issue: https://github.com/thorvg/thorvg/issues/3191 --- src/loaders/lottie/tvgLottieBuilder.cpp | 20 ++++++++--- src/loaders/lottie/tvgLottieModel.h | 46 +++++++++++++------------ src/loaders/lottie/tvgLottieParser.cpp | 13 +++---- 3 files changed, 45 insertions(+), 34 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 407c384a..cffd3a88 100644 --- a/src/loaders/lottie/tvgLottieBuilder.cpp +++ b/src/loaders/lottie/tvgLottieBuilder.cpp @@ -22,6 +22,13 @@ #include #include +#ifdef _WIN32 + #include +#elif defined(__linux__) || defined(__ZEPHYR__) + #include +#else + #include +#endif #include "tvgCommon.h" #include "tvgMath.h" @@ -220,10 +227,15 @@ static void _updateStroke(LottieStroke* stroke, float frameNo, RenderContext* ct ctx->propagator->strokeMiterlimit(stroke->miterLimit); if (stroke->dashattr) { - float dashes[2]; - dashes[0] = stroke->dashSize(frameNo, tween, exps); - dashes[1] = dashes[0] + stroke->dashGap(frameNo, tween, exps); - ctx->propagator->strokeDash(dashes, 2, stroke->dashOffset(frameNo, tween, exps)); + auto size = stroke->dashattr->size == 1 ? 2 : stroke->dashattr->size; + auto dashes = (float*)alloca(size * sizeof(float)); + for (uint8_t i = 0; i < stroke->dashattr->size; ++i) { + auto value = stroke->dashattr->values[i](frameNo, tween, exps); + //FIXME: allow the zero value in the engine level. + dashes[i] = value < FLT_EPSILON ? 0.01f : value; + } + if (stroke->dashattr->size == 1) dashes[1] = dashes[0]; + ctx->propagator->strokeDash(dashes, size, stroke->dashattr->offset(frameNo, tween, exps)); } else { ctx->propagator->strokeDash(nullptr, 0); } diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 4299cda7..f18f8c9f 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -37,35 +37,39 @@ struct LottieStroke { struct DashAttr { - //0: offset, 1: dash, 2: gap - LottieFloat value[3] = {0.0f, 0.0f, 0.0f}; + LottieFloat offset = 0.0f; + LottieFloat* values = nullptr; + uint8_t size = 0; + uint8_t allocated = 0; }; virtual ~LottieStroke() { + if (dashattr) delete[] dashattr->values; delete(dashattr); } - LottieFloat& dash(int no) + + LottieFloat& dashValue() { if (!dashattr) dashattr = new DashAttr; - return dashattr->value[no]; + + if (dashattr->size + 1 > dashattr->allocated) { + dashattr->allocated = dashattr->size + 2; + auto newValues = new LottieFloat[dashattr->allocated]; + for (uint8_t i = 0; i < dashattr->size; ++i) newValues[i] = LottieFloat(dashattr->values[i]); + delete[] dashattr->values; + dashattr->values = newValues; + } + + return dashattr->values[dashattr->size++]; } - float dashOffset(float frameNo, Tween& tween, LottieExpressions* exps) - { - return dash(0)(frameNo, tween, exps); - } - float dashSize(float frameNo, Tween& tween, LottieExpressions* exps) + LottieFloat& dashOffset() { - auto d = dash(1)(frameNo, tween, exps); - return (d > 0.0f) ? d : 0.0f; - } - - float dashGap(float frameNo, Tween& tween, LottieExpressions* exps) - { - return dash(2)(frameNo, tween, exps); + if (!dashattr) dashattr = new DashAttr; + return dashattr->offset; } LottieFloat width = 0.0f; @@ -614,9 +618,8 @@ struct LottieSolidStroke : LottieSolid, LottieStroke { if (width.ix == ix) return &width; if (dashattr) { - if (dashattr->value[0].ix == ix) return &dashattr->value[0]; - if (dashattr->value[1].ix == ix) return &dashattr->value[1]; - if (dashattr->value[2].ix == ix) return &dashattr->value[2]; + for (uint8_t i = 0; i < dashattr->size ; ++i) + if (dashattr->values[i].ix == ix) return &dashattr->values[i]; } return LottieSolid::property(ix); } @@ -723,9 +726,8 @@ struct LottieGradientStroke : LottieGradient, LottieStroke { if (width.ix == ix) return &width; if (dashattr) { - if (dashattr->value[0].ix == ix) return &dashattr->value[0]; - if (dashattr->value[1].ix == ix) return &dashattr->value[1]; - if (dashattr->value[2].ix == ix) return &dashattr->value[2]; + for (uint8_t i = 0; i < dashattr->size ; ++i) + if (dashattr->values[i].ix == ix) return &dashattr->values[i]; } return LottieGradient::property(ix); } diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index b8625f74..e36c9f10 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -612,15 +612,12 @@ void LottieParser::parseStrokeDash(LottieStroke* stroke) enterArray(); while (nextArrayValue()) { enterObject(); - int idx = 0; + const char* style = nullptr; while (auto key = nextObjectKey()) { - if (KEY_AS("n")) { - auto style = getString(); - if (!strcmp("o", style)) idx = 0; //offset - else if (!strcmp("d", style)) idx = 1; //dash - else if (!strcmp("g", style)) idx = 2; //gap - } else if (KEY_AS("v")) { - parseProperty(stroke->dash(idx)); + if (KEY_AS("n")) style = getString(); + else if (KEY_AS("v")) { + if (style && !strcmp("o", style)) parseProperty(stroke->dashOffset()); + else parseProperty(stroke->dashValue()); } else skip(); } }