diff --git a/examples/LottieExtension.cpp b/examples/LottieExtension.cpp index 32482705..00506712 100644 --- a/examples/LottieExtension.cpp +++ b/examples/LottieExtension.cpp @@ -96,7 +96,7 @@ struct UserExample : tvgexam::Example auto picture = slotAnimation->picture(); if (!tvgexam::verify(picture->load(EXAMPLE_DIR"/lottie/extensions/slotsample.json"))) return false; - const char* slotJson = R"({"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}})"; + const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0,0,1,1]}}}})"; if (!tvgexam::verify(slotAnimation->override(slotJson))) return false; sizing(picture, 0); diff --git a/examples/resources/lottie/extensions/slotsample.json b/examples/resources/lottie/extensions/slotsample.json index ecd25f57..4fa290c3 100644 --- a/examples/resources/lottie/extensions/slotsample.json +++ b/examples/resources/lottie/extensions/slotsample.json @@ -1 +1 @@ -{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.0.2","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":121,"w":550,"h":550,"nm":"C","assets":[],"layers":[{"ind":1,"ty":4,"nm":"S","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":120,"s":[360]}]},"p":{"a":0,"k":[275,275,0]},"a":{"a":0,"k":[-7.886,88.719,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[282.019,134.888]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"st","c":{"a":0,"k":[0.991,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":1,"lj":1,"ml":4,"nm":"S"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":9,"k":{"a":0,"k":[0,0.514,0.373,0.984,0.141,0.478,0.412,0.984,0.283,0.443,0.451,0.984,0.379,0.408,0.49,0.984,0.475,0.373,0.529,0.984,0.606,0.278,0.647,0.925,0.737,0.184,0.765,0.867,0.868,0.092,0.882,0.808,1,0,1,0.749],"sid":"gradient_fill"}},"s":{"a":0,"k":[-159.51,23.531]},"e":{"a":0,"k":[183.084,8.059]},"t":1,"nm":"G"},{"ty":"tr","p":{"a":0,"k":[-7.886,88.719]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"R"}],"ip":0,"op":300,"st":0}],"slots":{"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}},"markers":[]} \ No newline at end of file +{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.0.2","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":121,"w":550,"h":550,"nm":"C","assets":[],"layers":[{"ind":1,"ty":4,"nm":"S","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":120,"s":[360]}]},"p":{"a":0,"k":[275,275,0]},"a":{"a":0,"k":[-7.886,88.719,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[282.019,134.888]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"st","c":{"a":0,"k":[0.991,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":1,"lj":1,"ml":4,"nm":"S"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":9,"k":{"a":0,"k":[0,0.514,0.373,0.984,0.141,0.478,0.412,0.984,0.283,0.443,0.451,0.984,0.379,0.408,0.49,0.984,0.475,0.373,0.529,0.984,0.606,0.278,0.647,0.925,0.737,0.184,0.765,0.867,0.868,0.092,0.882,0.808,1,0,1,0.749]},"sid":"gradient_fill"},"s":{"a":0,"k":[-159.51,23.531]},"e":{"a":0,"k":[183.084,8.059]},"t":1,"nm":"G"},{"ty":"tr","p":{"a":0,"k":[-7.886,88.719]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"R"}],"ip":0,"op":300,"st":0}],"slots":{"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}},"markers":[]} \ No newline at end of file diff --git a/src/loaders/lottie/tvgLottieParser.cpp b/src/loaders/lottie/tvgLottieParser.cpp index 3b63e767..22e997f1 100644 --- a/src/loaders/lottie/tvgLottieParser.cpp +++ b/src/loaders/lottie/tvgLottieParser.cpp @@ -485,26 +485,31 @@ void LottieParser::parsePropertyInternal(T& prop) } +template +void LottieParser::registerSlot(LottieObject* obj) +{ + 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)->pairs.push({obj}); + return; + } + comp->slots.push(new LottieSlot(sid, obj, type)); +} + + template void LottieParser::parseProperty(T& prop, LottieObject* obj) { enterObject(); while (auto key = nextObjectKey()) { if (KEY_AS("k")) parsePropertyInternal(prop); - else if (obj && KEY_AS("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)->pairs.push({obj}); - break; - } - comp->slots.push(new LottieSlot(sid, obj, type)); - } else if (KEY_AS("x")) { - prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop); - } else if (KEY_AS("ix")) { - prop.ix = getInt(); - } else skip(key); + else if (obj && KEY_AS("sid")) registerSlot(obj); + else if (KEY_AS("x")) prop.exp = _expression(getStringCopy(), comp, context.layer, context.parent, &prop); + else if (KEY_AS("ix")) prop.ix = getInt(); + else skip(key); } prop.type = type; } @@ -750,19 +755,23 @@ LottieRoundedCorner* LottieParser::parseRoundedCorner() } +void LottieParser::parseColorStop(LottieGradient* gradient) +{ + enterObject(); + while (auto key = nextObjectKey()) { + if (KEY_AS("p")) gradient->colorStops.count = getInt(); + else if (KEY_AS("k")) parseProperty(gradient->colorStops, gradient); + else if (KEY_AS("sid")) registerSlot(gradient); + else skip(key); + } +} + + void LottieParser::parseGradient(LottieGradient* gradient, const char* key) { if (KEY_AS("t")) gradient->id = getInt(); else if (KEY_AS("o")) parseProperty(gradient->opacity, gradient); - else if (KEY_AS("g")) - { - enterObject(); - while (auto key = nextObjectKey()) { - if (KEY_AS("p")) gradient->colorStops.count = getInt(); - else if (KEY_AS("k")) parseProperty(gradient->colorStops, gradient); - else skip(key); - } - } + else if (KEY_AS("g")) parseColorStop(gradient); else if (KEY_AS("s")) parseProperty(gradient->start, gradient); else if (KEY_AS("e")) parseProperty(gradient->end, gradient); else if (KEY_AS("h")) parseProperty(gradient->height, gradient); @@ -1438,7 +1447,10 @@ bool LottieParser::apply(LottieSlot* slot) case LottieProperty::Type::ColorStop: { obj = new LottieGradient; context.parent = obj; - parseSlotProperty(static_cast(obj)->colorStops); + while (auto key = nextObjectKey()) { + if (KEY_AS("p")) parseColorStop(static_cast(obj)); + else skip(key); + } break; } case LottieProperty::Type::Color: { diff --git a/src/loaders/lottie/tvgLottieParser.h b/src/loaders/lottie/tvgLottieParser.h index 0a3996ef..51aa40fe 100644 --- a/src/loaders/lottie/tvgLottieParser.h +++ b/src/loaders/lottie/tvgLottieParser.h @@ -38,6 +38,7 @@ public: bool parse(); bool apply(LottieSlot* slot); const char* sid(bool first = false); + template void registerSlot(LottieObject* obj); LottieComposition* comp = nullptr; const char* dirName = nullptr; //base resource directory @@ -107,6 +108,7 @@ private: void parseTimeRemap(LottieLayer* layer); void parseStrokeDash(LottieStroke* stroke); void parseGradient(LottieGradient* gradient, const char* key); + void parseColorStop(LottieGradient* gradient); void parseTextRange(LottieText* text); void parseAssets(); void parseFonts(); diff --git a/test/capi/capiLottie.cpp b/test/capi/capiLottie.cpp index f61995f0..6489fb03 100644 --- a/test/capi/capiLottie.cpp +++ b/test/capi/capiLottie.cpp @@ -41,7 +41,7 @@ TEST_CASE("Lottie Slot", "[capiLottie]") REQUIRE(tvg_paint_get_type(picture, &type) == TVG_RESULT_SUCCESS); REQUIRE(type == TVG_TYPE_PICTURE); - const char* slotJson = R"({"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}})"; + const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}}})"; //Slot override before loaded REQUIRE(tvg_lottie_animation_override(animation, slotJson) == TVG_RESULT_INSUFFICIENT_CONDITION); diff --git a/test/resources/lottieslot.json b/test/resources/lottieslot.json index ecd25f57..4fa290c3 100644 --- a/test/resources/lottieslot.json +++ b/test/resources/lottieslot.json @@ -1 +1 @@ -{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.0.2","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":121,"w":550,"h":550,"nm":"C","assets":[],"layers":[{"ind":1,"ty":4,"nm":"S","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":120,"s":[360]}]},"p":{"a":0,"k":[275,275,0]},"a":{"a":0,"k":[-7.886,88.719,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[282.019,134.888]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"st","c":{"a":0,"k":[0.991,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":1,"lj":1,"ml":4,"nm":"S"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":9,"k":{"a":0,"k":[0,0.514,0.373,0.984,0.141,0.478,0.412,0.984,0.283,0.443,0.451,0.984,0.379,0.408,0.49,0.984,0.475,0.373,0.529,0.984,0.606,0.278,0.647,0.925,0.737,0.184,0.765,0.867,0.868,0.092,0.882,0.808,1,0,1,0.749],"sid":"gradient_fill"}},"s":{"a":0,"k":[-159.51,23.531]},"e":{"a":0,"k":[183.084,8.059]},"t":1,"nm":"G"},{"ty":"tr","p":{"a":0,"k":[-7.886,88.719]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"R"}],"ip":0,"op":300,"st":0}],"slots":{"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}},"markers":[]} \ No newline at end of file +{"v":"4.8.0","meta":{"g":"LottieFiles AE 3.0.2","a":"","k":"","d":"","tc":""},"fr":60,"ip":0,"op":121,"w":550,"h":550,"nm":"C","assets":[],"layers":[{"ind":1,"ty":4,"nm":"S","sr":1,"ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[0]},{"t":120,"s":[360]}]},"p":{"a":0,"k":[275,275,0]},"a":{"a":0,"k":[-7.886,88.719,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[282.019,134.888]},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"nm":"R"},{"ty":"st","c":{"a":0,"k":[0.991,0,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":3},"lc":1,"lj":1,"ml":4,"nm":"S"},{"ty":"gf","o":{"a":0,"k":100},"r":1,"g":{"p":9,"k":{"a":0,"k":[0,0.514,0.373,0.984,0.141,0.478,0.412,0.984,0.283,0.443,0.451,0.984,0.379,0.408,0.49,0.984,0.475,0.373,0.529,0.984,0.606,0.278,0.647,0.925,0.737,0.184,0.765,0.867,0.868,0.092,0.882,0.808,1,0,1,0.749]},"sid":"gradient_fill"},"s":{"a":0,"k":[-159.51,23.531]},"e":{"a":0,"k":[183.084,8.059]},"t":1,"nm":"G"},{"ty":"tr","p":{"a":0,"k":[-7.886,88.719]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"T"}],"nm":"R"}],"ip":0,"op":300,"st":0}],"slots":{"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}},"markers":[]} \ No newline at end of file diff --git a/test/testLottie.cpp b/test/testLottie.cpp index c883e95d..eea42d30 100644 --- a/test/testLottie.cpp +++ b/test/testLottie.cpp @@ -44,7 +44,7 @@ TEST_CASE("Lottie Slot", "[tvgLottie]") auto picture = animation->picture(); REQUIRE(picture->type() == Type::Picture); - const char* slotJson = R"({"gradient_fill":{"p":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}})"; + const char* slotJson = R"({"gradient_fill":{"p":{"p":2,"k":{"a":0,"k":[0,0.1,0.1,0.2,1,1,0.1,0.2,0.1,1]}}}})"; //Slot override before loaded REQUIRE(animation->override(slotJson) == Result::InsufficientCondition);