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