2 #include "lottieparser.h"
7 #define DEBUG_PRINT_TREE
9 // This parser implements JSON token-by-token parsing with an API that is
10 // more direct; we don't have to create handler object and
11 // callbacks. Instead, we retrieve values from the JSON stream by calling
12 // GetInt(), GetDouble(), GetString() and GetBool(), traverse into structures
13 // by calling EnterObject() and EnterArray(), and skip over unwanted data by
14 // calling SkipValue(). As we know the lottie file structure this way will be
15 // the efficient way of parsing the file.
17 // If you aren't sure of what's next in the JSON data, you can use PeekType()
18 // and PeekValue() to look ahead to the next object before reading it.
20 // If you call the wrong retrieval method--e.g. GetInt when the next JSON token
21 // is not an int, EnterObject or EnterArray when there isn't actually an object
22 // or array to read--the stream parsing will end immediately and no more data
25 // After calling EnterObject, you retrieve keys via NextObjectKey() and values
26 // via the normal getters. When NextObjectKey() returns null, you have exited
27 // the object, or you can call SkipObject() to skip to the end of the object
28 // immediately. If you fetch the entire object (i.e. NextObjectKey() returned
29 // null), you should not call SkipObject().
31 // After calling EnterArray(), you must alternate between calling
32 // NextArrayValue() to see if the array has more data, and then retrieving
33 // values via the normal getters. You can call SkipArray() to skip to the end of
34 // the array immediately. If you fetch the entire array (i.e. NextArrayValue()
35 // returned null), you should not call SkipArray().
37 // This parser uses in-situ strings, so the JSON buffer will be altered during
41 #include "lottiemodel.h"
42 #include "rapidjson/document.h"
43 #include "velapsedtimer.h"
47 RAPIDJSON_DIAG_OFF(effc++)
50 using namespace rapidjson;
52 class LookaheadParserHandler {
84 bool Uint64(uint64_t u)
96 bool RawNumber(const char *, SizeType, bool) { return false; }
97 bool String(const char *str, SizeType length, bool)
100 v_.SetString(str, length);
105 st_ = kEnteringObject;
108 bool Key(const char *str, SizeType length, bool)
111 v_.SetString(str, length);
114 bool EndObject(SizeType)
116 st_ = kExitingObject;
121 st_ = kEnteringArray;
124 bool EndArray(SizeType)
131 LookaheadParserHandler(char *str);
135 enum LookaheadParsingState {
150 LookaheadParsingState st_;
152 InsituStringStream ss_;
154 static const int parseFlags = kParseDefaultFlags | kParseInsituFlag;
157 class LottieParserImpl : protected LookaheadParserHandler {
159 LottieParserImpl(char *str) : LookaheadParserHandler(str) {}
164 const char *NextObjectKey();
165 bool NextArrayValue();
168 const char *GetString();
176 int PeekType(); // returns a rapidjson::Type, or -1 for no value (at end of
179 bool IsValid() { return st_ != kError; }
181 void Skip(const char *key);
183 LottieBlendMode getBlendMode();
184 CapStyle getLineCap();
185 JoinStyle getLineJoin();
186 FillRule getFillRule();
187 LOTTrimData::TrimType getTrimType();
188 MatteType getMatteType();
189 LayerType getLayerType();
191 std::shared_ptr<LOTCompositionData> composition() const
195 void parseComposition();
196 void parseAssets(LOTCompositionData *comp);
197 std::shared_ptr<LOTAsset> parseAsset();
198 void parseLayers(LOTCompositionData *comp);
199 std::shared_ptr<LOTData> parseLayer();
200 void parseMaskProperty(LOTLayerData *layer);
201 void parseShapesAttr(LOTLayerData *layer);
202 void parseObject(LOTGroupData *parent);
203 std::shared_ptr<LOTMaskData> parseMaskObject();
204 std::shared_ptr<LOTData> parseObjectTypeAttr();
205 std::shared_ptr<LOTData> parseGroupObject();
206 std::shared_ptr<LOTData> parseRectObject();
207 std::shared_ptr<LOTData> parseEllipseObject();
208 std::shared_ptr<LOTData> parseShapeObject();
209 std::shared_ptr<LOTData> parsePolystarObject();
211 std::shared_ptr<LOTTransformData> parseTransformObject();
212 std::shared_ptr<LOTData> parseFillObject();
213 std::shared_ptr<LOTData> parseGFillObject();
214 std::shared_ptr<LOTData> parseStrokeObject();
215 std::shared_ptr<LOTData> parseGStrokeObject();
216 std::shared_ptr<LOTData> parseTrimObject();
217 std::shared_ptr<LOTData> parseReapeaterObject();
219 void parseGradientProperty(LOTGradient *gradient, const char *key);
221 VPointF parseInperpolatorPoint();
222 void parseArrayValue(VPointF &pt);
223 void parseArrayValue(LottieColor &pt);
224 void parseArrayValue(float &val);
225 void parseArrayValue(int &val);
226 void parseArrayValue(LottieGradient &gradient);
227 void getValue(VPointF &val);
228 void getValue(float &val);
229 void getValue(LottieColor &val);
230 void getValue(int &val);
231 void getValue(LottieShapeData &shape);
232 void getValue(LottieGradient &gradient);
233 template <typename T>
234 bool parseKeyFrameValue(const char *key, LOTKeyFrameValue<T> &value);
235 template <typename T>
236 void parseKeyFrame(LOTAnimInfo<T> &obj);
237 template <typename T>
238 void parseProperty(LOTAnimatable<T> &obj);
239 template <typename T>
240 void parsePropertyHelper(LOTAnimatable<T> &obj);
242 void parseShapeKeyFrame(LOTAnimInfo<LottieShapeData> &obj);
243 void parseShapeProperty(LOTAnimatable<LottieShapeData> &obj);
244 void parseArrayValue(std::vector<VPointF> &v);
245 void parseDashProperty(LOTDashProperty &dash);
247 LottieColor toColor(const char *str);
249 void resolveLayerRefs();
252 std::shared_ptr<LOTCompositionData> mComposition;
253 LOTCompositionData * compRef;
254 LOTLayerData * curLayerRef;
255 std::vector<std::shared_ptr<LOTLayerData>> mLayersToUpdate;
256 void SkipOut(int depth);
259 LookaheadParserHandler::LookaheadParserHandler(char *str)
260 : v_(), st_(kInit), r_(), ss_(str)
262 r_.IterativeParseInit();
266 void LookaheadParserHandler::ParseNext()
268 if (r_.HasParseError()) {
273 if (!r_.IterativeParseNext<parseFlags>(ss_, *this)) {
274 vCritical << "Lottie file parsing error";
279 bool LottieParserImpl::EnterObject()
281 if (st_ != kEnteringObject) {
283 RAPIDJSON_ASSERT(false);
291 bool LottieParserImpl::EnterArray()
293 if (st_ != kEnteringArray) {
295 RAPIDJSON_ASSERT(false);
303 const char *LottieParserImpl::NextObjectKey()
305 if (st_ == kHasKey) {
306 const char *result = v_.GetString();
312 * The parser works with a prdefined rule that it will be only
313 * while (NextObjectKey()) for each object but in case of our nested group
314 * object we can call multiple time NextObjectKey() while exiting the object
315 * so ignore those and don't put parser in the error state.
317 if (st_ == kExitingArray || st_ == kEnteringObject) {
318 // #ifdef DEBUG_PARSER
319 // vDebug<<"Object: Exiting nested loop";
324 if (st_ != kExitingObject) {
325 RAPIDJSON_ASSERT(false);
334 bool LottieParserImpl::NextArrayValue()
336 if (st_ == kExitingArray) {
342 * same as NextObjectKey()
344 if (st_ == kExitingObject) {
345 // #ifdef DEBUG_PARSER
346 // vDebug<<"Array: Exiting nested loop";
351 if (st_ == kError || st_ == kHasKey) {
352 RAPIDJSON_ASSERT(false);
360 int LottieParserImpl::GetInt()
362 if (st_ != kHasNumber || !v_.IsInt()) {
364 RAPIDJSON_ASSERT(false);
368 int result = v_.GetInt();
373 double LottieParserImpl::GetDouble()
375 if (st_ != kHasNumber) {
377 RAPIDJSON_ASSERT(false);
381 double result = v_.GetDouble();
386 bool LottieParserImpl::GetBool()
388 if (st_ != kHasBool) {
390 RAPIDJSON_ASSERT(false);
394 bool result = v_.GetBool();
399 void LottieParserImpl::GetNull()
401 if (st_ != kHasNull) {
409 const char *LottieParserImpl::GetString()
411 if (st_ != kHasString) {
413 RAPIDJSON_ASSERT(false);
417 const char *result = v_.GetString();
422 void LottieParserImpl::SkipOut(int depth)
425 if (st_ == kEnteringArray || st_ == kEnteringObject) {
427 } else if (st_ == kExitingArray || st_ == kExitingObject) {
429 } else if (st_ == kError) {
430 RAPIDJSON_ASSERT(false);
438 void LottieParserImpl::SkipValue()
443 void LottieParserImpl::SkipArray()
448 void LottieParserImpl::SkipObject()
453 Value *LottieParserImpl::PeekValue()
455 if (st_ >= kHasNull && st_ <= kHasKey) {
462 int LottieParserImpl::PeekType()
464 if (st_ >= kHasNull && st_ <= kHasKey) {
468 if (st_ == kEnteringArray) {
472 if (st_ == kEnteringObject) {
479 void LottieParserImpl::Skip(const char *key)
481 if (PeekType() == kArrayType) {
484 } else if (PeekType() == kObjectType) {
492 LottieBlendMode LottieParserImpl::getBlendMode()
494 RAPIDJSON_ASSERT(PeekType() == kNumberType);
495 LottieBlendMode mode = LottieBlendMode::Normal;
499 mode = LottieBlendMode::Multiply;
502 mode = LottieBlendMode::Screen;
505 mode = LottieBlendMode::OverLay;
512 VRect LottieParserImpl::getRect()
515 RAPIDJSON_ASSERT(PeekType() == kObjectType);
517 while (const char *key = NextObjectKey()) {
518 if (0 == strcmp(key, "l")) {
519 RAPIDJSON_ASSERT(PeekType() == kNumberType);
521 } else if (0 == strcmp(key, "r")) {
522 RAPIDJSON_ASSERT(PeekType() == kNumberType);
523 r.setRight(GetInt());
524 } else if (0 == strcmp(key, "t")) {
525 RAPIDJSON_ASSERT(PeekType() == kNumberType);
527 } else if (0 == strcmp(key, "b")) {
528 RAPIDJSON_ASSERT(PeekType() == kNumberType);
529 r.setBottom(GetInt());
531 RAPIDJSON_ASSERT(false);
537 void LottieParserImpl::resolveLayerRefs()
539 for (const auto& i : mLayersToUpdate) {
540 LOTLayerData *layer = i.get();
541 auto search = compRef->mAssets.find(layer->mPreCompRefId);
542 if (search != compRef->mAssets.end()) {
543 layer->mChildren = search->second.get()->mLayers;
548 void LottieParserImpl::parseComposition()
550 RAPIDJSON_ASSERT(PeekType() == kObjectType);
552 std::shared_ptr<LOTCompositionData> sharedComposition =
553 std::make_shared<LOTCompositionData>();
554 LOTCompositionData *comp = sharedComposition.get();
556 while (const char *key = NextObjectKey()) {
557 if (0 == strcmp(key, "v")) {
558 RAPIDJSON_ASSERT(PeekType() == kStringType);
559 comp->mVersion = std::string(GetString());
560 } else if (0 == strcmp(key, "w")) {
561 RAPIDJSON_ASSERT(PeekType() == kNumberType);
562 comp->mSize.setWidth(GetInt());
563 } else if (0 == strcmp(key, "h")) {
564 RAPIDJSON_ASSERT(PeekType() == kNumberType);
565 comp->mSize.setHeight(GetInt());
566 } else if (0 == strcmp(key, "ip")) {
567 RAPIDJSON_ASSERT(PeekType() == kNumberType);
568 comp->mStartFrame = GetDouble();
569 } else if (0 == strcmp(key, "op")) {
570 RAPIDJSON_ASSERT(PeekType() == kNumberType);
571 comp->mEndFrame = GetDouble();
572 } else if (0 == strcmp(key, "fr")) {
573 RAPIDJSON_ASSERT(PeekType() == kNumberType);
574 comp->mFrameRate = GetDouble();
575 } else if (0 == strcmp(key, "assets")) {
577 } else if (0 == strcmp(key, "layers")) {
581 vWarning << "Composition Attribute Skipped : " << key;
587 comp->setStatic(comp->mRootLayer->isStatic());
588 comp->mRootLayer->mInFrame = comp->mStartFrame;
589 comp->mRootLayer->mOutFrame = comp->mEndFrame;
591 mComposition = sharedComposition;
594 void LottieParserImpl::parseAssets(LOTCompositionData *composition)
596 RAPIDJSON_ASSERT(PeekType() == kArrayType);
598 while (NextArrayValue()) {
599 std::shared_ptr<LOTAsset> asset = parseAsset();
600 composition->mAssets[asset->mRefId] = asset;
602 // update the precomp layers with the actual layer object
606 * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
609 std::shared_ptr<LOTAsset> LottieParserImpl::parseAsset()
611 RAPIDJSON_ASSERT(PeekType() == kObjectType);
612 std::shared_ptr<LOTAsset> sharedAsset = std::make_shared<LOTAsset>();
613 LOTAsset * asset = sharedAsset.get();
615 while (const char *key = NextObjectKey()) {
616 if (0 == strcmp(key, "ty")) { /* Type of layer: Shape. Value 4.*/
617 RAPIDJSON_ASSERT(PeekType() == kNumberType);
618 asset->mAssetType = GetInt();
619 } else if (0 == strcmp(key, "id")) { /* reference id*/
620 RAPIDJSON_ASSERT(PeekType() == kStringType);
621 asset->mRefId = std::string(GetString());
622 } else if (0 == strcmp(key, "layers")) {
623 RAPIDJSON_ASSERT(PeekType() == kArrayType);
625 while (NextArrayValue()) {
626 std::shared_ptr<LOTData> layer = parseLayer();
627 asset->mLayers.push_back(layer);
631 vWarning << "Asset Attribute Skipped : " << key;
639 void LottieParserImpl::parseLayers(LOTCompositionData *comp)
641 comp->mRootLayer = std::make_shared<LOTLayerData>();
642 comp->mRootLayer->mRoot = true;
643 comp->mRootLayer->mLayerType = LayerType::Precomp;
644 comp->mRootLayer->mTransform = std::make_shared<LOTTransformData>();
645 bool staticFlag = true;
646 RAPIDJSON_ASSERT(PeekType() == kArrayType);
648 while (NextArrayValue()) {
649 std::shared_ptr<LOTData> layer = parseLayer();
650 staticFlag &= layer->isStatic();
651 comp->mRootLayer->mChildren.push_back(layer);
653 comp->mRootLayer->setStatic(staticFlag);
656 LottieColor LottieParserImpl::toColor(const char *str)
660 RAPIDJSON_ASSERT(strlen(str) == 7);
661 RAPIDJSON_ASSERT(str[0] == '#');
663 char tmp[3] = {'\0', '\0', '\0'};
666 color.r = std::strtol(tmp, NULL, 16) / 255.0;
670 color.g = std::strtol(tmp, NULL, 16) / 255.0;
674 color.b = std::strtol(tmp, NULL, 16) / 255.0;
679 MatteType LottieParserImpl::getMatteType()
681 RAPIDJSON_ASSERT(PeekType() == kNumberType);
684 return MatteType::Alpha;
687 return MatteType::AlphaInv;
690 return MatteType::Luma;
693 return MatteType::LumaInv;
696 return MatteType::None;
701 LayerType LottieParserImpl::getLayerType()
703 RAPIDJSON_ASSERT(PeekType() == kNumberType);
706 return LayerType::Precomp;
709 return LayerType::Solid;
712 return LayerType::Image;
715 return LayerType::Null;
718 return LayerType::Shape;
721 return LayerType::Text;
724 return LayerType::Null;
730 * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
733 std::shared_ptr<LOTData> LottieParserImpl::parseLayer()
735 RAPIDJSON_ASSERT(PeekType() == kObjectType);
736 std::shared_ptr<LOTLayerData> sharedLayer =
737 std::make_shared<LOTLayerData>();
738 LOTLayerData *layer = sharedLayer.get();
740 bool hasLayerRef = false;
742 while (const char *key = NextObjectKey()) {
743 if (0 == strcmp(key, "ty")) { /* Type of layer*/
744 layer->mLayerType = getLayerType();
745 } else if (0 == strcmp(key, "ind")) { /*Layer index in AE. Used for
746 parenting and expressions.*/
747 RAPIDJSON_ASSERT(PeekType() == kNumberType);
748 layer->mId = GetInt();
749 } else if (0 == strcmp(key, "parent")) { /*Layer Parent. Uses "ind" of parent.*/
750 RAPIDJSON_ASSERT(PeekType() == kNumberType);
751 layer->mParentId = GetInt();
752 } else if (0 == strcmp(key, "refId")) { /*preComp Layer reference id*/
753 RAPIDJSON_ASSERT(PeekType() == kStringType);
754 layer->mPreCompRefId = std::string(GetString());
755 layer->mHasGradient = true;
756 mLayersToUpdate.push_back(sharedLayer);
758 } else if (0 == strcmp(key, "sr")) { // "Layer Time Stretching"
759 RAPIDJSON_ASSERT(PeekType() == kNumberType);
760 layer->mTimeStreatch = GetDouble();
761 } else if (0 == strcmp(key, "tm")) { // time remapping
762 parseProperty(layer->mTimeRemap);
763 } else if (0 == strcmp(key, "ip")) {
764 RAPIDJSON_ASSERT(PeekType() == kNumberType);
765 layer->mInFrame = std::round(GetDouble());
766 } else if (0 == strcmp(key, "op")) {
767 RAPIDJSON_ASSERT(PeekType() == kNumberType);
768 layer->mOutFrame = std::round(GetDouble());
769 } else if (0 == strcmp(key, "st")) {
770 RAPIDJSON_ASSERT(PeekType() == kNumberType);
771 layer->mStartFrame = GetDouble();
772 } else if (0 == strcmp(key, "bounds")) {
773 layer->mBound = getRect();
774 } else if (0 == strcmp(key, "bm")) {
775 layer->mBlendMode = getBlendMode();
776 } else if (0 == strcmp(key, "ks")) {
777 RAPIDJSON_ASSERT(PeekType() == kObjectType);
779 layer->mTransform = parseTransformObject();
780 } else if (0 == strcmp(key, "shapes")) {
781 parseShapesAttr(layer);
782 } else if (0 == strcmp(key, "sw")) {
783 layer->mSolidLayer.mWidth = GetInt();
784 } else if (0 == strcmp(key, "sh")) {
785 layer->mSolidLayer.mHeight = GetInt();
786 } else if (0 == strcmp(key, "sc")) {
787 layer->mSolidLayer.mColor = toColor(GetString());
788 } else if (0 == strcmp(key, "tt")) {
789 layer->mMatteType = getMatteType();
790 } else if (0 == strcmp(key, "hasMask")) {
791 layer->mHasMask = GetBool();
792 } else if (0 == strcmp(key, "masksProperties")) {
793 parseMaskProperty(layer);
796 vWarning << "Layer Attribute Skipped : " << key;
801 // update the static property of layer
802 bool staticFlag = true;
803 for (const auto& child : layer->mChildren) {
804 staticFlag &= child.get()->isStatic();
807 for (const auto& mask : layer->mMasks) {
808 staticFlag &= mask->isStatic();
811 layer->setStatic(staticFlag && layer->mTransform->isStatic() &&
817 void LottieParserImpl::parseMaskProperty(LOTLayerData *layer)
819 RAPIDJSON_ASSERT(PeekType() == kArrayType);
821 while (NextArrayValue()) {
822 layer->mMasks.push_back(parseMaskObject());
826 std::shared_ptr<LOTMaskData> LottieParserImpl::parseMaskObject()
828 std::shared_ptr<LOTMaskData> sharedMask = std::make_shared<LOTMaskData>();
829 LOTMaskData * obj = sharedMask.get();
831 RAPIDJSON_ASSERT(PeekType() == kObjectType);
833 while (const char *key = NextObjectKey()) {
834 if (0 == strcmp(key, "inv")) {
835 obj->mInv = GetBool();
836 } else if (0 == strcmp(key, "mode")) {
837 const char *str = GetString();
840 obj->mMode = LOTMaskData::Mode::None;
843 obj->mMode = LOTMaskData::Mode::Add;
846 obj->mMode = LOTMaskData::Mode::Substarct;
849 obj->mMode = LOTMaskData::Mode::Intersect;
852 obj->mMode = LOTMaskData::Mode::Difference;
855 obj->mMode = LOTMaskData::Mode::None;
858 } else if (0 == strcmp(key, "pt")) {
859 parseShapeProperty(obj->mShape);
860 } else if (0 == strcmp(key, "o")) {
861 parseProperty(obj->mOpacity);
866 obj->mIsStatic = obj->mShape.isStatic() && obj->mOpacity.isStatic();
870 void LottieParserImpl::parseShapesAttr(LOTLayerData *layer)
872 RAPIDJSON_ASSERT(PeekType() == kArrayType);
874 while (NextArrayValue()) {
879 std::shared_ptr<LOTData> LottieParserImpl::parseObjectTypeAttr()
881 RAPIDJSON_ASSERT(PeekType() == kStringType);
882 const char *type = GetString();
883 if (0 == strcmp(type, "gr")) {
884 return parseGroupObject();
885 } else if (0 == strcmp(type, "rc")) {
886 return parseRectObject();
887 } else if (0 == strcmp(type, "el")) {
888 return parseEllipseObject();
889 } else if (0 == strcmp(type, "tr")) {
890 return parseTransformObject();
891 } else if (0 == strcmp(type, "fl")) {
892 return parseFillObject();
893 } else if (0 == strcmp(type, "st")) {
894 return parseStrokeObject();
895 } else if (0 == strcmp(type, "gf")) {
896 curLayerRef->mHasGradient = true;
897 return parseGFillObject();
898 } else if (0 == strcmp(type, "gs")) {
899 curLayerRef->mHasGradient = true;
900 return parseGStrokeObject();
901 } else if (0 == strcmp(type, "sh")) {
902 return parseShapeObject();
903 } else if (0 == strcmp(type, "sr")) {
904 return parsePolystarObject();
905 } else if (0 == strcmp(type, "tm")) {
906 curLayerRef->mHasPathOperator = true;
907 return parseTrimObject();
908 } else if (0 == strcmp(type, "rp")) {
909 curLayerRef->mHasRepeater = true;
910 return parseReapeaterObject();
913 vDebug << "The Object Type not yet handled = " << type;
919 void LottieParserImpl::parseObject(LOTGroupData *parent)
921 RAPIDJSON_ASSERT(PeekType() == kObjectType);
923 while (const char *key = NextObjectKey()) {
924 if (0 == strcmp(key, "ty")) {
925 auto child = parseObjectTypeAttr();
927 if (child) parent->mChildren.push_back(child);
935 std::shared_ptr<LOTData> LottieParserImpl::parseGroupObject()
937 std::shared_ptr<LOTShapeGroupData> sharedGroup =
938 std::make_shared<LOTShapeGroupData>();
940 LOTShapeGroupData *group = sharedGroup.get();
941 while (const char *key = NextObjectKey()) {
942 if (0 == strcmp(key, "it")) {
943 RAPIDJSON_ASSERT(PeekType() == kArrayType);
945 while (NextArrayValue()) {
946 RAPIDJSON_ASSERT(PeekType() == kObjectType);
949 if (group->mChildren.back()->mType == LOTData::Type::Transform) {
950 group->mTransform = std::static_pointer_cast<LOTTransformData>(
951 group->mChildren.back());
952 group->mChildren.pop_back();
958 bool staticFlag = true;
959 for (const auto& child : group->mChildren) {
960 staticFlag &= child.get()->isStatic();
963 group->setStatic(staticFlag && group->mTransform->isStatic());
969 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
971 std::shared_ptr<LOTData> LottieParserImpl::parseRectObject()
973 std::shared_ptr<LOTRectData> sharedRect = std::make_shared<LOTRectData>();
974 LOTRectData * obj = sharedRect.get();
976 while (const char *key = NextObjectKey()) {
977 if (0 == strcmp(key, "p")) {
978 parseProperty(obj->mPos);
979 } else if (0 == strcmp(key, "s")) {
980 parseProperty(obj->mSize);
981 } else if (0 == strcmp(key, "r")) {
982 parseProperty(obj->mRound);
983 } else if (0 == strcmp(key, "d")) {
984 obj->mDirection = GetInt();
989 obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic() &&
990 obj->mRound.isStatic());
995 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json
997 std::shared_ptr<LOTData> LottieParserImpl::parseEllipseObject()
999 std::shared_ptr<LOTEllipseData> sharedEllipse =
1000 std::make_shared<LOTEllipseData>();
1001 LOTEllipseData *obj = sharedEllipse.get();
1003 while (const char *key = NextObjectKey()) {
1004 if (0 == strcmp(key, "p")) {
1005 parseProperty(obj->mPos);
1006 } else if (0 == strcmp(key, "s")) {
1007 parseProperty(obj->mSize);
1008 } else if (0 == strcmp(key, "d")) {
1009 obj->mDirection = GetInt();
1014 obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic());
1015 return sharedEllipse;
1019 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/shape.json
1021 std::shared_ptr<LOTData> LottieParserImpl::parseShapeObject()
1023 std::shared_ptr<LOTShapeData> sharedShape =
1024 std::make_shared<LOTShapeData>();
1025 LOTShapeData *obj = sharedShape.get();
1027 while (const char *key = NextObjectKey()) {
1028 if (0 == strcmp(key, "ks")) {
1029 parseShapeProperty(obj->mShape);
1030 } else if (0 == strcmp(key, "d")) {
1031 obj->mDirection = GetInt();
1034 vDebug << "Shape property ignored :" << key;
1039 obj->setStatic(obj->mShape.isStatic());
1045 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/star.json
1047 std::shared_ptr<LOTData> LottieParserImpl::parsePolystarObject()
1049 std::shared_ptr<LOTPolystarData> sharedPolystar =
1050 std::make_shared<LOTPolystarData>();
1051 LOTPolystarData *obj = sharedPolystar.get();
1053 while (const char *key = NextObjectKey()) {
1054 if (0 == strcmp(key, "p")) {
1055 parseProperty(obj->mPos);
1056 } else if (0 == strcmp(key, "pt")) {
1057 parseProperty(obj->mPointCount);
1058 } else if (0 == strcmp(key, "ir")) {
1059 parseProperty(obj->mInnerRadius);
1060 } else if (0 == strcmp(key, "is")) {
1061 parseProperty(obj->mInnerRoundness);
1062 } else if (0 == strcmp(key, "or")) {
1063 parseProperty(obj->mOuterRadius);
1064 } else if (0 == strcmp(key, "os")) {
1065 parseProperty(obj->mOuterRoundness);
1066 } else if (0 == strcmp(key, "r")) {
1067 parseProperty(obj->mRotation);
1068 } else if (0 == strcmp(key, "sy")) {
1069 int starType = GetInt();
1070 if (starType == 1) obj->mType = LOTPolystarData::PolyType::Star;
1071 if (starType == 2) obj->mType = LOTPolystarData::PolyType::Polygon;
1072 } else if (0 == strcmp(key, "d")) {
1073 obj->mDirection = GetInt();
1076 vDebug << "Polystar property ignored :" << key;
1082 obj->mPos.isStatic() && obj->mPointCount.isStatic() &&
1083 obj->mInnerRadius.isStatic() && obj->mInnerRoundness.isStatic() &&
1084 obj->mOuterRadius.isStatic() && obj->mOuterRoundness.isStatic() &&
1085 obj->mRotation.isStatic());
1087 return sharedPolystar;
1090 LOTTrimData::TrimType LottieParserImpl::getTrimType()
1092 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1095 return LOTTrimData::TrimType::Simultaneously;
1098 return LOTTrimData::TrimType::Individually;
1101 RAPIDJSON_ASSERT(0);
1107 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/trim.json
1109 std::shared_ptr<LOTData> LottieParserImpl::parseTrimObject()
1111 std::shared_ptr<LOTTrimData> sharedTrim = std::make_shared<LOTTrimData>();
1112 LOTTrimData * obj = sharedTrim.get();
1114 while (const char *key = NextObjectKey()) {
1115 if (0 == strcmp(key, "s")) {
1116 parseProperty(obj->mStart);
1117 } else if (0 == strcmp(key, "e")) {
1118 parseProperty(obj->mEnd);
1119 } else if (0 == strcmp(key, "o")) {
1120 parseProperty(obj->mOffset);
1121 } else if (0 == strcmp(key, "m")) {
1122 obj->mTrimType = getTrimType();
1125 vDebug << "Trim property ignored :" << key;
1130 obj->setStatic(obj->mStart.isStatic() && obj->mEnd.isStatic() &&
1131 obj->mOffset.isStatic());
1135 std::shared_ptr<LOTData> LottieParserImpl::parseReapeaterObject()
1137 std::shared_ptr<LOTRepeaterData> sharedRepeater =
1138 std::make_shared<LOTRepeaterData>();
1139 LOTRepeaterData *obj = sharedRepeater.get();
1141 while (const char *key = NextObjectKey()) {
1142 if (0 == strcmp(key, "c")) {
1143 parseProperty(obj->mCopies);
1144 } else if (0 == strcmp(key, "o")) {
1145 parseProperty(obj->mOffset);
1146 } else if (0 == strcmp(key, "tr")) {
1147 obj->mTransform = parseTransformObject();
1150 vDebug << "Repeater property ignored :" << key;
1155 obj->setStatic(obj->mCopies.isStatic() && obj->mOffset.isStatic() &&
1156 obj->mTransform->isStatic());
1158 return sharedRepeater;
1162 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/transform.json
1164 std::shared_ptr<LOTTransformData> LottieParserImpl::parseTransformObject()
1166 std::shared_ptr<LOTTransformData> sharedTransform =
1167 std::make_shared<LOTTransformData>();
1168 LOTTransformData *obj = sharedTransform.get();
1170 while (const char *key = NextObjectKey()) {
1171 if (0 == strcmp(key, "a")) {
1172 parseProperty(obj->mAnchor);
1173 } else if (0 == strcmp(key, "p")) {
1175 while (const char *key = NextObjectKey()) {
1176 if (0 == strcmp(key, "k")) {
1177 parsePropertyHelper(obj->mPosition);
1178 } else if (0 == strcmp(key, "s")) {
1179 obj->mSeparate = GetBool();
1180 } else if (obj->mSeparate && (0 == strcmp(key, "x"))) {
1181 parseProperty(obj->mX);
1182 } else if (obj->mSeparate && (0 == strcmp(key, "y"))) {
1183 parseProperty(obj->mY);
1188 } else if (0 == strcmp(key, "r")) {
1189 parseProperty(obj->mRotation);
1190 } else if (0 == strcmp(key, "s")) {
1191 parseProperty(obj->mScale);
1192 } else if (0 == strcmp(key, "sk")) {
1193 parseProperty(obj->mSkew);
1194 } else if (0 == strcmp(key, "sa")) {
1195 parseProperty(obj->mSkewAxis);
1196 } else if (0 == strcmp(key, "o")) {
1197 parseProperty(obj->mOpacity);
1202 obj->mStaticMatrix = obj->mAnchor.isStatic() && obj->mPosition.isStatic() &&
1203 obj->mRotation.isStatic() && obj->mScale.isStatic() &&
1204 obj->mSkew.isStatic() && obj->mSkewAxis.isStatic() &&
1205 obj->mX.isStatic() && obj->mY.isStatic();
1207 obj->setStatic(obj->mStaticMatrix && obj->mOpacity.isStatic());
1209 if (obj->mStaticMatrix) obj->cacheMatrix();
1211 return sharedTransform;
1215 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/fill.json
1217 std::shared_ptr<LOTData> LottieParserImpl::parseFillObject()
1219 std::shared_ptr<LOTFillData> sharedFill = std::make_shared<LOTFillData>();
1220 LOTFillData * obj = sharedFill.get();
1222 while (const char *key = NextObjectKey()) {
1223 if (0 == strcmp(key, "c")) {
1224 parseProperty(obj->mColor);
1225 } else if (0 == strcmp(key, "o")) {
1226 parseProperty(obj->mOpacity);
1227 } else if (0 == strcmp(key, "fillEnabled")) {
1228 obj->mEnabled = GetBool();
1229 } else if (0 == strcmp(key, "r")) {
1230 obj->mFillRule = getFillRule();
1233 vWarning << "Fill property skipped = " << key;
1238 obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic());
1244 * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineCap.json
1246 CapStyle LottieParserImpl::getLineCap()
1248 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1251 return CapStyle::Flat;
1254 return CapStyle::Round;
1257 return CapStyle::Square;
1262 FillRule LottieParserImpl::getFillRule()
1264 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1267 return FillRule::Winding;
1270 return FillRule::EvenOdd;
1273 return FillRule::Winding;
1279 * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineJoin.json
1281 JoinStyle LottieParserImpl::getLineJoin()
1283 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1286 return JoinStyle::Miter;
1289 return JoinStyle::Round;
1292 return JoinStyle::Bevel;
1298 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/stroke.json
1300 std::shared_ptr<LOTData> LottieParserImpl::parseStrokeObject()
1302 std::shared_ptr<LOTStrokeData> sharedStroke =
1303 std::make_shared<LOTStrokeData>();
1304 LOTStrokeData *obj = sharedStroke.get();
1306 while (const char *key = NextObjectKey()) {
1307 if (0 == strcmp(key, "c")) {
1308 parseProperty(obj->mColor);
1309 } else if (0 == strcmp(key, "o")) {
1310 parseProperty(obj->mOpacity);
1311 } else if (0 == strcmp(key, "w")) {
1312 parseProperty(obj->mWidth);
1313 } else if (0 == strcmp(key, "fillEnabled")) {
1314 obj->mEnabled = GetBool();
1315 } else if (0 == strcmp(key, "lc")) {
1316 obj->mCapStyle = getLineCap();
1317 } else if (0 == strcmp(key, "lj")) {
1318 obj->mJoinStyle = getLineJoin();
1319 } else if (0 == strcmp(key, "ml")) {
1320 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1321 obj->mMeterLimit = GetDouble();
1322 } else if (0 == strcmp(key, "d")) {
1323 parseDashProperty(obj->mDash);
1326 vWarning << "Stroke property skipped = " << key;
1331 obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic() &&
1332 obj->mWidth.isStatic() && obj->mDash.mStatic);
1333 return sharedStroke;
1336 void LottieParserImpl::parseGradientProperty(LOTGradient *obj, const char *key)
1338 if (0 == strcmp(key, "t")) {
1339 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1340 obj->mGradientType = GetInt();
1341 } else if (0 == strcmp(key, "o")) {
1342 parseProperty(obj->mOpacity);
1343 } else if (0 == strcmp(key, "s")) {
1344 parseProperty(obj->mStartPoint);
1345 } else if (0 == strcmp(key, "e")) {
1346 parseProperty(obj->mEndPoint);
1347 } else if (0 == strcmp(key, "h")) {
1348 parseProperty(obj->mHighlightLength);
1349 } else if (0 == strcmp(key, "a")) {
1350 parseProperty(obj->mHighlightAngle);
1351 } else if (0 == strcmp(key, "g")) {
1353 while (const char *key = NextObjectKey()) {
1354 if (0 == strcmp(key, "k")) {
1355 parseProperty(obj->mGradient);
1356 } else if (0 == strcmp(key, "p")) {
1357 obj->mColorPoints = GetInt();
1364 vWarning << "Gradient property skipped = " << key;
1369 obj->mOpacity.isStatic() && obj->mStartPoint.isStatic() &&
1370 obj->mEndPoint.isStatic() && obj->mHighlightAngle.isStatic() &&
1371 obj->mHighlightLength.isStatic() && obj->mGradient.isStatic());
1375 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gfill.json
1377 std::shared_ptr<LOTData> LottieParserImpl::parseGFillObject()
1379 std::shared_ptr<LOTGFillData> sharedGFill =
1380 std::make_shared<LOTGFillData>();
1381 LOTGFillData *obj = sharedGFill.get();
1383 while (const char *key = NextObjectKey()) {
1384 if (0 == strcmp(key, "r")) {
1385 obj->mFillRule = getFillRule();
1387 parseGradientProperty(obj, key);
1393 void LottieParserImpl::parseDashProperty(LOTDashProperty &dash)
1395 dash.mDashCount = 0;
1396 dash.mStatic = true;
1397 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1399 while (NextArrayValue()) {
1400 RAPIDJSON_ASSERT(PeekType() == kObjectType);
1402 while (const char *key = NextObjectKey()) {
1403 if (0 == strcmp(key, "v")) {
1404 parseProperty(dash.mDashArray[dash.mDashCount++]);
1411 // update the staic proprty
1412 for (int i = 0; i < dash.mDashCount; i++) {
1413 if (!dash.mDashArray[i].isStatic()) {
1414 dash.mStatic = false;
1421 * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gstroke.json
1423 std::shared_ptr<LOTData> LottieParserImpl::parseGStrokeObject()
1425 std::shared_ptr<LOTGStrokeData> sharedGStroke =
1426 std::make_shared<LOTGStrokeData>();
1427 LOTGStrokeData *obj = sharedGStroke.get();
1429 while (const char *key = NextObjectKey()) {
1430 if (0 == strcmp(key, "w")) {
1431 parseProperty(obj->mWidth);
1432 } else if (0 == strcmp(key, "lc")) {
1433 obj->mCapStyle = getLineCap();
1434 } else if (0 == strcmp(key, "lj")) {
1435 obj->mJoinStyle = getLineJoin();
1436 } else if (0 == strcmp(key, "ml")) {
1437 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1438 obj->mMeterLimit = GetDouble();
1439 } else if (0 == strcmp(key, "d")) {
1440 parseDashProperty(obj->mDash);
1442 parseGradientProperty(obj, key);
1446 obj->setStatic(obj->isStatic() && obj->mWidth.isStatic() &&
1447 obj->mDash.mStatic);
1448 return sharedGStroke;
1451 void LottieParserImpl::parseArrayValue(LottieColor &color)
1455 while (NextArrayValue()) {
1456 val[i++] = GetDouble();
1464 void LottieParserImpl::parseArrayValue(VPointF &pt)
1468 while (NextArrayValue()) {
1469 val[i++] = GetDouble();
1475 void LottieParserImpl::parseArrayValue(float &val)
1477 RAPIDJSON_ASSERT(0);
1481 void LottieParserImpl::parseArrayValue(int &val)
1483 RAPIDJSON_ASSERT(0);
1487 void LottieParserImpl::parseArrayValue(std::vector<VPointF> &v)
1489 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1491 while (NextArrayValue()) {
1492 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1495 parseArrayValue(pt);
1500 void LottieParserImpl::getValue(VPointF &pt)
1504 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1506 while (NextArrayValue()) {
1507 val[i++] = GetDouble();
1513 void LottieParserImpl::getValue(float &val)
1515 if (PeekType() == kArrayType) {
1517 while (NextArrayValue()) {
1520 } else if (PeekType() == kNumberType) {
1523 RAPIDJSON_ASSERT(0);
1527 void LottieParserImpl::getValue(LottieColor &color)
1531 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1533 while (NextArrayValue()) {
1534 val[i++] = GetDouble();
1541 void LottieParserImpl::parseArrayValue(LottieGradient &grad)
1543 while (NextArrayValue()) {
1544 grad.mGradient.push_back(GetDouble());
1548 void LottieParserImpl::getValue(LottieGradient &grad)
1550 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1552 while (NextArrayValue()) {
1553 grad.mGradient.push_back(GetDouble());
1557 void LottieParserImpl::getValue(int &val)
1559 if (PeekType() == kArrayType) {
1561 while (NextArrayValue()) {
1564 } else if (PeekType() == kNumberType) {
1567 RAPIDJSON_ASSERT(0);
1571 void LottieParserImpl::getValue(LottieShapeData &obj)
1573 std::vector<VPointF> inPoint; /* "i" */
1574 std::vector<VPointF> outPoint; /* "o" */
1575 std::vector<VPointF> vertices; /* "v" */
1576 std::vector<VPointF> points;
1577 bool closed = false;
1580 * The shape object could be wrapped by a array
1581 * if its part of the keyframe object
1583 bool arrayWrapper = (PeekType() == kArrayType);
1584 if (arrayWrapper) EnterArray();
1586 RAPIDJSON_ASSERT(PeekType() == kObjectType);
1588 while (const char *key = NextObjectKey()) {
1589 if (0 == strcmp(key, "i")) {
1590 parseArrayValue(inPoint);
1591 } else if (0 == strcmp(key, "o")) {
1592 parseArrayValue(outPoint);
1593 } else if (0 == strcmp(key, "v")) {
1594 parseArrayValue(vertices);
1595 } else if (0 == strcmp(key, "c")) {
1598 RAPIDJSON_ASSERT(0);
1602 // exit properly from the array
1603 if (arrayWrapper) NextArrayValue();
1606 * Convert the AE shape format to
1607 * list of bazier curves
1608 * The final structure will be Move +size*Cubic + Cubic (if the path is
1611 if (inPoint.size() != outPoint.size() ||
1612 inPoint.size() != vertices.size()) {
1613 vCritical << "The Shape data are corrupted";
1614 points = std::vector<VPointF>();
1616 int size = vertices.size();
1617 points.reserve(3 * size + 4);
1618 points.push_back(vertices[0]);
1619 for (int i = 1; i < size; i++) {
1620 points.push_back(vertices[i - 1] +
1621 outPoint[i - 1]); // CP1 = start + outTangent
1622 points.push_back(vertices[i] +
1623 inPoint[i]); // CP2 = end + inTangent
1624 points.push_back(vertices[i]); // end point
1628 points.push_back(vertices[size - 1] +
1629 outPoint[size - 1]); // CP1 = start + outTangent
1630 points.push_back(vertices[0] +
1631 inPoint[0]); // CP2 = end + inTangent
1632 points.push_back(vertices[0]); // end point
1635 obj.mPoints = std::move(points);
1636 obj.mClosed = closed;
1639 VPointF LottieParserImpl::parseInperpolatorPoint()
1642 RAPIDJSON_ASSERT(PeekType() == kObjectType);
1644 while (const char *key = NextObjectKey()) {
1645 if (0 == strcmp(key, "x")) {
1646 if (PeekType() == kNumberType) {
1647 cp.setX(GetDouble());
1649 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1651 while (NextArrayValue()) {
1652 cp.setX(GetDouble());
1656 if (0 == strcmp(key, "y")) {
1657 if (PeekType() == kNumberType) {
1658 cp.setY(GetDouble());
1660 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1662 while (NextArrayValue()) {
1663 cp.setY(GetDouble());
1671 template <typename T>
1672 bool LottieParserImpl::parseKeyFrameValue(const char * key,
1673 LOTKeyFrameValue<T> &value)
1675 if (0 == strcmp(key, "s")) {
1676 getValue(value.mStartValue);
1677 } else if (0 == strcmp(key, "e")) {
1678 getValue(value.mEndValue);
1686 bool LottieParserImpl::parseKeyFrameValue(const char * key,
1687 LOTKeyFrameValue<VPointF> &value)
1689 if (0 == strcmp(key, "s")) {
1690 getValue(value.mStartValue);
1691 } else if (0 == strcmp(key, "e")) {
1692 getValue(value.mEndValue);
1693 } else if (0 == strcmp(key, "ti")) {
1694 value.mPathKeyFrame = true;
1695 getValue(value.mInTangent);
1696 } else if (0 == strcmp(key, "to")) {
1697 value.mPathKeyFrame = true;
1698 getValue(value.mOutTangent);
1706 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
1708 template <typename T>
1709 void LottieParserImpl::parseKeyFrame(LOTAnimInfo<T> &obj)
1712 LOTKeyFrame<T> keyframe;
1715 const char * interpolatorKey = nullptr;
1717 while (const char *key = NextObjectKey()) {
1718 if (0 == strcmp(key, "i")) {
1719 inTangent = parseInperpolatorPoint();
1720 } else if (0 == strcmp(key, "o")) {
1721 outTangent = parseInperpolatorPoint();
1722 } else if (0 == strcmp(key, "n")) {
1723 if (PeekType() == kStringType) {
1724 interpolatorKey = GetString();
1726 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1728 while (NextArrayValue()) {
1729 RAPIDJSON_ASSERT(PeekType() == kStringType);
1730 interpolatorKey = GetString();
1734 } else if (0 == strcmp(key, "t")) {
1735 keyframe.mStartFrame = GetDouble();
1736 } else if (parseKeyFrameValue(key, keyframe.mValue)) {
1738 } else if (0 == strcmp(key, "h")) {
1743 vDebug << "key frame property skipped = " << key;
1749 if (!obj.mKeyFrames.empty()) {
1750 // update the endFrame value of current keyframe
1751 obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
1755 interpolatorKey = "hold_interpolator";
1756 inTangent = VPointF();
1757 outTangent = VPointF();
1758 keyframe.mValue.mEndValue = keyframe.mValue.mStartValue;
1759 keyframe.mEndFrame = keyframe.mStartFrame;
1762 // Try to find the interpolator from cache
1763 if (interpolatorKey) {
1764 auto search = compRef->mInterpolatorCache.find(interpolatorKey);
1765 if (search != compRef->mInterpolatorCache.end()) {
1766 keyframe.mInterpolator = search->second;
1768 keyframe.mInterpolator = std::make_shared<VInterpolator>(
1769 VInterpolator(outTangent, inTangent));
1770 compRef->mInterpolatorCache[interpolatorKey] =
1771 keyframe.mInterpolator;
1773 obj.mKeyFrames.push_back(keyframe);
1778 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shapeKeyframed.json
1782 * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
1784 void LottieParserImpl::parseShapeProperty(LOTAnimatable<LottieShapeData> &obj)
1787 while (const char *key = NextObjectKey()) {
1788 if (0 == strcmp(key, "k")) {
1789 if (PeekType() == kArrayType) {
1791 while (NextArrayValue()) {
1792 RAPIDJSON_ASSERT(PeekType() == kObjectType);
1795 std::make_unique<LOTAnimInfo<LottieShapeData>>();
1796 parseKeyFrame(*obj.mAnimInfo.get());
1799 getValue(obj.mValue);
1803 vDebug << "shape property ignored = " << key;
1810 template <typename T>
1811 void LottieParserImpl::parsePropertyHelper(LOTAnimatable<T> &obj)
1813 if (PeekType() == kNumberType) {
1814 /*single value property with no animation*/
1815 getValue(obj.mValue);
1817 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1819 while (NextArrayValue()) {
1820 /* property with keyframe info*/
1821 if (PeekType() == kObjectType) {
1823 obj.mAnimInfo = std::make_unique<LOTAnimInfo<T>>();
1824 parseKeyFrame(*obj.mAnimInfo.get());
1826 /* Read before modifying.
1827 * as there is no way of knowing if the
1828 * value of the array is either array of numbers
1829 * or array of object without entering the array
1830 * thats why this hack is there
1832 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1833 /*multi value property with no animation*/
1834 parseArrayValue(obj.mValue);
1835 /*break here as we already reached end of array*/
1843 * https://github.com/airbnb/lottie-web/tree/master/docs/json/properties
1845 template <typename T>
1846 void LottieParserImpl::parseProperty(LOTAnimatable<T> &obj)
1849 while (const char *key = NextObjectKey()) {
1850 if (0 == strcmp(key, "k")) {
1851 parsePropertyHelper(obj);
1858 #ifdef DEBUG_PRINT_TREE
1860 class LOTDataInspector {
1862 void visit(LOTCompositionData *obj, std::string level)
1866 << "Composition:: a: " << !obj->isStatic()
1867 << ", v: " << obj->mVersion
1868 << ", stFm: " << obj->startFrame()
1869 << ", endFm: " << obj->endFrame()<< "\n";
1871 visit(obj->mRootLayer.get(), level);
1872 level.erase(level.end() - 1, level.end());
1873 vDebug << " } " << level << "Composition End\n";
1875 void visit(LOTLayerData *obj, std::string level)
1879 << layerType(obj->mLayerType)
1880 << ", id:" << obj->mId << " Pid:" << obj->mParentId
1881 << ", a:" << !obj->isStatic()
1882 << ", inFm:" << obj->mInFrame
1883 << ", outFm:" << obj->mOutFrame
1885 visitChildren(static_cast<LOTGroupData *>(obj), level);
1888 << layerType(obj->mLayerType).c_str()
1889 << ", id: " << obj->mId << "\n";
1891 void visitChildren(LOTGroupData *obj, std::string level)
1894 for (const auto& child : obj->mChildren) visit(child.get(), level);
1897 void visit(LOTData *obj, std::string level) {
1898 switch (obj->mType) {
1899 case LOTData::Type::Repeater: {
1900 vDebug << level << "{ Repeater:";
1901 visitChildren(static_cast<LOTGroupData *>(obj), level);
1902 vDebug << level << "} Repeater";
1905 case LOTData::Type::ShapeGroup: {
1906 vDebug << level << "{ ShapeGroup: a:" << !obj->isStatic();
1907 visitChildren(static_cast<LOTGroupData *>(obj), level);
1908 vDebug << level << "} ShapeGroup";
1911 case LOTData::Type::Layer:{
1912 visit(static_cast<LOTLayerData *>(obj), level);
1915 case LOTData::Type::Trim:{
1916 vDebug << level << "{ Trim: a:" << !obj->isStatic() << " }";
1919 case LOTData::Type::Rect:{
1920 vDebug << level << "{ Rect: a:" << !obj->isStatic() << " }";
1923 case LOTData::Type::Ellipse:{
1924 vDebug << level << "{ Ellipse: a:" << !obj->isStatic() << " }";
1927 case LOTData::Type::Shape:{
1928 vDebug << level << "{ Shape: a:" << !obj->isStatic() << " }";
1931 case LOTData::Type::Polystar:{
1932 vDebug << level << "{ Polystar: a:" << !obj->isStatic() << " }";
1935 case LOTData::Type::Transform:{
1936 vDebug << level << "{ Transform: a: " << !obj->isStatic() << " }";
1939 case LOTData::Type::Stroke:{
1940 vDebug << level << "{ Stroke: a:" << !obj->isStatic() << " }";
1943 case LOTData::Type::GStroke:{
1944 vDebug << level << "{ GStroke: a:" << !obj->isStatic() << " }";
1947 case LOTData::Type::Fill:{
1948 vDebug << level << "{ Fill: a:" << !obj->isStatic() << " }";
1951 case LOTData::Type::GFill:{
1952 auto f = static_cast<LOTGFillData *>(obj);
1953 vDebug << level << "{ GFill: a:" << !f->isStatic()
1954 << ", ty:" << f->mGradientType << ", s:" << f->mStartPoint.value(0)
1955 << ", e:" << f->mEndPoint.value(0) << " }";
1963 std::string layerType(LayerType type)
1966 case LayerType::Precomp:
1967 return "Layer::Precomp";
1969 case LayerType::Null:
1970 return "Layer::Null";
1972 case LayerType::Shape:
1973 return "Layer::Shape";
1975 case LayerType::Solid:
1976 return "Layer::Solid";
1978 case LayerType::Image:
1979 return "Layer::Image";
1981 case LayerType::Text:
1982 return "Layer::Text";
1985 return "Layer::Unknown";
1993 LottieParser::~LottieParser()
1998 LottieParser::LottieParser(char *str) : d(new LottieParserImpl(str))
2000 d->parseComposition();
2003 std::shared_ptr<LOTModel> LottieParser::model()
2005 std::shared_ptr<LOTModel> model = std::make_shared<LOTModel>();
2006 model->mRoot = d->composition();
2007 model->mRoot->processRepeaterObjects();
2009 #ifdef DEBUG_PRINT_TREE
2010 LOTDataInspector inspector;
2011 inspector.visit(model->mRoot.get(), "");