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