lottie/parser: fix code indentations and a duplicated code
[platform/core/uifw/lottie-player.git] / src / lottie / lottieparser.cpp
1
2 #include "lottieparser.h"
3
4
5 //#define DEBUG_PARSER
6
7 #define DEBUG_PRINT_TREE
8
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.
16 //
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.
19 //
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
23 // will be delivered.
24 //
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().
30 //
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().
36 //
37 // This parser uses in-situ strings, so the JSON buffer will be altered during
38 // the parse.
39
40 #include <iostream>
41 #include "lottiemodel.h"
42 #include "rapidjson/document.h"
43 #include "velapsedtimer.h"
44
45 RAPIDJSON_DIAG_PUSH
46 #ifdef __GNUC__
47 RAPIDJSON_DIAG_OFF(effc++)
48 #endif
49
50 using namespace rapidjson;
51
52 class LookaheadParserHandler {
53 public:
54     bool Null()
55     {
56         st_ = kHasNull;
57         v_.SetNull();
58         return true;
59     }
60     bool Bool(bool b)
61     {
62         st_ = kHasBool;
63         v_.SetBool(b);
64         return true;
65     }
66     bool Int(int i)
67     {
68         st_ = kHasNumber;
69         v_.SetInt(i);
70         return true;
71     }
72     bool Uint(unsigned u)
73     {
74         st_ = kHasNumber;
75         v_.SetUint(u);
76         return true;
77     }
78     bool Int64(int64_t i)
79     {
80         st_ = kHasNumber;
81         v_.SetInt64(i);
82         return true;
83     }
84     bool Uint64(uint64_t u)
85     {
86         st_ = kHasNumber;
87         v_.SetUint64(u);
88         return true;
89     }
90     bool Double(double d)
91     {
92         st_ = kHasNumber;
93         v_.SetDouble(d);
94         return true;
95     }
96     bool RawNumber(const char *, SizeType, bool) { return false; }
97     bool String(const char *str, SizeType length, bool)
98     {
99         st_ = kHasString;
100         v_.SetString(str, length);
101         return true;
102     }
103     bool StartObject()
104     {
105         st_ = kEnteringObject;
106         return true;
107     }
108     bool Key(const char *str, SizeType length, bool)
109     {
110         st_ = kHasKey;
111         v_.SetString(str, length);
112         return true;
113     }
114     bool EndObject(SizeType)
115     {
116         st_ = kExitingObject;
117         return true;
118     }
119     bool StartArray()
120     {
121         st_ = kEnteringArray;
122         return true;
123     }
124     bool EndArray(SizeType)
125     {
126         st_ = kExitingArray;
127         return true;
128     }
129
130 protected:
131     LookaheadParserHandler(char *str);
132     void ParseNext();
133
134 protected:
135     enum LookaheadParsingState {
136         kInit,
137         kError,
138         kHasNull,
139         kHasBool,
140         kHasNumber,
141         kHasString,
142         kHasKey,
143         kEnteringObject,
144         kExitingObject,
145         kEnteringArray,
146         kExitingArray
147     };
148
149     Value                 v_;
150     LookaheadParsingState st_;
151     Reader                r_;
152     InsituStringStream    ss_;
153
154     static const int parseFlags = kParseDefaultFlags | kParseInsituFlag;
155 };
156
157 class LottieParserImpl : protected LookaheadParserHandler {
158 public:
159     LottieParserImpl(char *str) : LookaheadParserHandler(str) {}
160
161 public:
162     bool        EnterObject();
163     bool        EnterArray();
164     const char *NextObjectKey();
165     bool        NextArrayValue();
166     int         GetInt();
167     double      GetDouble();
168     const char *GetString();
169     bool        GetBool();
170     void        GetNull();
171
172     void   SkipObject();
173     void   SkipArray();
174     void   SkipValue();
175     Value *PeekValue();
176     int PeekType();  // returns a rapidjson::Type, or -1 for no value (at end of
177                      // object/array)
178
179     bool IsValid() { return st_ != kError; }
180
181     void                  Skip(const char *key);
182     VRect                 getRect();
183     LottieBlendMode       getBlendMode();
184     CapStyle              getLineCap();
185     JoinStyle             getLineJoin();
186     FillRule              getFillRule();
187     LOTTrimData::TrimType getTrimType();
188     MatteType             getMatteType();
189     LayerType             getLayerType();
190
191     std::shared_ptr<LOTCompositionData> composition() const
192     {
193         return mComposition;
194     }
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();
210
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();
218
219     void parseGradientProperty(LOTGradient *gradient, const char *key);
220
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);
241
242     void parseShapeKeyFrame(LOTAnimInfo<LottieShapeData> &obj);
243     void parseShapeProperty(LOTAnimatable<LottieShapeData> &obj);
244     void parseArrayValue(std::vector<VPointF> &v);
245     void parseDashProperty(LOTDashProperty &dash);
246
247     LottieColor toColor(const char *str);
248
249     void resolveLayerRefs();
250
251 protected:
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);
257 };
258
259 LookaheadParserHandler::LookaheadParserHandler(char *str)
260     : v_(), st_(kInit), r_(), ss_(str)
261 {
262     r_.IterativeParseInit();
263     ParseNext();
264 }
265
266 void LookaheadParserHandler::ParseNext()
267 {
268     if (r_.HasParseError()) {
269         st_ = kError;
270         return;
271     }
272
273     if (!r_.IterativeParseNext<parseFlags>(ss_, *this)) {
274         vCritical << "Lottie file parsing error";
275         RAPIDJSON_ASSERT(0);
276     }
277 }
278
279 bool LottieParserImpl::EnterObject()
280 {
281     if (st_ != kEnteringObject) {
282         st_ = kError;
283         RAPIDJSON_ASSERT(false);
284         return false;
285     }
286
287     ParseNext();
288     return true;
289 }
290
291 bool LottieParserImpl::EnterArray()
292 {
293     if (st_ != kEnteringArray) {
294         st_ = kError;
295         RAPIDJSON_ASSERT(false);
296         return false;
297     }
298
299     ParseNext();
300     return true;
301 }
302
303 const char *LottieParserImpl::NextObjectKey()
304 {
305     if (st_ == kHasKey) {
306         const char *result = v_.GetString();
307         ParseNext();
308         return result;
309     }
310
311     /* SPECIAL CASE
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.
316      * */
317     if (st_ == kExitingArray || st_ == kEnteringObject) {
318         // #ifdef DEBUG_PARSER
319         //         vDebug<<"Object: Exiting nested loop";
320         // #endif
321         return 0;
322     }
323
324     if (st_ != kExitingObject) {
325         RAPIDJSON_ASSERT(false);
326         st_ = kError;
327         return 0;
328     }
329
330     ParseNext();
331     return 0;
332 }
333
334 bool LottieParserImpl::NextArrayValue()
335 {
336     if (st_ == kExitingArray) {
337         ParseNext();
338         return false;
339     }
340
341     /* SPECIAL CASE
342      * same as  NextObjectKey()
343      */
344     if (st_ == kExitingObject) {
345         // #ifdef DEBUG_PARSER
346         //         vDebug<<"Array: Exiting nested loop";
347         // #endif
348         return 0;
349     }
350
351     if (st_ == kError || st_ == kHasKey) {
352         RAPIDJSON_ASSERT(false);
353         st_ = kError;
354         return false;
355     }
356
357     return true;
358 }
359
360 int LottieParserImpl::GetInt()
361 {
362     if (st_ != kHasNumber || !v_.IsInt()) {
363         st_ = kError;
364         RAPIDJSON_ASSERT(false);
365         return 0;
366     }
367
368     int result = v_.GetInt();
369     ParseNext();
370     return result;
371 }
372
373 double LottieParserImpl::GetDouble()
374 {
375     if (st_ != kHasNumber) {
376         st_ = kError;
377         RAPIDJSON_ASSERT(false);
378         return 0.;
379     }
380
381     double result = v_.GetDouble();
382     ParseNext();
383     return result;
384 }
385
386 bool LottieParserImpl::GetBool()
387 {
388     if (st_ != kHasBool) {
389         st_ = kError;
390         RAPIDJSON_ASSERT(false);
391         return false;
392     }
393
394     bool result = v_.GetBool();
395     ParseNext();
396     return result;
397 }
398
399 void LottieParserImpl::GetNull()
400 {
401     if (st_ != kHasNull) {
402         st_ = kError;
403         return;
404     }
405
406     ParseNext();
407 }
408
409 const char *LottieParserImpl::GetString()
410 {
411     if (st_ != kHasString) {
412         st_ = kError;
413         RAPIDJSON_ASSERT(false);
414         return 0;
415     }
416
417     const char *result = v_.GetString();
418     ParseNext();
419     return result;
420 }
421
422 void LottieParserImpl::SkipOut(int depth)
423 {
424     do {
425         if (st_ == kEnteringArray || st_ == kEnteringObject) {
426             ++depth;
427         } else if (st_ == kExitingArray || st_ == kExitingObject) {
428             --depth;
429         } else if (st_ == kError) {
430             RAPIDJSON_ASSERT(false);
431             return;
432         }
433
434         ParseNext();
435     } while (depth > 0);
436 }
437
438 void LottieParserImpl::SkipValue()
439 {
440     SkipOut(0);
441 }
442
443 void LottieParserImpl::SkipArray()
444 {
445     SkipOut(1);
446 }
447
448 void LottieParserImpl::SkipObject()
449 {
450     SkipOut(1);
451 }
452
453 Value *LottieParserImpl::PeekValue()
454 {
455     if (st_ >= kHasNull && st_ <= kHasKey) {
456         return &v_;
457     }
458
459     return 0;
460 }
461
462 int LottieParserImpl::PeekType()
463 {
464     if (st_ >= kHasNull && st_ <= kHasKey) {
465         return v_.GetType();
466     }
467
468     if (st_ == kEnteringArray) {
469         return kArrayType;
470     }
471
472     if (st_ == kEnteringObject) {
473         return kObjectType;
474     }
475
476     return -1;
477 }
478
479 void LottieParserImpl::Skip(const char *key)
480 {
481     if (PeekType() == kArrayType) {
482         EnterArray();
483         SkipArray();
484     } else if (PeekType() == kObjectType) {
485         EnterObject();
486         SkipObject();
487     } else {
488         SkipValue();
489     }
490 }
491
492 LottieBlendMode LottieParserImpl::getBlendMode()
493 {
494     RAPIDJSON_ASSERT(PeekType() == kNumberType);
495     LottieBlendMode mode = LottieBlendMode::Normal;
496
497     switch (GetInt()) {
498     case 1:
499         mode = LottieBlendMode::Multiply;
500         break;
501     case 2:
502         mode = LottieBlendMode::Screen;
503         break;
504     case 3:
505         mode = LottieBlendMode::OverLay;
506         break;
507     default:
508         break;
509     }
510     return mode;
511 }
512 VRect LottieParserImpl::getRect()
513 {
514     VRect r;
515     RAPIDJSON_ASSERT(PeekType() == kObjectType);
516     EnterObject();
517     while (const char *key = NextObjectKey()) {
518         if (0 == strcmp(key, "l")) {
519             RAPIDJSON_ASSERT(PeekType() == kNumberType);
520             r.setLeft(GetInt());
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);
526             r.setTop(GetInt());
527         } else if (0 == strcmp(key, "b")) {
528             RAPIDJSON_ASSERT(PeekType() == kNumberType);
529             r.setBottom(GetInt());
530         } else {
531             RAPIDJSON_ASSERT(false);
532         }
533     }
534     return r;
535 }
536
537 void LottieParserImpl::resolveLayerRefs()
538 {
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;
544         }
545     }
546 }
547
548 void LottieParserImpl::parseComposition()
549 {
550     RAPIDJSON_ASSERT(PeekType() == kObjectType);
551     EnterObject();
552     std::shared_ptr<LOTCompositionData> sharedComposition =
553         std::make_shared<LOTCompositionData>();
554     LOTCompositionData *comp = sharedComposition.get();
555     compRef = comp;
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")) {
576             parseAssets(comp);
577         } else if (0 == strcmp(key, "layers")) {
578             parseLayers(comp);
579         } else {
580 #ifdef DEBUG_PARSER
581             vWarning << "Composition Attribute Skipped : " << key;
582 #endif
583             Skip(key);
584         }
585     }
586     resolveLayerRefs();
587     comp->setStatic(comp->mRootLayer->isStatic());
588     comp->mRootLayer->mInFrame = comp->mStartFrame;
589     comp->mRootLayer->mOutFrame = comp->mEndFrame;
590
591     mComposition = sharedComposition;
592 }
593
594 void LottieParserImpl::parseAssets(LOTCompositionData *composition)
595 {
596     RAPIDJSON_ASSERT(PeekType() == kArrayType);
597     EnterArray();
598     while (NextArrayValue()) {
599         std::shared_ptr<LOTAsset> asset = parseAsset();
600         composition->mAssets[asset->mRefId] = asset;
601     }
602     // update the precomp layers with the actual layer object
603 }
604
605 /*
606  * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
607  *
608  */
609 std::shared_ptr<LOTAsset> LottieParserImpl::parseAsset()
610 {
611     RAPIDJSON_ASSERT(PeekType() == kObjectType);
612     std::shared_ptr<LOTAsset> sharedAsset = std::make_shared<LOTAsset>();
613     LOTAsset *                asset = sharedAsset.get();
614     EnterObject();
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);
624             EnterArray();
625             while (NextArrayValue()) {
626                 std::shared_ptr<LOTData> layer = parseLayer();
627                 asset->mLayers.push_back(layer);
628             }
629         } else {
630 #ifdef DEBUG_PARSER
631             vWarning << "Asset Attribute Skipped : " << key;
632 #endif
633             Skip(key);
634         }
635     }
636     return sharedAsset;
637 }
638
639 void LottieParserImpl::parseLayers(LOTCompositionData *comp)
640 {
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);
647     EnterArray();
648     while (NextArrayValue()) {
649         std::shared_ptr<LOTData> layer = parseLayer();
650         staticFlag &= layer->isStatic();
651         comp->mRootLayer->mChildren.push_back(layer);
652     }
653     comp->mRootLayer->setStatic(staticFlag);
654 }
655
656 LottieColor LottieParserImpl::toColor(const char *str)
657 {
658     LottieColor color;
659
660     RAPIDJSON_ASSERT(strlen(str) == 7);
661     RAPIDJSON_ASSERT(str[0] == '#');
662
663     char tmp[3] = {'\0', '\0', '\0'};
664     tmp[0] = str[1];
665     tmp[1] = str[2];
666     color.r = std::strtol(tmp, NULL, 16) / 255.0;
667
668     tmp[0] = str[3];
669     tmp[1] = str[4];
670     color.g = std::strtol(tmp, NULL, 16) / 255.0;
671
672     tmp[0] = str[5];
673     tmp[1] = str[6];
674     color.b = std::strtol(tmp, NULL, 16) / 255.0;
675
676     return color;
677 }
678
679 MatteType LottieParserImpl::getMatteType()
680 {
681     RAPIDJSON_ASSERT(PeekType() == kNumberType);
682     switch (GetInt()) {
683     case 1:
684         return MatteType::Alpha;
685         break;
686     case 2:
687         return MatteType::AlphaInv;
688         break;
689     case 3:
690         return MatteType::Luma;
691         break;
692     case 4:
693         return MatteType::LumaInv;
694         break;
695     default:
696         return MatteType::None;
697         break;
698     }
699 }
700
701 LayerType LottieParserImpl::getLayerType()
702 {
703     RAPIDJSON_ASSERT(PeekType() == kNumberType);
704     switch (GetInt()) {
705     case 0:
706         return LayerType::Precomp;
707         break;
708     case 1:
709         return LayerType::Solid;
710         break;
711     case 2:
712         return LayerType::Image;
713         break;
714     case 3:
715         return LayerType::Null;
716         break;
717     case 4:
718         return LayerType::Shape;
719         break;
720     case 5:
721         return LayerType::Text;
722         break;
723     default:
724         return LayerType::Null;
725         break;
726     }
727 }
728
729 /*
730  * https://github.com/airbnb/lottie-web/blob/master/docs/json/layers/shape.json
731  *
732  */
733 std::shared_ptr<LOTData> LottieParserImpl::parseLayer()
734 {
735     RAPIDJSON_ASSERT(PeekType() == kObjectType);
736     std::shared_ptr<LOTLayerData> sharedLayer =
737         std::make_shared<LOTLayerData>();
738     LOTLayerData *layer = sharedLayer.get();
739     curLayerRef = layer;
740     bool hasLayerRef = false;
741     EnterObject();
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);
757             hasLayerRef = true;
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);
778             EnterObject();
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);
794         } else {
795 #ifdef DEBUG_PARSER
796             vWarning << "Layer Attribute Skipped : " << key;
797 #endif
798             Skip(key);
799         }
800     }
801     // update the static property of layer
802     bool staticFlag = true;
803     for (const auto& child : layer->mChildren) {
804         staticFlag &= child.get()->isStatic();
805     }
806
807     for (const auto& mask : layer->mMasks) {
808         staticFlag &= mask->isStatic();
809     }
810
811     layer->setStatic(staticFlag && layer->mTransform->isStatic() &&
812                      !hasLayerRef);
813
814     return sharedLayer;
815 }
816
817 void LottieParserImpl::parseMaskProperty(LOTLayerData *layer)
818 {
819     RAPIDJSON_ASSERT(PeekType() == kArrayType);
820     EnterArray();
821     while (NextArrayValue()) {
822         layer->mMasks.push_back(parseMaskObject());
823     }
824 }
825
826 std::shared_ptr<LOTMaskData> LottieParserImpl::parseMaskObject()
827 {
828     std::shared_ptr<LOTMaskData> sharedMask = std::make_shared<LOTMaskData>();
829     LOTMaskData *                obj = sharedMask.get();
830
831     RAPIDJSON_ASSERT(PeekType() == kObjectType);
832     EnterObject();
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();
838             switch (str[0]) {
839             case 'n':
840                 obj->mMode = LOTMaskData::Mode::None;
841                 break;
842             case 'a':
843                 obj->mMode = LOTMaskData::Mode::Add;
844                 break;
845             case 's':
846                 obj->mMode = LOTMaskData::Mode::Substarct;
847                 break;
848             case 'i':
849                 obj->mMode = LOTMaskData::Mode::Intersect;
850                 break;
851             case 'f':
852                 obj->mMode = LOTMaskData::Mode::Difference;
853                 break;
854             default:
855                 obj->mMode = LOTMaskData::Mode::None;
856                 break;
857             }
858         } else if (0 == strcmp(key, "pt")) {
859             parseShapeProperty(obj->mShape);
860         } else if (0 == strcmp(key, "o")) {
861             parseProperty(obj->mOpacity);
862         } else {
863             Skip(key);
864         }
865     }
866     obj->mIsStatic = obj->mShape.isStatic() && obj->mOpacity.isStatic();
867     return sharedMask;
868 }
869
870 void LottieParserImpl::parseShapesAttr(LOTLayerData *layer)
871 {
872     RAPIDJSON_ASSERT(PeekType() == kArrayType);
873     EnterArray();
874     while (NextArrayValue()) {
875         parseObject(layer);
876     }
877 }
878
879 std::shared_ptr<LOTData> LottieParserImpl::parseObjectTypeAttr()
880 {
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();
911     } else {
912 #ifdef DEBUG_PARSER
913         vDebug << "The Object Type not yet handled = " << type;
914 #endif
915         return nullptr;
916     }
917 }
918
919 void LottieParserImpl::parseObject(LOTGroupData *parent)
920 {
921     RAPIDJSON_ASSERT(PeekType() == kObjectType);
922     EnterObject();
923     while (const char *key = NextObjectKey()) {
924         if (0 == strcmp(key, "ty")) {
925             auto child = parseObjectTypeAttr();
926             if (child) {
927                 if (child) parent->mChildren.push_back(child);
928             }
929         } else {
930             Skip(key);
931         }
932     }
933 }
934
935 std::shared_ptr<LOTData> LottieParserImpl::parseGroupObject()
936 {
937     std::shared_ptr<LOTShapeGroupData> sharedGroup =
938         std::make_shared<LOTShapeGroupData>();
939
940     LOTShapeGroupData *group = sharedGroup.get();
941     while (const char *key = NextObjectKey()) {
942         if (0 == strcmp(key, "it")) {
943             RAPIDJSON_ASSERT(PeekType() == kArrayType);
944             EnterArray();
945             while (NextArrayValue()) {
946                 RAPIDJSON_ASSERT(PeekType() == kObjectType);
947                 parseObject(group);
948             }
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();
953             }
954         } else {
955             Skip(key);
956         }
957     }
958     bool staticFlag = true;
959     for (const auto& child : group->mChildren) {
960         staticFlag &= child.get()->isStatic();
961     }
962
963     group->setStatic(staticFlag && group->mTransform->isStatic());
964
965     return sharedGroup;
966 }
967
968 /*
969  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
970  */
971 std::shared_ptr<LOTData> LottieParserImpl::parseRectObject()
972 {
973     std::shared_ptr<LOTRectData> sharedRect = std::make_shared<LOTRectData>();
974     LOTRectData *                obj = sharedRect.get();
975
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();
985         } else {
986             Skip(key);
987         }
988     }
989     obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic() &&
990                    obj->mRound.isStatic());
991     return sharedRect;
992 }
993
994 /*
995  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json
996  */
997 std::shared_ptr<LOTData> LottieParserImpl::parseEllipseObject()
998 {
999     std::shared_ptr<LOTEllipseData> sharedEllipse =
1000         std::make_shared<LOTEllipseData>();
1001     LOTEllipseData *obj = sharedEllipse.get();
1002
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();
1010         } else {
1011             Skip(key);
1012         }
1013     }
1014     obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic());
1015     return sharedEllipse;
1016 }
1017
1018 /*
1019  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/shape.json
1020  */
1021 std::shared_ptr<LOTData> LottieParserImpl::parseShapeObject()
1022 {
1023     std::shared_ptr<LOTShapeData> sharedShape =
1024         std::make_shared<LOTShapeData>();
1025     LOTShapeData *obj = sharedShape.get();
1026
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();
1032         } else {
1033 #ifdef DEBUG_PARSER
1034             vDebug << "Shape property ignored :" << key;
1035 #endif
1036             Skip(key);
1037         }
1038     }
1039     obj->setStatic(obj->mShape.isStatic());
1040
1041     return sharedShape;
1042 }
1043
1044 /*
1045  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/star.json
1046  */
1047 std::shared_ptr<LOTData> LottieParserImpl::parsePolystarObject()
1048 {
1049     std::shared_ptr<LOTPolystarData> sharedPolystar =
1050         std::make_shared<LOTPolystarData>();
1051     LOTPolystarData *obj = sharedPolystar.get();
1052
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();
1074         } else {
1075 #ifdef DEBUG_PARSER
1076             vDebug << "Polystar property ignored :" << key;
1077 #endif
1078             Skip(key);
1079         }
1080     }
1081     obj->setStatic(
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());
1086
1087     return sharedPolystar;
1088 }
1089
1090 LOTTrimData::TrimType LottieParserImpl::getTrimType()
1091 {
1092     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1093     switch (GetInt()) {
1094     case 1:
1095         return LOTTrimData::TrimType::Simultaneously;
1096         break;
1097     case 2:
1098         return LOTTrimData::TrimType::Individually;
1099         break;
1100     default:
1101         RAPIDJSON_ASSERT(0);
1102         break;
1103     }
1104 }
1105
1106 /*
1107  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/trim.json
1108  */
1109 std::shared_ptr<LOTData> LottieParserImpl::parseTrimObject()
1110 {
1111     std::shared_ptr<LOTTrimData> sharedTrim = std::make_shared<LOTTrimData>();
1112     LOTTrimData *                obj = sharedTrim.get();
1113
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();
1123         } else {
1124 #ifdef DEBUG_PARSER
1125             vDebug << "Trim property ignored :" << key;
1126 #endif
1127             Skip(key);
1128         }
1129     }
1130     obj->setStatic(obj->mStart.isStatic() && obj->mEnd.isStatic() &&
1131                    obj->mOffset.isStatic());
1132     return sharedTrim;
1133 }
1134
1135 std::shared_ptr<LOTData> LottieParserImpl::parseReapeaterObject()
1136 {
1137     std::shared_ptr<LOTRepeaterData> sharedRepeater =
1138         std::make_shared<LOTRepeaterData>();
1139     LOTRepeaterData *obj = sharedRepeater.get();
1140
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();
1148         } else {
1149 #ifdef DEBUG_PARSER
1150             vDebug << "Repeater property ignored :" << key;
1151 #endif
1152             Skip(key);
1153         }
1154     }
1155     obj->setStatic(obj->mCopies.isStatic() && obj->mOffset.isStatic() &&
1156                    obj->mTransform->isStatic());
1157
1158     return sharedRepeater;
1159 }
1160
1161 /*
1162  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/transform.json
1163  */
1164 std::shared_ptr<LOTTransformData> LottieParserImpl::parseTransformObject()
1165 {
1166     std::shared_ptr<LOTTransformData> sharedTransform =
1167         std::make_shared<LOTTransformData>();
1168     LOTTransformData *obj = sharedTransform.get();
1169
1170     while (const char *key = NextObjectKey()) {
1171         if (0 == strcmp(key, "a")) {
1172             parseProperty(obj->mAnchor);
1173         } else if (0 == strcmp(key, "p")) {
1174             EnterObject();
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);
1184                 }else {
1185                     Skip(key);
1186                 }
1187             }
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);
1198         } else {
1199             Skip(key);
1200         }
1201     }
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();
1206
1207     obj->setStatic(obj->mStaticMatrix && obj->mOpacity.isStatic());
1208
1209     if (obj->mStaticMatrix) obj->cacheMatrix();
1210
1211     return sharedTransform;
1212 }
1213
1214 /*
1215  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/fill.json
1216  */
1217 std::shared_ptr<LOTData> LottieParserImpl::parseFillObject()
1218 {
1219     std::shared_ptr<LOTFillData> sharedFill = std::make_shared<LOTFillData>();
1220     LOTFillData *                obj = sharedFill.get();
1221
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();
1231         } else {
1232 #ifdef DEBUG_PARSER
1233             vWarning << "Fill property skipped = " << key;
1234 #endif
1235             Skip(key);
1236         }
1237     }
1238     obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic());
1239
1240     return sharedFill;
1241 }
1242
1243 /*
1244  * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineCap.json
1245  */
1246 CapStyle LottieParserImpl::getLineCap()
1247 {
1248     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1249     switch (GetInt()) {
1250     case 1:
1251         return CapStyle::Flat;
1252         break;
1253     case 2:
1254         return CapStyle::Round;
1255         break;
1256     default:
1257         return CapStyle::Square;
1258         break;
1259     }
1260 }
1261
1262 FillRule LottieParserImpl::getFillRule()
1263 {
1264     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1265     switch (GetInt()) {
1266     case 1:
1267         return FillRule::Winding;
1268         break;
1269     case 2:
1270         return FillRule::EvenOdd;
1271         break;
1272     default:
1273         return FillRule::Winding;
1274         break;
1275     }
1276 }
1277
1278 /*
1279  * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineJoin.json
1280  */
1281 JoinStyle LottieParserImpl::getLineJoin()
1282 {
1283     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1284     switch (GetInt()) {
1285     case 1:
1286         return JoinStyle::Miter;
1287         break;
1288     case 2:
1289         return JoinStyle::Round;
1290         break;
1291     default:
1292         return JoinStyle::Bevel;
1293         break;
1294     }
1295 }
1296
1297 /*
1298  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/stroke.json
1299  */
1300 std::shared_ptr<LOTData> LottieParserImpl::parseStrokeObject()
1301 {
1302     std::shared_ptr<LOTStrokeData> sharedStroke =
1303         std::make_shared<LOTStrokeData>();
1304     LOTStrokeData *obj = sharedStroke.get();
1305
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);
1324         } else {
1325 #ifdef DEBUG_PARSER
1326             vWarning << "Stroke property skipped = " << key;
1327 #endif
1328             Skip(key);
1329         }
1330     }
1331     obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic() &&
1332                    obj->mWidth.isStatic() && obj->mDash.mStatic);
1333     return sharedStroke;
1334 }
1335
1336 void LottieParserImpl::parseGradientProperty(LOTGradient *obj, const char *key)
1337 {
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")) {
1352         EnterObject();
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();
1358             } else {
1359                 Skip(nullptr);
1360             }
1361         }
1362     } else {
1363 #ifdef DEBUG_PARSER
1364         vWarning << "Gradient property skipped = " << key;
1365 #endif
1366         Skip(key);
1367     }
1368     obj->setStatic(
1369         obj->mOpacity.isStatic() && obj->mStartPoint.isStatic() &&
1370         obj->mEndPoint.isStatic() && obj->mHighlightAngle.isStatic() &&
1371         obj->mHighlightLength.isStatic() && obj->mGradient.isStatic());
1372 }
1373
1374 /*
1375  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gfill.json
1376  */
1377 std::shared_ptr<LOTData> LottieParserImpl::parseGFillObject()
1378 {
1379     std::shared_ptr<LOTGFillData> sharedGFill =
1380         std::make_shared<LOTGFillData>();
1381     LOTGFillData *obj = sharedGFill.get();
1382
1383     while (const char *key = NextObjectKey()) {
1384         if (0 == strcmp(key, "r")) {
1385             obj->mFillRule = getFillRule();
1386         } else {
1387             parseGradientProperty(obj, key);
1388         }
1389     }
1390     return sharedGFill;
1391 }
1392
1393 void LottieParserImpl::parseDashProperty(LOTDashProperty &dash)
1394 {
1395     dash.mDashCount = 0;
1396     dash.mStatic = true;
1397     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1398     EnterArray();
1399     while (NextArrayValue()) {
1400         RAPIDJSON_ASSERT(PeekType() == kObjectType);
1401         EnterObject();
1402         while (const char *key = NextObjectKey()) {
1403             if (0 == strcmp(key, "v")) {
1404                 parseProperty(dash.mDashArray[dash.mDashCount++]);
1405             } else {
1406                 Skip(key);
1407             }
1408         }
1409     }
1410
1411     // update the staic proprty
1412     for (int i = 0; i < dash.mDashCount; i++) {
1413         if (!dash.mDashArray[i].isStatic()) {
1414             dash.mStatic = false;
1415             break;
1416         }
1417     }
1418 }
1419
1420 /*
1421  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gstroke.json
1422  */
1423 std::shared_ptr<LOTData> LottieParserImpl::parseGStrokeObject()
1424 {
1425     std::shared_ptr<LOTGStrokeData> sharedGStroke =
1426         std::make_shared<LOTGStrokeData>();
1427     LOTGStrokeData *obj = sharedGStroke.get();
1428
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);
1441         } else {
1442             parseGradientProperty(obj, key);
1443         }
1444     }
1445
1446     obj->setStatic(obj->isStatic() && obj->mWidth.isStatic() &&
1447                    obj->mDash.mStatic);
1448     return sharedGStroke;
1449 }
1450
1451 void LottieParserImpl::parseArrayValue(LottieColor &color)
1452 {
1453     float val[4];
1454     int   i = 0;
1455     while (NextArrayValue()) {
1456         val[i++] = GetDouble();
1457     }
1458
1459     color.r = val[0];
1460     color.g = val[1];
1461     color.b = val[2];
1462 }
1463
1464 void LottieParserImpl::parseArrayValue(VPointF &pt)
1465 {
1466     float val[4];
1467     int   i = 0;
1468     while (NextArrayValue()) {
1469         val[i++] = GetDouble();
1470     }
1471     pt.setX(val[0]);
1472     pt.setY(val[1]);
1473 }
1474
1475 void LottieParserImpl::parseArrayValue(float &val)
1476 {
1477     RAPIDJSON_ASSERT(0);
1478     val = GetDouble();
1479 }
1480
1481 void LottieParserImpl::parseArrayValue(int &val)
1482 {
1483     RAPIDJSON_ASSERT(0);
1484     val = GetInt();
1485 }
1486
1487 void LottieParserImpl::parseArrayValue(std::vector<VPointF> &v)
1488 {
1489     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1490     EnterArray();
1491     while (NextArrayValue()) {
1492         RAPIDJSON_ASSERT(PeekType() == kArrayType);
1493         EnterArray();
1494         VPointF pt;
1495         parseArrayValue(pt);
1496         v.push_back(pt);
1497     }
1498 }
1499
1500 void LottieParserImpl::getValue(VPointF &pt)
1501 {
1502     float val[4];
1503     int   i = 0;
1504     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1505     EnterArray();
1506     while (NextArrayValue()) {
1507         val[i++] = GetDouble();
1508     }
1509     pt.setX(val[0]);
1510     pt.setY(val[1]);
1511 }
1512
1513 void LottieParserImpl::getValue(float &val)
1514 {
1515     if (PeekType() == kArrayType) {
1516         EnterArray();
1517         while (NextArrayValue()) {
1518             val = GetDouble();
1519         }
1520     } else if (PeekType() == kNumberType) {
1521         val = GetDouble();
1522     } else {
1523         RAPIDJSON_ASSERT(0);
1524     }
1525 }
1526
1527 void LottieParserImpl::getValue(LottieColor &color)
1528 {
1529     float val[4];
1530     int   i = 0;
1531     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1532     EnterArray();
1533     while (NextArrayValue()) {
1534         val[i++] = GetDouble();
1535     }
1536     color.r = val[0];
1537     color.g = val[1];
1538     color.b = val[2];
1539 }
1540
1541 void LottieParserImpl::parseArrayValue(LottieGradient &grad)
1542 {
1543     while (NextArrayValue()) {
1544         grad.mGradient.push_back(GetDouble());
1545     }
1546 }
1547
1548 void LottieParserImpl::getValue(LottieGradient &grad)
1549 {
1550     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1551     EnterArray();
1552     while (NextArrayValue()) {
1553         grad.mGradient.push_back(GetDouble());
1554     }
1555 }
1556
1557 void LottieParserImpl::getValue(int &val)
1558 {
1559     if (PeekType() == kArrayType) {
1560         EnterArray();
1561         while (NextArrayValue()) {
1562             val = GetInt();
1563         }
1564     } else if (PeekType() == kNumberType) {
1565         val = GetInt();
1566     } else {
1567         RAPIDJSON_ASSERT(0);
1568     }
1569 }
1570
1571 void LottieParserImpl::getValue(LottieShapeData &obj)
1572 {
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;
1578
1579     /*
1580      * The shape object could be wrapped by a array
1581      * if its part of the keyframe object
1582      */
1583     bool arrayWrapper = (PeekType() == kArrayType);
1584     if (arrayWrapper) EnterArray();
1585
1586     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1587     EnterObject();
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")) {
1596             closed = GetBool();
1597         } else {
1598             RAPIDJSON_ASSERT(0);
1599             Skip(nullptr);
1600         }
1601     }
1602     // exit properly from the array
1603     if (arrayWrapper) NextArrayValue();
1604
1605     /*
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
1609      * closed one)
1610      */
1611     if (inPoint.size() != outPoint.size() ||
1612         inPoint.size() != vertices.size()) {
1613         vCritical << "The Shape data are corrupted";
1614         points = std::vector<VPointF>();
1615     } else {
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
1625         }
1626
1627         if (closed) {
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
1633         }
1634     }
1635     obj.mPoints = std::move(points);
1636     obj.mClosed = closed;
1637 }
1638
1639 VPointF LottieParserImpl::parseInperpolatorPoint()
1640 {
1641     VPointF cp;
1642     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1643     EnterObject();
1644     while (const char *key = NextObjectKey()) {
1645         if (0 == strcmp(key, "x")) {
1646             if (PeekType() == kNumberType) {
1647                 cp.setX(GetDouble());
1648             } else {
1649                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1650                 EnterArray();
1651                 while (NextArrayValue()) {
1652                     cp.setX(GetDouble());
1653                 }
1654             }
1655         }
1656         if (0 == strcmp(key, "y")) {
1657             if (PeekType() == kNumberType) {
1658                 cp.setY(GetDouble());
1659             } else {
1660                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1661                 EnterArray();
1662                 while (NextArrayValue()) {
1663                     cp.setY(GetDouble());
1664                 }
1665             }
1666         }
1667     }
1668     return cp;
1669 }
1670
1671 template <typename T>
1672 bool LottieParserImpl::parseKeyFrameValue(const char *         key,
1673                                           LOTKeyFrameValue<T> &value)
1674 {
1675     if (0 == strcmp(key, "s")) {
1676         getValue(value.mStartValue);
1677     } else if (0 == strcmp(key, "e")) {
1678         getValue(value.mEndValue);
1679     } else {
1680         return false;
1681     }
1682     return true;
1683 }
1684
1685 template <>
1686 bool LottieParserImpl::parseKeyFrameValue(const char *               key,
1687                                           LOTKeyFrameValue<VPointF> &value)
1688 {
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);
1699     } else {
1700         return false;
1701     }
1702     return true;
1703 }
1704
1705 /*
1706  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
1707  */
1708 template <typename T>
1709 void LottieParserImpl::parseKeyFrame(LOTAnimInfo<T> &obj)
1710 {
1711     EnterObject();
1712     LOTKeyFrame<T> keyframe;
1713     VPointF        inTangent;
1714     VPointF        outTangent;
1715     const char *   interpolatorKey = nullptr;
1716     bool           hold = false;
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();
1725             } else {
1726                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1727                 EnterArray();
1728                 while (NextArrayValue()) {
1729                     RAPIDJSON_ASSERT(PeekType() == kStringType);
1730                     interpolatorKey = GetString();
1731                 }
1732             }
1733             continue;
1734         } else if (0 == strcmp(key, "t")) {
1735             keyframe.mStartFrame = GetDouble();
1736         } else if (parseKeyFrameValue(key, keyframe.mValue)) {
1737             continue;
1738         } else if (0 == strcmp(key, "h")) {
1739             hold = GetInt();
1740             continue;
1741         } else {
1742 #ifdef DEBUG_PARSER
1743             vDebug << "key frame property skipped = " << key;
1744 #endif
1745             Skip(key);
1746         }
1747     }
1748
1749     if (!obj.mKeyFrames.empty()) {
1750         // update the endFrame value of current keyframe
1751         obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
1752     }
1753
1754     if (hold) {
1755         interpolatorKey = "hold_interpolator";
1756         inTangent = VPointF();
1757         outTangent = VPointF();
1758         keyframe.mValue.mEndValue = keyframe.mValue.mStartValue;
1759         keyframe.mEndFrame = keyframe.mStartFrame;
1760     }
1761
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;
1767         } else {
1768             keyframe.mInterpolator = std::make_shared<VInterpolator>(
1769                 VInterpolator(outTangent, inTangent));
1770             compRef->mInterpolatorCache[interpolatorKey] =
1771                 keyframe.mInterpolator;
1772         }
1773         obj.mKeyFrames.push_back(keyframe);
1774     }
1775 }
1776
1777 /*
1778  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shapeKeyframed.json
1779  */
1780
1781 /*
1782  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
1783  */
1784 void LottieParserImpl::parseShapeProperty(LOTAnimatable<LottieShapeData> &obj)
1785 {
1786     EnterObject();
1787     while (const char *key = NextObjectKey()) {
1788         if (0 == strcmp(key, "k")) {
1789             if (PeekType() == kArrayType) {
1790                 EnterArray();
1791                 while (NextArrayValue()) {
1792                     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1793                     if (!obj.mAnimInfo)
1794                         obj.mAnimInfo =
1795                             std::make_unique<LOTAnimInfo<LottieShapeData>>();
1796                     parseKeyFrame(*obj.mAnimInfo.get());
1797                 }
1798             } else {
1799                 getValue(obj.mValue);
1800             }
1801         } else {
1802 #ifdef DEBUG_PARSER
1803             vDebug << "shape property ignored = " << key;
1804 #endif
1805             Skip(nullptr);
1806         }
1807     }
1808 }
1809
1810 template <typename T>
1811 void LottieParserImpl::parsePropertyHelper(LOTAnimatable<T> &obj)
1812 {
1813     if (PeekType() == kNumberType) {
1814         /*single value property with no animation*/
1815         getValue(obj.mValue);
1816     } else {
1817         RAPIDJSON_ASSERT(PeekType() == kArrayType);
1818         EnterArray();
1819         while (NextArrayValue()) {
1820             /* property with keyframe info*/
1821             if (PeekType() == kObjectType) {
1822                 if (!obj.mAnimInfo)
1823                     obj.mAnimInfo = std::make_unique<LOTAnimInfo<T>>();
1824                 parseKeyFrame(*obj.mAnimInfo.get());
1825             } else {
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
1831                  */
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*/
1836                 break;
1837             }
1838         }
1839     }
1840 }
1841
1842 /*
1843  * https://github.com/airbnb/lottie-web/tree/master/docs/json/properties
1844  */
1845 template <typename T>
1846 void LottieParserImpl::parseProperty(LOTAnimatable<T> &obj)
1847 {
1848     EnterObject();
1849     while (const char *key = NextObjectKey()) {
1850         if (0 == strcmp(key, "k")) {
1851             parsePropertyHelper(obj);
1852         } else {
1853             Skip(key);
1854         }
1855     }
1856 }
1857
1858 #ifdef DEBUG_PRINT_TREE
1859
1860 class LOTDataInspector {
1861 public:
1862     void visit(LOTCompositionData *obj, std::string level)
1863     {
1864         vDebug << " { "
1865                << level
1866                << "Composition:: a: " << !obj->isStatic()
1867                << ", v: " << obj->mVersion
1868                << ", stFm: " << obj->startFrame()
1869                << ", endFm: " << obj->endFrame()<< "\n";
1870         level.append("\t");
1871         visit(obj->mRootLayer.get(), level);
1872         level.erase(level.end() - 1, level.end());
1873         vDebug << " } " << level << "Composition End\n";
1874     }
1875     void visit(LOTLayerData *obj, std::string level)
1876     {
1877         vDebug << level
1878                << "{ "
1879                << layerType(obj->mLayerType)
1880                << ", id:" << obj->mId << " Pid:" << obj->mParentId
1881                << ", a:" << !obj->isStatic()
1882                << ", inFm:" << obj->mInFrame
1883                << ", outFm:" << obj->mOutFrame
1884                << "\n";
1885         visitChildren(static_cast<LOTGroupData *>(obj), level);
1886         vDebug << level
1887                << "} "
1888                << layerType(obj->mLayerType).c_str()
1889                << ", id: " << obj->mId << "\n";
1890     }
1891     void visitChildren(LOTGroupData *obj, std::string level)
1892     {
1893         level.append("\t");
1894         for (const auto& child : obj->mChildren) visit(child.get(), level);
1895     }
1896
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";
1903             break;
1904         }
1905         case LOTData::Type::ShapeGroup: {
1906             vDebug << level << "{ ShapeGroup: a:" << !obj->isStatic();
1907             visitChildren(static_cast<LOTGroupData *>(obj), level);
1908             vDebug << level << "} ShapeGroup";
1909             break;
1910         }
1911         case LOTData::Type::Layer:{
1912             visit(static_cast<LOTLayerData *>(obj), level);
1913             break;
1914         }
1915         case LOTData::Type::Trim:{
1916             vDebug << level << "{ Trim: a:" << !obj->isStatic() << " }";
1917             break;
1918         }
1919         case LOTData::Type::Rect:{
1920             vDebug << level << "{ Rect: a:" << !obj->isStatic() << " }";
1921             break;
1922         }
1923         case LOTData::Type::Ellipse:{
1924             vDebug << level << "{ Ellipse: a:" << !obj->isStatic() << " }";
1925             break;
1926         }
1927         case LOTData::Type::Shape:{
1928             vDebug << level << "{ Shape: a:" << !obj->isStatic() << " }";
1929             break;
1930         }
1931         case LOTData::Type::Polystar:{
1932             vDebug << level << "{ Polystar: a:" << !obj->isStatic() << " }";
1933             break;
1934         }
1935         case LOTData::Type::Transform:{
1936             vDebug << level << "{ Transform: a: " << !obj->isStatic() << " }";
1937             break;
1938         }
1939         case LOTData::Type::Stroke:{
1940             vDebug << level << "{ Stroke: a:" << !obj->isStatic() << " }";
1941             break;
1942         }
1943         case LOTData::Type::GStroke:{
1944             vDebug << level << "{ GStroke: a:" << !obj->isStatic() << " }";
1945             break;
1946         }
1947         case LOTData::Type::Fill:{
1948             vDebug << level << "{ Fill: a:" << !obj->isStatic() << " }";
1949             break;
1950         }
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) << " }";
1956             break;
1957         }
1958         default:
1959             break;
1960         }
1961     }
1962
1963     std::string layerType(LayerType type)
1964     {
1965         switch (type) {
1966         case LayerType::Precomp:
1967             return "Layer::Precomp";
1968             break;
1969         case LayerType::Null:
1970             return "Layer::Null";
1971             break;
1972         case LayerType::Shape:
1973             return "Layer::Shape";
1974             break;
1975         case LayerType::Solid:
1976             return "Layer::Solid";
1977             break;
1978         case LayerType::Image:
1979             return "Layer::Image";
1980             break;
1981         case LayerType::Text:
1982             return "Layer::Text";
1983             break;
1984         default:
1985             return "Layer::Unknown";
1986             break;
1987         }
1988     }
1989 };
1990
1991 #endif
1992
1993 LottieParser::~LottieParser()
1994 {
1995     delete d;
1996 }
1997
1998 LottieParser::LottieParser(char *str) : d(new LottieParserImpl(str))
1999 {
2000     d->parseComposition();
2001 }
2002
2003 std::shared_ptr<LOTModel> LottieParser::model()
2004 {
2005     std::shared_ptr<LOTModel> model = std::make_shared<LOTModel>();
2006     model->mRoot = d->composition();
2007     model->mRoot->processRepeaterObjects();
2008
2009 #ifdef DEBUG_PRINT_TREE
2010     LOTDataInspector inspector;
2011     inspector.visit(model->mRoot.get(), "");
2012 #endif
2013
2014     return model;
2015 }
2016
2017 RAPIDJSON_DIAG_POP