lottie/text: added range selector flags

Duplicated text document properties (fill color, stroke color, and stroke width) to the text range.

If these properties are not defined in the range selector, ThorVG incorrectly overrides them with default values.

The correct behavior is to preserve the original values. The added flags address this issue.

Co-Authored-By: Hermet Park<hermet@lottiefiles.com>
This commit is contained in:
Jinny You 2025-04-03 14:42:07 +09:00 committed by Mira Grudzinska
parent 91bd6f35b9
commit 08fa801461
3 changed files with 42 additions and 20 deletions

View file

@ -1169,25 +1169,13 @@ void LottieBuilder::updateText(LottieLayer* layer, float frameNo)
opacity = (uint8_t)(opacity - f * (opacity - (*s)->style.opacity(frameNo)));
shape->opacity(opacity);
auto rangeColor = (*s)->style.fillColor(frameNo); //TODO: use flag to check whether it was really set
if (tvg::equal(f, 1.0f)) color = rangeColor;
else {
color.rgb[0] = lerp<uint8_t>(color.rgb[0], rangeColor.rgb[0], f);
color.rgb[1] = lerp<uint8_t>(color.rgb[1], rangeColor.rgb[1], f);
color.rgb[2] = lerp<uint8_t>(color.rgb[2], rangeColor.rgb[2], f);
}
(*s)->color(frameNo, color, strokeColor, f, exps);
fillOpacity = (uint8_t)(fillOpacity - f * (fillOpacity - (*s)->style.fillOpacity(frameNo)));
shape->fill(color.rgb[0], color.rgb[1], color.rgb[2], fillOpacity);
shape->stroke(f * (*s)->style.strokeWidth(frameNo) / scale);
if ((*s)->style.flags.strokeWidth) shape->stroke(f * (*s)->style.strokeWidth(frameNo) / scale);
if (shape->strokeWidth() > 0.0f) {
auto rangeColor = (*s)->style.strokeColor(frameNo); //TODO: use flag to check whether it was really set
if (tvg::equal(f, 1.0f)) strokeColor = rangeColor;
else {
strokeColor.rgb[0] = lerp<uint8_t>(strokeColor.rgb[0], rangeColor.rgb[0], f);
strokeColor.rgb[1] = lerp<uint8_t>(strokeColor.rgb[1], rangeColor.rgb[1], f);
strokeColor.rgb[2] = lerp<uint8_t>(strokeColor.rgb[2], rangeColor.rgb[2], f);
}
strokeOpacity = (uint8_t)(strokeOpacity - f * (strokeOpacity - (*s)->style.strokeOpacity(frameNo)));
shape->stroke(strokeColor.rgb[0], strokeColor.rgb[1], strokeColor.rgb[2], strokeOpacity);
shape->order(doc.stroke.below);

View file

@ -258,6 +258,13 @@ struct LottieTextRange
enum Shape : uint8_t { Square = 1, RampUp, RampDown, Triangle, Round, Smooth };
enum Unit : uint8_t { Percent = 1, Index };
LottieTextRange()
{
style.flags.fillColor = 0;
style.flags.strokeColor = 0;
style.flags.strokeWidth = 0;
}
~LottieTextRange()
{
free(interpolator);
@ -275,6 +282,11 @@ struct LottieTextRange
LottieOpacity fillOpacity = 255;
LottieOpacity strokeOpacity = 255;
LottieOpacity opacity = 255;
struct {
bool fillColor : 1;
bool strokeColor : 1;
bool strokeWidth : 1;
} flags;
} style;
LottieFloat offset = 0.0f;
@ -292,6 +304,22 @@ struct LottieTextRange
bool expressible = false;
float factor(float frameNo, float totalLen, float idx);
void color(float frameNo, RGB24& fillColor, RGB24& strokeColor, float factor, LottieExpressions* exps)
{
if (style.flags.fillColor) {
auto color = style.fillColor(frameNo, exps);
fillColor.rgb[0] = tvg::lerp<uint8_t>(fillColor.rgb[0], color.rgb[0], factor);
fillColor.rgb[1] = tvg::lerp<uint8_t>(fillColor.rgb[1], color.rgb[1], factor);
fillColor.rgb[2] = tvg::lerp<uint8_t>(fillColor.rgb[2], color.rgb[2], factor);
}
if (style.flags.strokeColor) {
auto color = style.strokeColor(frameNo, exps);
strokeColor.rgb[0] = tvg::lerp<uint8_t>(strokeColor.rgb[0], color.rgb[0], factor);
strokeColor.rgb[1] = tvg::lerp<uint8_t>(strokeColor.rgb[1], color.rgb[1], factor);
strokeColor.rgb[2] = tvg::lerp<uint8_t>(strokeColor.rgb[2], color.rgb[2], factor);
}
}
};

View file

@ -1195,11 +1195,17 @@ void LottieParser::parseTextRange(LottieText* text)
while (auto key = nextObjectKey()) {
if (KEY_AS("t")) parseProperty<LottieProperty::Type::Float>(selector->style.letterSpacing);
else if (KEY_AS("ls")) parseProperty<LottieProperty::Type::Color>(selector->style.lineSpacing);
else if (KEY_AS("fc")) parseProperty<LottieProperty::Type::Color>(selector->style.fillColor);
else if (KEY_AS("fo")) parseProperty<LottieProperty::Type::Color>(selector->style.fillOpacity);
else if (KEY_AS("sw")) parseProperty<LottieProperty::Type::Float>(selector->style.strokeWidth);
else if (KEY_AS("sc")) parseProperty<LottieProperty::Type::Color>(selector->style.strokeColor);
else if (KEY_AS("so")) parseProperty<LottieProperty::Type::Opacity>(selector->style.strokeOpacity);
else if (KEY_AS("fc")) {
parseProperty<LottieProperty::Type::Color>(selector->style.fillColor);
selector->style.flags.fillColor = true;
} else if (KEY_AS("fo")) parseProperty<LottieProperty::Type::Color>(selector->style.fillOpacity);
else if (KEY_AS("sw")) {
parseProperty<LottieProperty::Type::Float>(selector->style.strokeWidth);
selector->style.flags.strokeWidth = true;
} else if (KEY_AS("sc")) {
parseProperty<LottieProperty::Type::Color>(selector->style.strokeColor);
selector->style.flags.strokeColor = true;
} else if (KEY_AS("so")) parseProperty<LottieProperty::Type::Opacity>(selector->style.strokeOpacity);
else if (KEY_AS("o")) parseProperty<LottieProperty::Type::Opacity>(selector->style.opacity);
else if (KEY_AS("p")) parseProperty<LottieProperty::Type::Position>(selector->style.position);
else if (KEY_AS("s")) parseProperty<LottieProperty::Type::Position>(selector->style.scale);