mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
lottie: Support the slot overriding feature
Internal model and parser modifications have been made to parse "sid" and retrieve their data into the LottieComposition. This will enable dynamic changes to the following Lottie objects: The slot feature will encompass these properties: - LottieSolidStroke - LottieSolidFill - LottieGradientStroke - LottieGradientFill - LottieTextDoc" Issue: https://github.com/thorvg/thorvg/issues/1808 Co-authored-by: Hermet Park <hermet@lottiefiles.com>
This commit is contained in:
parent
a361924887
commit
362e6faacb
7 changed files with 200 additions and 20 deletions
|
@ -300,6 +300,27 @@ Paint* LottieLoader::paint()
|
|||
}
|
||||
|
||||
|
||||
bool LottieLoader::override(const char* slot)
|
||||
{
|
||||
if (!slot) return false;
|
||||
|
||||
//parsing slot json
|
||||
LottieParser parser(slot, dirName);
|
||||
auto sid = parser.sid();
|
||||
if (!sid) return false;
|
||||
|
||||
bool ret = false;
|
||||
|
||||
for (auto s = comp->slots.begin(); s < comp->slots.end(); ++s) {
|
||||
if (!strcmp((*s)->sid, sid)) continue;
|
||||
ret = parser.parse(*s);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool LottieLoader::frame(float no)
|
||||
{
|
||||
//no meaing to update if frame diff is less then 1ms
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
bool resize(Paint* paint, float w, float h) override;
|
||||
bool read() override;
|
||||
Paint* paint() override;
|
||||
bool override(const char* slot);
|
||||
|
||||
//Frame Controls
|
||||
bool frame(float no) override;
|
||||
|
|
|
@ -228,4 +228,9 @@ LottieComposition::~LottieComposition()
|
|||
for (auto f = fonts.begin(); f < fonts.end(); ++f) {
|
||||
delete(*f);
|
||||
}
|
||||
|
||||
//delete slots
|
||||
for (auto s = slots.begin(); s < slots.end(); ++s) {
|
||||
delete(*s);
|
||||
}
|
||||
}
|
|
@ -125,6 +125,11 @@ struct LottieObject
|
|||
free(name);
|
||||
}
|
||||
|
||||
virtual void override(LottieObject* prop)
|
||||
{
|
||||
TVGERR("LOTTIE", "Unsupported slot type");
|
||||
}
|
||||
|
||||
char* name = nullptr;
|
||||
Type type;
|
||||
bool statical = true; //no keyframes
|
||||
|
@ -183,6 +188,12 @@ struct LottieText : LottieObject
|
|||
LottieObject::type = LottieObject::Text;
|
||||
}
|
||||
|
||||
void override(LottieObject* prop) override
|
||||
{
|
||||
this->doc = static_cast<LottieText*>(prop)->doc;
|
||||
this->prepare();
|
||||
}
|
||||
|
||||
LottieTextDoc doc;
|
||||
LottieFont* font;
|
||||
LottieFloat spacing = 0.0f; //letter spacing
|
||||
|
@ -339,6 +350,12 @@ struct LottieSolidStroke : LottieSolid, LottieStroke
|
|||
LottieObject::type = LottieObject::SolidStroke;
|
||||
if (color.frames || opacity.frames || LottieStroke::dynamic()) statical = false;
|
||||
}
|
||||
|
||||
void override(LottieObject* prop) override
|
||||
{
|
||||
this->color = static_cast<LottieSolid*>(prop)->color;
|
||||
this->prepare();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -350,6 +367,12 @@ struct LottieSolidFill : LottieSolid
|
|||
if (color.frames || opacity.frames) statical = false;
|
||||
}
|
||||
|
||||
void override(LottieObject* prop) override
|
||||
{
|
||||
this->color = static_cast<LottieSolid*>(prop)->color;
|
||||
this->prepare();
|
||||
}
|
||||
|
||||
FillRule rule = FillRule::Winding;
|
||||
};
|
||||
|
||||
|
@ -468,6 +491,12 @@ struct LottieGradientFill : LottieGradient
|
|||
if (LottieGradient::prepare()) statical = false;
|
||||
}
|
||||
|
||||
void override(LottieObject* prop) override
|
||||
{
|
||||
this->colorStops = static_cast<LottieGradient*>(prop)->colorStops;
|
||||
this->prepare();
|
||||
}
|
||||
|
||||
FillRule rule = FillRule::Winding;
|
||||
};
|
||||
|
||||
|
@ -479,6 +508,12 @@ struct LottieGradientStroke : LottieGradient, LottieStroke
|
|||
LottieObject::type = LottieObject::GradientStroke;
|
||||
if (LottieGradient::prepare() || LottieStroke::dynamic()) statical = false;
|
||||
}
|
||||
|
||||
void override(LottieObject* prop) override
|
||||
{
|
||||
this->colorStops = static_cast<LottieGradient*>(prop)->colorStops;
|
||||
this->prepare();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -592,6 +627,24 @@ struct LottieLayer : LottieGroup
|
|||
};
|
||||
|
||||
|
||||
struct LottieSlot
|
||||
{
|
||||
char* sid;
|
||||
Array<LottieObject*> objs;
|
||||
LottieProperty::Type type;
|
||||
|
||||
LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type)
|
||||
{
|
||||
objs.push(obj);
|
||||
}
|
||||
|
||||
~LottieSlot()
|
||||
{
|
||||
free(sid);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct LottieComposition
|
||||
{
|
||||
~LottieComposition();
|
||||
|
@ -622,6 +675,7 @@ struct LottieComposition
|
|||
Array<LottieObject*> assets;
|
||||
Array<LottieInterpolator*> interpolators;
|
||||
Array<LottieFont*> fonts;
|
||||
Array<LottieSlot*> slots;
|
||||
bool initiated = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -334,6 +334,17 @@ void LottieParser::getInperpolatorPoint(Point& pt)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void LottieParser::parseSlotProperty(T& prop)
|
||||
{
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "p")) parseProperty(prop);
|
||||
else skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
bool LottieParser::parseTangent(const char *key, LottieVectorFrame<T>& value)
|
||||
{
|
||||
|
@ -456,13 +467,22 @@ void LottieParser::parsePropertyInternal(T& prop)
|
|||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void LottieParser::parseProperty(T& prop)
|
||||
template<LottieProperty::Type type, typename T>
|
||||
void LottieParser::parseProperty(T& prop, LottieObject* obj)
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "k")) parsePropertyInternal(prop);
|
||||
else skip(key);
|
||||
else if (obj && !strcmp(key, "sid")) {
|
||||
auto sid = getStringCopy();
|
||||
//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);
|
||||
return;
|
||||
}
|
||||
comp->slots.push(new LottieSlot(sid, obj, type));
|
||||
} else skip(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,8 +572,8 @@ LottieSolidFill* LottieParser::parseSolidFill()
|
|||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "nm")) fill->name = getStringCopy();
|
||||
else if (!strcmp(key, "c")) parseProperty(fill->color);
|
||||
else if (!strcmp(key, "o")) parseProperty(fill->opacity);
|
||||
else if (!strcmp(key, "c")) parseProperty<LottieProperty::Type::Color>(fill->color, fill);
|
||||
else if (!strcmp(key, "o")) parseProperty<LottieProperty::Type::Opacity>(fill->opacity, fill);
|
||||
else if (!strcmp(key, "fillEnabled")) fill->hidden |= !getBool();
|
||||
else if (!strcmp(key, "r")) fill->rule = getFillRule();
|
||||
else if (!strcmp(key, "hd")) fill->hidden = getBool();
|
||||
|
@ -590,9 +610,9 @@ LottieSolidStroke* LottieParser::parseSolidStroke()
|
|||
if (!stroke) return nullptr;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "c")) parseProperty(stroke->color);
|
||||
else if (!strcmp(key, "o")) parseProperty(stroke->opacity);
|
||||
else if (!strcmp(key, "w")) parseProperty(stroke->width);
|
||||
if (!strcmp(key, "c")) parseProperty<LottieProperty::Type::Color>(stroke->color, stroke);
|
||||
else if (!strcmp(key, "o")) parseProperty<LottieProperty::Type::Opacity>(stroke->opacity, stroke);
|
||||
else if (!strcmp(key, "w")) parseProperty<LottieProperty::Type::Float>(stroke->width, stroke);
|
||||
else if (!strcmp(key, "lc")) stroke->cap = getStrokeCap();
|
||||
else if (!strcmp(key, "lj")) stroke->join = getStrokeJoin();
|
||||
else if (!strcmp(key, "ml")) stroke->miterLimit = getFloat();
|
||||
|
@ -680,21 +700,23 @@ LottieRoundedCorner* LottieParser::parseRoundedCorner()
|
|||
|
||||
void LottieParser::parseGradient(LottieGradient* gradient, const char* key)
|
||||
{
|
||||
context->gradient = gradient;
|
||||
|
||||
if (!strcmp(key, "t")) gradient->id = getInt();
|
||||
else if (!strcmp(key, "o")) parseProperty(gradient->opacity);
|
||||
else if (!strcmp(key, "o")) parseProperty<LottieProperty::Type::Opacity>(gradient->opacity, gradient);
|
||||
else if (!strcmp(key, "g"))
|
||||
{
|
||||
enterObject();
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "p")) gradient->colorStops.count = getInt();
|
||||
else if (!strcmp(key, "k")) parseProperty(gradient->colorStops);
|
||||
else if (!strcmp(key, "k")) parseProperty<LottieProperty::Type::ColorStop>(gradient->colorStops, gradient);
|
||||
else skip(key);
|
||||
}
|
||||
}
|
||||
else if (!strcmp(key, "s")) parseProperty(gradient->start);
|
||||
else if (!strcmp(key, "e")) parseProperty(gradient->end);
|
||||
else if (!strcmp(key, "h")) parseProperty(gradient->height);
|
||||
else if (!strcmp(key, "a")) parseProperty(gradient->angle);
|
||||
else if (!strcmp(key, "s")) parseProperty<LottieProperty::Type::Point>(gradient->start, gradient);
|
||||
else if (!strcmp(key, "e")) parseProperty<LottieProperty::Type::Point>(gradient->end, gradient);
|
||||
else if (!strcmp(key, "h")) parseProperty<LottieProperty::Type::Float>(gradient->height, gradient);
|
||||
else if (!strcmp(key, "a")) parseProperty<LottieProperty::Type::Float>(gradient->angle, gradient);
|
||||
else skip(key);
|
||||
}
|
||||
|
||||
|
@ -704,8 +726,6 @@ LottieGradientFill* LottieParser::parseGradientFill()
|
|||
auto fill = new LottieGradientFill;
|
||||
if (!fill) return nullptr;
|
||||
|
||||
context->gradient = fill;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "nm")) fill->name = getStringCopy();
|
||||
else if (!strcmp(key, "r")) fill->rule = getFillRule();
|
||||
|
@ -724,8 +744,6 @@ LottieGradientStroke* LottieParser::parseGradientStroke()
|
|||
auto stroke = new LottieGradientStroke;
|
||||
if (!stroke) return nullptr;
|
||||
|
||||
context->gradient = stroke;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "nm")) stroke->name = getStringCopy();
|
||||
else if (!strcmp(key, "lc")) stroke->cap = getStrokeCap();
|
||||
|
@ -1055,7 +1073,7 @@ void LottieParser::parseText(Array<LottieObject*>& parent)
|
|||
auto text = new LottieText;
|
||||
|
||||
while (auto key = nextObjectKey()) {
|
||||
if (!strcmp(key, "d")) parseProperty(text->doc);
|
||||
if (!strcmp(key, "d")) parseProperty<LottieProperty::Type::TextDoc>(text->doc, text);
|
||||
else if (!strcmp(key, "a")) parseTextRange(text);
|
||||
//else if (!strcmp(key, "p")) TVGLOG("LOTTIE", "Text Follow Path (p) is not supported");
|
||||
//else if (!strcmp(key, "m")) TVGLOG("LOTTIE", "Text Alignment Option (m) is not supported");
|
||||
|
@ -1220,6 +1238,56 @@ void LottieParser::postProcess(Array<LottieGlyph*>& glyphes)
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
const char* LottieParser::sid()
|
||||
{
|
||||
//verify json
|
||||
if (!parseNext()) return nullptr;
|
||||
enterObject();
|
||||
return nextObjectKey();
|
||||
}
|
||||
|
||||
|
||||
bool LottieParser::parse(LottieSlot* slot)
|
||||
{
|
||||
enterObject();
|
||||
|
||||
LottieParser::Context context;
|
||||
this->context = &context;
|
||||
LottieObject* obj = nullptr; //slot object
|
||||
|
||||
switch (slot->type) {
|
||||
case LottieProperty::Type::ColorStop: {
|
||||
obj = new LottieGradient;
|
||||
context.gradient = static_cast<LottieGradient*>(obj);
|
||||
parseSlotProperty(static_cast<LottieGradient*>(obj)->colorStops);
|
||||
break;
|
||||
}
|
||||
case LottieProperty::Type::Color: {
|
||||
obj = new LottieSolid;
|
||||
parseSlotProperty(static_cast<LottieSolid*>(obj)->color);
|
||||
break;
|
||||
}
|
||||
case LottieProperty::Type::TextDoc: {
|
||||
obj = new LottieText;
|
||||
parseSlotProperty(static_cast<LottieText*>(obj)->doc);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
delete(obj);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LottieParser::parse()
|
||||
{
|
||||
//verify json.
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgLottieParserHandler.h"
|
||||
#include "tvgLottieProperty.h"
|
||||
|
||||
struct LottieParser : LookaheadParserHandler
|
||||
{
|
||||
|
@ -35,6 +36,8 @@ public:
|
|||
}
|
||||
|
||||
bool parse();
|
||||
bool parse(LottieSlot* slot);
|
||||
const char* sid();
|
||||
|
||||
LottieComposition* comp = nullptr;
|
||||
const char* dirName = nullptr; //base resource directory
|
||||
|
@ -66,7 +69,8 @@ private:
|
|||
template<typename T> bool parseTangent(const char *key, LottieScalarFrame<T>& value);
|
||||
template<typename T> void parseKeyFrame(T& prop);
|
||||
template<typename T> void parsePropertyInternal(T& prop);
|
||||
template<typename T> void parseProperty(T& prop);
|
||||
template<LottieProperty::Type type = LottieProperty::Type::Invalid, typename T> void parseProperty(T& prop, LottieObject* obj = nullptr);
|
||||
template<typename T> void parseSlotProperty(T& prop);
|
||||
|
||||
LottieObject* parseObject();
|
||||
LottieObject* parseAsset();
|
||||
|
|
|
@ -249,6 +249,15 @@ struct LottieGenericProperty : LottieProperty
|
|||
return frame->interpolate(frame + 1, frameNo);
|
||||
}
|
||||
|
||||
T& operator=(const T& other)
|
||||
{
|
||||
//shallow copy, used for slot overriding
|
||||
delete(frames);
|
||||
*this = other;
|
||||
const_cast<T&>(other).frames = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
float angle(float frameNo) { return 0; }
|
||||
void prepare() {}
|
||||
};
|
||||
|
@ -433,6 +442,15 @@ struct LottieColorStop : LottieProperty
|
|||
fill->colorStops(result.data, count);
|
||||
}
|
||||
|
||||
LottieColorStop& operator=(const LottieColorStop& other)
|
||||
{
|
||||
//shallow copy, used for slot overriding
|
||||
delete(frames);
|
||||
*this = other;
|
||||
const_cast<LottieColorStop&>(other).frames = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void prepare() {}
|
||||
};
|
||||
|
||||
|
@ -544,6 +562,15 @@ struct LottieTextDoc : LottieProperty
|
|||
return frame->value;
|
||||
}
|
||||
|
||||
LottieTextDoc& operator=(const LottieTextDoc& other)
|
||||
{
|
||||
//shallow copy, used for slot overriding
|
||||
delete(frames);
|
||||
*this = other;
|
||||
const_cast<LottieTextDoc&>(other).frames = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void prepare() {}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue