1 #include "lottiemodel.h"
6 class LottieRepeaterProcesser : public LOTDataVisitor {
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)
23 for (auto child : obj->mChildren) {
24 child.get()->accept(this);
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) {
41 shapeGroup->mChildren.push_back(cpChild);
43 mRepeaterFound = false;
53 void LOTCompositionData::processRepeaterObjects()
55 LottieRepeaterProcesser visitor;
59 VMatrix LOTTransformData::matrix(int frameNo) const
64 return computeMatrix(frameNo);
67 float LOTTransformData::opacity(int frameNo) const
69 return mOpacity.value(frameNo) / 100.f;
72 void LOTTransformData::cacheMatrix()
74 mCachedMatrix = computeMatrix(0);
77 VMatrix LOTTransformData::computeMatrix(int frameNo) const
80 VPointF position = mPosition.value(frameNo);
82 position.setX(mX.value(frameNo));
83 position.setY(mY.value(frameNo));
86 .rotate(mRotation.value(frameNo))
87 .scale(mScale.value(frameNo) / 100.f)
88 .translate(-mAnchor.value(frameNo));
92 int LOTStrokeData::getDashInfo(int frameNo, float *array) const
94 if (!mDash.mDashCount) return 0;
96 if (mDash.mDashCount % 2) {
97 for (int i = 0; i < mDash.mDashCount; i++) {
98 array[i] = mDash.mDashArray[i].value(frameNo);
100 return mDash.mDashCount;
101 } else { // even case when last gap info is not provided.
103 for (i = 0; i < mDash.mDashCount - 1; i++) {
104 array[i] = mDash.mDashArray[i].value(frameNo);
106 array[i] = array[i - 1];
107 array[i + 1] = mDash.mDashArray[i].value(frameNo);
108 return mDash.mDashCount + 1;
112 int LOTGStrokeData::getDashInfo(int frameNo, float *array) const
114 if (!mDash.mDashCount) return 0;
116 if (mDash.mDashCount % 2) {
117 for (int i = 0; i < mDash.mDashCount; i++) {
118 array[i] = mDash.mDashArray[i].value(frameNo);
120 return mDash.mDashCount;
121 } else { // even case when last gap info is not provided.
123 for (i = 0; i < mDash.mDashCount - 1; i++) {
124 array[i] = mDash.mDashArray[i].value(frameNo);
126 array[i] = array[i - 1];
127 array[i + 1] = mDash.mDashArray[i].value(frameNo);
128 return mDash.mDashCount + 1;
133 * Both the color stops and opacity stops are in the same array.
134 * There are {@link #colorPoints} colors sequentially as:
144 * The remainder of the array is the opacity stops sequentially as:
152 void LOTGradient::populate(VGradientStops &stops, int frameNo)
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;
161 int opacityArraySize = size - colorPoints * 4;
162 float *opacityPtr = ptr + (colorPoints * 4);
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) {
177 std::make_pair(colorStop, color.toColor(op2)));
179 float progress = (colorStop - stop1) / (stop2 - stop1);
180 float opacity = op1 + progress * (op2 - op1);
182 std::make_pair(colorStop, color.toColor(opacity)));
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])));
194 // add a color using color stop
196 stops.push_back(std::make_pair(
197 colorStop, color.toColor(opacityPtr[j + 1])));
199 float progress = (colorStop - opacityPtr[j - 2]) /
200 (opacityPtr[j] - opacityPtr[j - 2]);
203 progress * (opacityPtr[j + 1] - opacityPtr[j - 1]);
205 std::make_pair(colorStop, color.toColor(opacity)));
211 stops.push_back(std::make_pair(colorStop, color.toColor()));
217 void LOTGradient::update(std::unique_ptr<VGradient> &grad, int frameNo)
221 if (mGradientType == 1)
222 grad = std::make_unique<VLinearGradient>(0, 0, 0, 0);
224 grad = std::make_unique<VRadialGradient>(0, 0, 0, 0, 0, 0);
225 grad->mSpread = VGradient::Spread::Pad;
229 if (!mGradient.isStatic() || init) {
230 populate(grad->mStops, frameNo);
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());
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.
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);
264 grad->radial.cx + std::cos(angle) * progress * grad->radial.cradius;
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;