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
This commit is contained in:
Mira Grudzinska 2025-02-11 22:28:40 +01:00
parent 7968b9668e
commit 1354613d59
3 changed files with 47 additions and 37 deletions

View file

@ -22,6 +22,13 @@
#include <cstring>
#include <algorithm>
#ifdef _WIN32
#include <malloc.h>
#elif defined(__linux__) || defined(__ZEPHYR__)
#include <alloca.h>
#else
#include <stdlib.h>
#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);
}

View file

@ -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);
}

View file

@ -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<LottieProperty::Type::Float>(stroke->dash(idx));
if (KEY_AS("n")) style = getString();
else if (KEY_AS("v")) {
if (style && !strcmp("o", style)) parseProperty<LottieProperty::Type::Float>(stroke->dashOffset());
else parseProperty<LottieProperty::Type::Float>(stroke->dashValue());
} else skip(key);
}
}