lottie/slot: Support expressions overriding

issue: #3168

Co-Authored-By: Hermet Park <hermet@lottiefiles.com>
This commit is contained in:
Jinny You 2025-02-24 16:57:53 +08:00 committed by Mira Grudzinska
parent dec4b141ec
commit 7c27ae021d
3 changed files with 76 additions and 42 deletions

View file

@ -926,7 +926,7 @@ struct LottieSlot
void assign(LottieObject* target, bool byDefault); void assign(LottieObject* target, bool byDefault);
void reset(); void reset();
LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type) LottieSlot(LottieLayer* layer, LottieObject* parent, char* sid, LottieObject* obj, LottieProperty::Type type) : context{layer, parent}, sid(sid), type(type)
{ {
pairs.push({obj}); pairs.push({obj});
} }
@ -940,9 +940,15 @@ struct LottieSlot
} }
} }
struct {
LottieLayer* layer;
LottieObject* parent;
} context;
char* sid; char* sid;
Array<Pair> pairs; Array<Pair> pairs;
LottieProperty::Type type; LottieProperty::Type type;
bool overridden = false; bool overridden = false;
}; };

View file

@ -507,7 +507,7 @@ void LottieParser::registerSlot(LottieObject* obj, const char* sid)
(*slot)->pairs.push({obj}); (*slot)->pairs.push({obj});
return; return;
} }
comp->slots.push(new LottieSlot(strdup(sid), obj, type)); comp->slots.push(new LottieSlot(context.layer, context.parent, strdup(sid), obj, type));
} }
@ -1538,41 +1538,36 @@ bool LottieParser::apply(LottieSlot* slot, bool byDefault)
//OPTIMIZE: we can create the property directly, without object //OPTIMIZE: we can create the property directly, without object
LottieObject* obj = nullptr; //slot object LottieObject* obj = nullptr; //slot object
context = {slot->context.layer, slot->context.parent};
switch (slot->type) { switch (slot->type) {
case LottieProperty::Type::Position: { case LottieProperty::Type::Position: {
obj = new LottieTransform; obj = new LottieTransform;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::Position>(static_cast<LottieTransform*>(obj)->position); parseSlotProperty<LottieProperty::Type::Position>(static_cast<LottieTransform*>(obj)->position);
break; break;
} }
case LottieProperty::Type::Point: { case LottieProperty::Type::Point: {
obj = new LottieTransform; obj = new LottieTransform;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::Point>(static_cast<LottieTransform*>(obj)->scale); parseSlotProperty<LottieProperty::Type::Point>(static_cast<LottieTransform*>(obj)->scale);
break; break;
} }
case LottieProperty::Type::Float: { case LottieProperty::Type::Float: {
obj = new LottieTransform; obj = new LottieTransform;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::Float>(static_cast<LottieTransform*>(obj)->rotation); parseSlotProperty<LottieProperty::Type::Float>(static_cast<LottieTransform*>(obj)->rotation);
break; break;
} }
case LottieProperty::Type::Opacity: { case LottieProperty::Type::Opacity: {
obj = new LottieSolid; obj = new LottieSolid;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::Opacity>(static_cast<LottieSolid*>(obj)->opacity); parseSlotProperty<LottieProperty::Type::Opacity>(static_cast<LottieSolid*>(obj)->opacity);
break; break;
} }
case LottieProperty::Type::Color: { case LottieProperty::Type::Color: {
obj = new LottieSolid; obj = new LottieSolid;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::Color>(static_cast<LottieSolid*>(obj)->color); parseSlotProperty<LottieProperty::Type::Color>(static_cast<LottieSolid*>(obj)->color);
break; break;
} }
case LottieProperty::Type::ColorStop: { case LottieProperty::Type::ColorStop: {
obj = new LottieGradient; obj = new LottieGradient;
context.parent = obj;
while (auto key = nextObjectKey()) { while (auto key = nextObjectKey()) {
if (KEY_AS("p")) parseColorStop(static_cast<LottieGradient*>(obj)); if (KEY_AS("p")) parseColorStop(static_cast<LottieGradient*>(obj));
else skip(key); else skip(key);
@ -1581,7 +1576,6 @@ bool LottieParser::apply(LottieSlot* slot, bool byDefault)
} }
case LottieProperty::Type::TextDoc: { case LottieProperty::Type::TextDoc: {
obj = new LottieText; obj = new LottieText;
context.parent = obj;
parseSlotProperty<LottieProperty::Type::TextDoc>(static_cast<LottieText*>(obj)->doc); parseSlotProperty<LottieProperty::Type::TextDoc>(static_cast<LottieText*>(obj)->doc);
break; break;
} }
@ -1590,7 +1584,6 @@ bool LottieParser::apply(LottieSlot* slot, bool byDefault)
if (KEY_AS("p")) obj = parseAsset(); if (KEY_AS("p")) obj = parseAsset();
else skip(key); else skip(key);
} }
context.parent = obj;
break; break;
} }
default: break; default: break;

