1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved.
3 
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10 
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13 
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 
23 #include "lottieitem.h"
24 #include <algorithm>
25 #include <cmath>
26 #include <iterator>
27 #include "lottiekeypath.h"
28 #include "vbitmap.h"
29 #include "vpainter.h"
30 #include "vraster.h"
31 
32 /* Lottie Layer Rules
33  * 1. time stretch is pre calculated and applied to all the properties of the
34  * lottilayer model and all its children
35  * 2. The frame property could be reversed using,time-reverse layer property in
36  * AE. which means (start frame > endFrame) 3.
37  */
38 
transformProp(rlottie::Property prop)39 static bool transformProp(rlottie::Property prop)
40 {
41     switch (prop) {
42     case rlottie::Property::TrAnchor:
43     case rlottie::Property::TrScale:
44     case rlottie::Property::TrOpacity:
45     case rlottie::Property::TrPosition:
46     case rlottie::Property::TrRotation:
47         return true;
48     default:
49         return false;
50     }
51 }
fillProp(rlottie::Property prop)52 static bool fillProp(rlottie::Property prop)
53 {
54     switch (prop) {
55     case rlottie::Property::FillColor:
56     case rlottie::Property::FillOpacity:
57         return true;
58     default:
59         return false;
60     }
61 }
62 
strokeProp(rlottie::Property prop)63 static bool strokeProp(rlottie::Property prop)
64 {
65     switch (prop) {
66     case rlottie::Property::StrokeColor:
67     case rlottie::Property::StrokeOpacity:
68     case rlottie::Property::StrokeWidth:
69         return true;
70     default:
71         return false;
72     }
73 }
74 
createLayerItem(model::Layer * layerData,VArenaAlloc * allocator)75 static renderer::Layer *createLayerItem(model::Layer *layerData,
76                                         VArenaAlloc * allocator)
77 {
78     switch (layerData->mLayerType) {
79     case model::Layer::Type::Precomp: {
80         return allocator->make<renderer::CompLayer>(layerData, allocator);
81     }
82     case model::Layer::Type::Solid: {
83         return allocator->make<renderer::SolidLayer>(layerData);
84     }
85     case model::Layer::Type::Shape: {
86         return allocator->make<renderer::ShapeLayer>(layerData, allocator);
87     }
88     case model::Layer::Type::Null: {
89         return allocator->make<renderer::NullLayer>(layerData);
90     }
91     case model::Layer::Type::Image: {
92         return allocator->make<renderer::ImageLayer>(layerData);
93     }
94     default:
95         return nullptr;
96         break;
97     }
98 }
99 
Composition(std::shared_ptr<model::Composition> model)100 renderer::Composition::Composition(std::shared_ptr<model::Composition> model)
101     : mCurFrameNo(-1)
102 {
103     mModel = std::move(model);
104     mRootLayer = createLayerItem(mModel->mRootLayer, &mAllocator);
105     mRootLayer->setComplexContent(false);
106     mViewSize = mModel->size();
107 }
108 
setValue(const std::string & keypath,LOTVariant & value)109 void renderer::Composition::setValue(const std::string &keypath,
110                                      LOTVariant &       value)
111 {
112     LOTKeyPath key(keypath);
113     mRootLayer->resolveKeyPath(key, 0, value);
114 }
115 
update(int frameNo,const VSize & size,bool keepAspectRatio)116 bool renderer::Composition::update(int frameNo, const VSize &size,
117                                    bool keepAspectRatio)
118 {
119     // check if cached frame is same as requested frame.
120     if ((mViewSize == size) && (mCurFrameNo == frameNo) &&
121         (mKeepAspectRatio == keepAspectRatio))
122         return false;
123 
124     mViewSize = size;
125     mCurFrameNo = frameNo;
126     mKeepAspectRatio = keepAspectRatio;
127 
128     /*
129      * if viewbox dosen't scale exactly to the viewport
130      * we scale the viewbox keeping AspectRatioPreserved and then align the
131      * viewbox to the viewport using AlignCenter rule.
132      */
133     VMatrix m;
134     VSize   viewPort = mViewSize;
135     VSize   viewBox = mModel->size();
136     float   sx = float(viewPort.width()) / viewBox.width();
137     float   sy = float(viewPort.height()) / viewBox.height();
138     if (mKeepAspectRatio) {
139         float scale = std::min(sx, sy);
140         float tx = (viewPort.width() - viewBox.width() * scale) * 0.5f;
141         float ty = (viewPort.height() - viewBox.height() * scale) * 0.5f;
142         m.translate(tx, ty).scale(scale, scale);
143     } else {
144         m.scale(sx, sy);
145     }
146     mRootLayer->update(frameNo, m, 1.0);
147     return true;
148 }
149 
render(const rlottie::Surface & surface)150 bool renderer::Composition::render(const rlottie::Surface &surface)
151 {
152     mSurface.reset(reinterpret_cast<uchar *>(surface.buffer()),
153                    uint(surface.width()), uint(surface.height()),
154                    uint(surface.bytesPerLine()),
155                    VBitmap::Format::ARGB32_Premultiplied);
156 
157     /* schedule all preprocess task for this frame at once.
158      */
159     VRect clip(0, 0, int(surface.drawRegionWidth()),
160                int(surface.drawRegionHeight()));
161     mRootLayer->preprocess(clip);
162 
163     VPainter painter(&mSurface);
164     // set sub surface area for drawing.
165     painter.setDrawRegion(
166         VRect(int(surface.drawRegionPosX()), int(surface.drawRegionPosY()),
167               int(surface.drawRegionWidth()), int(surface.drawRegionHeight())));
168     mRootLayer->render(&painter, {}, {}, mSurfaceCache);
169     painter.end();
170     return true;
171 }
172 
update(int frameNo,const VMatrix & parentMatrix,float,const DirtyFlag & flag)173 void renderer::Mask::update(int frameNo, const VMatrix &parentMatrix,
174                             float /*parentAlpha*/, const DirtyFlag &flag)
175 {
176     bool dirtyPath = false;
177 
178     if (flag.testFlag(DirtyFlagBit::None) && mData->isStatic()) return;
179 
180     if (mData->mShape.isStatic()) {
181         if (mLocalPath.empty()) {
182             dirtyPath = true;
183             mData->mShape.value(frameNo, mLocalPath);
184         }
185     } else {
186         dirtyPath = true;
187         mData->mShape.value(frameNo, mLocalPath);
188     }
189     /* mask item dosen't inherit opacity */
190     mCombinedAlpha = mData->opacity(frameNo);
191 
192     if ( flag.testFlag(DirtyFlagBit::Matrix) || dirtyPath ) {
193         mFinalPath.clone(mLocalPath);
194         mFinalPath.transform(parentMatrix);
195         mRasterRequest = true;
196     }
197 }
198 
rle()199 VRle renderer::Mask::rle()
200 {
201     if (!vCompare(mCombinedAlpha, 1.0f)) {
202         VRle obj = mRasterizer.rle();
203         obj *= uchar(mCombinedAlpha * 255);
204         return obj;
205     } else {
206         return mRasterizer.rle();
207     }
208 }
209 
preprocess(const VRect & clip)210 void renderer::Mask::preprocess(const VRect &clip)
211 {
212     if (mRasterRequest)
213         mRasterizer.rasterize(mFinalPath, FillRule::Winding, clip);
214 }
215 
render(VPainter * painter,const VRle & inheritMask,const VRle & matteRle,SurfaceCache &)216 void renderer::Layer::render(VPainter *painter, const VRle &inheritMask,
217                              const VRle &matteRle, SurfaceCache &)
218 {
219     auto renderlist = renderList();
220 
221     if (renderlist.empty()) return;
222 
223     VRle mask;
224     if (mLayerMask) {
225         mask = mLayerMask->maskRle(painter->clipBoundingRect());
226         if (!inheritMask.empty()) mask = mask & inheritMask;
227         // if resulting mask is empty then return.
228         if (mask.empty()) return;
229     } else {
230         mask = inheritMask;
231     }
232 
233     for (auto &i : renderlist) {
234         painter->setBrush(i->mBrush);
235         VRle rle = i->rle();
236         if (matteRle.empty()) {
237             if (mask.empty()) {
238                 // no mask no matte
239                 painter->drawRle(VPoint(), rle);
240             } else {
241                 // only mask
242                 painter->drawRle(rle, mask);
243             }
244 
245         } else {
246             if (!mask.empty()) rle = rle & mask;
247 
248             if (rle.empty()) continue;
249             if (matteType() == model::MatteType::AlphaInv) {
250                 rle = rle - matteRle;
251                 painter->drawRle(VPoint(), rle);
252             } else {
253                 // render with matteRle as clip.
254                 painter->drawRle(rle, matteRle);
255             }
256         }
257     }
258 }
259 
preprocess(const VRect & clip)260 void renderer::LayerMask::preprocess(const VRect &clip)
261 {
262     for (auto &i : mMasks) {
263         i.preprocess(clip);
264     }
265 }
266 
LayerMask(model::Layer * layerData)267 renderer::LayerMask::LayerMask(model::Layer *layerData)
268 {
269     if (!layerData->mExtra) return;
270 
271     mMasks.reserve(layerData->mExtra->mMasks.size());
272 
273     for (auto &i : layerData->mExtra->mMasks) {
274         mMasks.emplace_back(i);
275         mStatic &= i->isStatic();
276     }
277 }
278 
update(int frameNo,const VMatrix & parentMatrix,float parentAlpha,const DirtyFlag & flag)279 void renderer::LayerMask::update(int frameNo, const VMatrix &parentMatrix,
280                                  float parentAlpha, const DirtyFlag &flag)
281 {
282     if (flag.testFlag(DirtyFlagBit::None) && isStatic()) return;
283 
284     for (auto &i : mMasks) {
285         i.update(frameNo, parentMatrix, parentAlpha, flag);
286     }
287     mDirty = true;
288 }
289 
maskRle(const VRect & clipRect)290 VRle renderer::LayerMask::maskRle(const VRect &clipRect)
291 {
292     if (!mDirty) return mRle;
293 
294     VRle rle;
295     for (auto &e : mMasks) {
296         const auto cur = [&]() {
297             if (e.inverted())
298                 return clipRect - e.rle();
299             else
300                 return e.rle();
301         }();
302 
303         switch (e.maskMode()) {
304         case model::Mask::Mode::Add: {
305             rle = rle + cur;
306             break;
307         }
308         case model::Mask::Mode::Substarct: {
309             if (rle.empty() && !clipRect.empty())
310                 rle = clipRect - cur;
311             else
312                 rle = rle - cur;
313             break;
314         }
315         case model::Mask::Mode::Intersect: {
316             if (rle.empty() && !clipRect.empty())
317                 rle = clipRect & cur;
318             else
319                 rle = rle & cur;
320             break;
321         }
322         case model::Mask::Mode::Difference: {
323             rle = rle ^ cur;
324             break;
325         }
326         default:
327             break;
328         }
329     }
330 
331     if (!rle.empty() && !rle.unique()) {
332         mRle.clone(rle);
333     } else {
334         mRle = rle;
335     }
336     mDirty = false;
337     return mRle;
338 }
339 
Layer(model::Layer * layerData)340 renderer::Layer::Layer(model::Layer *layerData) : mLayerData(layerData)
341 {
342     if (mLayerData->mHasMask)
343         mLayerMask = std::make_unique<renderer::LayerMask>(mLayerData);
344 }
345 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)346 bool renderer::Layer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
347                                      LOTVariant &value)
348 {
349     if (!keyPath.matches(name(), depth)) {
350         return false;
351     }
352 
353     if (!keyPath.skip(name())) {
354         if (keyPath.fullyResolvesTo(name(), depth) &&
355             transformProp(value.property())) {
356             //@TODO handle propery update.
357         }
358     }
359     return true;
360 }
361 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)362 bool renderer::ShapeLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
363                                           LOTVariant &value)
364 {
365     if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
366         if (keyPath.propagate(name(), depth)) {
367             uint newDepth = keyPath.nextDepth(name(), depth);
368             mRoot->resolveKeyPath(keyPath, newDepth, value);
369         }
370         return true;
371     }
372     return false;
373 }
374 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)375 bool renderer::CompLayer::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
376                                          LOTVariant &value)
377 {
378     if (renderer::Layer::resolveKeyPath(keyPath, depth, value)) {
379         if (keyPath.propagate(name(), depth)) {
380             uint newDepth = keyPath.nextDepth(name(), depth);
381             for (const auto &layer : mLayers) {
382                 layer->resolveKeyPath(keyPath, newDepth, value);
383             }
384         }
385         return true;
386     }
387     return false;
388 }
389 
update(int frameNumber,const VMatrix & parentMatrix,float parentAlpha)390 void renderer::Layer::update(int frameNumber, const VMatrix &parentMatrix,
391                              float parentAlpha)
392 {
393     mFrameNo = frameNumber;
394     // 1. check if the layer is part of the current frame
395     if (!visible()) return;
396 
397     float alpha = parentAlpha * opacity(frameNo());
398     if (vIsZero(alpha)) {
399         mCombinedAlpha = 0;
400         return;
401     }
402 
403     // 2. calculate the parent matrix and alpha
404     VMatrix m = matrix(frameNo());
405     m *= parentMatrix;
406 
407     // 3. update the dirty flag based on the change
408     if (mCombinedMatrix != m) {
409         mDirtyFlag |= DirtyFlagBit::Matrix;
410         mCombinedMatrix = m;
411     }
412 
413     if (!vCompare(mCombinedAlpha, alpha)) {
414         mDirtyFlag |= DirtyFlagBit::Alpha;
415         mCombinedAlpha = alpha;
416     }
417 
418     // 4. update the mask
419     if (mLayerMask) {
420         mLayerMask->update(frameNo(), mCombinedMatrix, mCombinedAlpha,
421                            mDirtyFlag);
422     }
423 
424     // 5. if no parent property change and layer is static then nothing to do.
425     if (!mLayerData->precompLayer() && flag().testFlag(DirtyFlagBit::None) &&
426         isStatic())
427         return;
428 
429     // 6. update the content of the layer
430     updateContent();
431 
432     // 7. reset the dirty flag
433     mDirtyFlag = DirtyFlagBit::None;
434 }
435 
matrix(int frameNo) const436 VMatrix renderer::Layer::matrix(int frameNo) const
437 {
438     return mParentLayer
439                ? (mLayerData->matrix(frameNo) * mParentLayer->matrix(frameNo))
440                : mLayerData->matrix(frameNo);
441 }
442 
visible() const443 bool renderer::Layer::visible() const
444 {
445     return (frameNo() >= mLayerData->inFrame() &&
446             frameNo() < mLayerData->outFrame());
447 }
448 
preprocess(const VRect & clip)449 void renderer::Layer::preprocess(const VRect &clip)
450 {
451     // layer dosen't contribute to the frame
452     if (skipRendering()) return;
453 
454     // preprocess layer masks
455     if (mLayerMask) mLayerMask->preprocess(clip);
456 
457     preprocessStage(clip);
458 }
459 
CompLayer(model::Layer * layerModel,VArenaAlloc * allocator)460 renderer::CompLayer::CompLayer(model::Layer *layerModel, VArenaAlloc *allocator)
461     : renderer::Layer(layerModel)
462 {
463     if (!mLayerData->mChildren.empty())
464         mLayers.reserve(mLayerData->mChildren.size());
465 
466     // 1. keep the layer in back-to-front order.
467     // as lottie model keeps the data in front-toback-order.
468     for (auto it = mLayerData->mChildren.crbegin();
469          it != mLayerData->mChildren.rend(); ++it) {
470         auto model = static_cast<model::Layer *>(*it);
471         auto item = createLayerItem(model, allocator);
472         if (item) mLayers.push_back(item);
473     }
474 
475     // 2. update parent layer
476     for (const auto &layer : mLayers) {
477         int id = layer->parentId();
478         if (id >= 0) {
479             auto search =
480                 std::find_if(mLayers.begin(), mLayers.end(),
481                              [id](const auto &val) { return val->id() == id; });
482             if (search != mLayers.end()) layer->setParentLayer(*search);
483         }
484     }
485 
486     // 4. check if its a nested composition
487     if (!layerModel->layerSize().empty()) {
488         mClipper = std::make_unique<renderer::Clipper>(layerModel->layerSize());
489     }
490 
491     if (mLayers.size() > 1) setComplexContent(true);
492 }
493 
render(VPainter * painter,const VRle & inheritMask,const VRle & matteRle,SurfaceCache & cache)494 void renderer::CompLayer::render(VPainter *painter, const VRle &inheritMask,
495                                  const VRle &matteRle, SurfaceCache &cache)
496 {
497     if (vIsZero(combinedAlpha())) return;
498 
499     if (vCompare(combinedAlpha(), 1.0)) {
500         renderHelper(painter, inheritMask, matteRle, cache);
501     } else {
502         if (complexContent()) {
503             VSize    size = painter->clipBoundingRect().size();
504             VPainter srcPainter;
505             VBitmap srcBitmap = cache.make_surface(size.width(), size.height());
506             srcPainter.begin(&srcBitmap);
507             renderHelper(&srcPainter, inheritMask, matteRle, cache);
508             srcPainter.end();
509             painter->drawBitmap(VPoint(), srcBitmap,
510                                 uchar(combinedAlpha() * 255.0f));
511             cache.release_surface(srcBitmap);
512         } else {
513             renderHelper(painter, inheritMask, matteRle, cache);
514         }
515     }
516 }
517 
renderHelper(VPainter * painter,const VRle & inheritMask,const VRle & matteRle,SurfaceCache & cache)518 void renderer::CompLayer::renderHelper(VPainter *    painter,
519                                        const VRle &  inheritMask,
520                                        const VRle &  matteRle,
521                                        SurfaceCache &cache)
522 {
523     VRle mask;
524     if (mLayerMask) {
525         mask = mLayerMask->maskRle(painter->clipBoundingRect());
526         if (!inheritMask.empty()) mask = mask & inheritMask;
527         // if resulting mask is empty then return.
528         if (mask.empty()) return;
529     } else {
530         mask = inheritMask;
531     }
532 
533     if (mClipper) {
534         mask = mClipper->rle(mask);
535         if (mask.empty()) return;
536     }
537 
538     renderer::Layer *matte = nullptr;
539     for (const auto &layer : mLayers) {
540         if (layer->hasMatte()) {
541             matte = layer;
542         } else {
543             if (layer->visible()) {
544                 if (matte) {
545                     if (matte->visible())
546                         renderMatteLayer(painter, mask, matteRle, matte, layer,
547                                          cache);
548                 } else {
549                     layer->render(painter, mask, matteRle, cache);
550                 }
551             }
552             matte = nullptr;
553         }
554     }
555 }
556 
renderMatteLayer(VPainter * painter,const VRle & mask,const VRle & matteRle,renderer::Layer * layer,renderer::Layer * src,SurfaceCache & cache)557 void renderer::CompLayer::renderMatteLayer(VPainter *painter, const VRle &mask,
558                                            const VRle &     matteRle,
559                                            renderer::Layer *layer,
560                                            renderer::Layer *src,
561                                            SurfaceCache &   cache)
562 {
563     VSize size = painter->clipBoundingRect().size();
564     // Decide if we can use fast matte.
565     // 1. draw src layer to matte buffer
566     VPainter srcPainter;
567     VBitmap  srcBitmap = cache.make_surface(size.width(), size.height());
568     srcPainter.begin(&srcBitmap);
569     src->render(&srcPainter, mask, matteRle, cache);
570     srcPainter.end();
571 
572     // 2. draw layer to layer buffer
573     VPainter layerPainter;
574     VBitmap  layerBitmap = cache.make_surface(size.width(), size.height());
575     layerPainter.begin(&layerBitmap);
576     layer->render(&layerPainter, mask, matteRle, cache);
577 
578     // 2.1update composition mode
579     switch (layer->matteType()) {
580     case model::MatteType::Alpha:
581     case model::MatteType::Luma: {
582         layerPainter.setBlendMode(BlendMode::DestIn);
583         break;
584     }
585     case model::MatteType::AlphaInv:
586     case model::MatteType::LumaInv: {
587         layerPainter.setBlendMode(BlendMode::DestOut);
588         break;
589     }
590     default:
591         break;
592     }
593 
594     // 2.2 update srcBuffer if the matte is luma type
595     if (layer->matteType() == model::MatteType::Luma ||
596         layer->matteType() == model::MatteType::LumaInv) {
597         srcBitmap.updateLuma();
598     }
599 
600     auto clip = layerPainter.clipBoundingRect();
601 
602     // if the layer has only one renderer then use it as the clip rect
603     // when blending 2 buffer and copy back to final buffer to avoid
604     // unnecessary pixel processing.
605     if (layer->renderList().size() == 1)
606     {
607         clip = layer->renderList()[0]->rle().boundingRect();
608     }
609 
610     // 2.3 draw src buffer as mask
611     layerPainter.drawBitmap(clip, srcBitmap, clip);
612     layerPainter.end();
613     // 3. draw the result buffer into painter
614     painter->drawBitmap(clip, layerBitmap, clip);
615 
616     cache.release_surface(srcBitmap);
617     cache.release_surface(layerBitmap);
618 }
619 
update(const VMatrix & matrix)620 void renderer::Clipper::update(const VMatrix &matrix)
621 {
622     mPath.reset();
623     mPath.addRect(VRectF(0, 0, mSize.width(), mSize.height()));
624     mPath.transform(matrix);
625     mRasterRequest = true;
626 }
627 
preprocess(const VRect & clip)628 void renderer::Clipper::preprocess(const VRect &clip)
629 {
630     if (mRasterRequest) mRasterizer.rasterize(mPath, FillRule::Winding, clip);
631 
632     mRasterRequest = false;
633 }
634 
rle(const VRle & mask)635 VRle renderer::Clipper::rle(const VRle &mask)
636 {
637     if (mask.empty()) return mRasterizer.rle();
638 
639     mMaskedRle.clone(mask);
640     mMaskedRle &= mRasterizer.rle();
641     return mMaskedRle;
642 }
643 
updateContent()644 void renderer::CompLayer::updateContent()
645 {
646     if (mClipper && flag().testFlag(DirtyFlagBit::Matrix)) {
647         mClipper->update(combinedMatrix());
648     }
649     int   mappedFrame = mLayerData->timeRemap(frameNo());
650     float alpha = combinedAlpha();
651     if (complexContent()) alpha = 1;
652     for (const auto &layer : mLayers) {
653         layer->update(mappedFrame, combinedMatrix(), alpha);
654     }
655 }
656 
preprocessStage(const VRect & clip)657 void renderer::CompLayer::preprocessStage(const VRect &clip)
658 {
659     // if layer has clipper
660     if (mClipper) mClipper->preprocess(clip);
661 
662     renderer::Layer *matte = nullptr;
663     for (const auto &layer : mLayers) {
664         if (layer->hasMatte()) {
665             matte = layer;
666         } else {
667             if (layer->visible()) {
668                 if (matte) {
669                     if (matte->visible()) {
670                         layer->preprocess(clip);
671                         matte->preprocess(clip);
672                     }
673                 } else {
674                     layer->preprocess(clip);
675                 }
676             }
677             matte = nullptr;
678         }
679     }
680 }
681 
SolidLayer(model::Layer * layerData)682 renderer::SolidLayer::SolidLayer(model::Layer *layerData)
683     : renderer::Layer(layerData)
684 {
685     mDrawableList = &mRenderNode;
686 }
687 
updateContent()688 void renderer::SolidLayer::updateContent()
689 {
690     if (flag() & DirtyFlagBit::Matrix) {
691         mPath.reset();
692         mPath.addRect(VRectF(0, 0, mLayerData->layerSize().width(),
693                             mLayerData->layerSize().height()));
694         mPath.transform(combinedMatrix());
695         mRenderNode.mFlag |= VDrawable::DirtyState::Path;
696         mRenderNode.mPath = mPath;
697     }
698     if (flag() & DirtyFlagBit::Alpha) {
699         model::Color color = mLayerData->solidColor();
700         VBrush       brush(color.toColor(combinedAlpha()));
701         mRenderNode.setBrush(brush);
702         mRenderNode.mFlag |= VDrawable::DirtyState::Brush;
703     }
704 }
705 
preprocessStage(const VRect & clip)706 void renderer::SolidLayer::preprocessStage(const VRect &clip)
707 {
708     mRenderNode.preprocess(clip);
709 }
710 
renderList()711 renderer::DrawableList renderer::SolidLayer::renderList()
712 {
713     if (skipRendering()) return {};
714 
715     return {&mDrawableList, 1};
716 }
717 
ImageLayer(model::Layer * layerData)718 renderer::ImageLayer::ImageLayer(model::Layer *layerData)
719     : renderer::Layer(layerData)
720 {
721     mDrawableList = &mRenderNode;
722 
723     if (!mLayerData->asset()) return;
724 
725     mTexture.mBitmap = mLayerData->asset()->bitmap();
726     VBrush brush(&mTexture);
727     mRenderNode.setBrush(brush);
728 }
729 
updateContent()730 void renderer::ImageLayer::updateContent()
731 {
732     if (!mLayerData->asset()) return;
733 
734     if (flag() & DirtyFlagBit::Matrix) {
735         mPath.reset();
736         mPath.addRect(VRectF(0, 0, mLayerData->asset()->mWidth,
737                             mLayerData->asset()->mHeight));
738         mPath.transform(combinedMatrix());
739         mRenderNode.mFlag |= VDrawable::DirtyState::Path;
740         mRenderNode.mPath = mPath;
741         mTexture.mMatrix = combinedMatrix();
742     }
743 
744     if (flag() & DirtyFlagBit::Alpha) {
745         mTexture.mAlpha = int(combinedAlpha() * 255);
746     }
747 }
748 
preprocessStage(const VRect & clip)749 void renderer::ImageLayer::preprocessStage(const VRect &clip)
750 {
751     mRenderNode.preprocess(clip);
752 }
753 
renderList()754 renderer::DrawableList renderer::ImageLayer::renderList()
755 {
756     if (skipRendering()) return {};
757 
758     return {&mDrawableList, 1};
759 }
760 
NullLayer(model::Layer * layerData)761 renderer::NullLayer::NullLayer(model::Layer *layerData)
762     : renderer::Layer(layerData)
763 {
764 }
updateContent()765 void renderer::NullLayer::updateContent() {}
766 
createContentItem(model::Object * contentData,VArenaAlloc * allocator)767 static renderer::Object *createContentItem(model::Object *contentData,
768                                            VArenaAlloc *  allocator)
769 {
770     switch (contentData->type()) {
771     case model::Object::Type::Group: {
772         return allocator->make<renderer::Group>(
773             static_cast<model::Group *>(contentData), allocator);
774     }
775     case model::Object::Type::Rect: {
776         return allocator->make<renderer::Rect>(
777             static_cast<model::Rect *>(contentData));
778     }
779     case model::Object::Type::Ellipse: {
780         return allocator->make<renderer::Ellipse>(
781             static_cast<model::Ellipse *>(contentData));
782     }
783     case model::Object::Type::Path: {
784         return allocator->make<renderer::Path>(
785             static_cast<model::Path *>(contentData));
786     }
787     case model::Object::Type::Polystar: {
788         return allocator->make<renderer::Polystar>(
789             static_cast<model::Polystar *>(contentData));
790     }
791     case model::Object::Type::Fill: {
792         return allocator->make<renderer::Fill>(
793             static_cast<model::Fill *>(contentData));
794     }
795     case model::Object::Type::GFill: {
796         return allocator->make<renderer::GradientFill>(
797             static_cast<model::GradientFill *>(contentData));
798     }
799     case model::Object::Type::Stroke: {
800         return allocator->make<renderer::Stroke>(
801             static_cast<model::Stroke *>(contentData));
802     }
803     case model::Object::Type::GStroke: {
804         return allocator->make<renderer::GradientStroke>(
805             static_cast<model::GradientStroke *>(contentData));
806     }
807     case model::Object::Type::Repeater: {
808         return allocator->make<renderer::Repeater>(
809             static_cast<model::Repeater *>(contentData), allocator);
810     }
811     case model::Object::Type::Trim: {
812         return allocator->make<renderer::Trim>(
813             static_cast<model::Trim *>(contentData));
814     }
815     default:
816         return nullptr;
817         break;
818     }
819 }
820 
ShapeLayer(model::Layer * layerData,VArenaAlloc * allocator)821 renderer::ShapeLayer::ShapeLayer(model::Layer *layerData,
822                                  VArenaAlloc * allocator)
823     : renderer::Layer(layerData),
824       mRoot(allocator->make<renderer::Group>(nullptr, allocator))
825 {
826     mRoot->addChildren(layerData, allocator);
827 
828     std::vector<renderer::Shape *> list;
829     mRoot->processPaintItems(list);
830 
831     if (layerData->hasPathOperator()) {
832         list.clear();
833         mRoot->processTrimItems(list);
834     }
835 }
836 
updateContent()837 void renderer::ShapeLayer::updateContent()
838 {
839     mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
840 
841     if (mLayerData->hasPathOperator()) {
842         mRoot->applyTrim();
843     }
844 }
845 
preprocessStage(const VRect & clip)846 void renderer::ShapeLayer::preprocessStage(const VRect &clip)
847 {
848     mDrawableList.clear();
849     mRoot->renderList(mDrawableList);
850 
851     for (auto &drawable : mDrawableList) drawable->preprocess(clip);
852 }
853 
renderList()854 renderer::DrawableList renderer::ShapeLayer::renderList()
855 {
856     if (skipRendering()) return {};
857 
858     mDrawableList.clear();
859     mRoot->renderList(mDrawableList);
860 
861     if (mDrawableList.empty()) return {};
862 
863     return {mDrawableList.data(), mDrawableList.size()};
864 }
865 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)866 bool renderer::Group::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
867                                      LOTVariant &value)
868 {
869     if (!keyPath.skip(name())) {
870         if (!keyPath.matches(mModel.name(), depth)) {
871             return false;
872         }
873 
874         if (!keyPath.skip(mModel.name())) {
875             if (keyPath.fullyResolvesTo(mModel.name(), depth) &&
876                 transformProp(value.property())) {
877                 mModel.filter()->addValue(value);
878             }
879         }
880     }
881 
882     if (keyPath.propagate(name(), depth)) {
883         uint newDepth = keyPath.nextDepth(name(), depth);
884         for (auto &child : mContents) {
885             child->resolveKeyPath(keyPath, newDepth, value);
886         }
887     }
888     return true;
889 }
890 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)891 bool renderer::Fill::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
892                                     LOTVariant &value)
893 {
894     if (!keyPath.matches(mModel.name(), depth)) {
895         return false;
896     }
897 
898     if (keyPath.fullyResolvesTo(mModel.name(), depth) &&
899         fillProp(value.property())) {
900         mModel.filter()->addValue(value);
901         return true;
902     }
903     return false;
904 }
905 
resolveKeyPath(LOTKeyPath & keyPath,uint depth,LOTVariant & value)906 bool renderer::Stroke::resolveKeyPath(LOTKeyPath &keyPath, uint depth,
907                                       LOTVariant &value)
908 {
909     if (!keyPath.matches(mModel.name(), depth)) {
910         return false;
911     }
912 
913     if (keyPath.fullyResolvesTo(mModel.name(), depth) &&
914         strokeProp(value.property())) {
915         mModel.filter()->addValue(value);
916         return true;
917     }
918     return false;
919 }
920 
Group(model::Group * data,VArenaAlloc * allocator)921 renderer::Group::Group(model::Group *data, VArenaAlloc *allocator)
922     : mModel(data)
923 {
924     addChildren(data, allocator);
925 }
926 
addChildren(model::Group * data,VArenaAlloc * allocator)927 void renderer::Group::addChildren(model::Group *data, VArenaAlloc *allocator)
928 {
929     if (!data) return;
930 
931     if (!data->mChildren.empty()) mContents.reserve(data->mChildren.size());
932 
933     // keep the content in back-to-front order.
934     // as lottie model keeps it in front-to-back order.
935     for (auto it = data->mChildren.crbegin(); it != data->mChildren.rend();
936          ++it) {
937         auto content = createContentItem(*it, allocator);
938         if (content) {
939             mContents.push_back(content);
940         }
941     }
942 }
943 
update(int frameNo,const VMatrix & parentMatrix,float parentAlpha,const DirtyFlag & flag)944 void renderer::Group::update(int frameNo, const VMatrix &parentMatrix,
945                              float parentAlpha, const DirtyFlag &flag)
946 {
947     DirtyFlag newFlag = flag;
948     float     alpha;
949 
950     if (mModel.hasModel() && mModel.transform()) {
951         VMatrix m = mModel.matrix(frameNo);
952 
953         m *= parentMatrix;
954         if (!(flag & DirtyFlagBit::Matrix) && !mModel.transform()->isStatic() &&
955             (m != mMatrix)) {
956             newFlag |= DirtyFlagBit::Matrix;
957         }
958 
959         mMatrix = m;
960 
961         alpha = parentAlpha * mModel.transform()->opacity(frameNo);
962         if (!vCompare(alpha, parentAlpha)) {
963             newFlag |= DirtyFlagBit::Alpha;
964         }
965     } else {
966         mMatrix = parentMatrix;
967         alpha = parentAlpha;
968     }
969 
970     for (const auto &content : mContents) {
971         content->update(frameNo, matrix(), alpha, newFlag);
972     }
973 }
974 
applyTrim()975 void renderer::Group::applyTrim()
976 {
977     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
978         auto content = (*i);
979         switch (content->type()) {
980         case renderer::Object::Type::Trim: {
981             static_cast<renderer::Trim *>(content)->update();
982             break;
983         }
984         case renderer::Object::Type::Group: {
985             static_cast<renderer::Group *>(content)->applyTrim();
986             break;
987         }
988         default:
989             break;
990         }
991     }
992 }
993 
renderList(std::vector<VDrawable * > & list)994 void renderer::Group::renderList(std::vector<VDrawable *> &list)
995 {
996     for (const auto &content : mContents) {
997         content->renderList(list);
998     }
999 }
1000 
processPaintItems(std::vector<renderer::Shape * > & list)1001 void renderer::Group::processPaintItems(std::vector<renderer::Shape *> &list)
1002 {
1003     size_t curOpCount = list.size();
1004     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
1005         auto content = (*i);
1006         switch (content->type()) {
1007         case renderer::Object::Type::Shape: {
1008             auto pathItem = static_cast<renderer::Shape *>(content);
1009             pathItem->setParent(this);
1010             list.push_back(pathItem);
1011             break;
1012         }
1013         case renderer::Object::Type::Paint: {
1014             static_cast<renderer::Paint *>(content)->addPathItems(list,
1015                                                                   curOpCount);
1016             break;
1017         }
1018         case renderer::Object::Type::Group: {
1019             static_cast<renderer::Group *>(content)->processPaintItems(list);
1020             break;
1021         }
1022         default:
1023             break;
1024         }
1025     }
1026 }
1027 
processTrimItems(std::vector<renderer::Shape * > & list)1028 void renderer::Group::processTrimItems(std::vector<renderer::Shape *> &list)
1029 {
1030     size_t curOpCount = list.size();
1031     for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
1032         auto content = (*i);
1033 
1034         switch (content->type()) {
1035         case renderer::Object::Type::Shape: {
1036             list.push_back(static_cast<renderer::Shape *>(content));
1037             break;
1038         }
1039         case renderer::Object::Type::Trim: {
1040             static_cast<renderer::Trim *>(content)->addPathItems(list,
1041                                                                  curOpCount);
1042             break;
1043         }
1044         case renderer::Object::Type::Group: {
1045             static_cast<renderer::Group *>(content)->processTrimItems(list);
1046             break;
1047         }
1048         default:
1049             break;
1050         }
1051     }
1052 }
1053 
1054 /*
1055  * renderer::Shape uses 2 path objects for path object reuse.
1056  * mLocalPath -  keeps track of the local path of the item before
1057  * applying path operation and transformation.
1058  * mTemp - keeps a referece to the mLocalPath and can be updated by the
1059  *          path operation objects(trim, merge path),
1060  * We update the DirtyPath flag if the path needs to be updated again
1061  * beacuse of local path or matrix or some path operation has changed which
1062  * affects the final path.
1063  * The PaintObject queries the dirty flag to check if it needs to compute the
1064  * final path again and calls finalPath() api to do the same.
1065  * finalPath() api passes a result Object so that we keep only one copy of
1066  * the path object in the paintItem (for memory efficiency).
1067  *   NOTE: As path objects are COW objects we have to be
1068  * carefull about the refcount so that we don't generate deep copy while
1069  * modifying the path objects.
1070  */
update(int frameNo,const VMatrix &,float,const DirtyFlag & flag)1071 void renderer::Shape::update(int              frameNo, const VMatrix &, float,
1072                              const DirtyFlag &flag)
1073 {
1074     mDirtyPath = false;
1075 
1076     // 1. update the local path if needed
1077     if (hasChanged(frameNo)) {
1078         // loose the reference to mLocalPath if any
1079         // from the last frame update.
1080         mTemp = VPath();
1081 
1082         updatePath(mLocalPath, frameNo);
1083         mDirtyPath = true;
1084     }
1085     // 2. keep a reference path in temp in case there is some
1086     // path operation like trim which will update the path.
1087     // we don't want to update the local path.
1088     mTemp = mLocalPath;
1089 
1090     // 3. mark the path dirty if matrix has changed.
1091     if (flag & DirtyFlagBit::Matrix) {
1092         mDirtyPath = true;
1093     }
1094 }
1095 
finalPath(VPath & result)1096 void renderer::Shape::finalPath(VPath &result)
1097 {
1098     result.addPath(mTemp, static_cast<renderer::Group *>(parent())->matrix());
1099 }
1100 
Rect(model::Rect * data)1101 renderer::Rect::Rect(model::Rect *data)
1102     : renderer::Shape(data->isStatic()), mData(data)
1103 {
1104 }
1105 
updatePath(VPath & path,int frameNo)1106 void renderer::Rect::updatePath(VPath &path, int frameNo)
1107 {
1108     VPointF pos = mData->mPos.value(frameNo);
1109     VPointF size = mData->mSize.value(frameNo);
1110     float   roundness = mData->roundness(frameNo);
1111     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1112              size.y());
1113 
1114     path.reset();
1115     path.addRoundRect(r, roundness, mData->direction());
1116 }
1117 
Ellipse(model::Ellipse * data)1118 renderer::Ellipse::Ellipse(model::Ellipse *data)
1119     : renderer::Shape(data->isStatic()), mData(data)
1120 {
1121 }
1122 
updatePath(VPath & path,int frameNo)1123 void renderer::Ellipse::updatePath(VPath &path, int frameNo)
1124 {
1125     VPointF pos = mData->mPos.value(frameNo);
1126     VPointF size = mData->mSize.value(frameNo);
1127     VRectF  r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1128              size.y());
1129 
1130     path.reset();
1131     path.addOval(r, mData->direction());
1132 }
1133 
Path(model::Path * data)1134 renderer::Path::Path(model::Path *data)
1135     : renderer::Shape(data->isStatic()), mData(data)
1136 {
1137 }
1138 
updatePath(VPath & path,int frameNo)1139 void renderer::Path::updatePath(VPath &path, int frameNo)
1140 {
1141     mData->mShape.value(frameNo, path);
1142 }
1143 
Polystar(model::Polystar * data)1144 renderer::Polystar::Polystar(model::Polystar *data)
1145     : renderer::Shape(data->isStatic()), mData(data)
1146 {
1147 }
1148 
updatePath(VPath & path,int frameNo)1149 void renderer::Polystar::updatePath(VPath &path, int frameNo)
1150 {
1151     VPointF pos = mData->mPos.value(frameNo);
1152     float   points = mData->mPointCount.value(frameNo);
1153     float   innerRadius = mData->mInnerRadius.value(frameNo);
1154     float   outerRadius = mData->mOuterRadius.value(frameNo);
1155     float   innerRoundness = mData->mInnerRoundness.value(frameNo);
1156     float   outerRoundness = mData->mOuterRoundness.value(frameNo);
1157     float   rotation = mData->mRotation.value(frameNo);
1158 
1159     path.reset();
1160     VMatrix m;
1161 
1162     if (mData->mPolyType == model::Polystar::PolyType::Star) {
1163         path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
1164                          outerRoundness, 0.0, 0.0, 0.0, mData->direction());
1165     } else {
1166         path.addPolygon(points, outerRadius, outerRoundness, 0.0, 0.0, 0.0,
1167                         mData->direction());
1168     }
1169 
1170     m.translate(pos.x(), pos.y()).rotate(rotation);
1171     m.rotate(rotation);
1172     path.transform(m);
1173 }
1174 
1175 /*
1176  * PaintData Node handling
1177  *
1178  */
Paint(bool staticContent)1179 renderer::Paint::Paint(bool staticContent) : mStaticContent(staticContent) {}
1180 
update(int frameNo,const VMatrix & parentMatrix,float parentAlpha,const DirtyFlag &)1181 void renderer::Paint::update(int frameNo, const VMatrix &parentMatrix,
1182                              float parentAlpha, const DirtyFlag & /*flag*/)
1183 {
1184     mRenderNodeUpdate = true;
1185     mContentToRender = updateContent(frameNo, parentMatrix, parentAlpha);
1186 }
1187 
updateRenderNode()1188 void renderer::Paint::updateRenderNode()
1189 {
1190     bool dirty = false;
1191     for (auto &i : mPathItems) {
1192         if (i->dirty()) {
1193             dirty = true;
1194             break;
1195         }
1196     }
1197 
1198     if (dirty) {
1199         mPath.reset();
1200         for (const auto &i : mPathItems) {
1201             i->finalPath(mPath);
1202         }
1203         mDrawable.setPath(mPath);
1204     } else {
1205         if (mDrawable.mFlag & VDrawable::DirtyState::Path)
1206             mDrawable.mPath = mPath;
1207     }
1208 }
1209 
renderList(std::vector<VDrawable * > & list)1210 void renderer::Paint::renderList(std::vector<VDrawable *> &list)
1211 {
1212     if (mRenderNodeUpdate) {
1213         updateRenderNode();
1214         mRenderNodeUpdate = false;
1215     }
1216 
1217     // Q: Why we even update the final path if we don't have content
1218     // to render ?
1219     // Ans: We update the render nodes because we will loose the
1220     // dirty path information at end of this frame.
1221     // so if we return early without updating the final path.
1222     // in the subsequent frame when we have content to render but
1223     // we may not able to update our final path properly as we
1224     // don't know what paths got changed in between.
1225     if (mContentToRender) list.push_back(&mDrawable);
1226 }
1227 
addPathItems(std::vector<renderer::Shape * > & list,size_t startOffset)1228 void renderer::Paint::addPathItems(std::vector<renderer::Shape *> &list,
1229                                    size_t                          startOffset)
1230 {
1231     std::copy(list.begin() + startOffset, list.end(),
1232               back_inserter(mPathItems));
1233 }
1234 
Fill(model::Fill * data)1235 renderer::Fill::Fill(model::Fill *data)
1236     : renderer::Paint(data->isStatic()), mModel(data)
1237 {
1238     mDrawable.setName(mModel.name());
1239 }
1240 
updateContent(int frameNo,const VMatrix &,float alpha)1241 bool renderer::Fill::updateContent(int frameNo, const VMatrix &, float alpha)
1242 {
1243     auto combinedAlpha = alpha * mModel.opacity(frameNo);
1244     auto color = mModel.color(frameNo).toColor(combinedAlpha);
1245 
1246     VBrush brush(color);
1247     mDrawable.setBrush(brush);
1248     mDrawable.setFillRule(mModel.fillRule());
1249 
1250     return !color.isTransparent();
1251 }
1252 
GradientFill(model::GradientFill * data)1253 renderer::GradientFill::GradientFill(model::GradientFill *data)
1254     : renderer::Paint(data->isStatic()), mData(data)
1255 {
1256     mDrawable.setName(mData->name());
1257 }
1258 
updateContent(int frameNo,const VMatrix & matrix,float alpha)1259 bool renderer::GradientFill::updateContent(int frameNo, const VMatrix &matrix,
1260                                            float alpha)
1261 {
1262     float combinedAlpha = alpha * mData->opacity(frameNo);
1263 
1264     mData->update(mGradient, frameNo);
1265     mGradient->setAlpha(combinedAlpha);
1266     mGradient->mMatrix = matrix;
1267     mDrawable.setBrush(VBrush(mGradient.get()));
1268     mDrawable.setFillRule(mData->fillRule());
1269 
1270     return !vIsZero(combinedAlpha);
1271 }
1272 
Stroke(model::Stroke * data)1273 renderer::Stroke::Stroke(model::Stroke *data)
1274     : renderer::Paint(data->isStatic()), mModel(data)
1275 {
1276     mDrawable.setName(mModel.name());
1277     if (mModel.hasDashInfo()) {
1278         mDrawable.setType(VDrawable::Type::StrokeWithDash);
1279     } else {
1280         mDrawable.setType(VDrawable::Type::Stroke);
1281     }
1282 }
1283 
1284 static vthread_local std::vector<float> Dash_Vector;
1285 
updateContent(int frameNo,const VMatrix & matrix,float alpha)1286 bool renderer::Stroke::updateContent(int frameNo, const VMatrix &matrix,
1287                                      float alpha)
1288 {
1289     auto combinedAlpha = alpha * mModel.opacity(frameNo);
1290     auto color = mModel.color(frameNo).toColor(combinedAlpha);
1291 
1292     VBrush brush(color);
1293     mDrawable.setBrush(brush);
1294     float scale = matrix.scale();
1295     mDrawable.setStrokeInfo(mModel.capStyle(), mModel.joinStyle(),
1296                             mModel.miterLimit(),
1297                             mModel.strokeWidth(frameNo) * scale);
1298 
1299     if (mModel.hasDashInfo()) {
1300         Dash_Vector.clear();
1301         mModel.getDashInfo(frameNo, Dash_Vector);
1302         if (!Dash_Vector.empty()) {
1303             for (auto &elm : Dash_Vector) elm *= scale;
1304             mDrawable.setDashInfo(Dash_Vector);
1305         }
1306     }
1307 
1308     return !color.isTransparent();
1309 }
1310 
GradientStroke(model::GradientStroke * data)1311 renderer::GradientStroke::GradientStroke(model::GradientStroke *data)
1312     : renderer::Paint(data->isStatic()), mData(data)
1313 {
1314     mDrawable.setName(mData->name());
1315     if (mData->hasDashInfo()) {
1316         mDrawable.setType(VDrawable::Type::StrokeWithDash);
1317     } else {
1318         mDrawable.setType(VDrawable::Type::Stroke);
1319     }
1320 }
1321 
updateContent(int frameNo,const VMatrix & matrix,float alpha)1322 bool renderer::GradientStroke::updateContent(int frameNo, const VMatrix &matrix,
1323                                              float alpha)
1324 {
1325     float combinedAlpha = alpha * mData->opacity(frameNo);
1326 
1327     mData->update(mGradient, frameNo);
1328     mGradient->setAlpha(combinedAlpha);
1329     mGradient->mMatrix = matrix;
1330     auto scale = mGradient->mMatrix.scale();
1331     mDrawable.setBrush(VBrush(mGradient.get()));
1332     mDrawable.setStrokeInfo(mData->capStyle(), mData->joinStyle(),
1333                             mData->miterLimit(), mData->width(frameNo) * scale);
1334 
1335     if (mData->hasDashInfo()) {
1336         Dash_Vector.clear();
1337         mData->getDashInfo(frameNo, Dash_Vector);
1338         if (!Dash_Vector.empty()) {
1339             for (auto &elm : Dash_Vector) elm *= scale;
1340             mDrawable.setDashInfo(Dash_Vector);
1341         }
1342     }
1343 
1344     return !vIsZero(combinedAlpha);
1345 }
1346 
update(int frameNo,const VMatrix &,float,const DirtyFlag &)1347 void renderer::Trim::update(int frameNo, const VMatrix & /*parentMatrix*/,
1348                             float /*parentAlpha*/, const DirtyFlag & /*flag*/)
1349 {
1350     mDirty = false;
1351 
1352     if (mCache.mFrameNo == frameNo) return;
1353 
1354     model::Trim::Segment segment = mData->segment(frameNo);
1355 
1356     if (!(vCompare(mCache.mSegment.start, segment.start) &&
1357           vCompare(mCache.mSegment.end, segment.end))) {
1358         mDirty = true;
1359         mCache.mSegment = segment;
1360     }
1361     mCache.mFrameNo = frameNo;
1362 }
1363 
update()1364 void renderer::Trim::update()
1365 {
1366     // when both path and trim are not dirty
1367     if (!(mDirty || pathDirty())) return;
1368 
1369     if (vCompare(mCache.mSegment.start, mCache.mSegment.end)) {
1370         for (auto &i : mPathItems) {
1371             i->updatePath(VPath());
1372         }
1373         return;
1374     }
1375 
1376     if (vCompare(std::fabs(mCache.mSegment.start - mCache.mSegment.end), 1)) {
1377         for (auto &i : mPathItems) {
1378             i->updatePath(i->localPath());
1379         }
1380         return;
1381     }
1382 
1383     if (mData->type() == model::Trim::TrimType::Simultaneously) {
1384         for (auto &i : mPathItems) {
1385             mPathMesure.setRange(mCache.mSegment.start, mCache.mSegment.end);
1386             i->updatePath(mPathMesure.trim(i->localPath()));
1387         }
1388     } else {  // model::Trim::TrimType::Individually
1389         float totalLength = 0.0;
1390         for (auto &i : mPathItems) {
1391             totalLength += i->localPath().length();
1392         }
1393         float start = totalLength * mCache.mSegment.start;
1394         float end = totalLength * mCache.mSegment.end;
1395 
1396         if (start < end) {
1397             float curLen = 0.0;
1398             for (auto &i : mPathItems) {
1399                 if (curLen > end) {
1400                     // update with empty path.
1401                     i->updatePath(VPath());
1402                     continue;
1403                 }
1404                 float len = i->localPath().length();
1405 
1406                 if (curLen < start && curLen + len < start) {
1407                     curLen += len;
1408                     // update with empty path.
1409                     i->updatePath(VPath());
1410                     continue;
1411                 } else if (start <= curLen && end >= curLen + len) {
1412                     // inside segment
1413                     curLen += len;
1414                     continue;
1415                 } else {
1416                     float local_start = start > curLen ? start - curLen : 0;
1417                     local_start /= len;
1418                     float local_end = curLen + len < end ? len : end - curLen;
1419                     local_end /= len;
1420                     mPathMesure.setRange(local_start, local_end);
1421                     i->updatePath(mPathMesure.trim(i->localPath()));
1422                     curLen += len;
1423                 }
1424             }
1425         }
1426     }
1427 }
1428 
addPathItems(std::vector<renderer::Shape * > & list,size_t startOffset)1429 void renderer::Trim::addPathItems(std::vector<renderer::Shape *> &list,
1430                                   size_t                          startOffset)
1431 {
1432     std::copy(list.begin() + startOffset, list.end(),
1433               back_inserter(mPathItems));
1434 }
1435 
Repeater(model::Repeater * data,VArenaAlloc * allocator)1436 renderer::Repeater::Repeater(model::Repeater *data, VArenaAlloc *allocator)
1437     : mRepeaterData(data)
1438 {
1439     assert(mRepeaterData->content());
1440 
1441     mCopies = mRepeaterData->maxCopies();
1442 
1443     for (int i = 0; i < mCopies; i++) {
1444         auto content = allocator->make<renderer::Group>(
1445             mRepeaterData->content(), allocator);
1446         // content->setParent(this);
1447         mContents.push_back(content);
1448     }
1449 }
1450 
update(int frameNo,const VMatrix & parentMatrix,float parentAlpha,const DirtyFlag & flag)1451 void renderer::Repeater::update(int frameNo, const VMatrix &parentMatrix,
1452                                 float parentAlpha, const DirtyFlag &flag)
1453 {
1454     DirtyFlag newFlag = flag;
1455 
1456     float copies = mRepeaterData->copies(frameNo);
1457     int   visibleCopies = int(copies);
1458 
1459     if (visibleCopies == 0) {
1460         mHidden = true;
1461         return;
1462     }
1463 
1464     mHidden = false;
1465 
1466     if (!mRepeaterData->isStatic()) newFlag |= DirtyFlagBit::Matrix;
1467 
1468     float offset = mRepeaterData->offset(frameNo);
1469     float startOpacity = mRepeaterData->mTransform.startOpacity(frameNo);
1470     float endOpacity = mRepeaterData->mTransform.endOpacity(frameNo);
1471 
1472     newFlag |= DirtyFlagBit::Alpha;
1473 
1474     for (int i = 0; i < mCopies; ++i) {
1475         float newAlpha =
1476             parentAlpha * lerp(startOpacity, endOpacity, i / copies);
1477 
1478         // hide rest of the copies , @TODO find a better solution.
1479         if (i >= visibleCopies) newAlpha = 0;
1480 
1481         VMatrix result = mRepeaterData->mTransform.matrix(frameNo, i + offset) *
1482                          parentMatrix;
1483         mContents[i]->update(frameNo, result, newAlpha, newFlag);
1484     }
1485 }
1486 
renderList(std::vector<VDrawable * > & list)1487 void renderer::Repeater::renderList(std::vector<VDrawable *> &list)
1488 {
1489     if (mHidden) return;
1490     return renderer::Group::renderList(list);
1491 }
1492