2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 #include<unordered_map>
27 #include"vinterpolator.h"
35 class LOTCompositionData;
37 class LOTTransformData;
38 class LOTShapeGroupData;
43 class LOTRepeaterData;
49 class LottieShapeData;
50 class LOTPolystarData;
62 enum class LayerType {
74 LottieColor() = default;
75 LottieColor(float red, float green , float blue):r(red), g(green),b(blue){}
76 VColor toColor(float a=1){ return VColor((255 * r), (255 * g), (255 * b), (255 * a));}
77 friend inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2);
78 friend inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2);
85 inline LottieColor operator-(const LottieColor &c1, const LottieColor &c2)
87 return LottieColor(c1.r - c2.r, c1.g - c2.g, c1.b - c2.b);
89 inline LottieColor operator+(const LottieColor &c1, const LottieColor &c2)
91 return LottieColor(c1.r + c2.r, c1.g + c2.g, c1.b + c2.b);
94 inline const LottieColor operator*(const LottieColor &c, float m)
95 { return LottieColor(c.r*m, c.g*m, c.b*m); }
97 inline const LottieColor operator*(float m, const LottieColor &c)
98 { return LottieColor(c.r*m, c.g*m, c.b*m); }
100 class LottieShapeData
103 void reserve(int size) {
104 mPoints.reserve(mPoints.size() + size);
106 void toPath(VPath& path) {
109 if (mPoints.empty()) return;
111 int size = mPoints.size();
112 const VPointF *points = mPoints.data();
113 /* reserve exact memory requirement at once
114 * ptSize = size + 1(size + close)
115 * elmSize = size/3 cubic + 1 move + 1 close
117 path.reserve(size + 1 , size/3 + 2);
118 path.moveTo(points[0]);
119 for (int i = 1 ; i < size; i+=3) {
120 path.cubicTo(points[i], points[i+1], points[i+2]);
126 std::vector<VPointF> mPoints;
127 bool mClosed = false; /* "c" */
133 inline T lerp(const T& start, const T& end, float t)
135 return start + t * (end - start);
138 inline LottieShapeData lerp(const LottieShapeData& start, const LottieShapeData& end, float t)
140 if (start.mPoints.size() != start.mPoints.size())
141 return LottieShapeData();
143 LottieShapeData result;
144 result.reserve(start.mPoints.size());
145 for (unsigned int i = 0 ; i < start.mPoints.size(); i++) {
146 result.mPoints.push_back(start.mPoints[i] + t * (end.mPoints[i] - start.mPoints[i]));
151 template <typename T>
152 struct LOTKeyFrameValue
156 T value(float t) const {
157 return lerp(mStartValue, mEndValue, t);
159 float angle(float ) const { return 0;}
163 struct LOTKeyFrameValue<VPointF>
169 bool mPathKeyFrame = false;
171 VPointF value(float t) const {
174 * position along the path calcualated
175 * using bezier at progress length (t * bezlen)
177 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
178 mEndValue + mInTangent, mEndValue);
179 return b.pointAt(b.tAtLength(t * b.length()));
182 return lerp(mStartValue, mEndValue, t);
186 float angle(float t) const {
188 VBezier b = VBezier::fromPoints(mStartValue, mStartValue + mOutTangent,
189 mEndValue + mInTangent, mEndValue);
190 return b.angleAt(b.tAtLength(t * b.length()));
201 float progress(int frameNo) const {
202 return mInterpolator->value((frameNo - mStartFrame) / (mEndFrame - mStartFrame));
204 T value(int frameNo) const {
205 return mValue.value(progress(frameNo));
207 float angle(int frameNo) const {
208 return mValue.angle(progress(frameNo));
212 float mStartFrame{0};
214 std::shared_ptr<VInterpolator> mInterpolator;
215 LOTKeyFrameValue<T> mValue;
222 T value(int frameNo) const {
223 if (mKeyFrames.front().mStartFrame >= frameNo)
224 return mKeyFrames.front().mValue.mStartValue;
225 if(mKeyFrames.back().mEndFrame <= frameNo)
226 return mKeyFrames.back().mValue.mEndValue;
228 for(const auto &keyFrame : mKeyFrames) {
229 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
230 return keyFrame.value(frameNo);
235 float angle(int frameNo) const {
236 if ((mKeyFrames.front().mStartFrame >= frameNo) ||
237 (mKeyFrames.back().mEndFrame <= frameNo) )
240 for(const auto &keyFrame : mKeyFrames) {
241 if (frameNo >= keyFrame.mStartFrame && frameNo < keyFrame.mEndFrame)
242 return keyFrame.angle(frameNo);
247 bool changed(int prevFrame, int curFrame) {
248 int first = mKeyFrames.front().mStartFrame;
249 int last = mKeyFrames.back().mEndFrame;
251 if ((first > prevFrame && first > curFrame) ||
252 (last < prevFrame && last < curFrame)) {
260 std::vector<LOTKeyFrame<T>> mKeyFrames;
267 LOTAnimatable():mValue(),mAnimInfo(nullptr){}
268 LOTAnimatable(const T &value): mValue(value){}
269 bool isStatic() const {if (mAnimInfo) return false; else return true;}
270 T value(int frameNo) const {
271 return isStatic() ? mValue : mAnimInfo->value(frameNo);
273 float angle(int frameNo) const {
274 return isStatic() ? 0 : mAnimInfo->angle(frameNo);
276 bool changed(int prevFrame, int curFrame) {
277 return isStatic() ? false : mAnimInfo->changed(prevFrame, curFrame);
281 int mPropertyIndex{0}; /* "ix" */
282 std::unique_ptr<LOTAnimInfo<T>> mAnimInfo;
285 enum class LottieBlendMode
293 class LOTDataVisitor;
313 LOTData(LOTData::Type type): mType(type){}
314 inline LOTData::Type type() const {return mType;}
315 bool isStatic() const{return mStatic;}
316 void setStatic(bool value) {mStatic = value;}
317 bool hidden() const {return mHidden;}
324 class LOTGroupData: public LOTData
327 LOTGroupData(LOTData::Type type):LOTData(type){}
329 std::vector<std::shared_ptr<LOTData>> mChildren;
330 std::shared_ptr<LOTTransformData> mTransform;
333 class LOTShapeGroupData : public LOTGroupData
336 LOTShapeGroupData():LOTGroupData(LOTData::Type::ShapeGroup){}
348 Type mAssetType{Type::Precomp};
349 std::string mRefId; // ref id
350 std::vector<std::shared_ptr<LOTData>> mLayers;
354 std::string mImagePath;
357 class LOTLayerData : public LOTGroupData
360 LOTLayerData():LOTGroupData(LOTData::Type::Layer){}
361 bool hasPathOperator() const noexcept {return mHasPathOperator;}
362 bool hasGradient() const noexcept {return mHasGradient;}
363 bool hasMask() const noexcept {return mHasMask;}
364 bool hasRepeater() const noexcept {return mHasRepeater;}
365 bool root() const noexcept {return mRoot;}
366 int id() const noexcept{ return mId;}
367 int parentId() const noexcept{ return mParentId;}
368 int inFrame() const noexcept{return mInFrame;}
369 int outFrame() const noexcept{return mOutFrame;}
370 int startFrame() const noexcept{return mStartFrame;}
371 int solidWidth() const noexcept{return mSolidLayer.mWidth;}
372 int solidHeight() const noexcept{return mSolidLayer.mHeight;}
373 LottieColor solidColor() const noexcept{return mSolidLayer.mColor;}
374 bool autoOrient() const noexcept{return mAutoOrient;}
375 int timeRemap(int frameNo) const;
376 VSize layerSize() const {return mLayerSize;}
384 MatteType mMatteType{MatteType::None};
386 LayerType mLayerType{LayerType::Null}; //lottie layer type (solid/shape/precomp)
387 int mParentId{-1}; // Lottie the id of the parent in the composition
388 int mId{-1}; // Lottie the group id used for parenting.
393 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
394 float mTimeStreatch{1.0f};
395 std::string mPreCompRefId;
396 LOTAnimatable<float> mTimeRemap; /* "tm" */
397 SolidLayer mSolidLayer;
398 bool mHasPathOperator{false};
399 bool mHasMask{false};
400 bool mHasRepeater{false};
401 bool mHasGradient{false};
403 bool mAutoOrient{false};
404 std::vector<std::shared_ptr<LOTMaskData>> mMasks;
405 LOTCompositionData *mCompRef{nullptr};
406 std::shared_ptr<LOTAsset> mAsset;
409 class LOTCompositionData : public LOTData
412 LOTCompositionData():LOTData(LOTData::Type::Composition){}
413 double duration() const {
414 return isStatic() ? startFrame() :
415 frameDuration() / frameRate(); // in second
417 size_t frameAtPos(double pos) const {
418 if (pos < 0) pos = 0;
419 if (pos > 1) pos = 1;
420 return isStatic() ? 0 : pos * frameDuration();
422 long frameAtTime(double timeInSec) const {
423 return isStatic() ? startFrame() : frameAtPos(timeInSec / duration());
425 long frameDuration() const {return mEndFrame - mStartFrame -1;}
426 float frameRate() const {return mFrameRate;}
427 long startFrame() const {return mStartFrame;}
428 long endFrame() const {return mEndFrame;}
429 VSize size() const {return mSize;}
430 void processRepeaterObjects();
432 std::string mVersion;
436 float mFrameRate{60};
437 LottieBlendMode mBlendMode{LottieBlendMode::Normal};
438 std::shared_ptr<LOTLayerData> mRootLayer;
439 std::unordered_map<std::string,
440 std::shared_ptr<VInterpolator>> mInterpolatorCache;
441 std::unordered_map<std::string,
442 std::shared_ptr<LOTAsset>> mAssets;
447 * TimeRemap has the value in time domain(in sec)
448 * To get the proper mapping first we get the mapped time at the current frame Number
449 * then we need to convert mapped time to frame number using the composition time line
450 * Ex: at frame 10 the mappend time is 0.5(500 ms) which will be convert to frame number
451 * 30 if the frame rate is 60. or will result to frame number 15 if the frame rate is 30.
453 inline int LOTLayerData::timeRemap(int frameNo) const
456 * only consider startFrame() when there is no timeRemap.
457 * when a layer has timeremap bodymovin updates the startFrame()
458 * of all child layer so we don't have to take care of it.
460 frameNo = mTimeRemap.isStatic() ? frameNo - startFrame():
461 mCompRef->frameAtTime(mTimeRemap.value(frameNo));
462 /* Apply time streatch if it has any.
463 * Time streatch is just a factor by which the animation will speedup or slow
464 * down with respect to the overal animation.
465 * Time streach factor is already applied to the layers inFrame and outFrame.
466 * @TODO need to find out if timestreatch also affects the in and out frame of the
467 * child layers or not. */
468 return frameNo / mTimeStreatch;
473 LOTAnimatable<float> mRx{0};
474 LOTAnimatable<float> mRy{0};
475 LOTAnimatable<float> mRz{0};
478 class LOTTransformData : public LOTData
481 LOTTransformData():LOTData(LOTData::Type::Transform),mScale({100, 100}){}
482 VMatrix matrix(int frameNo, bool autoOrient = false) const;
483 VMatrix matrixForRepeater(int frameNo, float multiplier) const;
484 float opacity(int frameNo) const { return mOpacity.value(frameNo)/100;}
485 float startOpacity(int frameNo) const { return mStartOpacity.value(frameNo)/100;}
486 float endOpacity(int frameNo) const { return mEndOpacity.value(frameNo)/100;}
488 bool staticMatrix() const {return mStaticMatrix;}
489 bool ddd() const {return m3D ? true : false;}
491 VMatrix computeMatrix(int frameNo, bool autoOrient = false) const;
493 std::unique_ptr<LOT3DData> m3D;
494 LOTAnimatable<float> mRotation{0}; /* "r" */
495 LOTAnimatable<VPointF> mScale; /* "s" */
496 LOTAnimatable<VPointF> mPosition; /* "p" */
497 LOTAnimatable<float> mX{0};
498 LOTAnimatable<float> mY{0};
499 LOTAnimatable<VPointF> mAnchor; /* "a" */
500 LOTAnimatable<float> mOpacity{100}; /* "o" */
501 LOTAnimatable<float> mSkew{0}; /* "sk" */
502 LOTAnimatable<float> mSkewAxis{0}; /* "sa" */
503 LOTAnimatable<float> mStartOpacity{100}; /* "so" */
504 LOTAnimatable<float> mEndOpacity{100}; /* "eo" */
505 bool mStaticMatrix{true};
506 bool mSeparate{false};
507 VMatrix mCachedMatrix;
510 class LOTFillData : public LOTData
513 LOTFillData():LOTData(LOTData::Type::Fill){}
514 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
515 FillRule fillRule() const {return mFillRule;}
517 FillRule mFillRule{FillRule::Winding}; /* "r" */
518 LOTAnimatable<LottieColor> mColor; /* "c" */
519 LOTAnimatable<int> mOpacity{100}; /* "o" */
520 bool mEnabled{true}; /* "fillEnabled" */
523 struct LOTDashProperty
525 LOTAnimatable<float> mDashArray[5]; /* "d" "g" "o"*/
530 class LOTStrokeData : public LOTData
533 LOTStrokeData():LOTData(LOTData::Type::Stroke){}
534 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
535 float width(int frameNo) const {return mWidth.value(frameNo);}
536 CapStyle capStyle() const {return mCapStyle;}
537 JoinStyle joinStyle() const {return mJoinStyle;}
538 float meterLimit() const{return mMeterLimit;}
539 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
540 int getDashInfo(int frameNo, float *array) const;
542 LOTAnimatable<LottieColor> mColor; /* "c" */
543 LOTAnimatable<int> mOpacity{100}; /* "o" */
544 LOTAnimatable<float> mWidth{0}; /* "w" */
545 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
546 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
547 float mMeterLimit{0}; /* "ml" */
548 LOTDashProperty mDash;
549 bool mEnabled{true}; /* "fillEnabled" */
555 friend inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2);
556 friend inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2);
557 friend inline LottieGradient operator*(float m, const LottieGradient &g);
559 std::vector<float> mGradient;
562 inline LottieGradient operator+(const LottieGradient &g1, const LottieGradient &g2)
564 if (g1.mGradient.size() != g2.mGradient.size())
568 newG.mGradient = g1.mGradient;
570 auto g2It = g2.mGradient.begin();
571 for(auto &i : newG.mGradient) {
579 inline LottieGradient operator-(const LottieGradient &g1, const LottieGradient &g2)
581 if (g1.mGradient.size() != g2.mGradient.size())
584 newG.mGradient = g1.mGradient;
586 auto g2It = g2.mGradient.begin();
587 for(auto &i : newG.mGradient) {
595 inline LottieGradient operator*(float m, const LottieGradient &g)
598 newG.mGradient = g.mGradient;
600 for(auto &i : newG.mGradient) {
608 class LOTGradient : public LOTData
611 LOTGradient(LOTData::Type type):LOTData(type){}
612 inline float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
613 void update(std::unique_ptr<VGradient> &grad, int frameNo);
616 void populate(VGradientStops &stops, int frameNo);
618 int mGradientType{1}; /* "t" Linear=1 , Radial = 2*/
619 LOTAnimatable<VPointF> mStartPoint; /* "s" */
620 LOTAnimatable<VPointF> mEndPoint; /* "e" */
621 LOTAnimatable<float> mHighlightLength{0}; /* "h" */
622 LOTAnimatable<float> mHighlightAngle{0}; /* "a" */
623 LOTAnimatable<int> mOpacity{0}; /* "o" */
624 LOTAnimatable<LottieGradient> mGradient; /* "g" */
625 int mColorPoints{-1};
626 bool mEnabled{true}; /* "fillEnabled" */
629 class LOTGFillData : public LOTGradient
632 LOTGFillData():LOTGradient(LOTData::Type::GFill){}
633 FillRule fillRule() const {return mFillRule;}
635 FillRule mFillRule{FillRule::Winding}; /* "r" */
638 class LOTGStrokeData : public LOTGradient
641 LOTGStrokeData():LOTGradient(LOTData::Type::GStroke){}
642 float width(int frameNo) const {return mWidth.value(frameNo);}
643 CapStyle capStyle() const {return mCapStyle;}
644 JoinStyle joinStyle() const {return mJoinStyle;}
645 float meterLimit() const{return mMeterLimit;}
646 bool hasDashInfo() const { return !(mDash.mDashCount == 0);}
647 int getDashInfo(int frameNo, float *array) const;
649 LOTAnimatable<float> mWidth; /* "w" */
650 CapStyle mCapStyle{CapStyle::Flat}; /* "lc" */
651 JoinStyle mJoinStyle{JoinStyle::Miter}; /* "lj" */
652 float mMeterLimit{0}; /* "ml" */
653 LOTDashProperty mDash;
656 class LOTPath : public LOTData
659 LOTPath(LOTData::Type type):LOTData(type){}
660 VPath::Direction direction() { if (mDirection == 3) return VPath::Direction::CCW;
661 else return VPath::Direction::CW;}
666 class LOTShapeData : public LOTPath
669 LOTShapeData():LOTPath(LOTData::Type::Shape){}
672 LOTAnimatable<LottieShapeData> mShape;
685 float opacity(int frameNo) const {return mOpacity.value(frameNo)/100.0;}
686 bool isStatic() const {return mIsStatic;}
688 LOTAnimatable<LottieShapeData> mShape;
689 LOTAnimatable<float> mOpacity;
691 bool mIsStatic{true};
692 LOTMaskData::Mode mMode;
695 class LOTRectData : public LOTPath
698 LOTRectData():LOTPath(LOTData::Type::Rect){}
700 LOTAnimatable<VPointF> mPos;
701 LOTAnimatable<VPointF> mSize;
702 LOTAnimatable<float> mRound{0};
705 class LOTEllipseData : public LOTPath
708 LOTEllipseData():LOTPath(LOTData::Type::Ellipse){}
710 LOTAnimatable<VPointF> mPos;
711 LOTAnimatable<VPointF> mSize;
714 class LOTPolystarData : public LOTPath
717 enum class PolyType {
721 LOTPolystarData():LOTPath(LOTData::Type::Polystar){}
723 LOTPolystarData::PolyType mType{PolyType::Polygon};
724 LOTAnimatable<VPointF> mPos;
725 LOTAnimatable<float> mPointCount{0};
726 LOTAnimatable<float> mInnerRadius{0};
727 LOTAnimatable<float> mOuterRadius{0};
728 LOTAnimatable<float> mInnerRoundness{0};
729 LOTAnimatable<float> mOuterRoundness{0};
730 LOTAnimatable<float> mRotation{0};
733 class LOTTrimData : public LOTData
740 enum class TrimType {
744 LOTTrimData():LOTData(LOTData::Type::Trim){}
745 Segment segment(int frameNo) const {
746 float start = mStart.value(frameNo)/100.0f;
747 float end = mEnd.value(frameNo)/100.0f;
748 float offset = fmod(mOffset.value(frameNo), 360.0f)/ 360.0f;
751 // normalize start, end value to 0 - 1
752 if (fabs(start) > 1) start = fmod(start, 1);
753 if (fabs(end) > 1) end = fmod(end, 1);
755 if (start >= 0 && end >= 0) {
756 s.start = std::min(start, end);
757 s.end = std::max(start, end);
758 } else if (start < 0 && end < 0) {
761 s.start = std::min(start, end);
762 s.end = std::max(start, end);
764 // one of them is -ve so it a loop
765 if (start < 0) start +=1;
766 if (end < 0) end +=1;
767 s.start = std::max(start, end);
768 s.end = std::min(start, end);
773 LOTTrimData::TrimType type() const {return mTrimType;}
775 LOTAnimatable<float> mStart{0};
776 LOTAnimatable<float> mEnd{0};
777 LOTAnimatable<float> mOffset{0};
778 LOTTrimData::TrimType mTrimType{TrimType::Simultaneously};
781 class LOTRepeaterData : public LOTGroupData
784 LOTRepeaterData():LOTGroupData(LOTData::Type::Repeater){}
785 bool hasMtrixChange(int /*frameNo*/) const {
786 return !(mTransform->isStatic() && mOffset.isStatic());
788 float copies(int frameNo) const {return mCopies.value(frameNo);}
789 float offset(int frameNo) const {return mOffset.value(frameNo);}
791 LOTAnimatable<float> mCopies{0};
792 LOTAnimatable<float> mOffset{0};
798 bool isStatic() const {return mRoot->isStatic();}
799 double duration() const {return mRoot->duration();}
800 size_t frameDuration() const {return mRoot->frameDuration();}
801 size_t frameRate() const {return mRoot->frameRate();}
802 size_t startFrame() const {return mRoot->startFrame();}
803 size_t endFrame() const {return mRoot->endFrame();}
804 size_t frameAtPos(double pos) const {return mRoot->frameAtPos(pos);}
806 std::shared_ptr<LOTCompositionData> mRoot;