diff --git a/src/loaders/lottie/tvgLottieLoader.cpp b/src/loaders/lottie/tvgLottieLoader.cpp index d435e406..da5ba729 100644 --- a/src/loaders/lottie/tvgLottieLoader.cpp +++ b/src/loaders/lottie/tvgLottieLoader.cpp @@ -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 diff --git a/src/loaders/lottie/tvgLottieLoader.h b/src/loaders/lottie/tvgLottieLoader.h index 3c848e4c..94385a61 100644 --- a/src/loaders/lottie/tvgLottieLoader.h +++ b/src/loaders/lottie/tvgLottieLoader.h @@ -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; diff --git a/src/loaders/lottie/tvgLottieModel.cpp b/src/loaders/lottie/tvgLottieModel.cpp index 42d1ece2..2ba1499d 100644 --- a/src/loaders/lottie/tvgLottieModel.cpp +++ b/src/loaders/lottie/tvgLottieModel.cpp @@ -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); + } } \ No newline at end of file diff --git a/src/loaders/lottie/tvgLottieModel.h b/src/loaders/lottie/tvgLottieModel.h index 1e6e44fe..4ed89220 100644 --- a/src/loaders/lottie/tvgLottieModel.h +++ b/src/loaders/lottie/tvgLottieModel.h @@ -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(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(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(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(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(prop)->colorStops; + this->prepare(); + } }; @@ -592,6 +627,24 @@ struct LottieLayer : LottieGroup }; +struct LottieSlot +{ + char* sid; + Array 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 assets; Array interpolators; Array fonts; + Array slots; bool initiated = false; }; diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 6a564f8f..66ec23db 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -334,6 +334,17 @@ void LottieParser::getInperpolatorPoint(Point& pt) } } + +template +void LottieParser::parseSlotProperty(T& prop) +{ + while (auto key = nextObjectKey()) { + if (!strcmp(key, "p")) parseProperty(prop); + else skip(key); + } +} + + template bool LottieParser::parseTangent(const char *key, LottieVectorFrame& value) { @@ -456,13 +467,22 @@ void LottieParser::parsePropertyInternal(T& prop) } -template -void LottieParser::parseProperty(T& prop) +template +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(fill->color, fill); + else if (!strcmp(key, "o")) parseProperty(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(stroke->color, stroke); + else if (!strcmp(key, "o")) parseProperty(stroke->opacity, stroke); + else if (!strcmp(key, "w")) parseProperty(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(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(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(gradient->start, gradient); + else if (!strcmp(key, "e")) parseProperty(gradient->end, gradient); + else if (!strcmp(key, "h")) parseProperty(gradient->height, gradient); + else if (!strcmp(key, "a")) parseProperty(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& parent) auto text = new LottieText; while (auto key = nextObjectKey()) { - if (!strcmp(key, "d")) parseProperty(text->doc); + if (!strcmp(key, "d")) parseProperty(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& 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(obj); + parseSlotProperty(static_cast(obj)->colorStops); + break; + } + case LottieProperty::Type::Color: { + obj = new LottieSolid; + parseSlotProperty(static_cast(obj)->color); + break; + } + case LottieProperty::Type::TextDoc: { + obj = new LottieText; + parseSlotProperty(static_cast(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. diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index ae8f6c5c..f1a892c3 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -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 bool parseTangent(const char *key, LottieScalarFrame& value); template void parseKeyFrame(T& prop); template void parsePropertyInternal(T& prop); - template void parseProperty(T& prop); + template void parseProperty(T& prop, LottieObject* obj = nullptr); + template void parseSlotProperty(T& prop); LottieObject* parseObject(); LottieObject* parseAsset(); diff --git a/src/loaders/lottie/tvgLottieProperty.h b/src/loaders/lottie/tvgLottieProperty.h index 7ee0bad8..0945c90e 100644 --- a/src/loaders/lottie/tvgLottieProperty.h +++ b/src/loaders/lottie/tvgLottieProperty.h @@ -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(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(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(other).frames = nullptr; + return *this; + } + void prepare() {} };