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 committed by Hermet Park
parent a933465430
commit 391777091c
3 changed files with 45 additions and 34 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"
@ -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);
}

View file

@ -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;
}
float dashOffset(float frameNo, Tween& tween, LottieExpressions* exps)
{
return dash(0)(frameNo, tween, exps);
return dashattr->values[dashattr->size++];
}
float dashSize(float frameNo, Tween& tween, LottieExpressions* exps)
{
auto d = dash(1)(frameNo, tween, exps);
return (d > 0.0f) ? d : 0.0f;
}
float dashGap(float frameNo, Tween& tween, LottieExpressions* exps)
LottieFloat& dashOffset()
{
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);
}

View file

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