30cf7c8a47660cd87ed1e6466e388bfa74b60264
[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
802     // update the static property of layer
803     bool staticFlag = true;
804     for (const auto& child : layer->mChildren) {
805         staticFlag &= child.get()->isStatic();
806     }
807
808     for (const auto& mask : layer->mMasks) {
809         staticFlag &= mask->isStatic();
810     }
811
812     layer->setStatic(staticFlag && layer->mTransform->isStatic() &&
813                      !hasLayerRef);
814     layer->mCompRef = compRef;
815     return sharedLayer;
816 }
817
818 void LottieParserImpl::parseMaskProperty(LOTLayerData *layer)
819 {
820     RAPIDJSON_ASSERT(PeekType() == kArrayType);
821     EnterArray();
822     while (NextArrayValue()) {
823         layer->mMasks.push_back(parseMaskObject());
824     }
825 }
826
827 std::shared_ptr<LOTMaskData> LottieParserImpl::parseMaskObject()
828 {
829     std::shared_ptr<LOTMaskData> sharedMask = std::make_shared<LOTMaskData>();
830     LOTMaskData *                obj = sharedMask.get();
831
832     RAPIDJSON_ASSERT(PeekType() == kObjectType);
833     EnterObject();
834     while (const char *key = NextObjectKey()) {
835         if (0 == strcmp(key, "inv")) {
836             obj->mInv = GetBool();
837         } else if (0 == strcmp(key, "mode")) {
838             const char *str = GetString();
839             switch (str[0]) {
840             case 'n':
841                 obj->mMode = LOTMaskData::Mode::None;
842                 break;
843             case 'a':
844                 obj->mMode = LOTMaskData::Mode::Add;
845                 break;
846             case 's':
847                 obj->mMode = LOTMaskData::Mode::Substarct;
848                 break;
849             case 'i':
850                 obj->mMode = LOTMaskData::Mode::Intersect;
851                 break;
852             case 'f':
853                 obj->mMode = LOTMaskData::Mode::Difference;
854                 break;
855             default:
856                 obj->mMode = LOTMaskData::Mode::None;
857                 break;
858             }
859         } else if (0 == strcmp(key, "pt")) {
860             parseShapeProperty(obj->mShape);
861         } else if (0 == strcmp(key, "o")) {
862             parseProperty(obj->mOpacity);
863         } else {
864             Skip(key);
865         }
866     }
867     obj->mIsStatic = obj->mShape.isStatic() && obj->mOpacity.isStatic();
868     return sharedMask;
869 }
870
871 void LottieParserImpl::parseShapesAttr(LOTLayerData *layer)
872 {
873     RAPIDJSON_ASSERT(PeekType() == kArrayType);
874     EnterArray();
875     while (NextArrayValue()) {
876         parseObject(layer);
877     }
878 }
879
880 std::shared_ptr<LOTData> LottieParserImpl::parseObjectTypeAttr()
881 {
882     RAPIDJSON_ASSERT(PeekType() == kStringType);
883     const char *type = GetString();
884     if (0 == strcmp(type, "gr")) {
885         return parseGroupObject();
886     } else if (0 == strcmp(type, "rc")) {
887         return parseRectObject();
888     } else if (0 == strcmp(type, "el")) {
889         return parseEllipseObject();
890     } else if (0 == strcmp(type, "tr")) {
891         return parseTransformObject();
892     } else if (0 == strcmp(type, "fl")) {
893         return parseFillObject();
894     } else if (0 == strcmp(type, "st")) {
895         return parseStrokeObject();
896     } else if (0 == strcmp(type, "gf")) {
897         curLayerRef->mHasGradient = true;
898         return parseGFillObject();
899     } else if (0 == strcmp(type, "gs")) {
900         curLayerRef->mHasGradient = true;
901         return parseGStrokeObject();
902     } else if (0 == strcmp(type, "sh")) {
903         return parseShapeObject();
904     } else if (0 == strcmp(type, "sr")) {
905         return parsePolystarObject();
906     } else if (0 == strcmp(type, "tm")) {
907         curLayerRef->mHasPathOperator = true;
908         return parseTrimObject();
909     } else if (0 == strcmp(type, "rp")) {
910         curLayerRef->mHasRepeater = true;
911         return parseReapeaterObject();
912     } else {
913 #ifdef DEBUG_PARSER
914         vDebug << "The Object Type not yet handled = " << type;
915 #endif
916         return nullptr;
917     }
918 }
919
920 void LottieParserImpl::parseObject(LOTGroupData *parent)
921 {
922     RAPIDJSON_ASSERT(PeekType() == kObjectType);
923     EnterObject();
924     while (const char *key = NextObjectKey()) {
925         if (0 == strcmp(key, "ty")) {
926             auto child = parseObjectTypeAttr();
927             if (child && !child->hidden()) parent->mChildren.push_back(child);
928         } else {
929             Skip(key);
930         }
931     }
932 }
933
934 std::shared_ptr<LOTData> LottieParserImpl::parseGroupObject()
935 {
936     std::shared_ptr<LOTShapeGroupData> sharedGroup =
937         std::make_shared<LOTShapeGroupData>();
938
939     LOTShapeGroupData *group = sharedGroup.get();
940     while (const char *key = NextObjectKey()) {
941         if (0 == strcmp(key, "it")) {
942             RAPIDJSON_ASSERT(PeekType() == kArrayType);
943             EnterArray();
944             while (NextArrayValue()) {
945                 RAPIDJSON_ASSERT(PeekType() == kObjectType);
946                 parseObject(group);
947             }
948             if (group->mChildren.back()->mType == LOTData::Type::Transform) {
949                 group->mTransform = std::static_pointer_cast<LOTTransformData>(
950                     group->mChildren.back());
951                 group->mChildren.pop_back();
952             }
953         } else {
954             Skip(key);
955         }
956     }
957     bool staticFlag = true;
958     for (const auto& child : group->mChildren) {
959         staticFlag &= child.get()->isStatic();
960     }
961
962     group->setStatic(staticFlag && group->mTransform->isStatic());
963
964     return sharedGroup;
965 }
966
967 /*
968  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/rect.json
969  */
970 std::shared_ptr<LOTData> LottieParserImpl::parseRectObject()
971 {
972     std::shared_ptr<LOTRectData> sharedRect = std::make_shared<LOTRectData>();
973     LOTRectData *                obj = sharedRect.get();
974
975     while (const char *key = NextObjectKey()) {
976         if (0 == strcmp(key, "p")) {
977             parseProperty(obj->mPos);
978         } else if (0 == strcmp(key, "s")) {
979             parseProperty(obj->mSize);
980         } else if (0 == strcmp(key, "r")) {
981             parseProperty(obj->mRound);
982         } else if (0 == strcmp(key, "d")) {
983             obj->mDirection = GetInt();
984         } else if (0 == strcmp(key, "hd")) {
985             obj->mHidden = GetBool();
986         } else {
987             Skip(key);
988         }
989     }
990     obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic() &&
991                    obj->mRound.isStatic());
992     return sharedRect;
993 }
994
995 /*
996  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/ellipse.json
997  */
998 std::shared_ptr<LOTData> LottieParserImpl::parseEllipseObject()
999 {
1000     std::shared_ptr<LOTEllipseData> sharedEllipse =
1001         std::make_shared<LOTEllipseData>();
1002     LOTEllipseData *obj = sharedEllipse.get();
1003
1004     while (const char *key = NextObjectKey()) {
1005         if (0 == strcmp(key, "p")) {
1006             parseProperty(obj->mPos);
1007         } else if (0 == strcmp(key, "s")) {
1008             parseProperty(obj->mSize);
1009         } else if (0 == strcmp(key, "d")) {
1010             obj->mDirection = GetInt();
1011         } else if (0 == strcmp(key, "hd")) {
1012             obj->mHidden = GetBool();
1013         } else {
1014             Skip(key);
1015         }
1016     }
1017     obj->setStatic(obj->mPos.isStatic() && obj->mSize.isStatic());
1018     return sharedEllipse;
1019 }
1020
1021 /*
1022  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/shape.json
1023  */
1024 std::shared_ptr<LOTData> LottieParserImpl::parseShapeObject()
1025 {
1026     std::shared_ptr<LOTShapeData> sharedShape =
1027         std::make_shared<LOTShapeData>();
1028     LOTShapeData *obj = sharedShape.get();
1029
1030     while (const char *key = NextObjectKey()) {
1031         if (0 == strcmp(key, "ks")) {
1032             parseShapeProperty(obj->mShape);
1033         } else if (0 == strcmp(key, "d")) {
1034             obj->mDirection = GetInt();
1035         } else if (0 == strcmp(key, "hd")) {
1036             obj->mHidden = GetBool();
1037         } else {
1038 #ifdef DEBUG_PARSER
1039             vDebug << "Shape property ignored :" << key;
1040 #endif
1041             Skip(key);
1042         }
1043     }
1044     obj->setStatic(obj->mShape.isStatic());
1045
1046     return sharedShape;
1047 }
1048
1049 /*
1050  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/star.json
1051  */
1052 std::shared_ptr<LOTData> LottieParserImpl::parsePolystarObject()
1053 {
1054     std::shared_ptr<LOTPolystarData> sharedPolystar =
1055         std::make_shared<LOTPolystarData>();
1056     LOTPolystarData *obj = sharedPolystar.get();
1057
1058     while (const char *key = NextObjectKey()) {
1059         if (0 == strcmp(key, "p")) {
1060             parseProperty(obj->mPos);
1061         } else if (0 == strcmp(key, "pt")) {
1062             parseProperty(obj->mPointCount);
1063         } else if (0 == strcmp(key, "ir")) {
1064             parseProperty(obj->mInnerRadius);
1065         } else if (0 == strcmp(key, "is")) {
1066             parseProperty(obj->mInnerRoundness);
1067         } else if (0 == strcmp(key, "or")) {
1068             parseProperty(obj->mOuterRadius);
1069         } else if (0 == strcmp(key, "os")) {
1070             parseProperty(obj->mOuterRoundness);
1071         } else if (0 == strcmp(key, "r")) {
1072             parseProperty(obj->mRotation);
1073         } else if (0 == strcmp(key, "sy")) {
1074             int starType = GetInt();
1075             if (starType == 1) obj->mType = LOTPolystarData::PolyType::Star;
1076             if (starType == 2) obj->mType = LOTPolystarData::PolyType::Polygon;
1077         } else if (0 == strcmp(key, "d")) {
1078             obj->mDirection = GetInt();
1079         } else if (0 == strcmp(key, "hd")) {
1080             obj->mHidden = GetBool();
1081         } else {
1082 #ifdef DEBUG_PARSER
1083             vDebug << "Polystar property ignored :" << key;
1084 #endif
1085             Skip(key);
1086         }
1087     }
1088     obj->setStatic(
1089         obj->mPos.isStatic() && obj->mPointCount.isStatic() &&
1090         obj->mInnerRadius.isStatic() && obj->mInnerRoundness.isStatic() &&
1091         obj->mOuterRadius.isStatic() && obj->mOuterRoundness.isStatic() &&
1092         obj->mRotation.isStatic());
1093
1094     return sharedPolystar;
1095 }
1096
1097 LOTTrimData::TrimType LottieParserImpl::getTrimType()
1098 {
1099     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1100     switch (GetInt()) {
1101     case 1:
1102         return LOTTrimData::TrimType::Simultaneously;
1103         break;
1104     case 2:
1105         return LOTTrimData::TrimType::Individually;
1106         break;
1107     default:
1108         RAPIDJSON_ASSERT(0);
1109         break;
1110     }
1111 }
1112
1113 /*
1114  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/trim.json
1115  */
1116 std::shared_ptr<LOTData> LottieParserImpl::parseTrimObject()
1117 {
1118     std::shared_ptr<LOTTrimData> sharedTrim = std::make_shared<LOTTrimData>();
1119     LOTTrimData *                obj = sharedTrim.get();
1120
1121     while (const char *key = NextObjectKey()) {
1122         if (0 == strcmp(key, "s")) {
1123             parseProperty(obj->mStart);
1124         } else if (0 == strcmp(key, "e")) {
1125             parseProperty(obj->mEnd);
1126         } else if (0 == strcmp(key, "o")) {
1127             parseProperty(obj->mOffset);
1128         } else if (0 == strcmp(key, "m")) {
1129             obj->mTrimType = getTrimType();
1130         } else if (0 == strcmp(key, "hd")) {
1131             obj->mHidden = GetBool();
1132         } else {
1133 #ifdef DEBUG_PARSER
1134             vDebug << "Trim property ignored :" << key;
1135 #endif
1136             Skip(key);
1137         }
1138     }
1139     obj->setStatic(obj->mStart.isStatic() && obj->mEnd.isStatic() &&
1140                    obj->mOffset.isStatic());
1141     return sharedTrim;
1142 }
1143
1144 std::shared_ptr<LOTData> LottieParserImpl::parseReapeaterObject()
1145 {
1146     std::shared_ptr<LOTRepeaterData> sharedRepeater =
1147         std::make_shared<LOTRepeaterData>();
1148     LOTRepeaterData *obj = sharedRepeater.get();
1149
1150     while (const char *key = NextObjectKey()) {
1151         if (0 == strcmp(key, "c")) {
1152             parseProperty(obj->mCopies);
1153         } else if (0 == strcmp(key, "o")) {
1154             parseProperty(obj->mOffset);
1155         } else if (0 == strcmp(key, "tr")) {
1156             obj->mTransform = parseTransformObject();
1157         } else if (0 == strcmp(key, "hd")) {
1158             obj->mHidden = GetBool();
1159         } else {
1160 #ifdef DEBUG_PARSER
1161             vDebug << "Repeater property ignored :" << key;
1162 #endif
1163             Skip(key);
1164         }
1165     }
1166     obj->setStatic(obj->mCopies.isStatic() && obj->mOffset.isStatic() &&
1167                    obj->mTransform->isStatic());
1168
1169     return sharedRepeater;
1170 }
1171
1172 /*
1173  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/transform.json
1174  */
1175 std::shared_ptr<LOTTransformData> LottieParserImpl::parseTransformObject()
1176 {
1177     std::shared_ptr<LOTTransformData> sharedTransform =
1178         std::make_shared<LOTTransformData>();
1179     LOTTransformData *obj = sharedTransform.get();
1180
1181     while (const char *key = NextObjectKey()) {
1182         if (0 == strcmp(key, "a")) {
1183             parseProperty(obj->mAnchor);
1184         } else if (0 == strcmp(key, "p")) {
1185             EnterObject();
1186             while (const char *key = NextObjectKey()) {
1187                 if (0 == strcmp(key, "k")) {
1188                     parsePropertyHelper(obj->mPosition);
1189                 } else if (0 == strcmp(key, "s")) {
1190                     obj->mSeparate = GetBool();
1191                 } else if (obj->mSeparate && (0 == strcmp(key, "x"))) {
1192                     parseProperty(obj->mX);
1193                 } else if (obj->mSeparate && (0 == strcmp(key, "y"))) {
1194                     parseProperty(obj->mY);
1195                 }else {
1196                     Skip(key);
1197                 }
1198             }
1199         } else if (0 == strcmp(key, "r")) {
1200             parseProperty(obj->mRotation);
1201         } else if (0 == strcmp(key, "s")) {
1202             parseProperty(obj->mScale);
1203         } else if (0 == strcmp(key, "sk")) {
1204             parseProperty(obj->mSkew);
1205         } else if (0 == strcmp(key, "sa")) {
1206             parseProperty(obj->mSkewAxis);
1207         } else if (0 == strcmp(key, "o")) {
1208             parseProperty(obj->mOpacity);
1209         }  else if (0 == strcmp(key, "hd")) {
1210             obj->mHidden = GetBool();
1211         } else {
1212             Skip(key);
1213         }
1214     }
1215     obj->mStaticMatrix = obj->mAnchor.isStatic() && obj->mPosition.isStatic() &&
1216                          obj->mRotation.isStatic() && obj->mScale.isStatic() &&
1217                          obj->mSkew.isStatic() && obj->mSkewAxis.isStatic() &&
1218                          obj->mX.isStatic() && obj->mY.isStatic();
1219
1220     obj->setStatic(obj->mStaticMatrix && obj->mOpacity.isStatic());
1221
1222     if (obj->mStaticMatrix) obj->cacheMatrix();
1223
1224     return sharedTransform;
1225 }
1226
1227 /*
1228  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/fill.json
1229  */
1230 std::shared_ptr<LOTData> LottieParserImpl::parseFillObject()
1231 {
1232     std::shared_ptr<LOTFillData> sharedFill = std::make_shared<LOTFillData>();
1233     LOTFillData *                obj = sharedFill.get();
1234
1235     while (const char *key = NextObjectKey()) {
1236         if (0 == strcmp(key, "c")) {
1237             parseProperty(obj->mColor);
1238         } else if (0 == strcmp(key, "o")) {
1239             parseProperty(obj->mOpacity);
1240         } else if (0 == strcmp(key, "fillEnabled")) {
1241             obj->mEnabled = GetBool();
1242         } else if (0 == strcmp(key, "r")) {
1243             obj->mFillRule = getFillRule();
1244         }  else if (0 == strcmp(key, "hd")) {
1245             obj->mHidden = GetBool();
1246         } else {
1247 #ifdef DEBUG_PARSER
1248             vWarning << "Fill property skipped = " << key;
1249 #endif
1250             Skip(key);
1251         }
1252     }
1253     obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic());
1254
1255     return sharedFill;
1256 }
1257
1258 /*
1259  * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineCap.json
1260  */
1261 CapStyle LottieParserImpl::getLineCap()
1262 {
1263     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1264     switch (GetInt()) {
1265     case 1:
1266         return CapStyle::Flat;
1267         break;
1268     case 2:
1269         return CapStyle::Round;
1270         break;
1271     default:
1272         return CapStyle::Square;
1273         break;
1274     }
1275 }
1276
1277 FillRule LottieParserImpl::getFillRule()
1278 {
1279     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1280     switch (GetInt()) {
1281     case 1:
1282         return FillRule::Winding;
1283         break;
1284     case 2:
1285         return FillRule::EvenOdd;
1286         break;
1287     default:
1288         return FillRule::Winding;
1289         break;
1290     }
1291 }
1292
1293 /*
1294  * https://github.com/airbnb/lottie-web/blob/master/docs/json/helpers/lineJoin.json
1295  */
1296 JoinStyle LottieParserImpl::getLineJoin()
1297 {
1298     RAPIDJSON_ASSERT(PeekType() == kNumberType);
1299     switch (GetInt()) {
1300     case 1:
1301         return JoinStyle::Miter;
1302         break;
1303     case 2:
1304         return JoinStyle::Round;
1305         break;
1306     default:
1307         return JoinStyle::Bevel;
1308         break;
1309     }
1310 }
1311
1312 /*
1313  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/stroke.json
1314  */
1315 std::shared_ptr<LOTData> LottieParserImpl::parseStrokeObject()
1316 {
1317     std::shared_ptr<LOTStrokeData> sharedStroke =
1318         std::make_shared<LOTStrokeData>();
1319     LOTStrokeData *obj = sharedStroke.get();
1320
1321     while (const char *key = NextObjectKey()) {
1322         if (0 == strcmp(key, "c")) {
1323             parseProperty(obj->mColor);
1324         } else if (0 == strcmp(key, "o")) {
1325             parseProperty(obj->mOpacity);
1326         } else if (0 == strcmp(key, "w")) {
1327             parseProperty(obj->mWidth);
1328         } else if (0 == strcmp(key, "fillEnabled")) {
1329             obj->mEnabled = GetBool();
1330         } else if (0 == strcmp(key, "lc")) {
1331             obj->mCapStyle = getLineCap();
1332         } else if (0 == strcmp(key, "lj")) {
1333             obj->mJoinStyle = getLineJoin();
1334         } else if (0 == strcmp(key, "ml")) {
1335             RAPIDJSON_ASSERT(PeekType() == kNumberType);
1336             obj->mMeterLimit = GetDouble();
1337         } else if (0 == strcmp(key, "d")) {
1338             parseDashProperty(obj->mDash);
1339         } else if (0 == strcmp(key, "hd")) {
1340             obj->mHidden = GetBool();
1341         } else {
1342 #ifdef DEBUG_PARSER
1343             vWarning << "Stroke property skipped = " << key;
1344 #endif
1345             Skip(key);
1346         }
1347     }
1348     obj->setStatic(obj->mColor.isStatic() && obj->mOpacity.isStatic() &&
1349                    obj->mWidth.isStatic() && obj->mDash.mStatic);
1350     return sharedStroke;
1351 }
1352
1353 void LottieParserImpl::parseGradientProperty(LOTGradient *obj, const char *key)
1354 {
1355     if (0 == strcmp(key, "t")) {
1356         RAPIDJSON_ASSERT(PeekType() == kNumberType);
1357         obj->mGradientType = GetInt();
1358     } else if (0 == strcmp(key, "o")) {
1359         parseProperty(obj->mOpacity);
1360     } else if (0 == strcmp(key, "s")) {
1361         parseProperty(obj->mStartPoint);
1362     } else if (0 == strcmp(key, "e")) {
1363         parseProperty(obj->mEndPoint);
1364     } else if (0 == strcmp(key, "h")) {
1365         parseProperty(obj->mHighlightLength);
1366     } else if (0 == strcmp(key, "a")) {
1367         parseProperty(obj->mHighlightAngle);
1368     } else if (0 == strcmp(key, "g")) {
1369         EnterObject();
1370         while (const char *key = NextObjectKey()) {
1371             if (0 == strcmp(key, "k")) {
1372                 parseProperty(obj->mGradient);
1373             } else if (0 == strcmp(key, "p")) {
1374                 obj->mColorPoints = GetInt();
1375             } else {
1376                 Skip(nullptr);
1377             }
1378         }
1379     } else if (0 == strcmp(key, "hd")) {
1380         obj->mHidden = GetBool();
1381     } else {
1382 #ifdef DEBUG_PARSER
1383         vWarning << "Gradient property skipped = " << key;
1384 #endif
1385         Skip(key);
1386     }
1387     obj->setStatic(
1388         obj->mOpacity.isStatic() && obj->mStartPoint.isStatic() &&
1389         obj->mEndPoint.isStatic() && obj->mHighlightAngle.isStatic() &&
1390         obj->mHighlightLength.isStatic() && obj->mGradient.isStatic());
1391 }
1392
1393 /*
1394  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gfill.json
1395  */
1396 std::shared_ptr<LOTData> LottieParserImpl::parseGFillObject()
1397 {
1398     std::shared_ptr<LOTGFillData> sharedGFill =
1399         std::make_shared<LOTGFillData>();
1400     LOTGFillData *obj = sharedGFill.get();
1401
1402     while (const char *key = NextObjectKey()) {
1403         if (0 == strcmp(key, "r")) {
1404             obj->mFillRule = getFillRule();
1405         } else {
1406             parseGradientProperty(obj, key);
1407         }
1408     }
1409     return sharedGFill;
1410 }
1411
1412 void LottieParserImpl::parseDashProperty(LOTDashProperty &dash)
1413 {
1414     dash.mDashCount = 0;
1415     dash.mStatic = true;
1416     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1417     EnterArray();
1418     while (NextArrayValue()) {
1419         RAPIDJSON_ASSERT(PeekType() == kObjectType);
1420         EnterObject();
1421         while (const char *key = NextObjectKey()) {
1422             if (0 == strcmp(key, "v")) {
1423                 parseProperty(dash.mDashArray[dash.mDashCount++]);
1424             } else {
1425                 Skip(key);
1426             }
1427         }
1428     }
1429
1430     // update the staic proprty
1431     for (int i = 0; i < dash.mDashCount; i++) {
1432         if (!dash.mDashArray[i].isStatic()) {
1433             dash.mStatic = false;
1434             break;
1435         }
1436     }
1437 }
1438
1439 /*
1440  * https://github.com/airbnb/lottie-web/blob/master/docs/json/shapes/gstroke.json
1441  */
1442 std::shared_ptr<LOTData> LottieParserImpl::parseGStrokeObject()
1443 {
1444     std::shared_ptr<LOTGStrokeData> sharedGStroke =
1445         std::make_shared<LOTGStrokeData>();
1446     LOTGStrokeData *obj = sharedGStroke.get();
1447
1448     while (const char *key = NextObjectKey()) {
1449         if (0 == strcmp(key, "w")) {
1450             parseProperty(obj->mWidth);
1451         } else if (0 == strcmp(key, "lc")) {
1452             obj->mCapStyle = getLineCap();
1453         } else if (0 == strcmp(key, "lj")) {
1454             obj->mJoinStyle = getLineJoin();
1455         } else if (0 == strcmp(key, "ml")) {
1456             RAPIDJSON_ASSERT(PeekType() == kNumberType);
1457             obj->mMeterLimit = GetDouble();
1458         } else if (0 == strcmp(key, "d")) {
1459             parseDashProperty(obj->mDash);
1460         } else {
1461             parseGradientProperty(obj, key);
1462         }
1463     }
1464
1465     obj->setStatic(obj->isStatic() && obj->mWidth.isStatic() &&
1466                    obj->mDash.mStatic);
1467     return sharedGStroke;
1468 }
1469
1470 void LottieParserImpl::parseArrayValue(LottieColor &color)
1471 {
1472     float val[4];
1473     int   i = 0;
1474     while (NextArrayValue()) {
1475         val[i++] = GetDouble();
1476     }
1477
1478     color.r = val[0];
1479     color.g = val[1];
1480     color.b = val[2];
1481 }
1482
1483 void LottieParserImpl::parseArrayValue(VPointF &pt)
1484 {
1485     float val[4];
1486     int   i = 0;
1487     while (NextArrayValue()) {
1488         val[i++] = GetDouble();
1489     }
1490     pt.setX(val[0]);
1491     pt.setY(val[1]);
1492 }
1493
1494 void LottieParserImpl::parseArrayValue(float &val)
1495 {
1496     RAPIDJSON_ASSERT(0);
1497     val = GetDouble();
1498 }
1499
1500 void LottieParserImpl::parseArrayValue(int &val)
1501 {
1502     RAPIDJSON_ASSERT(0);
1503     val = GetInt();
1504 }
1505
1506 void LottieParserImpl::parseArrayValue(std::vector<VPointF> &v)
1507 {
1508     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1509     EnterArray();
1510     while (NextArrayValue()) {
1511         RAPIDJSON_ASSERT(PeekType() == kArrayType);
1512         EnterArray();
1513         VPointF pt;
1514         parseArrayValue(pt);
1515         v.push_back(pt);
1516     }
1517 }
1518
1519 void LottieParserImpl::getValue(VPointF &pt)
1520 {
1521     float val[4];
1522     int   i = 0;
1523     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1524     EnterArray();
1525     while (NextArrayValue()) {
1526         val[i++] = GetDouble();
1527     }
1528     pt.setX(val[0]);
1529     pt.setY(val[1]);
1530 }
1531
1532 void LottieParserImpl::getValue(float &val)
1533 {
1534     if (PeekType() == kArrayType) {
1535         EnterArray();
1536         while (NextArrayValue()) {
1537             val = GetDouble();
1538         }
1539     } else if (PeekType() == kNumberType) {
1540         val = GetDouble();
1541     } else {
1542         RAPIDJSON_ASSERT(0);
1543     }
1544 }
1545
1546 void LottieParserImpl::getValue(LottieColor &color)
1547 {
1548     float val[4];
1549     int   i = 0;
1550     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1551     EnterArray();
1552     while (NextArrayValue()) {
1553         val[i++] = GetDouble();
1554     }
1555     color.r = val[0];
1556     color.g = val[1];
1557     color.b = val[2];
1558 }
1559
1560 void LottieParserImpl::parseArrayValue(LottieGradient &grad)
1561 {
1562     while (NextArrayValue()) {
1563         grad.mGradient.push_back(GetDouble());
1564     }
1565 }
1566
1567 void LottieParserImpl::getValue(LottieGradient &grad)
1568 {
1569     RAPIDJSON_ASSERT(PeekType() == kArrayType);
1570     EnterArray();
1571     while (NextArrayValue()) {
1572         grad.mGradient.push_back(GetDouble());
1573     }
1574 }
1575
1576 void LottieParserImpl::getValue(int &val)
1577 {
1578     if (PeekType() == kArrayType) {
1579         EnterArray();
1580         while (NextArrayValue()) {
1581             val = GetInt();
1582         }
1583     } else if (PeekType() == kNumberType) {
1584         val = GetInt();
1585     } else {
1586         RAPIDJSON_ASSERT(0);
1587     }
1588 }
1589
1590 void LottieParserImpl::getValue(LottieShapeData &obj)
1591 {
1592     std::vector<VPointF> inPoint;  /* "i" */
1593     std::vector<VPointF> outPoint; /* "o" */
1594     std::vector<VPointF> vertices; /* "v" */
1595     std::vector<VPointF> points;
1596     bool                 closed = false;
1597
1598     /*
1599      * The shape object could be wrapped by a array
1600      * if its part of the keyframe object
1601      */
1602     bool arrayWrapper = (PeekType() == kArrayType);
1603     if (arrayWrapper) EnterArray();
1604
1605     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1606     EnterObject();
1607     while (const char *key = NextObjectKey()) {
1608         if (0 == strcmp(key, "i")) {
1609             parseArrayValue(inPoint);
1610         } else if (0 == strcmp(key, "o")) {
1611             parseArrayValue(outPoint);
1612         } else if (0 == strcmp(key, "v")) {
1613             parseArrayValue(vertices);
1614         } else if (0 == strcmp(key, "c")) {
1615             closed = GetBool();
1616         } else {
1617             RAPIDJSON_ASSERT(0);
1618             Skip(nullptr);
1619         }
1620     }
1621     // exit properly from the array
1622     if (arrayWrapper) NextArrayValue();
1623
1624     // shape data could be empty.
1625     if (inPoint.empty() || outPoint.empty() || vertices.empty()) return;
1626
1627     /*
1628      * Convert the AE shape format to
1629      * list of bazier curves
1630      * The final structure will be Move +size*Cubic + Cubic (if the path is
1631      * closed one)
1632      */
1633     if (inPoint.size() != outPoint.size() ||
1634         inPoint.size() != vertices.size()) {
1635         vCritical << "The Shape data are corrupted";
1636         points = std::vector<VPointF>();
1637     } else {
1638         int size = vertices.size();
1639         points.reserve(3 * size + 4);
1640         points.push_back(vertices[0]);
1641         for (int i = 1; i < size; i++) {
1642             points.push_back(vertices[i - 1] +
1643                              outPoint[i - 1]);  // CP1 = start + outTangent
1644             points.push_back(vertices[i] +
1645                              inPoint[i]);   // CP2 = end + inTangent
1646             points.push_back(vertices[i]);  // end point
1647         }
1648
1649         if (closed) {
1650             points.push_back(vertices[size - 1] +
1651                              outPoint[size - 1]);  // CP1 = start + outTangent
1652             points.push_back(vertices[0] +
1653                              inPoint[0]);   // CP2 = end + inTangent
1654             points.push_back(vertices[0]);  // end point
1655         }
1656     }
1657     obj.mPoints = std::move(points);
1658     obj.mClosed = closed;
1659 }
1660
1661 VPointF LottieParserImpl::parseInperpolatorPoint()
1662 {
1663     VPointF cp;
1664     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1665     EnterObject();
1666     while (const char *key = NextObjectKey()) {
1667         if (0 == strcmp(key, "x")) {
1668             if (PeekType() == kNumberType) {
1669                 cp.setX(GetDouble());
1670             } else {
1671                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1672                 EnterArray();
1673                 while (NextArrayValue()) {
1674                     cp.setX(GetDouble());
1675                 }
1676             }
1677         }
1678         if (0 == strcmp(key, "y")) {
1679             if (PeekType() == kNumberType) {
1680                 cp.setY(GetDouble());
1681             } else {
1682                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1683                 EnterArray();
1684                 while (NextArrayValue()) {
1685                     cp.setY(GetDouble());
1686                 }
1687             }
1688         }
1689     }
1690     return cp;
1691 }
1692
1693 template <typename T>
1694 bool LottieParserImpl::parseKeyFrameValue(const char *         key,
1695                                           LOTKeyFrameValue<T> &value)
1696 {
1697     if (0 == strcmp(key, "s")) {
1698         getValue(value.mStartValue);
1699     } else if (0 == strcmp(key, "e")) {
1700         getValue(value.mEndValue);
1701     } else {
1702         return false;
1703     }
1704     return true;
1705 }
1706
1707 template <>
1708 bool LottieParserImpl::parseKeyFrameValue(const char *               key,
1709                                           LOTKeyFrameValue<VPointF> &value)
1710 {
1711     if (0 == strcmp(key, "s")) {
1712         getValue(value.mStartValue);
1713     } else if (0 == strcmp(key, "e")) {
1714         getValue(value.mEndValue);
1715     } else if (0 == strcmp(key, "ti")) {
1716         value.mPathKeyFrame = true;
1717         getValue(value.mInTangent);
1718     } else if (0 == strcmp(key, "to")) {
1719         value.mPathKeyFrame = true;
1720         getValue(value.mOutTangent);
1721     } else {
1722         return false;
1723     }
1724     return true;
1725 }
1726
1727 /*
1728  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/multiDimensionalKeyframed.json
1729  */
1730 template <typename T>
1731 void LottieParserImpl::parseKeyFrame(LOTAnimInfo<T> &obj)
1732 {
1733     EnterObject();
1734     LOTKeyFrame<T> keyframe;
1735     VPointF        inTangent;
1736     VPointF        outTangent;
1737     const char *   interpolatorKey = nullptr;
1738     bool           hold = false;
1739     bool           lastFrame = true;
1740     while (const char *key = NextObjectKey()) {
1741         if (0 == strcmp(key, "i")) {
1742             inTangent = parseInperpolatorPoint();
1743         } else if (0 == strcmp(key, "o")) {
1744             outTangent = parseInperpolatorPoint();
1745         } else if (0 == strcmp(key, "n")) {
1746             if (PeekType() == kStringType) {
1747                 interpolatorKey = GetString();
1748             } else {
1749                 RAPIDJSON_ASSERT(PeekType() == kArrayType);
1750                 EnterArray();
1751                 while (NextArrayValue()) {
1752                     RAPIDJSON_ASSERT(PeekType() == kStringType);
1753                     interpolatorKey = GetString();
1754                 }
1755             }
1756             continue;
1757         } else if (0 == strcmp(key, "t")) {
1758             keyframe.mStartFrame = GetDouble();
1759         } else if (parseKeyFrameValue(key, keyframe.mValue)) {
1760             lastFrame = false;
1761             continue;
1762         } else if (0 == strcmp(key, "h")) {
1763             hold = GetInt();
1764             continue;
1765         } else {
1766 #ifdef DEBUG_PARSER
1767             vDebug << "key frame property skipped = " << key;
1768 #endif
1769             Skip(key);
1770         }
1771     }
1772
1773     if (!obj.mKeyFrames.empty()) {
1774         // update the endFrame value of current keyframe
1775         obj.mKeyFrames.back().mEndFrame = keyframe.mStartFrame;
1776     }
1777
1778     if (hold) {
1779         interpolatorKey = "hold_interpolator";
1780         inTangent = VPointF();
1781         outTangent = VPointF();
1782         keyframe.mValue.mEndValue = keyframe.mValue.mStartValue;
1783         keyframe.mEndFrame = keyframe.mStartFrame;
1784     }
1785
1786     char charArray[20];
1787     if (!(lastFrame || interpolatorKey)) {
1788         snprintf(charArray, 20, "%.2f_%.2f_%.2f_%.2f",
1789                  inTangent.x(), inTangent.y(), outTangent.x(), outTangent.y());
1790         interpolatorKey = charArray;
1791     }
1792
1793     // Try to find the interpolator from cache
1794     if (interpolatorKey) {
1795         auto search = compRef->mInterpolatorCache.find(interpolatorKey);
1796         if (search != compRef->mInterpolatorCache.end()) {
1797             keyframe.mInterpolator = search->second;
1798         } else {
1799             keyframe.mInterpolator = std::make_shared<VInterpolator>(
1800                 VInterpolator(outTangent, inTangent));
1801             compRef->mInterpolatorCache[interpolatorKey] =
1802                 keyframe.mInterpolator;
1803         }
1804         obj.mKeyFrames.push_back(keyframe);
1805     }
1806 }
1807
1808 /*
1809  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shapeKeyframed.json
1810  */
1811
1812 /*
1813  * https://github.com/airbnb/lottie-web/blob/master/docs/json/properties/shape.json
1814  */
1815 void LottieParserImpl::parseShapeProperty(LOTAnimatable<LottieShapeData> &obj)
1816 {
1817     EnterObject();
1818     while (const char *key = NextObjectKey()) {
1819         if (0 == strcmp(key, "k")) {
1820             if (PeekType() == kArrayType) {
1821                 EnterArray();
1822                 while (NextArrayValue()) {
1823                     RAPIDJSON_ASSERT(PeekType() == kObjectType);
1824                     if (!obj.mAnimInfo)
1825                         obj.mAnimInfo =
1826                             std::make_unique<LOTAnimInfo<LottieShapeData>>();
1827                     parseKeyFrame(*obj.mAnimInfo.get());
1828                 }
1829             } else {
1830                 getValue(obj.mValue);
1831             }
1832         } else {
1833 #ifdef DEBUG_PARSER
1834             vDebug << "shape property ignored = " << key;
1835 #endif
1836             Skip(nullptr);
1837         }
1838     }
1839 }
1840
1841 template <typename T>
1842 void LottieParserImpl::parsePropertyHelper(LOTAnimatable<T> &obj)
1843 {
1844     if (PeekType() == kNumberType) {
1845         /*single value property with no animation*/
1846         getValue(obj.mValue);
1847     } else {
1848         RAPIDJSON_ASSERT(PeekType() == kArrayType);
1849         EnterArray();
1850         while (NextArrayValue()) {
1851             /* property with keyframe info*/
1852             if (PeekType() == kObjectType) {
1853                 if (!obj.mAnimInfo)
1854                     obj.mAnimInfo = std::make_unique<LOTAnimInfo<T>>();
1855                 parseKeyFrame(*obj.mAnimInfo.get());
1856             } else {
1857                 /* Read before modifying.
1858                  * as there is no way of knowing if the
1859                  * value of the array is either array of numbers
1860                  * or array of object without entering the array
1861                  * thats why this hack is there
1862                  */
1863                 RAPIDJSON_ASSERT(PeekType() == kNumberType);
1864                 /*multi value property with no animation*/
1865                 parseArrayValue(obj.mValue);
1866                 /*break here as we already reached end of array*/
1867                 break;
1868             }
1869         }
1870     }
1871 }
1872
1873 /*
1874  * https://github.com/airbnb/lottie-web/tree/master/docs/json/properties
1875  */
1876 template <typename T>
1877 void LottieParserImpl::parseProperty(LOTAnimatable<T> &obj)
1878 {
1879     EnterObject();
1880     while (const char *key = NextObjectKey()) {
1881         if (0 == strcmp(key, "k")) {
1882             parsePropertyHelper(obj);
1883         } else {
1884             Skip(key);
1885         }
1886     }
1887 }
1888
1889 #ifdef DEBUG_PRINT_TREE
1890
1891 class LOTDataInspector {
1892 public:
1893     void visit(LOTCompositionData *obj, std::string level)
1894     {
1895         vDebug << " { "
1896                << level
1897                << "Composition:: a: " << !obj->isStatic()
1898                << ", v: " << obj->mVersion
1899                << ", stFm: " << obj->startFrame()
1900                << ", endFm: " << obj->endFrame()<< "\n";
1901         level.append("\t");
1902         visit(obj->mRootLayer.get(), level);
1903         level.erase(level.end() - 1, level.end());
1904         vDebug << " } " << level << "Composition End\n";
1905     }
1906     void visit(LOTLayerData *obj, std::string level)
1907     {
1908         vDebug << level
1909                << "{ "
1910                << layerType(obj->mLayerType)
1911                << ", id:" << obj->mId << " Pid:" << obj->mParentId
1912                << ", a:" << !obj->isStatic()
1913                << ", "<<matteType(obj->mMatteType)
1914                << ", mask:"<<obj->hasMask()
1915                << ", inFm:" << obj->mInFrame
1916                << ", outFm:" << obj->mOutFrame
1917                << ", stFm:" << obj->mStartFrame
1918                << ", ts:" << obj->mTimeStreatch
1919                << "\n";
1920         visitChildren(static_cast<LOTGroupData *>(obj), level);
1921         vDebug << level
1922                << "} "
1923                << layerType(obj->mLayerType).c_str()
1924                << ", id: " << obj->mId << "\n";
1925     }
1926     void visitChildren(LOTGroupData *obj, std::string level)
1927     {
1928         level.append("\t");
1929         for (const auto& child : obj->mChildren) visit(child.get(), level);
1930         if (obj->mTransform)
1931             visit(obj->mTransform.get(), level);
1932     }
1933
1934     void visit(LOTData *obj, std::string level) {
1935         switch (obj->mType) {
1936         case LOTData::Type::Repeater: {
1937             vDebug << level << "{ Repeater:";
1938             visitChildren(static_cast<LOTGroupData *>(obj), level);
1939             vDebug << level << "} Repeater";
1940             break;
1941         }
1942         case LOTData::Type::ShapeGroup: {
1943             vDebug << level << "{ ShapeGroup: a:" << !obj->isStatic();
1944             visitChildren(static_cast<LOTGroupData *>(obj), level);
1945             vDebug << level << "} ShapeGroup";
1946             break;
1947         }
1948         case LOTData::Type::Layer:{
1949             visit(static_cast<LOTLayerData *>(obj), level);
1950             break;
1951         }
1952         case LOTData::Type::Trim:{
1953             vDebug << level << "{ Trim: a:" << !obj->isStatic() << " }";
1954             break;
1955         }
1956         case LOTData::Type::Rect:{
1957             vDebug << level << "{ Rect: a:" << !obj->isStatic() << " }";
1958             break;
1959         }
1960         case LOTData::Type::Ellipse:{
1961             vDebug << level << "{ Ellipse: a:" << !obj->isStatic() << " }";
1962             break;
1963         }
1964         case LOTData::Type::Shape:{
1965             vDebug << level << "{ Shape: a:" << !obj->isStatic() << " }";
1966             break;
1967         }
1968         case LOTData::Type::Polystar:{
1969             vDebug << level << "{ Polystar: a:" << !obj->isStatic() << " }";
1970             break;
1971         }
1972         case LOTData::Type::Transform:{
1973             vDebug << level << "{ Transform: a: " << !obj->isStatic() << " }";
1974             break;
1975         }
1976         case LOTData::Type::Stroke:{
1977             vDebug << level << "{ Stroke: a:" << !obj->isStatic() << " }";
1978             break;
1979         }
1980         case LOTData::Type::GStroke:{
1981             vDebug << level << "{ GStroke: a:" << !obj->isStatic() << " }";
1982             break;
1983         }
1984         case LOTData::Type::Fill:{
1985             vDebug << level << "{ Fill: a:" << !obj->isStatic() << " }";
1986             break;
1987         }
1988         case LOTData::Type::GFill:{
1989             auto f = static_cast<LOTGFillData *>(obj);
1990             vDebug << level << "{ GFill: a:" << !f->isStatic()
1991                    << ", ty:" << f->mGradientType << ", s:" << f->mStartPoint.value(0)
1992                    << ", e:" << f->mEndPoint.value(0) << " }";
1993             break;
1994         }
1995         default:
1996             break;
1997         }
1998     }
1999
2000     std::string matteType(MatteType type)
2001     {
2002         switch (type) {
2003         case MatteType::None:
2004             return "Matte::None";
2005             break;
2006         case MatteType::Alpha:
2007             return "Matte::Alpha";
2008             break;
2009         case MatteType::AlphaInv:
2010             return "Matte::AlphaInv";
2011             break;
2012         case MatteType::Luma:
2013             return "Matte::Luma";
2014             break;
2015         case MatteType::LumaInv:
2016             return "Matte::LumaInv";
2017             break;
2018         default:
2019             return "Matte::Unknown";
2020             break;
2021         }
2022     }
2023     std::string layerType(LayerType type)
2024     {
2025         switch (type) {
2026         case LayerType::Precomp:
2027             return "Layer::Precomp";
2028             break;
2029         case LayerType::Null:
2030             return "Layer::Null";
2031             break;
2032         case LayerType::Shape:
2033             return "Layer::Shape";
2034             break;
2035         case LayerType::Solid:
2036             return "Layer::Solid";
2037             break;
2038         case LayerType::Image:
2039             return "Layer::Image";
2040             break;
2041         case LayerType::Text:
2042             return "Layer::Text";
2043             break;
2044         default:
2045             return "Layer::Unknown";
2046             break;
2047         }
2048     }
2049 };
2050
2051 #endif
2052
2053 LottieParser::~LottieParser()
2054 {
2055     delete d;
2056 }
2057
2058 LottieParser::LottieParser(char *str) : d(new LottieParserImpl(str))
2059 {
2060     d->parseComposition();
2061 }
2062
2063 std::shared_ptr<LOTModel> LottieParser::model()
2064 {
2065     std::shared_ptr<LOTModel> model = std::make_shared<LOTModel>();
2066     model->mRoot = d->composition();
2067     model->mRoot->processRepeaterObjects();
2068
2069 #ifdef DEBUG_PRINT_TREE
2070     LOTDataInspector inspector;
2071     inspector.visit(model->mRoot.get(), "");
2072 #endif
2073
2074     return model;
2075 }
2076
2077 RAPIDJSON_DIAG_POP