From 1354613d59ad62c81e861280bb49584d100c1b93 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 | 24 +++++++++---- src/loaders/lottie/tvgLottieModel.h | 47 +++++++++++++------------ src/loaders/lottie/tvgLottieParser.cpp | 13 +++---- 3 files changed, 47 insertions(+), 37 deletions(-) diff --git a/src/loaders/lottie/tvgLottieBuilder.cpp b/src/loaders/lottie/tvgLottieBuilder.cpp index 628a49ee..99aeacfc 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" @@ -230,10 +237,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, exps); - dashes[1] = dashes[0] + stroke->dashGap(frameNo, exps); - P(ctx->propagator)->strokeDash(dashes, 2, stroke->dashOffset(frameNo, 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, 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]; + P(ctx->propagator)->strokeDash(dashes, size, stroke->dashattr->offset(frameNo, exps)); } else { ctx->propagator->stroke(nullptr, 0); } @@ -478,7 +490,7 @@ void LottieBuilder::updateRect(LottieGroup* parent, LottieObject** child, float } else { r = std::min({r, size.x * 0.5f, size.y * 0.5f}); } - + if (!ctx->repeaters.empty()) { auto shape = rect->pooling(); shape->reset(); @@ -528,7 +540,7 @@ static void _appendCircle(Shape* shape, float cx, float cy, float rx, float ry, points[i] *= *transform; } } - + shape->appendPath(commands, cmdsCnt, points, ptsCnt); } diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 15708124..4f4b63dc 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -37,36 +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, LottieExpressions* exps) - { - return dash(0)(frameNo, exps); - } - float dashGap(float frameNo, LottieExpressions* exps) + LottieFloat& dashOffset() { - return dash(2)(frameNo, exps); - } - - float dashSize(float frameNo, LottieExpressions* exps) - { - auto d = dash(1)(frameNo, exps); - if (d == 0.0f) return 0.1f; - else return d; + if (!dashattr) dashattr = new DashAttr; + return dashattr->offset; } LottieFloat width = 0.0f; @@ -615,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); } @@ -724,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 e05d58f6..73e63dd2 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -651,15 +651,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(key); } }