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