1 /*
2  * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "../../lv_conf_internal.h"
24 #if LV_USE_THORVG_INTERNAL
25 
26 #ifndef _TVG_LOTTIE_MODEL_H_
27 #define _TVG_LOTTIE_MODEL_H_
28 
29 #include <cstring>
30 
31 #include "tvgCommon.h"
32 #include "tvgRender.h"
33 #include "tvgLottieProperty.h"
34 #include "tvgLottieRenderPooler.h"
35 
36 
37 struct LottieComposition;
38 
39 struct LottieStroke
40 {
41     struct DashAttr
42     {
43         //0: offset, 1: dash, 2: gap
44         LottieFloat value[3] = {0.0f, 0.0f, 0.0f};
45     };
46 
~LottieStrokeLottieStroke47     virtual ~LottieStroke()
48     {
49         delete(dashattr);
50     }
51 
dashLottieStroke52     LottieFloat& dash(int no)
53     {
54         if (!dashattr) dashattr = new DashAttr;
55         return dashattr->value[no];
56     }
57 
dashOffsetLottieStroke58     float dashOffset(float frameNo, LottieExpressions* exps)
59     {
60         return dash(0)(frameNo, exps);
61     }
62 
dashGapLottieStroke63     float dashGap(float frameNo, LottieExpressions* exps)
64     {
65         return dash(2)(frameNo, exps);
66     }
67 
dashSizeLottieStroke68     float dashSize(float frameNo, LottieExpressions* exps)
69     {
70         auto d = dash(1)(frameNo, exps);
71         if (d == 0.0f) return 0.1f;
72         else return d;
73     }
74 
75     LottieFloat width = 0.0f;
76     DashAttr* dashattr = nullptr;
77     float miterLimit = 0;
78     StrokeCap cap = StrokeCap::Round;
79     StrokeJoin join = StrokeJoin::Round;
80 };
81 
82 
83 struct LottieEffect
84 {
85     enum Type : uint8_t
86     {
87         GaussianBlur = 0,
88     };
89 
~LottieEffectLottieEffect90     virtual ~LottieEffect() {}
91 
92     Type type;
93     bool enable = false;
94 };
95 
96 
97 struct LottieGaussianBlur : LottieEffect
98 {
99     LottieSlider blurness = 0.0f;
100     LottieCheckbox direction = 0;
101     LottieCheckbox wrap = 0;
102 
LottieGaussianBlurLottieGaussianBlur103     LottieGaussianBlur()
104     {
105         type = GaussianBlur;
106     }
107 };
108 
109 
110 struct LottieMask
111 {
112     LottiePathSet pathset;
113     LottieFloat expand = 0.0f;
114     LottieOpacity opacity = 255;
115     CompositeMethod method;
116     bool inverse = false;
117 };
118 
119 
120 struct LottieObject
121 {
122     enum Type : uint8_t
123     {
124         Composition = 0,
125         Layer,
126         Group,
127         Transform,
128         SolidFill,
129         SolidStroke,
130         GradientFill,
131         GradientStroke,
132         Rect,
133         Ellipse,
134         Path,
135         Polystar,
136         Image,
137         Trimpath,
138         Text,
139         Repeater,
140         RoundedCorner,
141         OffsetPath
142     };
143 
~LottieObjectLottieObject144     virtual ~LottieObject()
145     {
146     }
147 
overrideLottieObject148     virtual void override(LottieProperty* prop)
149     {
150         TVGERR("LOTTIE", "Unsupported slot type");
151     }
152 
mergeableLottieObject153     virtual bool mergeable() { return false; }
propertyLottieObject154     virtual LottieProperty* property(uint16_t ix) { return nullptr; }
155 
156     unsigned long id = 0;
157     Type type;
158     bool hidden = false;       //remove?
159 };
160 
161 
162 struct LottieGlyph
163 {
164     Array<LottieObject*> children;   //glyph shapes.
165     float width;
166     char* code;
167     char* family = nullptr;
168     char* style = nullptr;
169     uint16_t size;
170     uint8_t len;
171 
prepareLottieGlyph172     void prepare()
173     {
174         len = strlen(code);
175     }
176 
~LottieGlyphLottieGlyph177     ~LottieGlyph()
178     {
179         for (auto p = children.begin(); p < children.end(); ++p) delete(*p);
180         free(code);
181     }
182 };
183 
184 
185 struct LottieTextStyle
186 {
187     LottieColor fillColor = RGB24{255, 255, 255};
188     LottieColor strokeColor = RGB24{255, 255, 255};
189     LottiePosition position = Point{0, 0};
190     LottiePoint scale = Point{100, 100};
191     LottieFloat letterSpacing = 0.0f;
192     LottieFloat lineSpacing = 0.0f;
193     LottieFloat strokeWidth = 0.0f;
194     LottieFloat rotation = 0.0f;
195     LottieOpacity fillOpacity = 255;
196     LottieOpacity strokeOpacity = 255;
197     LottieOpacity opacity = 255;
198 };
199 
200 
201 struct LottieTextRange
202 {
203     enum Based : uint8_t { Chars = 1, CharsExcludingSpaces, Words, Lines };
204     enum Shape : uint8_t { Square = 1, RampUp, RampDown, Triangle, Round, Smooth };
205     enum Unit : uint8_t { Percent = 1, Index };
206 
207     LottieTextStyle style;
208     LottieFloat offset = 0.0f;
209     LottieFloat maxEase = 0.0f;
210     LottieFloat minEase = 0.0f;
211     LottieFloat maxAmount = 0.0f;
212     LottieFloat smoothness = 0.0f;
213     LottieFloat start = 0.0f;
214     LottieFloat end = FLT_MAX;
215     Based based = Chars;
216     Shape shape = Square;
217     Unit rangeUnit = Percent;
218     uint8_t random = 0;
219     bool expressible = false;
220 
221     void range(float frameNo, float totalLen, float& start, float& end);
222 };
223 
224 
225 struct LottieFont
226 {
227     enum Origin : uint8_t { Local = 0, CssURL, ScriptURL, FontURL, Embedded };
228 
~LottieFontLottieFont229     ~LottieFont()
230     {
231         for (auto c = chars.begin(); c < chars.end(); ++c) delete(*c);
232         free(style);
233         free(family);
234         free(name);
235     }
236 
237     Array<LottieGlyph*> chars;
238     char* name = nullptr;
239     char* family = nullptr;
240     char* style = nullptr;
241     float ascent = 0.0f;
242     Origin origin = Embedded;
243 };
244 
245 struct LottieMarker
246 {
247     char* name = nullptr;
248     float time = 0.0f;
249     float duration = 0.0f;
250 
~LottieMarkerLottieMarker251     ~LottieMarker()
252     {
253         free(name);
254     }
255 };
256 
257 struct LottieText : LottieObject, LottieRenderPooler<tvg::Shape>
258 {
prepareLottieText259     void prepare()
260     {
261         LottieObject::type = LottieObject::Text;
262     }
263 
overrideLottieText264     void override(LottieProperty* prop) override
265     {
266         this->doc = *static_cast<LottieTextDoc*>(prop);
267         this->prepare();
268     }
269 
propertyLottieText270     LottieProperty* property(uint16_t ix) override
271     {
272         if (doc.ix == ix) return &doc;
273         return nullptr;
274     }
275 
276     LottieTextDoc doc;
277     LottieFont* font;
278     Array<LottieTextRange*> ranges;
279 
~LottieTextLottieText280     ~LottieText()
281     {
282         for (auto r = ranges.begin(); r < ranges.end(); ++r) delete(*r);
283     }
284 };
285 
286 
287 struct LottieTrimpath : LottieObject
288 {
289     enum Type : uint8_t { Simultaneous = 1, Individual = 2 };
290 
prepareLottieTrimpath291     void prepare()
292     {
293         LottieObject::type = LottieObject::Trimpath;
294     }
295 
mergeableLottieTrimpath296     bool mergeable() override
297     {
298         if (!start.frames && start.value == 0.0f && !end.frames && end.value == 100.0f && !offset.frames && offset.value == 0.0f) return true;
299         return false;
300     }
301 
propertyLottieTrimpath302     LottieProperty* property(uint16_t ix) override
303     {
304         if (start.ix == ix) return &start;
305         if (end.ix == ix) return &end;
306         if (offset.ix == ix) return &offset;
307         return nullptr;
308     }
309 
310     void segment(float frameNo, float& start, float& end, LottieExpressions* exps);
311 
312     LottieFloat start = 0.0f;
313     LottieFloat end = 100.0f;
314     LottieFloat offset = 0.0f;
315     Type type = Simultaneous;
316 };
317 
318 
319 struct LottieShape : LottieObject, LottieRenderPooler<tvg::Shape>
320 {
321     bool clockwise = true;   //clockwise or counter-clockwise
322 
~LottieShapeLottieShape323     virtual ~LottieShape() {}
324 
mergeableLottieShape325     bool mergeable() override
326     {
327         return true;
328     }
329 
prepareLottieShape330     void prepare(LottieObject::Type type)
331     {
332         LottieObject::type = type;
333     }
334 };
335 
336 
337 struct LottieRoundedCorner : LottieObject
338 {
prepareLottieRoundedCorner339     void prepare()
340     {
341         LottieObject::type = LottieObject::RoundedCorner;
342     }
343 
propertyLottieRoundedCorner344     LottieProperty* property(uint16_t ix) override
345     {
346         if (radius.ix == ix) return &radius;
347         return nullptr;
348     }
349 
350     LottieFloat radius = 0.0f;
351 };
352 
353 
354 struct LottiePath : LottieShape
355 {
prepareLottiePath356     void prepare()
357     {
358         LottieShape::prepare(LottieObject::Path);
359     }
360 
propertyLottiePath361     LottieProperty* property(uint16_t ix) override
362     {
363         if (pathset.ix == ix) return &pathset;
364         return nullptr;
365     }
366 
367     LottiePathSet pathset;
368 };
369 
370 
371 struct LottieRect : LottieShape
372 {
prepareLottieRect373     void prepare()
374     {
375         LottieShape::prepare(LottieObject::Rect);
376     }
377 
propertyLottieRect378     LottieProperty* property(uint16_t ix) override
379     {
380         if (position.ix == ix) return &position;
381         if (size.ix == ix) return &size;
382         if (radius.ix == ix) return &radius;
383         return nullptr;
384     }
385 
386     LottiePosition position = Point{0.0f, 0.0f};
387     LottiePoint size = Point{0.0f, 0.0f};
388     LottieFloat radius = 0.0f;       //rounded corner radius
389 };
390 
391 
392 struct LottiePolyStar : LottieShape
393 {
394     enum Type : uint8_t {Star = 1, Polygon};
395 
prepareLottiePolyStar396     void prepare()
397     {
398         LottieShape::prepare(LottieObject::Polystar);
399     }
400 
propertyLottiePolyStar401     LottieProperty* property(uint16_t ix) override
402     {
403         if (position.ix == ix) return &position;
404         if (innerRadius.ix == ix) return &innerRadius;
405         if (outerRadius.ix == ix) return &outerRadius;
406         if (innerRoundness.ix == ix) return &innerRoundness;
407         if (outerRoundness.ix == ix) return &outerRoundness;
408         if (rotation.ix == ix) return &rotation;
409         if (ptsCnt.ix == ix) return &ptsCnt;
410         return nullptr;
411     }
412 
413     LottiePosition position = Point{0.0f, 0.0f};
414     LottieFloat innerRadius = 0.0f;
415     LottieFloat outerRadius = 0.0f;
416     LottieFloat innerRoundness = 0.0f;
417     LottieFloat outerRoundness = 0.0f;
418     LottieFloat rotation = 0.0f;
419     LottieFloat ptsCnt = 0.0f;
420     Type type = Polygon;
421 };
422 
423 
424 struct LottieEllipse : LottieShape
425 {
prepareLottieEllipse426     void prepare()
427     {
428         LottieShape::prepare(LottieObject::Ellipse);
429     }
430 
propertyLottieEllipse431     LottieProperty* property(uint16_t ix) override
432     {
433         if (position.ix == ix) return &position;
434         if (size.ix == ix) return &size;
435         return nullptr;
436     }
437 
438     LottiePosition position = Point{0.0f, 0.0f};
439     LottiePoint size = Point{0.0f, 0.0f};
440 };
441 
442 
443 struct LottieTransform : LottieObject
444 {
445     struct SeparateCoord
446     {
447         LottieFloat x = 0.0f;
448         LottieFloat y = 0.0f;
449     };
450 
451     struct RotationEx
452     {
453         LottieFloat x = 0.0f;
454         LottieFloat y = 0.0f;
455     };
456 
~LottieTransformLottieTransform457     ~LottieTransform()
458     {
459         delete(coords);
460         delete(rotationEx);
461     }
462 
prepareLottieTransform463     void prepare()
464     {
465         LottieObject::type = LottieObject::Transform;
466     }
467 
mergeableLottieTransform468     bool mergeable() override
469     {
470         if (!opacity.frames && opacity.value == 255) return true;
471         return false;
472     }
473 
propertyLottieTransform474     LottieProperty* property(uint16_t ix) override
475     {
476         if (position.ix == ix) return &position;
477         if (rotation.ix == ix) return &rotation;
478         if (scale.ix == ix) return &scale;
479         if (anchor.ix == ix) return &anchor;
480         if (opacity.ix == ix) return &opacity;
481         if (skewAngle.ix == ix) return &skewAngle;
482         if (skewAxis.ix == ix) return &skewAxis;
483         if (coords) {
484             if (coords->x.ix == ix) return &coords->x;
485             if (coords->y.ix == ix) return &coords->y;
486         }
487         return nullptr;
488     }
489 
490     LottiePosition position = Point{0.0f, 0.0f};
491     LottieFloat rotation = 0.0f;           //z rotation
492     LottiePoint scale = Point{100.0f, 100.0f};
493     LottiePoint anchor = Point{0.0f, 0.0f};
494     LottieOpacity opacity = 255;
495     LottieFloat skewAngle = 0.0f;
496     LottieFloat skewAxis = 0.0f;
497 
498     SeparateCoord* coords = nullptr;       //either a position or separate coordinates
499     RotationEx* rotationEx = nullptr;      //extension for 3d rotation
500 };
501 
502 
503 struct LottieSolid : LottieObject
504 {
505     LottieColor color = RGB24{255, 255, 255};
506     LottieOpacity opacity = 255;
507 
propertyLottieSolid508     LottieProperty* property(uint16_t ix) override
509     {
510         if (color.ix == ix) return &color;
511         if (opacity.ix == ix) return &opacity;
512         return nullptr;
513     }
514 };
515 
516 
517 struct LottieSolidStroke : LottieSolid, LottieStroke
518 {
prepareLottieSolidStroke519     void prepare()
520     {
521         LottieObject::type = LottieObject::SolidStroke;
522     }
523 
propertyLottieSolidStroke524     LottieProperty* property(uint16_t ix) override
525     {
526         if (width.ix == ix) return &width;
527         if (dashattr) {
528             if (dashattr->value[0].ix == ix) return &dashattr->value[0];
529             if (dashattr->value[1].ix == ix) return &dashattr->value[1];
530             if (dashattr->value[2].ix == ix) return &dashattr->value[2];
531         }
532         return LottieSolid::property(ix);
533     }
534 
overrideLottieSolidStroke535     void override(LottieProperty* prop) override
536     {
537         this->color = *static_cast<LottieColor*>(prop);
538         this->prepare();
539     }
540 };
541 
542 
543 struct LottieSolidFill : LottieSolid
544 {
prepareLottieSolidFill545     void prepare()
546     {
547         LottieObject::type = LottieObject::SolidFill;
548     }
549 
overrideLottieSolidFill550     void override(LottieProperty* prop) override
551     {
552         this->color = *static_cast<LottieColor*>(prop);
553         this->prepare();
554     }
555 
556     FillRule rule = FillRule::Winding;
557 };
558 
559 
560 struct LottieGradient : LottieObject
561 {
prepareLottieGradient562     bool prepare()
563     {
564         if (!colorStops.populated) {
565             auto count = colorStops.count;  //colorstop count can be modified after population
566             if (colorStops.frames) {
567                 for (auto v = colorStops.frames->begin(); v < colorStops.frames->end(); ++v) {
568                     colorStops.count = populate(v->value, count);
569                 }
570             } else {
571                 colorStops.count = populate(colorStops.value, count);
572             }
573             colorStops.populated = true;
574         }
575         if (start.frames || end.frames || height.frames || angle.frames || opacity.frames || colorStops.frames) return true;
576         return false;
577     }
578 
propertyLottieGradient579     LottieProperty* property(uint16_t ix) override
580     {
581         if (start.ix == ix) return &start;
582         if (end.ix == ix) return &end;
583         if (height.ix == ix) return &height;
584         if (angle.ix == ix) return &angle;
585         if (opacity.ix == ix) return &opacity;
586         if (colorStops.ix == ix) return &colorStops;
587         return nullptr;
588     }
589 
590 
591     uint32_t populate(ColorStop& color, size_t count);
592     Fill* fill(float frameNo, LottieExpressions* exps);
593 
594     LottiePoint start = Point{0.0f, 0.0f};
595     LottiePoint end = Point{0.0f, 0.0f};
596     LottieFloat height = 0.0f;
597     LottieFloat angle = 0.0f;
598     LottieOpacity opacity = 255;
599     LottieColorStop colorStops;
600     uint8_t id = 0;    //1: linear, 2: radial
601 };
602 
603 
604 struct LottieGradientFill : LottieGradient
605 {
prepareLottieGradientFill606     void prepare()
607     {
608         LottieObject::type = LottieObject::GradientFill;
609         LottieGradient::prepare();
610     }
611 
overrideLottieGradientFill612     void override(LottieProperty* prop) override
613     {
614         this->colorStops = *static_cast<LottieColorStop*>(prop);
615         this->prepare();
616     }
617 
618     FillRule rule = FillRule::Winding;
619 };
620 
621 
622 struct LottieGradientStroke : LottieGradient, LottieStroke
623 {
prepareLottieGradientStroke624     void prepare()
625     {
626         LottieObject::type = LottieObject::GradientStroke;
627         LottieGradient::prepare();
628     }
629 
propertyLottieGradientStroke630     LottieProperty* property(uint16_t ix) override
631     {
632         if (width.ix == ix) return &width;
633         if (dashattr) {
634             if (dashattr->value[0].ix == ix) return &dashattr->value[0];
635             if (dashattr->value[1].ix == ix) return &dashattr->value[1];
636             if (dashattr->value[2].ix == ix) return &dashattr->value[2];
637         }
638         return LottieGradient::property(ix);
639     }
640 
overrideLottieGradientStroke641     void override(LottieProperty* prop) override
642     {
643         this->colorStops = *static_cast<LottieColorStop*>(prop);
644         this->prepare();
645     }
646 };
647 
648 
649 struct LottieImage : LottieObject, LottieRenderPooler<tvg::Picture>
650 {
651     union {
652         char* b64Data = nullptr;
653         char* path;
654     };
655     char* mimeType = nullptr;
656     uint32_t size = 0;
657     float width = 0.0f;
658     float height = 0.0f;
659 
660     ~LottieImage();
661     void prepare();
662 };
663 
664 
665 struct LottieRepeater : LottieObject
666 {
prepareLottieRepeater667     void prepare()
668     {
669         LottieObject::type = LottieObject::Repeater;
670     }
671 
propertyLottieRepeater672     LottieProperty* property(uint16_t ix) override
673     {
674         if (copies.ix == ix) return &copies;
675         if (offset.ix == ix) return &offset;
676         if (position.ix == ix) return &position;
677         if (rotation.ix == ix) return &rotation;
678         if (scale.ix == ix) return &scale;
679         if (anchor.ix == ix) return &anchor;
680         if (startOpacity.ix == ix) return &startOpacity;
681         if (endOpacity.ix == ix) return &endOpacity;
682         return nullptr;
683     }
684 
685     LottieFloat copies = 0.0f;
686     LottieFloat offset = 0.0f;
687 
688     //Transform
689     LottiePosition position = Point{0.0f, 0.0f};
690     LottieFloat rotation = 0.0f;
691     LottiePoint scale = Point{100.0f, 100.0f};
692     LottiePoint anchor = Point{0.0f, 0.0f};
693     LottieOpacity startOpacity = 255;
694     LottieOpacity endOpacity = 255;
695     bool inorder = true;        //true: higher,  false: lower
696 };
697 
698 
699 struct LottieOffsetPath : LottieObject
700 {
prepareLottieOffsetPath701     void prepare()
702     {
703         LottieObject::type = LottieObject::OffsetPath;
704     }
705 
706     LottieFloat offset = 0.0f;
707     LottieFloat miterLimit = 4.0f;
708     StrokeJoin join = StrokeJoin::Miter;
709 };
710 
711 
712 struct LottieGroup : LottieObject, LottieRenderPooler<tvg::Shape>
713 {
714     LottieGroup();
715 
~LottieGroupLottieGroup716     virtual ~LottieGroup()
717     {
718         for (auto p = children.begin(); p < children.end(); ++p) delete(*p);
719     }
720 
721     void prepare(LottieObject::Type type = LottieObject::Group);
mergeableLottieGroup722     bool mergeable() override { return allowMerge; }
723 
contentLottieGroup724     LottieObject* content(unsigned long id)
725     {
726         if (this->id == id) return this;
727 
728         //source has children, find recursively.
729         for (auto c = children.begin(); c < children.end(); ++c) {
730             auto child = *c;
731             if (child->type == LottieObject::Type::Group || child->type == LottieObject::Type::Layer) {
732                 if (auto ret = static_cast<LottieGroup*>(child)->content(id)) return ret;
733             } else if (child->id == id) return child;
734         }
735         return nullptr;
736     }
737 
738     Scene* scene = nullptr;
739     Array<LottieObject*> children;
740 
741     bool reqFragment : 1;   //requirement to fragment the render context
742     bool buildDone : 1;     //completed in building the composition.
743     bool trimpath : 1;      //this group has a trimpath.
744     bool visible : 1;       //this group has visible contents.
745     bool allowMerge : 1;    //if this group is consisted of simple (transformed) shapes.
746 };
747 
748 
749 struct LottieLayer : LottieGroup
750 {
751     enum Type : uint8_t {Precomp = 0, Solid, Image, Null, Shape, Text};
752 
753     ~LottieLayer();
754 
opacityLottieLayer755     uint8_t opacity(float frameNo)
756     {
757         //return zero if the visibility is false.
758         if (type == Null) return 255;
759         return transform->opacity(frameNo);
760     }
761 
mergeableLottieLayer762     bool mergeable() override { return false; }
763 
764     void prepare(RGB24* color = nullptr);
765     float remap(LottieComposition* comp, float frameNo, LottieExpressions* exp);
766 
767     char* name = nullptr;
768     LottieLayer* parent = nullptr;
769     LottieFloat timeRemap = 0.0f;
770     LottieLayer* comp = nullptr;  //Precompositor, current layer is belonges.
771     LottieTransform* transform = nullptr;
772     Array<LottieMask*> masks;
773     Array<LottieEffect*> effects;
774     LottieLayer* matteTarget = nullptr;
775 
776     LottieRenderPooler<tvg::Shape> statical;  //static pooler for solid fill and clipper
777 
778     float timeStretch = 1.0f;
779     float w = 0.0f, h = 0.0f;
780     float inFrame = 0.0f;
781     float outFrame = 0.0f;
782     float startFrame = 0.0f;
783     unsigned long rid = 0;      //pre-composition reference id.
784     int16_t mid = -1;           //id of the matte layer.
785     int16_t pidx = -1;          //index of the parent layer.
786     int16_t idx = -1;           //index of the current layer.
787 
788     struct {
789         float frameNo = -1.0f;
790         Matrix matrix;
791         uint8_t opacity;
792     } cache;
793 
794     CompositeMethod matteType = CompositeMethod::None;
795     BlendMethod blendMethod = BlendMethod::Normal;
796     Type type = Null;
797     bool autoOrient = false;
798     bool matteSrc = false;
799 
layerByIdLottieLayer800     LottieLayer* layerById(unsigned long id)
801     {
802         for (auto child = children.begin(); child < children.end(); ++child) {
803             if ((*child)->type != LottieObject::Type::Layer) continue;
804             auto layer = static_cast<LottieLayer*>(*child);
805             if (layer->id == id) return layer;
806         }
807         return nullptr;
808     }
809 
layerByIdxLottieLayer810     LottieLayer* layerByIdx(int16_t idx)
811     {
812         for (auto child = children.begin(); child < children.end(); ++child) {
813             if ((*child)->type != LottieObject::Type::Layer) continue;
814             auto layer = static_cast<LottieLayer*>(*child);
815             if (layer->idx == idx) return layer;
816         }
817         return nullptr;
818     }
819 };
820 
821 
822 struct LottieSlot
823 {
824     struct Pair {
825         LottieObject* obj;
826         LottieProperty* prop;
827     };
828 
829     void assign(LottieObject* target);
830     void reset();
831 
LottieSlotLottieSlot832     LottieSlot(char* sid, LottieObject* obj, LottieProperty::Type type) : sid(sid), type(type)
833     {
834         pairs.push({obj, 0});
835     }
836 
~LottieSlotLottieSlot837     ~LottieSlot()
838     {
839         free(sid);
840         if (!overridden) return;
841         for (auto pair = pairs.begin(); pair < pairs.end(); ++pair) {
842             delete(pair->prop);
843         }
844     }
845 
846     char* sid;
847     Array<Pair> pairs;
848     LottieProperty::Type type;
849     bool overridden = false;
850 };
851 
852 
853 struct LottieComposition
854 {
855     ~LottieComposition();
856 
durationLottieComposition857     float duration() const
858     {
859         return frameCnt() / frameRate;  // in second
860     }
861 
frameAtTimeLottieComposition862     float frameAtTime(float timeInSec) const
863     {
864         auto p = timeInSec / duration();
865         if (p < 0.0f) p = 0.0f;
866         return p * frameCnt();
867     }
868 
timeAtFrameLottieComposition869     float timeAtFrame(float frameNo)
870     {
871         return (frameNo - root->inFrame) / frameRate;
872     }
873 
frameCntLottieComposition874     float frameCnt() const
875     {
876         return root->outFrame - root->inFrame;
877     }
878 
assetLottieComposition879     LottieLayer* asset(unsigned long id)
880     {
881         for (auto asset = assets.begin(); asset < assets.end(); ++asset) {
882             auto layer = static_cast<LottieLayer*>(*asset);
883             if (layer->id == id) return layer;
884         }
885         return nullptr;
886     }
887 
888     LottieLayer* root = nullptr;
889     char* version = nullptr;
890     char* name = nullptr;
891     float w, h;
892     float frameRate;
893     Array<LottieObject*> assets;
894     Array<LottieInterpolator*> interpolators;
895     Array<LottieFont*> fonts;
896     Array<LottieSlot*> slots;
897     Array<LottieMarker*> markers;
898     bool expressions = false;
899     bool initiated = false;
900 };
901 
902 #endif //_TVG_LOTTIE_MODEL_H_
903 
904 #endif /* LV_USE_THORVG_INTERNAL */
905 
906