View file

@ -34,6 +34,7 @@
struct LottieFont; struct LottieFont;
struct LottieLayer; struct LottieLayer;
struct LottieObject; struct LottieObject;
struct LottieProperty;
template<typename T> template<typename T>
@ -109,23 +110,6 @@ struct LottieVectorFrame
}; };
//Property would have an either keyframes or single value.
struct LottieProperty
{
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Image, Invalid };
LottieExpression* exp = nullptr;
Type type;
uint8_t ix; //property index
//TODO: Apply common bodies?
virtual ~LottieProperty() {}
virtual uint32_t frameCnt() = 0;
virtual uint32_t nearest(float time) = 0;
virtual float frameNo(int32_t key) = 0;
};
struct LottieExpression struct LottieExpression
{ {
enum LoopMode : uint8_t { None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue }; enum LoopMode : uint8_t { None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue };
@ -143,6 +127,18 @@ struct LottieExpression
LoopMode mode = None; LoopMode mode = None;
} loop; } loop;
LottieExpression() {}
LottieExpression(const LottieExpression* rhs)
{
code = strdup(rhs->code);
comp = rhs->comp;
layer = rhs->layer;
object = rhs->object;
property = rhs->property;
disabled = rhs->disabled;
}
~LottieExpression() ~LottieExpression()
{ {
free(code); free(code);
@ -150,6 +146,36 @@ struct LottieExpression
}; };
//Property would have an either keyframes or single value.
struct LottieProperty
{
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Image, Invalid };
LottieExpression* exp = nullptr;
Type type;
uint8_t ix; //property index
//TODO: Apply common bodies?
virtual ~LottieProperty() {}
virtual uint32_t frameCnt() = 0;
virtual uint32_t nearest(float time) = 0;
virtual float frameNo(int32_t key) = 0;
bool copy(LottieProperty* rhs, bool shallow)
{
if (!rhs->exp) return false;
if (shallow) {
exp = rhs->exp;
rhs->exp = nullptr;
} else {
exp = new LottieExpression(rhs->exp);
}
exp->property = this;
return true;
}
};
static void _copy(PathSet* pathset, Array<Point>& outPts, Matrix* transform) static void _copy(PathSet* pathset, Array<Point>& outPts, Matrix* transform)
{ {
Array<Point> inPts; Array<Point> inPts;
@ -263,7 +289,7 @@ struct LottieGenericProperty : LottieProperty
LottieGenericProperty(const LottieGenericProperty<T>& rhs) LottieGenericProperty(const LottieGenericProperty<T>& rhs)
{ {
copy(rhs); copy(const_cast<LottieGenericProperty<T>&>(rhs));
type = rhs.type; type = rhs.type;
ix = rhs.ix; ix = rhs.ix;
} }
@ -336,12 +362,15 @@ struct LottieGenericProperty : LottieProperty
return operator()(frameNo); return operator()(frameNo);
} }
void copy(const LottieGenericProperty<T>& rhs, bool shallow = true) void copy(LottieGenericProperty<T>& rhs, bool shallow = true)
{ {
if (LottieProperty::copy(&rhs, shallow)) return;
if (rhs.frames) { if (rhs.frames) {
if (shallow) { if (shallow) {
frames = rhs.frames; frames = rhs.frames;
const_cast<LottieGenericProperty<T>&>(rhs).frames = nullptr; const_cast<LottieGenericProperty<T>&>(rhs).frames = nullptr;
rhs.frames = nullptr;
} else { } else {
frames = new Array<LottieScalarFrame<T>>; frames = new Array<LottieScalarFrame<T>>;
*frames = *rhs.frames; *frames = *rhs.frames;
@ -518,7 +547,7 @@ struct LottieColorStop : LottieProperty
LottieColorStop(const LottieColorStop& rhs) LottieColorStop(const LottieColorStop& rhs)
{ {
copy(rhs); copy(const_cast<LottieColorStop&>(rhs));
type = rhs.type; type = rhs.type;
ix = rhs.ix; ix = rhs.ix;
} }
@ -629,19 +658,21 @@ struct LottieColorStop : LottieProperty
return fill->colorStops(result.data, count); return fill->colorStops(result.data, count);
} }
void copy(const LottieColorStop& rhs, bool shallow = true) void copy(LottieColorStop& rhs, bool shallow = true)
{ {
if (LottieProperty::copy(&rhs, shallow)) return;
if (rhs.frames) { if (rhs.frames) {
if (shallow) { if (shallow) {
frames = rhs.frames; frames = rhs.frames;
const_cast<LottieColorStop&>(rhs).frames = nullptr; rhs.frames = nullptr;
} else { } else {
frames = new Array<LottieScalarFrame<ColorStop>>; frames = new Array<LottieScalarFrame<ColorStop>>;
*frames = *rhs.frames; *frames = *rhs.frames;
} }
} else { } else {
value = rhs.value; value = rhs.value;
const_cast<LottieColorStop&>(rhs).value = ColorStop(); rhs.value = ColorStop();
} }
populated = rhs.populated; populated = rhs.populated;
count = rhs.count; count = rhs.count;
@ -775,7 +806,7 @@ struct LottieTextDoc : LottieProperty
LottieTextDoc(const LottieTextDoc& rhs) LottieTextDoc(const LottieTextDoc& rhs)
{ {
copy(rhs); copy(const_cast<LottieTextDoc&>(rhs));
type = rhs.type; type = rhs.type;
ix = rhs.ix; ix = rhs.ix;
} }
@ -853,20 +884,22 @@ struct LottieTextDoc : LottieProperty
return frame->value; return frame->value;
} }
void copy(const LottieTextDoc& rhs, bool shallow = true) void copy(LottieTextDoc& rhs, bool shallow = true)
{ {
if (LottieProperty::copy(&rhs, shallow)) return;
if (rhs.frames) { if (rhs.frames) {
if (shallow) { if (shallow) {
frames = rhs.frames; frames = rhs.frames;
const_cast<LottieTextDoc&>(rhs).frames = nullptr; rhs.frames = nullptr;
} else { } else {
frames = new Array<LottieScalarFrame<TextDocument>>; frames = new Array<LottieScalarFrame<TextDocument>>;
*frames = *rhs.frames; *frames = *rhs.frames;
} }
} else { } else {
value = rhs.value; value = rhs.value;
const_cast<LottieTextDoc&>(rhs).value.text = nullptr; rhs.value.text = nullptr;
const_cast<LottieTextDoc&>(rhs).value.name = nullptr; rhs.value.name = nullptr;
} }
} }
@ -889,7 +922,7 @@ struct LottieBitmap : LottieProperty
LottieBitmap(const LottieBitmap& rhs) LottieBitmap(const LottieBitmap& rhs)
{ {
copy(rhs); copy(const_cast<LottieBitmap&>(rhs));
type = rhs.type; type = rhs.type;
ix = rhs.ix; ix = rhs.ix;
} }
@ -912,8 +945,10 @@ struct LottieBitmap : LottieProperty
uint32_t nearest(float time) override { return 0; } uint32_t nearest(float time) override { return 0; }
float frameNo(int32_t key) override { return 0; } float frameNo(int32_t key) override { return 0; }
void copy(const LottieBitmap& rhs, bool shallow = true) void copy(LottieBitmap& rhs, bool shallow = true)
{ {
if (LottieProperty::copy(&rhs, shallow)) return;
if (shallow) { if (shallow) {
b64Data = rhs.b64Data; b64Data = rhs.b64Data;
mimeType = rhs.mimeType; mimeType = rhs.mimeType;
@ -927,8 +962,8 @@ struct LottieBitmap : LottieProperty
width = rhs.width; width = rhs.width;
height = rhs.height; height = rhs.height;
const_cast<LottieBitmap&>(rhs).b64Data = nullptr; rhs.b64Data = nullptr;
const_cast<LottieBitmap&>(rhs).mimeType = nullptr; rhs.mimeType = nullptr;
} }
}; };