2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the LGPL License, Version 2.1 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * https://www.gnu.org/licenses/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
22 #include<unordered_map>
25 #include"vinterpolator.h"
33 class LOTCompositionData;
35 class LOTTransformData;
36 class LOTShapeGroupData;
41 class LOTRepeaterData;
47 class LottieShapeData;
48 class LOTPolystarData;
60 enum class LayerType {
72 LottieColor() = default;
73 LottieColor(float red, float green , float blue):r(red), g(green),b(blue){}
74 VColor toColor(float a=1){ return VColor((255 * r), (255 * g), (255 * b), (255 * a));}
75 friend inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2);
76 friend inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2);
83 inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
85 return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
87 inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
89 return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
92 inline const LottieColor operator*(const LottieColor &c, float m)
93 { return LottieColor(c.r*m, c.g*m, c.b*m); }
95 inline const LottieColor operator*(float m, const LottieColor &c)
96 { return LottieColor(c.r*m, c.g*m, c.b*m); }
101 void reserve(int size) {
102 mPoints.reserve(mPoints.size() + size);
104 void toPath(VPath& path) {
107 if (mPoints.empty()) return;
109 int size = mPoints.size();
110 const VPointF *points = mPoints.data();
111 /* reserve exact memory requirement at once
112 * ptSize = size + 1(size + close)
113 * elmSize = size/3 cubic + 1 move + 1 close
115 path.reserve(size + 1 , size/3 + 2);
116 path.moveTo(points[0]);
117 for (int i = 1 ; i < size; i+=3) {
118 path.cubicTo(points[i], points[i+1], points[i+2]);
124 std::vector<VPointF> mPoints;
125 bool mClosed = false; /* "c" */
131 inline T lerp(const T& start, const T& end, float t)
133 return start + t * (end - start);
136 inline LottieShapeData lerp(const LottieShapeData& start, const LottieShapeData& end, float t)
138 if (start.mPoints.size() != start.mPoints.size())
139 return LottieShapeData();
141 LottieShapeData result;
142 result.reserve(start.mPoints.size());
143 for (unsigned int i = 0 ; i < start.mPoints.size(); i++) {
144 result.mPoints.push_back(start.mPoints[i] + t * (end.mPoints[i] - start.mPoints[i]));
149 template <typename T>
150 struct LOTKeyFrameValue
154 T value(float t) const {
155 return lerp(mStartValue, mEndValue, t);
157 float angle(float ) const { return 0;}
161 struct LOTKeyFrameValue<VPointF>
167 bool mPathKeyFrame = false;
169 VPointF value(float t) const {
172 * position along the path calcualated
173 * using bezier at progress length (t * bezlen)
175 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
176 mEndValue + mInTangent, mEndValue);
177 return b.pointAt(b.tAtLength(t * b.length()));
180 return lerp(mStartValue, mEndValue, t);
184 float angle(float t) const {
186 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
187 mEndValue + mInTangent, mEndValue);
188 return b.angleAt(b.tAtLength(t * b.length()));
199 float progress(int frameNo) const {
200 return mInterpolator->value((frameNo - mStartFrame) / (mEndFrame - mStartFrame));
202 T value(int frameNo) const {
203 return mValue.value(progress(frameNo));
205 float angle(int frameNo) const {
206 return mValue.angle(progress(frameNo));
210 float mStartFrame{0};
212 std::shared_ptr<VInterpolator> mInterpolator;
213 LOTKeyFrameValue<T> mValue;
220 T value(int frameNo) const {
221 if (mKeyFrames.front().mStartFrame >= frameNo)
222 return mKeyFrames.front().mValue.mStartValue;
223 if(mKeyFrames.back().mEndFrame <= frameNo)
224 return mKeyFrames.back().mValue.mEndValue;
226 for(const auto &keyFrame : mKeyFrames) {
227 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
228 return keyFrame.value(frameNo);
233 float angle(int frameNo) const {
234 if ((mKeyFrames.front().mStartFrame >= frameNo) ||
235 (mKeyFrames.back().mEndFrame <= frameNo) )
238 for(const auto &keyFrame : mKeyFrames) {
239 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
240 return keyFrame.angle(frameNo);
245 bool changed(int prevFrame, int curFrame) {
246 int first = mKeyFrames.front().mStartFrame;
247 int last = mKeyFrames.back().mEndFrame;
249 if ((first > prevFrame && first > curFrame) ||
250 (last < prevFrame && last < curFrame)) {
258 std::vector<LOTKeyFrame<T>> mKeyFrames;
265 LOTAnimatable():mValue(),mAnimInfo(nullptr){}
266 LOTAnimatable(const T &value): mValue(value){}
267 bool isStatic() const {if (mAnimInfo) return false; else return true;}
268 T value(int frameNo) const {
269 return isStatic() ? mValue : mAnimInfo->value(frameNo);
271 float angle(int frameNo) const {
272 return isStatic() ? 0 : mAnimInfo->angle(frameNo);
274 bool changed(int prevFrame, int curFrame) {
275 return isStatic() ? false : mAnimInfo->changed(prevFrame, curFrame);
279 int mPropertyIndex{0}; /* "ix" */
280 std::unique_ptr<LOTAnimInfo<T>> mAnimInfo;
283 enum class LottieBlendMode
291 class LOTDataVisitor;
311 LOTData(LOTData::Type type): mType(type){}
312 inline LOTData::Type type() const {return mType;}
313 bool isStatic() const{return mStatic;}
314 void setStatic(bool value) {mStatic = value;}
315 bool hidden() const {return mHidden;}
322 class LOTGroupData: public LOTData
325 LOTGroupData(LOTData::Type type):LOTData(type){}
327 std::vector<std::shared_ptr<LOTData>> mChildren;
328 std::shared_ptr<LOTTransformData> mTransform;
331 class LOTShapeGroupData : public LOTGroupData
334 LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
340 int mAssetType; //lottie asset type (precomp/char/image)
341 std::string mRefId; // ref id
342 std::vector<std::shared_ptr<LOTData>> mLayers;
345 class LOTLayerData : public LOTGroupData
348 LOTLayerData():LOTGroupData(LOTData::Type::Layer){}
349 bool hasPathOperator() const noexcept {return mHasPathOperator;}
350 bool hasGradient() const noexcept {return mHasGradient;}
351 bool hasMask() const noexcept {return mHasMask;}
352 bool hasRepeater() const noexcept {return mHasRepeater;}
353 bool root() const noexcept {return mRoot;}
354 int id() const noexcept{ return mId;}
355 int parentId() const noexcept{ return mParentId;}
356 int inFrame() const noexcept{return mInFrame;}
357 int outFrame() const noexcept{return mOutFrame;}
358 int startFrame() const noexcept{return mStartFrame;}
359 int solidWidth() const noexcept{return mSolidLayer.mWidth;}
360 int solidHeight() const noexcept{return mSolidLayer.mHeight;}
361 LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
362 bool autoOrient() const noexcept{return mAutoOrient;}
363 int timeRemap(int frameNo) const;
371 MatteType mMatteType{MatteType::None};
373 LayerType mLayerType{LayerType::Null}; //lottie layer type (solid/shape/precomp)
374 int mParentId{-1}; // Lottie the id of the parent in the composition
375 int mId{-1}; // Lottie the group id used for parenting.
379 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
380 float mTimeStreatch{1.0f};
381 std::string mPreCompRefId;
382 LOTAnimatable<float> mTimeRemap; /* "tm" */
383 SolidLayer mSolidLayer;
384 bool mHasPathOperator{false};
385 bool mHasMask{false};
386 bool mHasRepeater{false};
387 bool mHasGradient{false};
389 bool mAutoOrient{false};
390 std::vector<std::shared_ptr<LOTMaskData>> mMasks;
391 LOTCompositionData *mCompRef{nullptr};
394 class LOTCompositionData : public LOTData
397 LOTCompositionData():LOTData(LOTData::Type::Composition){}
398 double duration() const {
399 return isStatic() ? startFrame() :
400 frameDuration() / frameRate(); // in second
402 size_t frameAtPos(double pos) const {
403 if (pos < 0) pos = 0;
404 if (pos > 1) pos = 1;
405 return isStatic() ? 0 : pos * frameDuration();
407 long frameAtTime(double timeInSec) const {
408 return isStatic() ? startFrame() : frameAtPos(timeInSec / duration());
410 long frameDuration() const {return mEndFrame - mStartFrame -1;}
411 float frameRate() const {return mFrameRate;}
412 long startFrame() const {return mStartFrame;}
413 long endFrame() const {return mEndFrame;}
414 VSize size() const {return mSize;}
415 void processRepeaterObjects();
417 std::string mVersion;
421 float mFrameRate{60};
422 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
423 std::shared_ptr<LOTLayerData> mRootLayer;
424 std::unordered_map<std::string,
425 std::shared_ptr<VInterpolator>> mInterpolatorCache;
426 std::unordered_map<std::string,
427 std::shared_ptr<LOTAsset>> mAssets;
432 * TimeRemap has the value in time domain(in sec)
433 * To get the proper mapping first we get the mapped time at the current frame Number
434 * then we need to convert mapped time to frame number using the composition time line
435 * Ex: at frame 10 the mappend time is 0.5(500 ms) which will be convert to frame number
436 * 30 if the frame rate is 60. or will result to frame number 15 if the frame rate is 30.
438 inline int LOTLayerData::timeRemap(int frameNo) const
440 frameNo = mTimeRemap.isStatic() ? frameNo :
441 mCompRef->frameAtTime(mTimeRemap.value(frameNo));
442 /* Apply time streatch if it has any.
443 * Time streatch is just a factor by which the animation will speedup or slow
444 * down with respect to the overal animation.
445 * Time streach factor is already applied to the layers inFrame and outFrame.
446 * @TODO need to find out if timestreatch also affects the in and out frame of the
447 * child layers or not. */
448 return frameNo / mTimeStreatch;
453 LOTAnimatable<float> mRx{0};
454 LOTAnimatable<float> mRy{0};
455 LOTAnimatable<float> mRz{0};
458 class LOTTransformData : public LOTData
461 LOTTransformData():LOTData(LOTData::Type::Transform),mScale({100, 100}){}
462 VMatrix matrix(int frameNo, bool autoOrient = false) const;
463 VMatrix matrixForRepeater(int frameNo, float multiplier) const;
464 float opacity(int frameNo) const { return mOpacity.value(frameNo)/100;}
465 float startOpacity(int frameNo) const { return mStartOpacity.value(frameNo)/100;}
466 float endOpacity(int frameNo) const { return mEndOpacity.value(frameNo)/100;}
468 bool staticMatrix() const {return mStaticMatrix;}
469 bool ddd() const {return m3D ? true : false;}
471 VMatrix computeMatrix(int frameNo, bool autoOrient = false) const;
473 std::unique_ptr<LOT3DData> m3D;
474 LOTAnimatable<float> mRotation{0}; /* "r" */
475 LOTAnimatable<VPointF> mScale; /* "s" */
476 LOTAnimatable<VPointF> mPosition; /* "p" */
477 LOTAnimatable<float> mX{0};
478 LOTAnimatable<float> mY{0};
479 LOTAnimatable<VPointF> mAnchor; /* "a" */
480 LOTAnimatable<float> mOpacity{100}; /* "o" */
481 LOTAnimatable<float> mSkew{0}; /* "sk" */
482 LOTAnimatable<float> mSkewAxis{0}; /* "sa" */
483 LOTAnimatable<float> mStartOpacity{100}; /* "so" */
484 LOTAnimatable<float> mEndOpacity{100}; /* "eo" */
485 bool mStaticMatrix{true};
486 bool mSeparate{false};
487 VMatrix mCachedMatrix;
490 class LOTFillData : public LOTData
493 LOTFillData():LOTData(LOTData::Type::Fill){}
494 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
495 FillRule fillRule() const {return mFillRule;}
497 FillRule mFillRule{FillRule::Winding}; /* "r" */
498 LOTAnimatable<LottieColor> mColor; /* "c" */
499 LOTAnimatable<int> mOpacity{100}; /* "o" */
500 bool mEnabled{true}; /* "fillEnabled" */
503 struct LOTDashProperty
505 LOTAnimatable<float> mDashArray[5]; /* "d" "g" "o"*/
510 class LOTStrokeData : public LOTData
513 LOTStrokeData():LOTData(LOTData::Type::Stroke){}
514 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
515 float width(int frameNo) const {return mWidth.value(frameNo);}
516 CapStyle capStyle() const {return mCapStyle;}
517 JoinStyle joinStyle() const {return mJoinStyle;}
518 float meterLimit() const{return mMeterLimit;}
519 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
520 int getDashInfo(int frameNo, float *array) const;
522 LOTAnimatable<LottieColor> mColor; /* "c" */
523 LOTAnimatable<int> mOpacity{100}; /* "o" */
524 LOTAnimatable<float> mWidth{0}; /* "w" */
525 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
526 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
527 float mMeterLimit{0}; /* "ml" */
528 LOTDashProperty mDash;
529 bool mEnabled{true}; /* "fillEnabled" */
535 friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
536 friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
537 friend inline LottieGradient operator*(float m, const LottieGradient &g);
539 std::vector<float> mGradient;
542 inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
544 if (g1.mGradient.size() != g2.mGradient.size())
548 newG.mGradient = g1.mGradient;
550 auto g2It = g2.mGradient.begin();
551 for(auto &i : newG.mGradient) {
559 inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
561 if (g1.mGradient.size() != g2.mGradient.size())
564 newG.mGradient = g1.mGradient;
566 auto g2It = g2.mGradient.begin();
567 for(auto &i : newG.mGradient) {
575 inline LottieGradient operator*(float m, const LottieGradient &g)
578 newG.mGradient = g.mGradient;
580 for(auto &i : newG.mGradient) {
588 class LOTGradient : public LOTData
591 LOTGradient(LOTData::Type type):LOTData(type){}
592 inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
593 void update(std::unique_ptr<VGradient> &grad, int frameNo);
596 void populate(VGradientStops &stops, int frameNo);
598 int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/
599 LOTAnimatable<VPointF> mStartPoint; /* "s" */
600 LOTAnimatable<VPointF> mEndPoint; /* "e" */
601 LOTAnimatable<float> mHighlightLength{0}; /* "h" */
602 LOTAnimatable<float> mHighlightAngle{0}; /* "a" */
603 LOTAnimatable<int> mOpacity{0}; /* "o" */
604 LOTAnimatable<LottieGradient> mGradient; /* "g" */
605 int mColorPoints{-1};
606 bool mEnabled{true}; /* "fillEnabled" */
609 class LOTGFillData : public LOTGradient
612 LOTGFillData():LOTGradient(LOTData::Type::GFill){}
613 FillRule fillRule() const {return mFillRule;}
615 FillRule mFillRule{FillRule::Winding}; /* "r" */
618 class LOTGStrokeData : public LOTGradient
621 LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
622 float width(int frameNo) const {return mWidth.value(frameNo);}
623 CapStyle capStyle() const {return mCapStyle;}
624 JoinStyle joinStyle() const {return mJoinStyle;}
625 float meterLimit() const{return mMeterLimit;}
626 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
627 int getDashInfo(int frameNo, float *array) const;
629 LOTAnimatable<float> mWidth; /* "w" */
630 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
631 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
632 float mMeterLimit{0}; /* "ml" */
633 LOTDashProperty mDash;
636 class LOTPath : public LOTData
639 LOTPath(LOTData::Type type):LOTData(type){}
640 VPath::Direction direction() { if (mDirection == 3) return VPath::Direction::CCW;
641 else return VPath::Direction::CW;}
646 class LOTShapeData : public LOTPath
649 LOTShapeData():LOTPath(LOTData::Type::Shape){}
652 LOTAnimatable<LottieShapeData> mShape;
665 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
666 bool isStatic() const {return mIsStatic;}
668 LOTAnimatable<LottieShapeData> mShape;
669 LOTAnimatable<float> mOpacity;
671 bool mIsStatic{true};
672 LOTMaskData::Mode mMode;
675 class LOTRectData : public LOTPath
678 LOTRectData():LOTPath(LOTData::Type::Rect){}
680 LOTAnimatable<VPointF> mPos;
681 LOTAnimatable<VPointF> mSize;
682 LOTAnimatable<float> mRound{0};
685 class LOTEllipseData : public LOTPath
688 LOTEllipseData():LOTPath(LOTData::Type::Ellipse){}
690 LOTAnimatable<VPointF> mPos;
691 LOTAnimatable<VPointF> mSize;
694 class LOTPolystarData : public LOTPath
697 enum class PolyType {
701 LOTPolystarData():LOTPath(LOTData::Type::Polystar){}
703 LOTPolystarData::PolyType mType{PolyType::Polygon};
704 LOTAnimatable<VPointF> mPos;
705 LOTAnimatable<float> mPointCount{0};
706 LOTAnimatable<float> mInnerRadius{0};
707 LOTAnimatable<float> mOuterRadius{0};
708 LOTAnimatable<float> mInnerRoundness{0};
709 LOTAnimatable<float> mOuterRoundness{0};
710 LOTAnimatable<float> mRotation{0};
713 class LOTTrimData : public LOTData
720 enum class TrimType {
724 LOTTrimData():LOTData(LOTData::Type::Trim){}
725 Segment segment(int frameNo) const {
726 float start = mStart.value(frameNo)/100.0f;
727 float end = mEnd.value(frameNo)/100.0f;
728 float offset = fmod(mOffset.value(frameNo), 360.0f)/ 360.0f;
731 // normalize start, end value to 0 - 1
732 if (fabs(start) > 1) start = fmod(start, 1);
733 if (fabs(end) > 1) end = fmod(end, 1);
735 if (start >= 0 && end >= 0) {
736 s.start = std::min(start, end);
737 s.end = std::max(start, end);
738 } else if (start < 0 && end < 0) {
741 s.start = std::min(start, end);
742 s.end = std::max(start, end);
744 // one of them is -ve so it a loop
745 if (start < 0) start +=1;
746 if (end < 0) end +=1;
747 s.start = std::max(start, end);
748 s.end = std::min(start, end);
753 LOTTrimData::TrimType type() const {return mTrimType;}
755 LOTAnimatable<float> mStart{0};
756 LOTAnimatable<float> mEnd{0};
757 LOTAnimatable<float> mOffset{0};
758 LOTTrimData::TrimType mTrimType{TrimType::Simultaneously};
761 class LOTRepeaterData : public LOTGroupData
764 LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater){}
765 bool hasMtrixChange(int /*frameNo*/) const {
766 return !(mTransform->isStatic() && mOffset.isStatic());
768 float copies(int frameNo) const {return mCopies.value(frameNo);}
769 float offset(int frameNo) const {return mOffset.value(frameNo);}
771 LOTAnimatable<float> mCopies{0};
772 LOTAnimatable<float> mOffset{0};
778 bool isStatic() const {return mRoot->isStatic();}
779 double duration() const {return mRoot->duration();}
780 size_t frameDuration() const {return mRoot->frameDuration();}
781 size_t frameRate() const {return mRoot->frameRate();}
782 size_t startFrame() const {return mRoot->startFrame();}
783 size_t endFrame() const {return mRoot->endFrame();}
784 size_t frameAtPos(double pos) const {return mRoot->frameAtPos(pos);}
786 std::shared_ptr<LOTCompositionData> mRoot;