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_PROPERTY_H_
27 #define _TVG_LOTTIE_PROPERTY_H_
28
29 #include <algorithm>
30 #include "tvgMath.h"
31 #include "tvgLottieCommon.h"
32 #include "tvgLottieInterpolator.h"
33 #include "tvgLottieExpressions.h"
34 #include "tvgLottieModifier.h"
35
36
37 struct LottieFont;
38 struct LottieLayer;
39 struct LottieObject;
40
41
42 template<typename T>
43 struct LottieScalarFrame
44 {
45 T value; //keyframe value
46 float no; //frame number
47 LottieInterpolator* interpolator;
48 bool hold = false; //do not interpolate.
49
interpolateLottieScalarFrame50 T interpolate(LottieScalarFrame<T>* next, float frameNo)
51 {
52 auto t = (frameNo - no) / (next->no - no);
53 if (interpolator) t = interpolator->progress(t);
54
55 if (hold) {
56 if (t < 1.0f) return value;
57 else return next->value;
58 }
59 return lerp(value, next->value, t);
60 }
61 };
62
63
64 template<typename T>
65 struct LottieVectorFrame
66 {
67 T value; //keyframe value
68 float no; //frame number
69 LottieInterpolator* interpolator;
70 T outTangent, inTangent;
71 float length;
72 bool hasTangent = false;
73 bool hold = false;
74
interpolateLottieVectorFrame75 T interpolate(LottieVectorFrame* next, float frameNo)
76 {
77 auto t = (frameNo - no) / (next->no - no);
78 if (interpolator) t = interpolator->progress(t);
79
80 if (hold) {
81 if (t < 1.0f) return value;
82 else return next->value;
83 }
84
85 if (hasTangent) {
86 Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
87 return bz.at(bz.atApprox(t * length, length));
88 } else {
89 return lerp(value, next->value, t);
90 }
91 }
92
angleLottieVectorFrame93 float angle(LottieVectorFrame* next, float frameNo)
94 {
95 if (!hasTangent) {
96 Point dp = next->value - value;
97 return rad2deg(tvg::atan2(dp.y, dp.x));
98 }
99
100 auto t = (frameNo - no) / (next->no - no);
101 if (interpolator) t = interpolator->progress(t);
102 Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
103 t = bz.atApprox(t * length, length);
104 return bz.angle(t >= 1.0f ? 0.99f : (t <= 0.0f ? 0.01f : t));
105 }
106
prepareLottieVectorFrame107 void prepare(LottieVectorFrame* next)
108 {
109 Bezier bz = {value, value + outTangent, next->value + inTangent, next->value};
110 length = bz.lengthApprox();
111 }
112 };
113
114
115 //Property would have an either keyframes or single value.
116 struct LottieProperty
117 {
118 enum class Type : uint8_t { Point = 0, Float, Opacity, Color, PathSet, ColorStop, Position, TextDoc, Invalid };
119
120 LottieExpression* exp = nullptr;
121 Type type;
122 uint8_t ix; //property index
123
124 //TODO: Apply common bodies?
~LottiePropertyLottieProperty125 virtual ~LottieProperty() {}
126 virtual uint32_t frameCnt() = 0;
127 virtual uint32_t nearest(float time) = 0;
128 virtual float frameNo(int32_t key) = 0;
129 };
130
131
132 struct LottieExpression
133 {
134 enum LoopMode : uint8_t { None = 0, InCycle = 1, InPingPong, InOffset, InContinue, OutCycle, OutPingPong, OutOffset, OutContinue };
135
136 char* code;
137 LottieComposition* comp;
138 LottieLayer* layer;
139 LottieObject* object;
140 LottieProperty* property;
141 bool disabled = false;
142
143 struct {
144 uint32_t key = 0; //the keyframe number repeating to
145 float in = FLT_MAX; //looping duration in frame number
146 LoopMode mode = None;
147 } loop;
148
~LottieExpressionLottieExpression149 ~LottieExpression()
150 {
151 free(code);
152 }
153 };
154
155
_copy(PathSet * pathset,Array<Point> & outPts,Matrix * transform)156 static void _copy(PathSet* pathset, Array<Point>& outPts, Matrix* transform)
157 {
158 Array<Point> inPts;
159
160 if (transform) {
161 for (int i = 0; i < pathset->ptsCnt; ++i) {
162 Point pt = pathset->pts[i];
163 pt *= *transform;
164 outPts.push(pt);
165 }
166 } else {
167 inPts.data = pathset->pts;
168 inPts.count = pathset->ptsCnt;
169 outPts.push(inPts);
170 inPts.data = nullptr;
171 }
172 }
173
174
_copy(PathSet * pathset,Array<PathCommand> & outCmds)175 static void _copy(PathSet* pathset, Array<PathCommand>& outCmds)
176 {
177 Array<PathCommand> inCmds;
178 inCmds.data = pathset->cmds;
179 inCmds.count = pathset->cmdsCnt;
180 outCmds.push(inCmds);
181 inCmds.data = nullptr;
182 }
183
184
185 template<typename T>
_bsearch(T * frames,float frameNo)186 uint32_t _bsearch(T* frames, float frameNo)
187 {
188 int32_t low = 0;
189 int32_t high = int32_t(frames->count) - 1;
190
191 while (low <= high) {
192 auto mid = low + (high - low) / 2;
193 auto frame = frames->data + mid;
194 if (frameNo < frame->no) high = mid - 1;
195 else low = mid + 1;
196 }
197 if (high < low) low = high;
198 if (low < 0) low = 0;
199 return low;
200 }
201
202
203 template<typename T>
_nearest(T * frames,float frameNo)204 uint32_t _nearest(T* frames, float frameNo)
205 {
206 if (frames) {
207 auto key = _bsearch(frames, frameNo);
208 if (key == frames->count - 1) return key;
209 return (fabsf(frames->data[key].no - frameNo) < fabsf(frames->data[key + 1].no - frameNo)) ? key : (key + 1);
210 }
211 return 0;
212 }
213
214
215 template<typename T>
_frameNo(T * frames,int32_t key)216 float _frameNo(T* frames, int32_t key)
217 {
218 if (!frames) return 0.0f;
219 if (key < 0) key = 0;
220 if (key >= (int32_t) frames->count) key = (int32_t)(frames->count - 1);
221 return (*frames)[key].no;
222 }
223
224
225 template<typename T>
_loop(T * frames,float frameNo,LottieExpression * exp)226 float _loop(T* frames, float frameNo, LottieExpression* exp)
227 {
228 if (frameNo >= exp->loop.in || frameNo < frames->first().no || frameNo < frames->last().no) return frameNo;
229
230 frameNo -= frames->first().no;
231
232 switch (exp->loop.mode) {
233 case LottieExpression::LoopMode::InCycle: {
234 return fmodf(frameNo, frames->last().no - frames->first().no) + (*frames)[exp->loop.key].no;
235 }
236 case LottieExpression::LoopMode::InPingPong: {
237 auto range = frames->last().no - (*frames)[exp->loop.key].no;
238 auto forward = (static_cast<int>(frameNo / range) % 2) == 0 ? true : false;
239 frameNo = fmodf(frameNo, range);
240 return (forward ? frameNo : (range - frameNo)) + (*frames)[exp->loop.key].no;
241 }
242 case LottieExpression::LoopMode::OutCycle: {
243 return fmodf(frameNo, (*frames)[frames->count - 1 - exp->loop.key].no - frames->first().no) + frames->first().no;
244 }
245 case LottieExpression::LoopMode::OutPingPong: {
246 auto range = (*frames)[frames->count - 1 - exp->loop.key].no - frames->first().no;
247 auto forward = (static_cast<int>(frameNo / range) % 2) == 0 ? true : false;
248 frameNo = fmodf(frameNo, range);
249 return (forward ? frameNo : (range - frameNo)) + frames->first().no;
250 }
251 default: break;
252 }
253 return frameNo;
254 }
255
256
257 template<typename T>
258 struct LottieGenericProperty : LottieProperty
259 {
260 //Property has an either keyframes or single value.
261 Array<LottieScalarFrame<T>>* frames = nullptr;
262 T value;
263
LottieGenericPropertyLottieGenericProperty264 LottieGenericProperty(T v) : value(v) {}
LottieGenericPropertyLottieGenericProperty265 LottieGenericProperty() {}
266
~LottieGenericPropertyLottieGenericProperty267 ~LottieGenericProperty()
268 {
269 release();
270 }
271
releaseLottieGenericProperty272 void release()
273 {
274 delete(frames);
275 frames = nullptr;
276 if (exp) {
277 delete(exp);
278 exp = nullptr;
279 }
280 }
281
nearestLottieGenericProperty282 uint32_t nearest(float frameNo) override
283 {
284 return _nearest(frames, frameNo);
285 }
286
frameCntLottieGenericProperty287 uint32_t frameCnt() override
288 {
289 return frames ? frames->count : 1;
290 }
291
frameNoLottieGenericProperty292 float frameNo(int32_t key) override
293 {
294 return _frameNo(frames, key);
295 }
296
newFrameLottieGenericProperty297 LottieScalarFrame<T>& newFrame()
298 {
299 if (!frames) frames = new Array<LottieScalarFrame<T>>;
300 if (frames->count + 1 >= frames->reserved) {
301 auto old = frames->reserved;
302 frames->grow(frames->count + 2);
303 memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<T>) * (frames->reserved - old));
304 }
305 ++frames->count;
306 return frames->last();
307 }
308
nextFrameLottieGenericProperty309 LottieScalarFrame<T>& nextFrame()
310 {
311 return (*frames)[frames->count];
312 }
313
operatorLottieGenericProperty314 T operator()(float frameNo)
315 {
316 if (!frames) return value;
317 if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
318 if (frameNo >= frames->last().no) return frames->last().value;
319
320 auto frame = frames->data + _bsearch(frames, frameNo);
321 if (tvg::equal(frame->no, frameNo)) return frame->value;
322 return frame->interpolate(frame + 1, frameNo);
323 }
324
operatorLottieGenericProperty325 T operator()(float frameNo, LottieExpressions* exps)
326 {
327 if (exps && exp) {
328 T out{};
329 if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
330 if (exps->result<LottieGenericProperty<T>>(frameNo, out, exp)) return out;
331 }
332 return operator()(frameNo);
333 }
334
335 LottieGenericProperty<T>& operator=(const LottieGenericProperty<T>& other)
336 {
337 //shallow copy, used for slot overriding
338 if (other.frames) {
339 frames = other.frames;
340 const_cast<LottieGenericProperty<T>&>(other).frames = nullptr;
341 } else value = other.value;
342 return *this;
343 }
344
angleLottieGenericProperty345 float angle(float frameNo) { return 0; }
prepareLottieGenericProperty346 void prepare() {}
347 };
348
349
350 struct LottiePathSet : LottieProperty
351 {
352 Array<LottieScalarFrame<PathSet>>* frames = nullptr;
353 PathSet value;
354
~LottiePathSetLottiePathSet355 ~LottiePathSet()
356 {
357 release();
358 }
359
releaseLottiePathSet360 void release()
361 {
362 if (exp) {
363 delete(exp);
364 exp = nullptr;
365 }
366
367 free(value.cmds);
368 free(value.pts);
369
370 if (!frames) return;
371
372 for (auto p = frames->begin(); p < frames->end(); ++p) {
373 free((*p).value.cmds);
374 free((*p).value.pts);
375 }
376 free(frames->data);
377 free(frames);
378 }
379
nearestLottiePathSet380 uint32_t nearest(float frameNo) override
381 {
382 return _nearest(frames, frameNo);
383 }
384
frameCntLottiePathSet385 uint32_t frameCnt() override
386 {
387 return frames ? frames->count : 1;
388 }
389
frameNoLottiePathSet390 float frameNo(int32_t key) override
391 {
392 return _frameNo(frames, key);
393 }
394
newFrameLottiePathSet395 LottieScalarFrame<PathSet>& newFrame()
396 {
397 if (!frames) {
398 frames = static_cast<Array<LottieScalarFrame<PathSet>>*>(calloc(1, sizeof(Array<LottieScalarFrame<PathSet>>)));
399 }
400 if (frames->count + 1 >= frames->reserved) {
401 auto old = frames->reserved;
402 frames->grow(frames->count + 2);
403 memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<PathSet>) * (frames->reserved - old));
404 }
405 ++frames->count;
406 return frames->last();
407 }
408
nextFrameLottiePathSet409 LottieScalarFrame<PathSet>& nextFrame()
410 {
411 return (*frames)[frames->count];
412 }
413
operatorLottiePathSet414 bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offsetPath)
415 {
416 PathSet* path = nullptr;
417 LottieScalarFrame<PathSet>* frame = nullptr;
418 float t;
419 bool interpolate = false;
420
421 if (!frames) path = &value;
422 else if (frames->count == 1 || frameNo <= frames->first().no) path = &frames->first().value;
423 else if (frameNo >= frames->last().no) path = &frames->last().value;
424 else {
425 frame = frames->data + _bsearch(frames, frameNo);
426 if (tvg::equal(frame->no, frameNo)) path = &frame->value;
427 else if (frame->value.ptsCnt != (frame + 1)->value.ptsCnt) {
428 path = &frame->value;
429 TVGLOG("LOTTIE", "Different numbers of points in consecutive frames - interpolation omitted.");
430 } else {
431 t = (frameNo - frame->no) / ((frame + 1)->no - frame->no);
432 if (frame->interpolator) t = frame->interpolator->progress(t);
433 if (frame->hold) path = &(frame + ((t < 1.0f) ? 0 : 1))->value;
434 else interpolate = true;
435 }
436 }
437
438 if (!interpolate) {
439 if (roundness) {
440 if (offsetPath) {
441 Array<PathCommand> cmds1(path->cmdsCnt);
442 Array<Point> pts1(path->ptsCnt);
443 roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds1, pts1, transform);
444 return offsetPath->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts);
445 }
446 return roundness->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts, transform);
447 }
448 if (offsetPath) return offsetPath->modifyPath(path->cmds, path->cmdsCnt, path->pts, path->ptsCnt, cmds, pts);
449
450 _copy(path, cmds);
451 _copy(path, pts, transform);
452 return true;
453 }
454
455 auto s = frame->value.pts;
456 auto e = (frame + 1)->value.pts;
457
458 if (!roundness && !offsetPath) {
459 for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e) {
460 auto pt = lerp(*s, *e, t);
461 if (transform) pt *= *transform;
462 pts.push(pt);
463 }
464 _copy(&frame->value, cmds);
465 return true;
466 }
467
468 auto interpPts = (Point*)malloc(frame->value.ptsCnt * sizeof(Point));
469 auto p = interpPts;
470 for (auto i = 0; i < frame->value.ptsCnt; ++i, ++s, ++e, ++p) {
471 *p = lerp(*s, *e, t);
472 if (transform) *p *= *transform;
473 }
474
475 if (roundness) {
476 if (offsetPath) {
477 Array<PathCommand> cmds1;
478 Array<Point> pts1;
479 roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds1, pts1, nullptr);
480 offsetPath->modifyPath(cmds1.data, cmds1.count, pts1.data, pts1.count, cmds, pts);
481 } else roundness->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts, nullptr);
482 } else if (offsetPath) offsetPath->modifyPath(frame->value.cmds, frame->value.cmdsCnt, interpPts, frame->value.ptsCnt, cmds, pts);
483
484 free(interpPts);
485
486 return true;
487 }
488
489
operatorLottiePathSet490 bool operator()(float frameNo, Array<PathCommand>& cmds, Array<Point>& pts, Matrix* transform, const LottieRoundnessModifier* roundness, const LottieOffsetModifier* offsetPath, LottieExpressions* exps)
491 {
492 if (exps && exp) {
493 if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
494 if (exps->result<LottiePathSet>(frameNo, cmds, pts, transform, roundness, offsetPath, exp)) return true;
495 }
496 return operator()(frameNo, cmds, pts, transform, roundness, offsetPath);
497 }
498
prepareLottiePathSet499 void prepare() {}
500 };
501
502
503 struct LottieColorStop : LottieProperty
504 {
505 Array<LottieScalarFrame<ColorStop>>* frames = nullptr;
506 ColorStop value;
507 uint16_t count = 0; //colorstop count
508 bool populated = false;
509
~LottieColorStopLottieColorStop510 ~LottieColorStop()
511 {
512 release();
513 }
514
releaseLottieColorStop515 void release()
516 {
517 if (exp) {
518 delete(exp);
519 exp = nullptr;
520 }
521
522 if (value.data) {
523 free(value.data);
524 value.data = nullptr;
525 }
526
527 if (!frames) return;
528
529 for (auto p = frames->begin(); p < frames->end(); ++p) {
530 free((*p).value.data);
531 }
532 free(frames->data);
533 free(frames);
534 frames = nullptr;
535 }
536
nearestLottieColorStop537 uint32_t nearest(float frameNo) override
538 {
539 return _nearest(frames, frameNo);
540 }
541
frameCntLottieColorStop542 uint32_t frameCnt() override
543 {
544 return frames ? frames->count : 1;
545 }
546
frameNoLottieColorStop547 float frameNo(int32_t key) override
548 {
549 return _frameNo(frames, key);
550 }
551
newFrameLottieColorStop552 LottieScalarFrame<ColorStop>& newFrame()
553 {
554 if (!frames) {
555 frames = static_cast<Array<LottieScalarFrame<ColorStop>>*>(calloc(1, sizeof(Array<LottieScalarFrame<ColorStop>>)));
556 }
557 if (frames->count + 1 >= frames->reserved) {
558 auto old = frames->reserved;
559 frames->grow(frames->count + 2);
560 memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<ColorStop>) * (frames->reserved - old));
561 }
562 ++frames->count;
563 return frames->last();
564 }
565
nextFrameLottieColorStop566 LottieScalarFrame<ColorStop>& nextFrame()
567 {
568 return (*frames)[frames->count];
569 }
570
operatorLottieColorStop571 Result operator()(float frameNo, Fill* fill, LottieExpressions* exps)
572 {
573 if (exps && exp) {
574 if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
575 if (exps->result<LottieColorStop>(frameNo, fill, exp)) return Result::Success;
576 }
577
578 if (!frames) return fill->colorStops(value.data, count);
579
580 if (frames->count == 1 || frameNo <= frames->first().no) {
581 return fill->colorStops(frames->first().value.data, count);
582 }
583
584 if (frameNo >= frames->last().no) {
585 return fill->colorStops(frames->last().value.data, count);
586 }
587
588 auto frame = frames->data + _bsearch(frames, frameNo);
589 if (tvg::equal(frame->no, frameNo)) return fill->colorStops(frame->value.data, count);
590
591 //interpolate
592 auto t = (frameNo - frame->no) / ((frame + 1)->no - frame->no);
593 if (frame->interpolator) t = frame->interpolator->progress(t);
594
595 if (frame->hold) {
596 if (t < 1.0f) fill->colorStops(frame->value.data, count);
597 else fill->colorStops((frame + 1)->value.data, count);
598 }
599
600 auto s = frame->value.data;
601 auto e = (frame + 1)->value.data;
602
603 Array<Fill::ColorStop> result;
604
605 for (auto i = 0; i < count; ++i, ++s, ++e) {
606 auto offset = lerp(s->offset, e->offset, t);
607 auto r = lerp(s->r, e->r, t);
608 auto g = lerp(s->g, e->g, t);
609 auto b = lerp(s->b, e->b, t);
610 auto a = lerp(s->a, e->a, t);
611 result.push({offset, r, g, b, a});
612 }
613 return fill->colorStops(result.data, count);
614 }
615
616 LottieColorStop& operator=(const LottieColorStop& other)
617 {
618 //shallow copy, used for slot overriding
619 if (other.frames) {
620 frames = other.frames;
621 const_cast<LottieColorStop&>(other).frames = nullptr;
622 } else {
623 value = other.value;
624 const_cast<LottieColorStop&>(other).value = {nullptr, nullptr};
625 }
626 populated = other.populated;
627 count = other.count;
628
629 return *this;
630 }
631
prepareLottieColorStop632 void prepare() {}
633 };
634
635
636 struct LottiePosition : LottieProperty
637 {
638 Array<LottieVectorFrame<Point>>* frames = nullptr;
639 Point value;
640
LottiePositionLottiePosition641 LottiePosition(Point v) : value(v)
642 {
643 }
644
~LottiePositionLottiePosition645 ~LottiePosition()
646 {
647 release();
648 }
649
releaseLottiePosition650 void release()
651 {
652 delete(frames);
653 frames = nullptr;
654
655 if (exp) {
656 delete(exp);
657 exp = nullptr;
658 }
659 }
660
nearestLottiePosition661 uint32_t nearest(float frameNo) override
662 {
663 return _nearest(frames, frameNo);
664 }
665
frameCntLottiePosition666 uint32_t frameCnt() override
667 {
668 return frames ? frames->count : 1;
669 }
670
frameNoLottiePosition671 float frameNo(int32_t key) override
672 {
673 return _frameNo(frames, key);
674 }
675
newFrameLottiePosition676 LottieVectorFrame<Point>& newFrame()
677 {
678 if (!frames) frames = new Array<LottieVectorFrame<Point>>;
679 if (frames->count + 1 >= frames->reserved) {
680 auto old = frames->reserved;
681 frames->grow(frames->count + 2);
682 memset((void*)(frames->data + old), 0x00, sizeof(LottieVectorFrame<Point>) * (frames->reserved - old));
683 }
684 ++frames->count;
685 return frames->last();
686 }
687
nextFrameLottiePosition688 LottieVectorFrame<Point>& nextFrame()
689 {
690 return (*frames)[frames->count];
691 }
692
operatorLottiePosition693 Point operator()(float frameNo)
694 {
695 if (!frames) return value;
696 if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
697 if (frameNo >= frames->last().no) return frames->last().value;
698
699 auto frame = frames->data + _bsearch(frames, frameNo);
700 if (tvg::equal(frame->no, frameNo)) return frame->value;
701 return frame->interpolate(frame + 1, frameNo);
702 }
703
operatorLottiePosition704 Point operator()(float frameNo, LottieExpressions* exps)
705 {
706 Point out{};
707 if (exps && exp) {
708 if (exp->loop.mode != LottieExpression::LoopMode::None) frameNo = _loop(frames, frameNo, exp);
709 if (exps->result<LottiePosition>(frameNo, out, exp)) return out;
710 }
711 return operator()(frameNo);
712 }
713
angleLottiePosition714 float angle(float frameNo)
715 {
716 if (!frames || frames->count == 1) return 0;
717
718 if (frameNo <= frames->first().no) return frames->first().angle(frames->data + 1, frames->first().no);
719 if (frameNo >= frames->last().no) {
720 auto frame = frames->data + frames->count - 2;
721 return frame->angle(frame + 1, frames->last().no);
722 }
723
724 auto frame = frames->data + _bsearch(frames, frameNo);
725 return frame->angle(frame + 1, frameNo);
726 }
727
prepareLottiePosition728 void prepare()
729 {
730 if (!frames || frames->count < 2) return;
731 for (auto frame = frames->begin() + 1; frame < frames->end(); ++frame) {
732 (frame - 1)->prepare(frame);
733 }
734 }
735 };
736
737
738 struct LottieTextDoc : LottieProperty
739 {
740 Array<LottieScalarFrame<TextDocument>>* frames = nullptr;
741 TextDocument value;
742
~LottieTextDocLottieTextDoc743 ~LottieTextDoc()
744 {
745 release();
746 }
747
releaseLottieTextDoc748 void release()
749 {
750 if (exp) {
751 delete(exp);
752 exp = nullptr;
753 }
754
755 if (value.text) {
756 free(value.text);
757 value.text = nullptr;
758 }
759 if (value.name) {
760 free(value.name);
761 value.name = nullptr;
762 }
763
764 if (!frames) return;
765
766 for (auto p = frames->begin(); p < frames->end(); ++p) {
767 free((*p).value.text);
768 free((*p).value.name);
769 }
770 delete(frames);
771 frames = nullptr;
772 }
773
nearestLottieTextDoc774 uint32_t nearest(float frameNo) override
775 {
776 return _nearest(frames, frameNo);
777 }
778
frameCntLottieTextDoc779 uint32_t frameCnt() override
780 {
781 return frames ? frames->count : 1;
782 }
783
frameNoLottieTextDoc784 float frameNo(int32_t key) override
785 {
786 return _frameNo(frames, key);
787 }
788
newFrameLottieTextDoc789 LottieScalarFrame<TextDocument>& newFrame()
790 {
791 if (!frames) frames = new Array<LottieScalarFrame<TextDocument>>;
792 if (frames->count + 1 >= frames->reserved) {
793 auto old = frames->reserved;
794 frames->grow(frames->count + 2);
795 memset((void*)(frames->data + old), 0x00, sizeof(LottieScalarFrame<TextDocument>) * (frames->reserved - old));
796 }
797 ++frames->count;
798 return frames->last();
799 }
800
nextFrameLottieTextDoc801 LottieScalarFrame<TextDocument>& nextFrame()
802 {
803 return (*frames)[frames->count];
804 }
805
operatorLottieTextDoc806 TextDocument& operator()(float frameNo)
807 {
808 if (!frames) return value;
809 if (frames->count == 1 || frameNo <= frames->first().no) return frames->first().value;
810 if (frameNo >= frames->last().no) return frames->last().value;
811
812 auto frame = frames->data + _bsearch(frames, frameNo);
813 return frame->value;
814 }
815
816 LottieTextDoc& operator=(const LottieTextDoc& other)
817 {
818 //shallow copy, used for slot overriding
819 if (other.frames) {
820 frames = other.frames;
821 const_cast<LottieTextDoc&>(other).frames = nullptr;
822 } else {
823 value = other.value;
824 const_cast<LottieTextDoc&>(other).value.text = nullptr;
825 const_cast<LottieTextDoc&>(other).value.name = nullptr;
826 }
827 return *this;
828 }
829
prepareLottieTextDoc830 void prepare() {}
831 };
832
833
834 using LottiePoint = LottieGenericProperty<Point>;
835 using LottieFloat = LottieGenericProperty<float>;
836 using LottieOpacity = LottieGenericProperty<uint8_t>;
837 using LottieColor = LottieGenericProperty<RGB24>;
838 using LottieSlider = LottieFloat;
839 using LottieCheckbox = LottieGenericProperty<int8_t>;
840
841 #endif //_TVG_LOTTIE_PROPERTY_H_
842
843 #endif /* LV_USE_THORVG_INTERNAL */
844
845