lottie: Support the slot reverting feature

Implemented the ability to revert Lottie slots by calling override with nullptr.
This functionality allows for the complete reversal of applied slots.

usage:
- `animation->override(nullptr)`

Co-Authored-By: Hermet Park <hermet@lottiefiles.com>
This commit is contained in:
Jinny You 2024-03-21 15:11:43 +09:00 committed by Hermet Park
parent 48f58637e1
commit b3f09cab6b
8 changed files with 112 additions and 34 deletions

View file

@ -2357,7 +2357,7 @@ TVG_API Tvg_Animation* tvg_lottie_animation_new();
* \brief Override the lottie properties through the slot data. (Experimental API) * \brief Override the lottie properties through the slot data. (Experimental API)
* *
* \param[in] animation The Tvg_Animation object to override the property with the slot. * \param[in] animation The Tvg_Animation object to override the property with the slot.
* \param[in] slot The lottie slot data in json. * \param[in] slot The Lottie slot data in json, or @c nullptr to reset.
* *
* \return Tvg_Animation A new Tvg_LottieAnimation object. * \return Tvg_Animation A new Tvg_LottieAnimation object.
* \retval TVG_RESULT_SUCCESS Succeed. * \retval TVG_RESULT_SUCCESS Succeed.

View file

@ -25,7 +25,7 @@ public:
/** /**
* @brief Override Lottie properties using slot data. * @brief Override Lottie properties using slot data.
* *
* @param[in] slot The Lottie slot data in JSON format. * @param[in] slot The Lottie slot data in JSON format to override, or @c nullptr to reset.
* *
* @retval Result::Success When succeed. * @retval Result::Success When succeed.
* @retval Result::InsufficientCondition In case the animation is not loaded. * @retval Result::InsufficientCondition In case the animation is not loaded.

View file

@ -302,8 +302,12 @@ Paint* LottieLoader::paint()
bool LottieLoader::override(const char* slot) bool LottieLoader::override(const char* slot)
{ {
if (!slot || !comp || comp->slots.count == 0) return false; if (!comp || comp->slots.count == 0) return false;
auto success = true;
//override slots
if (slot) {
//TODO: Crashed, does this necessary? //TODO: Crashed, does this necessary?
auto temp = strdup(slot); auto temp = strdup(slot);
@ -311,19 +315,26 @@ bool LottieLoader::override(const char* slot)
LottieParser parser(temp, dirName); LottieParser parser(temp, dirName);
auto idx = 0; auto idx = 0;
auto success = true;
while (auto sid = parser.sid(idx == 0)) { while (auto sid = parser.sid(idx == 0)) {
for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) { for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) {
if (strcmp((*s)->sid, sid)) continue; if (strcmp((*s)->sid, sid)) continue;
if (!parser.parse(*s)) success = false; if (!parser.apply(*s)) success = false;
break; break;
} }
++idx; ++idx;
} }
if (idx < 1) success = false; if (idx < 1) success = false;
free(temp); free(temp);
overriden = success;
//reset slots
} else if (overriden) {
for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) {
(*s)->reset();
}
overriden = false;
}
return success; return success;
} }

View file

@ -44,6 +44,7 @@ public:
char* dirName = nullptr; //base resource directory char* dirName = nullptr; //base resource directory
bool copy = false; //"content" is owned by this loader bool copy = false; //"content" is owned by this loader
bool overriden = false; //overridden properties with slots.
LottieLoader(); LottieLoader();
~LottieLoader(); ~LottieLoader();

View file

@ -632,19 +632,89 @@ struct LottieLayer : LottieGroup
struct LottieSlot struct LottieSlot
{ {
char* sid; struct Pair {
Array<LottieObject*> objs; LottieObject* obj;
LottieProperty::Type type; LottieProperty* prop;
};
void assign(LottieObject* target)
{
//apply slot object to all targets
for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) {
//backup the original properties before overwriting
if (!overriden) {
switch (type) {
case LottieProperty::Type::ColorStop: {
pair->prop = new LottieColorStop;
*static_cast<LottieColorStop*>(pair->prop) = static_cast<LottieGradient*>(pair->obj)->colorStops;
break;
}
case LottieProperty::Type::Color: {
pair->prop = new LottieColor;
*static_cast<LottieColor*>(pair->prop) = static_cast<LottieSolid*>(pair->obj)->color;
break;
}
case LottieProperty::Type::TextDoc: {
pair->prop = new LottieTextDoc;
*static_cast<LottieTextDoc*>(pair->prop) = static_cast<LottieText*>(pair->obj)->doc;
break;
}
default: break;
}
}
//FIXME: it overrides the object's whole properties, but it acutally needs a single property of it.
pair->obj->override(target);
}
overriden = true;
}
void reset()
{
if (!overriden) return;
for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) {
switch (type) {
case LottieProperty::Type::ColorStop: {
static_cast<LottieGradient*>(pair->obj)->colorStops.release();
static_cast<LottieGradient*>(pair->obj)->colorStops = *static_cast<LottieColorStop*>(pair->prop);
break;
}
case LottieProperty::Type::Color: {
static_cast<LottieSolid*>(pair->obj)->color.release();
static_cast<LottieSolid*>(pair->obj)->color = *static_cast<LottieColor*>(pair->prop);
break;
}
case LottieProperty::Type::TextDoc: {
static_cast<LottieText*>(pair->obj)->doc.release();
static_cast<LottieText*>(pair->obj)->doc = *static_cast<LottieTextDoc*>(pair->prop);
break;
}
default: break;
}
delete(pair->prop);
pair->prop = nullptr;
}
overriden = false;
}
LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type) LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type)
{ {
objs.push(obj); pairs.push({obj});
} }
~LottieSlot() ~LottieSlot()
{ {
free(sid); free(sid);
if (!overriden) return;
for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) {
delete(pair->prop);
} }
}
char* sid;
Array<Pair> pairs;
LottieProperty::Type type;
bool overriden = false;
}; };

View file

@ -478,7 +478,7 @@ void LottieParser::parseProperty(T& prop, LottieObject* obj)
//append object if the slot already exists. //append object if the slot already exists.
for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) { for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) {
if (strcmp((*slot)->sid, sid)) continue; if (strcmp((*slot)->sid, sid)) continue;
(*slot)->objs.push(obj); (*slot)->pairs.push({obj});
return; return;
} }
comp->slots.push(new LottieSlot(sid, obj, type)); comp->slots.push(new LottieSlot(sid, obj, type));
@ -1250,10 +1250,11 @@ const char* LottieParser::sid(bool first)
} }
bool LottieParser::parse(LottieSlot* slot) bool LottieParser::apply(LottieSlot* slot)
{ {
enterObject(); enterObject();
//OPTIMIZE: we can create the property directly, without object
LottieObject* obj = nullptr; //slot object LottieObject* obj = nullptr; //slot object
switch (slot->type) { switch (slot->type) {
@ -1278,10 +1279,7 @@ bool LottieParser::parse(LottieSlot* slot)
if (!obj || Invalid()) return false; if (!obj || Invalid()) return false;
//apply slot object to all targets slot->assign(obj);
for (auto target = slot->objs.begin(); target < slot->objs.end(); ++target) {
(*target)->override(obj);
}
delete(obj); delete(obj);

View file

@ -36,7 +36,7 @@ public:
} }
bool parse(); bool parse();
bool parse(LottieSlot* slot); bool apply(LottieSlot* slot);
const char* sid(bool first = false); const char* sid(bool first = false);
LottieComposition* comp = nullptr; LottieComposition* comp = nullptr;

View file

@ -204,6 +204,7 @@ uint32_t bsearch(T* frames, float frameNo)
struct LottieProperty struct LottieProperty
{ {
enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid }; enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid };
virtual ~LottieProperty() {}
}; };
@ -258,7 +259,6 @@ struct LottieGenericProperty : LottieProperty
T& operator=(const T& other) T& operator=(const T& other)
{ {
//shallow copy, used for slot overriding //shallow copy, used for slot overriding
delete(frames);
if (other.frames) { if (other.frames) {
frames = other.frames; frames = other.frames;
const_cast<T&>(other).frames = nullptr; const_cast<T&>(other).frames = nullptr;
@ -467,7 +467,6 @@ struct LottieColorStop : LottieProperty
LottieColorStop& operator=(const LottieColorStop& other) LottieColorStop& operator=(const LottieColorStop& other)
{ {
//shallow copy, used for slot overriding //shallow copy, used for slot overriding
release();
if (other.frames) { if (other.frames) {
frames = other.frames; frames = other.frames;
const_cast<LottieColorStop&>(other).frames = nullptr; const_cast<LottieColorStop&>(other).frames = nullptr;
@ -613,7 +612,6 @@ struct LottieTextDoc : LottieProperty
LottieTextDoc& operator=(const LottieTextDoc& other) LottieTextDoc& operator=(const LottieTextDoc& other)
{ {
//shallow copy, used for slot overriding //shallow copy, used for slot overriding
release();
if (other.frames) { if (other.frames) {
frames = other.frames; frames = other.frames;
const_cast<LottieTextDoc&>(other).frames = nullptr; const_cast<LottieTextDoc&>(other).frames = nullptr;