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)
*
* \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.
* \retval TVG_RESULT_SUCCESS Succeed.

View file

@ -25,7 +25,7 @@ public:
/**
* @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::InsufficientCondition In case the animation is not loaded.

View file

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

View file

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

View file

@ -632,19 +632,89 @@ struct LottieLayer : LottieGroup
struct LottieSlot
{
char* sid;
Array<LottieObject*> objs;
LottieProperty::Type type;
struct Pair {
LottieObject* obj;
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)
{
objs.push(obj);
pairs.push({obj});
}
~LottieSlot()
{
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.
for (auto slot = comp->slots.begin(); slot < comp->slots.end(); ++slot) {
if (strcmp((*slot)->sid, sid)) continue;
(*slot)->objs.push(obj);
(*slot)->pairs.push({obj});
return;
}
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();
//OPTIMIZE: we can create the property directly, without object
LottieObject* obj = nullptr; //slot object
switch (slot->type) {
@ -1278,10 +1279,7 @@ bool LottieParser::parse(LottieSlot* slot)
if (!obj || Invalid()) return false;
//apply slot object to all targets
for (auto target = slot->objs.begin(); target < slot->objs.end(); ++target) {
(*target)->override(obj);
}
slot->assign(obj);
delete(obj);

View file

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

View file

@ -204,6 +204,7 @@ uint32_t bsearch(T* frames, float frameNo)
struct LottieProperty
{
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)
{
//shallow copy, used for slot overriding
delete(frames);
if (other.frames) {
frames = other.frames;
const_cast<T&>(other).frames = nullptr;
@ -467,7 +467,6 @@ struct LottieColorStop : LottieProperty
LottieColorStop& operator=(const LottieColorStop& other)
{
//shallow copy, used for slot overriding
release();
if (other.frames) {
frames = other.frames;
const_cast<LottieColorStop&>(other).frames = nullptr;
@ -613,7 +612,6 @@ struct LottieTextDoc : LottieProperty
LottieTextDoc& operator=(const LottieTextDoc& other)
{
//shallow copy, used for slot overriding
release();
if (other.frames) {
frames = other.frames;
const_cast<LottieTextDoc&>(other).frames = nullptr;