lottie: refactor to use line api to calculate angle.
[platform/core/uifw/lottie-player.git] / src / lottie / lottiemodel.cpp
1 #include "lottiemodel.h"
2 #include "vline.h"
3 #include <cassert>
4 #include <stack>
5
6 class LottieRepeaterProcesser {
7 public:
8     void visitChildren(LOTGroupData *obj)
9     {
10         for (const auto& child : obj->mChildren) {
11             if (child->mType == LOTData::Type::Repeater) {
12                 LOTRepeaterData *repeater =
13                     static_cast<LOTRepeaterData *>(child.get());
14                 std::shared_ptr<LOTShapeGroupData> sharedShapeGroup =
15                     std::make_shared<LOTShapeGroupData>();
16                 LOTShapeGroupData *shapeGroup = sharedShapeGroup.get();
17                 repeater->mChildren.push_back(sharedShapeGroup);
18                 // copy all the child of the object till repeater and
19                 // move that in to a group and then add that group to
20                 // the repeater object.
21                 for (const auto& cpChild : obj->mChildren) {
22                     if (cpChild == child) break;
23                     shapeGroup->mChildren.push_back(cpChild);
24                 }
25             }
26         }
27     }
28
29     void visit(LOTData *obj) {
30         switch (obj->mType) {
31         case LOTData::Type::Repeater:
32         case LOTData::Type::ShapeGroup:
33         case LOTData::Type::Layer:{
34             visitChildren(static_cast<LOTGroupData *>(obj));
35             break;
36         }
37         default:
38             break;
39         }
40     }
41 };
42
43
44 void LOTCompositionData::processRepeaterObjects()
45 {
46     LottieRepeaterProcesser visitor;
47     visitor.visit(mRootLayer.get());
48 }
49
50 VMatrix LOTTransformData::matrix(int frameNo) const
51 {
52     if (mStaticMatrix)
53         return mCachedMatrix;
54     else
55         return computeMatrix(frameNo);
56 }
57
58 float LOTTransformData::opacity(int frameNo) const
59 {
60     return mOpacity.value(frameNo) / 100.f;
61 }
62
63 void LOTTransformData::cacheMatrix()
64 {
65     mCachedMatrix = computeMatrix(0);
66 }
67
68 VMatrix LOTTransformData::computeMatrix(int frameNo) const
69 {
70     VMatrix m;
71     VPointF position = mPosition.value(frameNo);
72     if (mSeparate) {
73         position.setX(mX.value(frameNo));
74         position.setY(mY.value(frameNo));
75     }
76     m.translate(position)
77         .rotate(mRotation.value(frameNo))
78         .scale(mScale.value(frameNo) / 100.f)
79         .translate(-mAnchor.value(frameNo));
80     return m;
81 }
82
83 int LOTStrokeData::getDashInfo(int frameNo, float *array) const
84 {
85     if (!mDash.mDashCount) return 0;
86     // odd case
87     if (mDash.mDashCount % 2) {
88         for (int i = 0; i < mDash.mDashCount; i++) {
89             array[i] = mDash.mDashArray[i].value(frameNo);
90         }
91         return mDash.mDashCount;
92     } else {  // even case when last gap info is not provided.
93         int i;
94         for (i = 0; i < mDash.mDashCount - 1; i++) {
95             array[i] = mDash.mDashArray[i].value(frameNo);
96         }
97         array[i] = array[i - 1];
98         array[i + 1] = mDash.mDashArray[i].value(frameNo);
99         return mDash.mDashCount + 1;
100     }
101 }
102
103 int LOTGStrokeData::getDashInfo(int frameNo, float *array) const
104 {
105     if (!mDash.mDashCount) return 0;
106     // odd case
107     if (mDash.mDashCount % 2) {
108         for (int i = 0; i < mDash.mDashCount; i++) {
109             array[i] = mDash.mDashArray[i].value(frameNo);
110         }
111         return mDash.mDashCount;
112     } else {  // even case when last gap info is not provided.
113         int i;
114         for (i = 0; i < mDash.mDashCount - 1; i++) {
115             array[i] = mDash.mDashArray[i].value(frameNo);
116         }
117         array[i] = array[i - 1];
118         array[i + 1] = mDash.mDashArray[i].value(frameNo);
119         return mDash.mDashCount + 1;
120     }
121 }
122
123 /**
124  * Both the color stops and opacity stops are in the same array.
125  * There are {@link #colorPoints} colors sequentially as:
126  * [
127  *     ...,
128  *     position,
129  *     red,
130  *     green,
131  *     blue,
132  *     ...
133  * ]
134  *
135  * The remainder of the array is the opacity stops sequentially as:
136  * [
137  *     ...,
138  *     position,
139  *     opacity,
140  *     ...
141  * ]
142  */
143 void LOTGradient::populate(VGradientStops &stops, int frameNo)
144 {
145     LottieGradient gradData = mGradient.value(frameNo);
146     int            size = gradData.mGradient.size();
147     float *        ptr = gradData.mGradient.data();
148     int            colorPoints = mColorPoints;
149     if (colorPoints == -1) {  // for legacy bodymovin (ref: lottie-android)
150         colorPoints = size / 4;
151     }
152     int    opacityArraySize = size - colorPoints * 4;
153     float *opacityPtr = ptr + (colorPoints * 4);
154     stops.clear();
155     int j = 0;
156     for (int i = 0; i < colorPoints; i++) {
157         float       colorStop = ptr[0];
158         LottieColor color = LottieColor(ptr[1], ptr[2], ptr[3]);
159         if (opacityArraySize) {
160             if (j == opacityArraySize) {
161                 // already reached the end
162                 float stop1 = opacityPtr[j - 4];
163                 float op1 = opacityPtr[j - 3];
164                 float stop2 = opacityPtr[j - 2];
165                 float op2 = opacityPtr[j - 1];
166                 if (colorStop > stop2) {
167                     stops.push_back(
168                         std::make_pair(colorStop, color.toColor(op2)));
169                 } else {
170                     float progress = (colorStop - stop1) / (stop2 - stop1);
171                     float opacity = op1 + progress * (op2 - op1);
172                     stops.push_back(
173                         std::make_pair(colorStop, color.toColor(opacity)));
174                 }
175                 continue;
176             }
177             for (; j < opacityArraySize; j += 2) {
178                 float opacityStop = opacityPtr[j];
179                 if (opacityStop < colorStop) {
180                     // add a color using opacity stop
181                     stops.push_back(std::make_pair(
182                         opacityStop, color.toColor(opacityPtr[j + 1])));
183                     continue;
184                 }
185                 // add a color using color stop
186                 if (j == 0) {
187                     stops.push_back(std::make_pair(
188                         colorStop, color.toColor(opacityPtr[j + 1])));
189                 } else {
190                     float progress = (colorStop - opacityPtr[j - 2]) /
191                                      (opacityPtr[j] - opacityPtr[j - 2]);
192                     float opacity =
193                         opacityPtr[j - 1] +
194                         progress * (opacityPtr[j + 1] - opacityPtr[j - 1]);
195                     stops.push_back(
196                         std::make_pair(colorStop, color.toColor(opacity)));
197                 }
198                 j += 2;
199                 break;
200             }
201         } else {
202             stops.push_back(std::make_pair(colorStop, color.toColor()));
203         }
204         ptr += 4;
205     }
206 }
207
208 void LOTGradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
209 {
210     bool init = false;
211     if (!grad) {
212         if (mGradientType == 1)
213             grad = std::make_unique<VLinearGradient>(0, 0, 0, 0);
214         else
215             grad = std::make_unique<VRadialGradient>(0, 0, 0, 0, 0, 0);
216         grad->mSpread = VGradient::Spread::Pad;
217         init = true;
218     }
219
220     if (!mGradient.isStatic() || init) {
221         populate(grad->mStops, frameNo);
222     }
223
224     if (mGradientType == 1) {  // linear gradient
225         VPointF start = mStartPoint.value(frameNo);
226         VPointF end = mEndPoint.value(frameNo);
227         grad->linear.x1 = start.x();
228         grad->linear.y1 = start.y();
229         grad->linear.x2 = end.x();
230         grad->linear.y2 = end.y();
231     } else {  // radial gradient
232         VPointF start = mStartPoint.value(frameNo);
233         VPointF end = mEndPoint.value(frameNo);
234         grad->radial.cx = start.x();
235         grad->radial.cy = start.y();
236         grad->radial.cradius =
237             VLine::length(start.x(), start.y(), end.x(), end.y());
238         /*
239          * Focal point is the point lives in highlight length distance from
240          * center along the line (start, end)  and rotated by highlight angle.
241          * below calculation first finds the quadrant(angle) on which the point
242          * lives by applying inverse slope formula then adds the rotation angle
243          * to find the final angle. then point is retrived using circle equation
244          * of center, angle and distance.
245          */
246         float progress = mHighlightLength.value(frameNo) / 100.0f;
247         if (vCompare(progress, 1.0f)) progress = 0.99f;
248         float startAngle = VLine(start, end).angle();
249         float highlightAngle = mHighlightAngle.value(frameNo);
250         float angle = ((startAngle + highlightAngle) * M_PI) / 180.0f;
251         grad->radial.fx =
252             grad->radial.cx + std::cos(angle) * progress * grad->radial.cradius;
253         grad->radial.fy =
254             grad->radial.cy + std::sin(angle) * progress * grad->radial.cradius;
255         // Lottie dosen't have any focal radius concept.
256         grad->radial.fradius = 0;
257     }
258 }