2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 #include "lottieitem.h"
26 #include "vimageloader.h"
29 * 1. time stretch is pre calculated and applied to all the properties of the
30 * lottilayer model and all its children
31 * 2. The frame property could be reversed using,time-reverse layer property in
32 * AE. which means (start frame > endFrame) 3.
35 LOTCompItem::LOTCompItem(LOTModel *model)
36 : mRootModel(model), mUpdateViewBox(false), mCurFrameNo(-1)
38 mCompData = model->mRoot.get();
39 mRootLayer = createLayerItem(mCompData->mRootLayer.get());
40 mRootLayer->updateStaticProperty();
41 mViewSize = mCompData->size();
44 std::unique_ptr<LOTLayerItem>
45 LOTCompItem::createLayerItem(LOTLayerData *layerData)
47 switch (layerData->mLayerType) {
48 case LayerType::Precomp: {
49 return std::make_unique<LOTCompLayerItem>(layerData);
52 case LayerType::Solid: {
53 return std::make_unique<LOTSolidLayerItem>(layerData);
56 case LayerType::Shape: {
57 return std::make_unique<LOTShapeLayerItem>(layerData);
60 case LayerType::Null: {
61 return std::make_unique<LOTNullLayerItem>(layerData);
64 case LayerType::Image: {
65 return std::make_unique<LOTImageLayerItem>(layerData);
74 void LOTCompItem::resize(const VSize &size)
76 if (mViewSize == size) return;
78 mUpdateViewBox = true;
81 VSize LOTCompItem::size() const
86 bool LOTCompItem::update(int frameNo)
88 // check if cached frame is same as requested frame.
89 if (!mUpdateViewBox && (mCurFrameNo == frameNo)) return false;
92 * if viewbox dosen't scale exactly to the viewport
93 * we scale the viewbox keeping AspectRatioPreserved and then align the
94 * viewbox to the viewport using AlignCenter rule.
96 VSize viewPort = mViewSize;
97 VSize viewBox = mCompData->size();
99 float sx = float(viewPort.width()) / viewBox.width();
100 float sy = float(viewPort.height()) / viewBox.height();
101 float scale = fmin(sx, sy);
102 float tx = (viewPort.width() - viewBox.width() * scale) * 0.5;
103 float ty = (viewPort.height() - viewBox.height() * scale) * 0.5;
106 m.translate(tx, ty).scale(scale, scale);
107 mRootLayer->update(frameNo, m, 1.0);
109 mCurFrameNo = frameNo;
110 mUpdateViewBox = false;
114 void LOTCompItem::buildRenderTree()
116 mRootLayer->buildLayerNode();
119 const LOTLayerNode * LOTCompItem::renderTree() const
121 return mRootLayer->layerNode();
124 bool LOTCompItem::render(const lottie::Surface &surface)
126 VBitmap bitmap(reinterpret_cast<uchar *>(surface.buffer()),
127 surface.width(), surface.height(),
128 surface.bytesPerLine(), VBitmap::Format::ARGB32_Premultiplied);
130 /* schedule all preprocess task for this frame at once.
132 mDrawableList.clear();
133 mRootLayer->renderList(mDrawableList);
134 VRect clip(0, 0, surface.width(), surface.height());
135 for (auto &e : mDrawableList) {
139 VPainter painter(&bitmap);
140 mRootLayer->render(&painter, {}, {});
145 void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
146 float parentAlpha, const DirtyFlag &flag)
148 if (flag.testFlag(DirtyFlagBit::None) && mData->isStatic()) return;
150 if (mData->mShape.isStatic()) {
151 if (mLocalPath.empty()) {
152 mData->mShape.value(frameNo).toPath(mLocalPath);
155 mData->mShape.value(frameNo).toPath(mLocalPath);
157 float opacity = mData->opacity(frameNo);
158 opacity = opacity * parentAlpha;
159 mCombinedAlpha = opacity;
161 mFinalPath.clone(mLocalPath);
162 mFinalPath.transform(parentMatrix);
164 VPath tmp = mFinalPath;
166 if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
169 VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
173 VRle LOTMaskItem::rle()
175 if (mRleFuture && mRleFuture->valid()) {
176 mRle = mRleFuture->get();
177 if (!vCompare(mCombinedAlpha, 1.0f))
178 mRle *= (mCombinedAlpha * 255);
179 if (mData->mInv) mRle.invert();
184 void LOTLayerItem::buildLayerNode()
187 mLayerCNode = std::make_unique<LOTLayerNode>();
188 mLayerCNode->mMaskList.ptr = nullptr;
189 mLayerCNode->mMaskList.size = 0;
190 mLayerCNode->mLayerList.ptr = nullptr;
191 mLayerCNode->mLayerList.size = 0;
192 mLayerCNode->mNodeList.ptr = nullptr;
193 mLayerCNode->mNodeList.size = 0;
194 mLayerCNode->mMatte = MatteNone;
195 mLayerCNode->mVisible = 0;
196 mLayerCNode->mClipPath.ptPtr = nullptr;
197 mLayerCNode->mClipPath.elmPtr = nullptr;
198 mLayerCNode->mClipPath.ptCount = 0;
199 mLayerCNode->mClipPath.elmCount = 0;
201 mLayerCNode->mVisible = visible();
204 switch (mLayerData->mMatteType) {
205 case MatteType::Alpha:
206 mLayerCNode->mMatte = MatteAlpha;
208 case MatteType::AlphaInv:
209 mLayerCNode->mMatte = MatteAlphaInv;
211 case MatteType::Luma:
212 mLayerCNode->mMatte = MatteLuma;
214 case MatteType::LumaInv:
215 mLayerCNode->mMatte = MatteLumaInv;
218 mLayerCNode->mMatte = MatteNone;
224 for (const auto &mask : mLayerMask->mMasks) {
226 const std::vector<VPath::Element> &elm = mask.mFinalPath.elements();
227 const std::vector<VPointF> & pts = mask.mFinalPath.points();
228 const float *ptPtr = reinterpret_cast<const float *>(pts.data());
229 const char * elmPtr = reinterpret_cast<const char *>(elm.data());
230 cNode.mPath.ptPtr = ptPtr;
231 cNode.mPath.ptCount = pts.size();
232 cNode.mPath.elmPtr = elmPtr;
233 cNode.mPath.elmCount = elm.size();
234 switch (mask.maskMode()) {
235 case LOTMaskData::Mode::Add:
236 cNode.mMode = MaskModeAdd;
238 case LOTMaskData::Mode::Substarct:
239 cNode.mMode = MaskModeSubstract;
241 case LOTMaskData::Mode::Intersect:
242 cNode.mMode = MaskModeIntersect;
244 case LOTMaskData::Mode::Difference:
245 cNode.mMode = MaskModeDifference;
248 cNode.mMode = MaskModeAdd;
251 mMasksCNode.push_back(std::move(cNode));
253 mLayerCNode->mMaskList.ptr = mMasksCNode.data();
254 mLayerCNode->mMaskList.size = mMasksCNode.size();
258 void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &matteRle)
260 mDrawableList.clear();
261 renderList(mDrawableList);
265 mask = mLayerMask->maskRle(painter->clipBoundingRect());
266 if (!inheritMask.empty())
267 mask = mask & inheritMask;
268 // if resulting mask is empty then return.
275 for (auto &i : mDrawableList) {
276 painter->setBrush(i->mBrush);
278 if (matteRle.empty()) {
281 painter->drawRle(VPoint(), rle);
284 painter->drawRle(rle, mask);
288 if (!mask.empty()) rle = rle & mask;
290 if (rle.empty()) continue;
291 if (matteType() == MatteType::AlphaInv) {
292 rle = rle - matteRle;
293 painter->drawRle(VPoint(), rle);
295 // render with matteRle as clip.
296 painter->drawRle(rle, matteRle);
302 LOTLayerMaskItem::LOTLayerMaskItem(LOTLayerData *layerData)
304 mMasks.reserve(layerData->mMasks.size());
306 for (auto &i : layerData->mMasks) {
307 mMasks.emplace_back(i.get());
308 mStatic &= i->isStatic();
312 void LOTLayerMaskItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
314 if (flag.testFlag(DirtyFlagBit::None) && isStatic()) return;
316 for (auto &i : mMasks) {
317 i.update(frameNo, parentMatrix, parentAlpha, flag);
322 VRle LOTLayerMaskItem::maskRle(const VRect &clipRect)
324 if (!mDirty) return mRle;
327 for (auto &i : mMasks) {
328 switch (i.maskMode()) {
329 case LOTMaskData::Mode::Add: {
333 case LOTMaskData::Mode::Substarct: {
334 if (rle.empty() && !clipRect.empty())
335 rle = VRle::toRle(clipRect);
339 case LOTMaskData::Mode::Intersect: {
343 case LOTMaskData::Mode::Difference: {
358 LOTLayerItem::LOTLayerItem(LOTLayerData *layerData): mLayerData(layerData)
360 if (mLayerData->mHasMask)
361 mLayerMask = std::make_unique<LOTLayerMaskItem>(mLayerData);
364 void LOTLayerItem::updateStaticProperty()
366 if (mParentLayer) mParentLayer->updateStaticProperty();
368 mStatic = mLayerData->isStatic();
369 mStatic = mParentLayer ? (mStatic & mParentLayer->isStatic()) : mStatic;
370 mStatic = mPrecompLayer ? (mStatic & mPrecompLayer->isStatic()) : mStatic;
373 void LOTLayerItem::update(int frameNumber, const VMatrix &parentMatrix,
376 mFrameNo = frameNumber;
377 // 1. check if the layer is part of the current frame
378 if (!visible()) return;
380 // 2. calculate the parent matrix and alpha
381 VMatrix m = matrix(frameNo());
383 float alpha = parentAlpha * opacity(frameNo());
385 // 3. update the dirty flag based on the change
386 if (!mCombinedMatrix.fuzzyCompare(m)) {
387 mDirtyFlag |= DirtyFlagBit::Matrix;
389 if (!vCompare(mCombinedAlpha, alpha)) {
390 mDirtyFlag |= DirtyFlagBit::Alpha;
393 mCombinedAlpha = alpha;
395 // 4. update the mask
397 mLayerMask->update(frameNo(), m, alpha, mDirtyFlag);
400 // 5. if no parent property change and layer is static then nothing to do.
401 if (flag().testFlag(DirtyFlagBit::None) && isStatic()) return;
403 // 6. update the content of the layer
406 // 7. reset the dirty flag
407 mDirtyFlag = DirtyFlagBit::None;
410 float LOTLayerItem::opacity(int frameNo) const
412 return mLayerData->mTransform->opacity(frameNo);
415 VMatrix LOTLayerItem::matrix(int frameNo) const
418 return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient()) *
419 mParentLayer->matrix(frameNo);
421 return mLayerData->mTransform->matrix(frameNo, mLayerData->autoOrient());
424 bool LOTLayerItem::visible() const
426 if (frameNo() >= mLayerData->inFrame() &&
427 frameNo() < mLayerData->outFrame())
433 LOTCompLayerItem::LOTCompLayerItem(LOTLayerData *layerModel)
434 : LOTLayerItem(layerModel)
436 // 1. create layer item
437 for (auto &i : mLayerData->mChildren) {
438 LOTLayerData *layerModel = static_cast<LOTLayerData *>(i.get());
439 auto layerItem = LOTCompItem::createLayerItem(layerModel);
440 if (layerItem) mLayers.push_back(std::move(layerItem));
443 // 2. update parent layer
444 for (const auto &layer : mLayers) {
445 int id = layer->parentId();
447 auto search = std::find_if(mLayers.begin(), mLayers.end(),
448 [id](const auto& val){ return val->id() == id;});
449 if (search != mLayers.end()) layer->setParentLayer((*search).get());
451 // update the precomp layer if its not the root layer.
452 if (!layerModel->root()) layer->setPrecompLayer(this);
455 // 3. keep the layer in back-to-front order.
456 // as lottie model keeps the data in front-toback-order.
457 std::reverse(mLayers.begin(), mLayers.end());
459 // 4. check if its a nested composition
460 if (!layerModel->layerSize().empty()) {
461 mClipper = std::make_unique<LOTClipperItem>(layerModel->layerSize());
465 void LOTCompLayerItem::updateStaticProperty()
467 LOTLayerItem::updateStaticProperty();
469 for (const auto &layer : mLayers) {
470 layer->updateStaticProperty();
474 void LOTCompLayerItem::buildLayerNode()
476 LOTLayerItem::buildLayerNode();
478 const std::vector<VPath::Element> &elm = mClipper->mPath.elements();
479 const std::vector<VPointF> & pts = mClipper->mPath.points();
480 const float *ptPtr = reinterpret_cast<const float *>(pts.data());
481 const char * elmPtr = reinterpret_cast<const char *>(elm.data());
482 layerNode()->mClipPath.ptPtr = ptPtr;
483 layerNode()->mClipPath.elmPtr = elmPtr;
484 layerNode()->mClipPath.ptCount = 2 * pts.size();
485 layerNode()->mClipPath.elmCount = elm.size();
487 if (mLayers.size() != mLayersCNode.size()) {
488 for (const auto &layer : mLayers) {
489 layer->buildLayerNode();
490 mLayersCNode.push_back(layer->layerNode());
492 layerNode()->mLayerList.ptr = mLayersCNode.data();
493 layerNode()->mLayerList.size = mLayersCNode.size();
495 for (const auto &layer : mLayers) {
496 layer->buildLayerNode();
501 void LOTCompLayerItem::render(VPainter *painter, const VRle &inheritMask, const VRle &matteRle)
505 mask = mLayerMask->maskRle(painter->clipBoundingRect());
506 if (!inheritMask.empty())
507 mask = mask & inheritMask;
508 // if resulting mask is empty then return.
517 mask = mClipper->rle();
519 mask = mClipper->rle() & mask;
523 LOTLayerItem *matteLayer = nullptr;
524 for (const auto &layer : mLayers) {
525 if (layer->hasMatte()) {
527 vWarning << "two consecutive layer has matter : not supported";
529 matteLayer = layer.get();
533 if (layer->visible()) {
535 if (matteLayer->visible())
536 renderMatteLayer(painter, mask, matteRle, matteLayer, layer.get());
538 layer->render(painter, mask, matteRle);
542 matteLayer = nullptr;
546 void LOTCompLayerItem::renderMatteLayer(VPainter *painter,
548 const VRle &matteRle,
552 VSize size = painter->clipBoundingRect().size();
553 // Decide if we can use fast matte.
554 // 1. draw src layer to matte buffer
556 VBitmap srcBitmap(size.width(), size.height(), VBitmap::Format::ARGB32_Premultiplied);
557 srcPainter.begin(&srcBitmap);
558 src->render(&srcPainter, mask, matteRle);
561 // 2. draw layer to layer buffer
562 VPainter layerPainter;
563 VBitmap layerBitmap(size.width(), size.height(), VBitmap::Format::ARGB32_Premultiplied);
564 layerPainter.begin(&layerBitmap);
565 layer->render(&layerPainter, mask, matteRle);
567 // 2.1update composition mode
568 switch (layer->matteType()) {
569 case MatteType::Alpha:
570 case MatteType::Luma: {
571 layerPainter.setCompositionMode(VPainter::CompositionMode::CompModeDestIn);
574 case MatteType::AlphaInv:
575 case MatteType::LumaInv: {
576 layerPainter.setCompositionMode(VPainter::CompositionMode::CompModeDestOut);
583 //2.2 update srcBuffer if the matte is luma type
584 if (layer->matteType() == MatteType::Luma ||
585 layer->matteType() == MatteType::LumaInv) {
586 srcBitmap.updateLuma();
589 // 2.3 draw src buffer as mask
590 layerPainter.drawBitmap(VPoint(), srcBitmap);
592 // 3. draw the result buffer into painter
593 painter->drawBitmap(VPoint(), layerBitmap);
596 void LOTClipperItem::update(const VMatrix &matrix)
599 mPath.addRect(VRectF(0,0, mSize.width(), mSize.height()));
600 mPath.transform(matrix);
604 if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
607 VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
611 VRle LOTClipperItem::rle()
613 if (mRleFuture && mRleFuture->valid()) {
614 mRle = mRleFuture->get();
619 void LOTCompLayerItem::updateContent()
621 if (mClipper && flag().testFlag(DirtyFlagBit::Matrix)) {
622 mClipper->update(combinedMatrix());
624 int mappedFrame = mLayerData->timeRemap(frameNo());
625 for (const auto &layer : mLayers) {
626 layer->update( mappedFrame, combinedMatrix(), combinedAlpha());
630 void LOTCompLayerItem::renderList(std::vector<VDrawable *> &list)
632 if (!visible()) return;
634 for (const auto &layer : mLayers) {
635 layer->renderList(list);
639 LOTSolidLayerItem::LOTSolidLayerItem(LOTLayerData *layerData)
640 : LOTLayerItem(layerData)
644 void LOTSolidLayerItem::updateContent()
647 mRenderNode = std::make_unique<LOTDrawable>();
648 mRenderNode->mType = VDrawable::Type::Fill;
649 mRenderNode->mFlag |= VDrawable::DirtyState::All;
652 if (flag() & DirtyFlagBit::Matrix) {
655 VRectF(0, 0, mLayerData->solidWidth(), mLayerData->solidHeight()));
656 path.transform(combinedMatrix());
657 mRenderNode->mFlag |= VDrawable::DirtyState::Path;
658 mRenderNode->mPath = path;
660 if (flag() & DirtyFlagBit::Alpha) {
661 LottieColor color = mLayerData->solidColor();
662 VBrush brush(color.toColor(combinedAlpha()));
663 mRenderNode->setBrush(brush);
664 mRenderNode->mFlag |= VDrawable::DirtyState::Brush;
668 void LOTSolidLayerItem::buildLayerNode()
670 LOTLayerItem::buildLayerNode();
672 mDrawableList.clear();
673 renderList(mDrawableList);
676 for (auto &i : mDrawableList) {
677 LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
679 mCNodeList.push_back(lotDrawable->mCNode.get());
681 layerNode()->mNodeList.ptr = mCNodeList.data();
682 layerNode()->mNodeList.size = mCNodeList.size();
685 void LOTSolidLayerItem::renderList(std::vector<VDrawable *> &list)
687 if (!visible()) return;
689 list.push_back(mRenderNode.get());
692 LOTImageLayerItem::LOTImageLayerItem(LOTLayerData *layerData)
693 : LOTLayerItem(layerData)
697 void LOTImageLayerItem::updateContent()
700 mRenderNode = std::make_unique<LOTDrawable>();
701 mRenderNode->mType = VDrawable::Type::Fill;
702 mRenderNode->mFlag |= VDrawable::DirtyState::All;
704 //@TODO find a better way to load
705 // so that can be shared by multiple layers
706 if (!mLayerData->mAsset->mImagePath.empty()) {
707 VBitmap img = VImageLoader::instance().load(mLayerData->mAsset->mImagePath.c_str());
709 mRenderNode->setBrush(brush);
713 if (flag() & DirtyFlagBit::Matrix) {
716 VRectF(0, 0, mLayerData->mAsset->mWidth, mLayerData->mAsset->mHeight));
717 path.transform(combinedMatrix());
718 mRenderNode->mFlag |= VDrawable::DirtyState::Path;
719 mRenderNode->mPath = path;
720 mRenderNode->mBrush.setMatrix(combinedMatrix());
723 if (flag() & DirtyFlagBit::Alpha) {
724 //@TODO handle alpha with the image.
728 void LOTImageLayerItem::renderList(std::vector<VDrawable *> &list)
730 if (!visible()) return;
732 list.push_back(mRenderNode.get());
735 void LOTImageLayerItem::buildLayerNode()
737 LOTLayerItem::buildLayerNode();
739 mDrawableList.clear();
740 renderList(mDrawableList);
743 for (auto &i : mDrawableList) {
744 LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
746 mCNodeList.push_back(lotDrawable->mCNode.get());
748 layerNode()->mNodeList.ptr = mCNodeList.data();
749 layerNode()->mNodeList.size = mCNodeList.size();
752 LOTNullLayerItem::LOTNullLayerItem(LOTLayerData *layerData)
753 : LOTLayerItem(layerData)
756 void LOTNullLayerItem::updateContent() {}
758 LOTShapeLayerItem::LOTShapeLayerItem(LOTLayerData *layerData)
759 : LOTLayerItem(layerData)
761 mRoot = std::make_unique<LOTContentGroupItem>(nullptr);
762 mRoot->addChildren(layerData);
764 std::vector<LOTPathDataItem *> list;
765 mRoot->processPaintItems(list);
767 if (layerData->hasPathOperator()) {
769 mRoot->processTrimItems(list);
773 std::unique_ptr<LOTContentItem>
774 LOTShapeLayerItem::createContentItem(LOTData *contentData)
776 switch (contentData->type()) {
777 case LOTData::Type::ShapeGroup: {
778 return std::make_unique<LOTContentGroupItem>(
779 static_cast<LOTGroupData *>(contentData));
782 case LOTData::Type::Rect: {
783 return std::make_unique<LOTRectItem>(static_cast<LOTRectData *>(contentData));
786 case LOTData::Type::Ellipse: {
787 return std::make_unique<LOTEllipseItem>(static_cast<LOTEllipseData *>(contentData));
790 case LOTData::Type::Shape: {
791 return std::make_unique<LOTShapeItem>(static_cast<LOTShapeData *>(contentData));
794 case LOTData::Type::Polystar: {
795 return std::make_unique<LOTPolystarItem>(static_cast<LOTPolystarData *>(contentData));
798 case LOTData::Type::Fill: {
799 return std::make_unique<LOTFillItem>(static_cast<LOTFillData *>(contentData));
802 case LOTData::Type::GFill: {
803 return std::make_unique<LOTGFillItem>(static_cast<LOTGFillData *>(contentData));
806 case LOTData::Type::Stroke: {
807 return std::make_unique<LOTStrokeItem>(static_cast<LOTStrokeData *>(contentData));
810 case LOTData::Type::GStroke: {
811 return std::make_unique<LOTGStrokeItem>(static_cast<LOTGStrokeData *>(contentData));
814 case LOTData::Type::Repeater: {
815 return std::make_unique<LOTRepeaterItem>(static_cast<LOTRepeaterData *>(contentData));
818 case LOTData::Type::Trim: {
819 return std::make_unique<LOTTrimItem>(static_cast<LOTTrimData *>(contentData));
828 void LOTShapeLayerItem::updateContent()
830 mRoot->update(frameNo(), combinedMatrix(), combinedAlpha(), flag());
832 if (mLayerData->hasPathOperator()) {
837 void LOTShapeLayerItem::buildLayerNode()
839 LOTLayerItem::buildLayerNode();
841 mDrawableList.clear();
842 renderList(mDrawableList);
845 for (auto &i : mDrawableList) {
846 LOTDrawable *lotDrawable = static_cast<LOTDrawable *>(i);
848 mCNodeList.push_back(lotDrawable->mCNode.get());
850 layerNode()->mNodeList.ptr = mCNodeList.data();
851 layerNode()->mNodeList.size = mCNodeList.size();
854 void LOTShapeLayerItem::renderList(std::vector<VDrawable *> &list)
856 if (!visible()) return;
857 mRoot->renderList(list);
860 LOTContentGroupItem::LOTContentGroupItem(LOTGroupData *data) : mData(data)
865 void LOTContentGroupItem::addChildren(LOTGroupData *data)
869 for (auto &i : data->mChildren) {
870 auto content = LOTShapeLayerItem::createContentItem(i.get());
872 content->setParent(this);
873 mContents.push_back(std::move(content));
877 // keep the content in back-to-front order.
878 std::reverse(mContents.begin(), mContents.end());
881 void LOTContentGroupItem::update(int frameNo, const VMatrix &parentMatrix,
882 float parentAlpha, const DirtyFlag &flag)
884 VMatrix m = parentMatrix;
885 float alpha = parentAlpha;
886 DirtyFlag newFlag = flag;
888 if (mData && mData->mTransform) {
889 // update the matrix and the flag
890 if ((flag & DirtyFlagBit::Matrix) ||
891 !mData->mTransform->staticMatrix()) {
892 newFlag |= DirtyFlagBit::Matrix;
894 m = mData->mTransform->matrix(frameNo);
896 alpha *= mData->mTransform->opacity(frameNo);
898 if (!vCompare(alpha, parentAlpha)) {
899 newFlag |= DirtyFlagBit::Alpha;
905 for (const auto &content : mContents) {
906 content->update(frameNo, m, alpha, newFlag);
910 void LOTContentGroupItem::applyTrim()
912 for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
913 auto content = (*i).get();
914 if (auto trim = dynamic_cast<LOTTrimItem *>(content)) {
916 } else if (auto group = dynamic_cast<LOTContentGroupItem *>(content)) {
922 void LOTContentGroupItem::renderList(std::vector<VDrawable *> &list)
924 for (const auto &content : mContents) {
925 content->renderList(list);
929 void LOTContentGroupItem::processPaintItems(
930 std::vector<LOTPathDataItem *> &list)
932 int curOpCount = list.size();
933 for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
934 auto content = (*i).get();
935 if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
936 // add it to the list
937 list.push_back(pathNode);
938 } else if (auto paintNode = dynamic_cast<LOTPaintDataItem *>(content)) {
939 // the node is a paint data node update the path list of the paint item.
940 paintNode->addPathItems(list, curOpCount);
941 } else if (auto groupNode =
942 dynamic_cast<LOTContentGroupItem *>(content)) {
943 // update the groups node with current list
944 groupNode->processPaintItems(list);
949 void LOTContentGroupItem::processTrimItems(
950 std::vector<LOTPathDataItem *> &list)
952 int curOpCount = list.size();
953 for (auto i = mContents.rbegin(); i != mContents.rend(); ++i) {
954 auto content = (*i).get();
955 if (auto pathNode = dynamic_cast<LOTPathDataItem *>(content)) {
956 // add it to the list
957 list.push_back(pathNode);
958 } else if (auto trimNode = dynamic_cast<LOTTrimItem *>(content)) {
959 // the node is a paint data node update the path list of the paint item.
960 trimNode->addPathItems(list, curOpCount);
961 } else if (auto groupNode =
962 dynamic_cast<LOTContentGroupItem *>(content)) {
963 // update the groups node with current list
964 groupNode->processTrimItems(list);
970 * LOTPathDataItem uses 3 path objects for path object reuse.
971 * mLocalPath - keeps track of the local path of the item before
972 * applying path operation and transformation.
973 * mTemp - keeps a referece to the mLocalPath and can be updated by the
974 * path operation objects(trim, merge path),
975 * mFinalPath - it takes a deep copy of the intermediate path(mTemp) each time
976 * when the path is dirty(so if path changes every frame we don't realloc just copy to the
978 * NOTE: As path objects are COW objects we have to be carefull about the refcount so that
979 * we don't generate deep copy while modifying the path objects.
981 void LOTPathDataItem::update(int frameNo, const VMatrix &,
982 float, const DirtyFlag &flag)
984 mPathChanged = false;
986 // 1. update the local path if needed
987 if (hasChanged(frameNo)) {
988 // loose the reference to mLocalPath if any
989 // from the last frame update.
992 updatePath(mLocalPath, frameNo);
996 // 2. keep a reference path in temp in case there is some
997 // path operation like trim which will update the path.
998 // we don't want to update the local path.
1001 // 3. compute the final path with parentMatrix
1002 if ((flag & DirtyFlagBit::Matrix) || mPathChanged) {
1003 mPathChanged = true;
1007 const VPath & LOTPathDataItem::finalPath()
1009 if (mPathChanged || mNeedUpdate) {
1010 mFinalPath.clone(mTemp);
1011 mFinalPath.transform(static_cast<LOTContentGroupItem *>(parent())->matrix());
1012 mNeedUpdate = false;
1016 LOTRectItem::LOTRectItem(LOTRectData *data)
1017 : LOTPathDataItem(data->isStatic()), mData(data)
1021 void LOTRectItem::updatePath(VPath& path, int frameNo)
1023 VPointF pos = mData->mPos.value(frameNo);
1024 VPointF size = mData->mSize.value(frameNo);
1025 float roundness = mData->mRound.value(frameNo);
1026 VRectF r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1030 path.addRoundRect(r, roundness, mData->direction());
1033 LOTEllipseItem::LOTEllipseItem(LOTEllipseData *data)
1034 : LOTPathDataItem(data->isStatic()), mData(data)
1038 void LOTEllipseItem::updatePath(VPath& path, int frameNo)
1040 VPointF pos = mData->mPos.value(frameNo);
1041 VPointF size = mData->mSize.value(frameNo);
1042 VRectF r(pos.x() - size.x() / 2, pos.y() - size.y() / 2, size.x(),
1046 path.addOval(r, mData->direction());
1049 LOTShapeItem::LOTShapeItem(LOTShapeData *data)
1050 : LOTPathDataItem(data->isStatic()), mData(data)
1054 void LOTShapeItem::updatePath(VPath& path, int frameNo)
1056 mData->mShape.value(frameNo).toPath(path);
1059 LOTPolystarItem::LOTPolystarItem(LOTPolystarData *data)
1060 : LOTPathDataItem(data->isStatic()), mData(data)
1064 void LOTPolystarItem::updatePath(VPath& path, int frameNo)
1066 VPointF pos = mData->mPos.value(frameNo);
1067 float points = mData->mPointCount.value(frameNo);
1068 float innerRadius = mData->mInnerRadius.value(frameNo);
1069 float outerRadius = mData->mOuterRadius.value(frameNo);
1070 float innerRoundness = mData->mInnerRoundness.value(frameNo);
1071 float outerRoundness = mData->mOuterRoundness.value(frameNo);
1072 float rotation = mData->mRotation.value(frameNo);
1077 if (mData->mType == LOTPolystarData::PolyType::Star) {
1078 path.addPolystar(points, innerRadius, outerRadius, innerRoundness,
1079 outerRoundness, 0.0, 0.0, 0.0, mData->direction());
1081 path.addPolygon(points, outerRadius, outerRoundness, 0.0, 0.0, 0.0,
1082 mData->direction());
1085 m.translate(pos.x(), pos.y()).rotate(rotation);
1091 * PaintData Node handling
1094 LOTPaintDataItem::LOTPaintDataItem(bool staticContent):mDrawable(std::make_unique<LOTDrawable>()),
1095 mStaticContent(staticContent){}
1097 void LOTPaintDataItem::update(int frameNo, const VMatrix &parentMatrix,
1098 float parentAlpha, const DirtyFlag &flag)
1100 mRenderNodeUpdate = true;
1101 mParentAlpha = parentAlpha;
1105 updateContent(frameNo);
1108 void LOTPaintDataItem::updateRenderNode()
1111 for (auto &i : mPathItems) {
1121 for (auto &i : mPathItems) {
1122 mPath.addPath(i->finalPath());
1124 mDrawable->setPath(mPath);
1126 if (mDrawable->mFlag & VDrawable::DirtyState::Path)
1127 mDrawable->mPath = mPath;
1131 void LOTPaintDataItem::renderList(std::vector<VDrawable *> &list)
1133 if (mRenderNodeUpdate) {
1135 LOTPaintDataItem::updateRenderNode();
1136 mRenderNodeUpdate = false;
1138 list.push_back(mDrawable.get());
1142 void LOTPaintDataItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
1144 std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
1148 LOTFillItem::LOTFillItem(LOTFillData *data)
1149 : LOTPaintDataItem(data->isStatic()), mData(data)
1153 void LOTFillItem::updateContent(int frameNo)
1155 LottieColor c = mData->mColor.value(frameNo);
1156 float opacity = mData->opacity(frameNo);
1157 mColor = c.toColor(opacity);
1158 mFillRule = mData->fillRule();
1161 void LOTFillItem::updateRenderNode()
1163 VColor color = mColor;
1165 color.setAlpha(color.a * parentAlpha());
1166 VBrush brush(color);
1167 mDrawable->setBrush(brush);
1168 mDrawable->setFillRule(mFillRule);
1171 LOTGFillItem::LOTGFillItem(LOTGFillData *data)
1172 : LOTPaintDataItem(data->isStatic()), mData(data)
1176 void LOTGFillItem::updateContent(int frameNo)
1178 mData->update(mGradient, frameNo);
1179 mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
1180 mFillRule = mData->fillRule();
1183 void LOTGFillItem::updateRenderNode()
1185 mGradient->setAlpha(parentAlpha());
1186 mDrawable->setBrush(VBrush(mGradient.get()));
1187 mDrawable->setFillRule(mFillRule);
1190 LOTStrokeItem::LOTStrokeItem(LOTStrokeData *data)
1191 : LOTPaintDataItem(data->isStatic()), mData(data)
1196 void LOTStrokeItem::updateContent(int frameNo)
1198 LottieColor c = mData->mColor.value(frameNo);
1199 float opacity = mData->opacity(frameNo);
1200 mColor = c.toColor(opacity);
1201 mCap = mData->capStyle();
1202 mJoin = mData->joinStyle();
1203 mMiterLimit = mData->meterLimit();
1204 mWidth = mData->width(frameNo);
1205 if (mData->hasDashInfo()) {
1206 mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1210 static float getScale(const VMatrix &matrix)
1212 constexpr float SQRT_2 = 1.41421;
1214 VPointF p2(SQRT_2, SQRT_2);
1215 p1 = matrix.map(p1);
1216 p2 = matrix.map(p2);
1217 VPointF final = p2 - p1;
1219 return std::sqrt(final.x() * final.x() + final.y() * final.y()) / 2.0;
1222 void LOTStrokeItem::updateRenderNode()
1224 VColor color = mColor;
1226 color.setAlpha(color.a * parentAlpha());
1227 VBrush brush(color);
1228 mDrawable->setBrush(brush);
1229 float scale = getScale(static_cast<LOTContentGroupItem *>(parent())->matrix());
1230 mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
1232 if (mDashArraySize) {
1233 for (int i = 0 ; i < mDashArraySize ; i++)
1234 mDashArray[i] *= scale;
1236 /* AE draw the dash even if dash value is 0 */
1237 if (vCompare(mDashArray[0], 0.0f)) mDashArray[0]= 0.1;
1239 mDrawable->setDashInfo(mDashArray, mDashArraySize);
1243 LOTGStrokeItem::LOTGStrokeItem(LOTGStrokeData *data)
1244 : LOTPaintDataItem(data->isStatic()), mData(data)
1249 void LOTGStrokeItem::updateContent(int frameNo)
1251 mData->update(mGradient, frameNo);
1252 mGradient->mMatrix = static_cast<LOTContentGroupItem *>(parent())->matrix();
1253 mCap = mData->capStyle();
1254 mJoin = mData->joinStyle();
1255 mMiterLimit = mData->meterLimit();
1256 mWidth = mData->width(frameNo);
1257 if (mData->hasDashInfo()) {
1258 mDashArraySize = mData->getDashInfo(frameNo, mDashArray);
1262 void LOTGStrokeItem::updateRenderNode()
1264 float scale = getScale(mGradient->mMatrix);
1265 mGradient->setAlpha(parentAlpha());
1266 mDrawable->setBrush(VBrush(mGradient.get()));
1267 mDrawable->setStrokeInfo(mCap, mJoin, mMiterLimit,
1269 if (mDashArraySize) {
1270 for (int i = 0 ; i < mDashArraySize ; i++)
1271 mDashArray[i] *= scale;
1272 mDrawable->setDashInfo(mDashArray, mDashArraySize);
1276 LOTTrimItem::LOTTrimItem(LOTTrimData *data) : mData(data) {}
1278 void LOTTrimItem::update(int frameNo, const VMatrix &/*parentMatrix*/,
1279 float /*parentAlpha*/, const DirtyFlag &/*flag*/)
1283 if (mCache.mFrameNo == frameNo) return;
1285 LOTTrimData::Segment segment = mData->segment(frameNo);
1287 if (!(vCompare(mCache.mSegment.start, segment.start) &&
1288 vCompare(mCache.mSegment.end, segment.end))) {
1290 mCache.mSegment = segment;
1292 mCache.mFrameNo = frameNo;
1295 void LOTTrimItem::update()
1297 // when both path and trim are not dirty
1298 if (!(mDirty || pathDirty())) return;
1300 if (vCompare(mCache.mSegment.start, mCache.mSegment.end)) {
1301 for (auto &i : mPathItems) {
1302 i->updatePath(VPath());
1307 if (vCompare(std::fabs(mCache.mSegment.start - mCache.mSegment.end) , 1)) {
1308 for (auto &i : mPathItems) {
1309 i->updatePath(i->localPath());
1314 if (mData->type() == LOTTrimData::TrimType::Simultaneously) {
1315 for (auto &i : mPathItems) {
1317 pm.setStart(mCache.mSegment.start);
1318 pm.setEnd(mCache.mSegment.end);
1319 i->updatePath(pm.trim(i->localPath()));
1321 } else { // LOTTrimData::TrimType::Individually
1322 float totalLength = 0.0;
1323 for (auto &i : mPathItems) {
1324 totalLength += i->localPath().length();
1326 float start = totalLength * mCache.mSegment.start;
1327 float end = totalLength * mCache.mSegment.end;
1331 for (auto &i : mPathItems) {
1333 // update with empty path.
1334 i->updatePath(VPath());
1337 float len = i->localPath().length();
1339 if (curLen < start && curLen + len < start) {
1341 // update with empty path.
1342 i->updatePath(VPath());
1344 } else if (start <= curLen && end >= curLen + len) {
1349 float local_start = start > curLen ? start - curLen : 0;
1351 float local_end = curLen + len < end ? len : end - curLen;
1354 pm.setStart(local_start);
1355 pm.setEnd(local_end);
1356 VPath p = pm.trim(i->localPath());
1367 void LOTTrimItem::addPathItems(std::vector<LOTPathDataItem *> &list, int startOffset)
1369 std::copy(list.begin() + startOffset, list.end(), back_inserter(mPathItems));
1373 LOTRepeaterItem::LOTRepeaterItem(LOTRepeaterData *data) : mData(data)
1375 assert(mData->mChildren.size() == 1);
1376 LOTGroupData *root = reinterpret_cast<LOTGroupData *>(mData->mChildren[0].get());
1379 for (int i= 0; i < mData->copies(0); i++) {
1380 auto content = std::make_unique<LOTContentGroupItem>(static_cast<LOTGroupData *>(root));
1381 content->setParent(this);
1382 mContents.push_back(std::move(content));
1386 void LOTRepeaterItem::update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag)
1389 DirtyFlag newFlag = flag;
1391 if (mData->hasMtrixChange(frameNo)) {
1392 newFlag |= DirtyFlagBit::Matrix;
1395 float multiplier = mData->offset(frameNo);
1396 float startOpacity = mData->mTransform->startOpacity(frameNo);
1397 float endOpacity = mData->mTransform->endOpacity(frameNo);
1399 float copies = mData->copies(frameNo);
1400 if (!vCompare(copies, 1)) copies -=1;
1402 newFlag |= DirtyFlagBit::Alpha;
1403 for (const auto &content : mContents) {
1404 float newAlpha = parentAlpha * lerp(startOpacity, endOpacity, index / copies);
1405 VMatrix result = mData->mTransform->matrixForRepeater(frameNo, multiplier) * parentMatrix;
1406 content->update(frameNo, result, newAlpha, newFlag);
1412 static void updateGStops(LOTNode *n, const VGradient *grad)
1414 if (grad->mStops.size() != n->mGradient.stopCount) {
1415 if (n->mGradient.stopCount)
1416 free(n->mGradient.stopPtr);
1417 n->mGradient.stopCount = grad->mStops.size();
1418 n->mGradient.stopPtr = (LOTGradientStop *) malloc(n->mGradient.stopCount * sizeof(LOTGradientStop));
1421 LOTGradientStop *ptr = n->mGradient.stopPtr;
1422 for (const auto &i : grad->mStops) {
1424 ptr->a = i.second.alpha() * grad->alpha();
1425 ptr->r = i.second.red();
1426 ptr->g = i.second.green();
1427 ptr->b = i.second.blue();
1433 void LOTDrawable::sync()
1436 mCNode = std::make_unique<LOTNode>();
1437 mCNode->mGradient.stopPtr = nullptr;
1438 mCNode->mGradient.stopCount = 0;
1441 mCNode->mFlag = ChangeFlagNone;
1442 if (mFlag & DirtyState::None) return;
1444 if (mFlag & DirtyState::Path) {
1445 if (mStroke.mDash.size()) {
1446 VDasher dasher(mStroke.mDash.data(), mStroke.mDash.size());
1447 mPath = dasher.dashed(mPath);
1449 const std::vector<VPath::Element> &elm = mPath.elements();
1450 const std::vector<VPointF> & pts = mPath.points();
1451 const float *ptPtr = reinterpret_cast<const float *>(pts.data());
1452 const char * elmPtr = reinterpret_cast<const char *>(elm.data());
1453 mCNode->mPath.elmPtr = elmPtr;
1454 mCNode->mPath.elmCount = elm.size();
1455 mCNode->mPath.ptPtr = ptPtr;
1456 mCNode->mPath.ptCount = 2 * pts.size();
1457 mCNode->mFlag |= ChangeFlagPath;
1460 if (mStroke.enable) {
1461 mCNode->mStroke.width = mStroke.width;
1462 mCNode->mStroke.meterLimit = mStroke.meterLimit;
1463 mCNode->mStroke.enable = 1;
1465 switch (mStroke.cap) {
1466 case CapStyle::Flat:
1467 mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1469 case CapStyle::Square:
1470 mCNode->mStroke.cap = LOTCapStyle::CapSquare;
1472 case CapStyle::Round:
1473 mCNode->mStroke.cap = LOTCapStyle::CapRound;
1476 mCNode->mStroke.cap = LOTCapStyle::CapFlat;
1480 switch (mStroke.join) {
1481 case JoinStyle::Miter:
1482 mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1484 case JoinStyle::Bevel:
1485 mCNode->mStroke.join = LOTJoinStyle::JoinBevel;
1487 case JoinStyle::Round:
1488 mCNode->mStroke.join = LOTJoinStyle::JoinRound;
1491 mCNode->mStroke.join = LOTJoinStyle::JoinMiter;
1495 mCNode->mStroke.enable = 0;
1498 switch (mFillRule) {
1499 case FillRule::EvenOdd:
1500 mCNode->mFillRule = LOTFillRule::FillEvenOdd;
1503 mCNode->mFillRule = LOTFillRule::FillWinding;
1507 switch (mBrush.type()) {
1508 case VBrush::Type::Solid:
1509 mCNode->mBrushType = LOTBrushType::BrushSolid;
1510 mCNode->mColor.r = mBrush.mColor.r;
1511 mCNode->mColor.g = mBrush.mColor.g;
1512 mCNode->mColor.b = mBrush.mColor.b;
1513 mCNode->mColor.a = mBrush.mColor.a;
1515 case VBrush::Type::LinearGradient: {
1516 mCNode->mBrushType = LOTBrushType::BrushGradient;
1517 mCNode->mGradient.type = LOTGradientType::GradientLinear;
1518 VPointF s = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x1,
1519 mBrush.mGradient->linear.y1});
1520 VPointF e = mBrush.mGradient->mMatrix.map({mBrush.mGradient->linear.x2,
1521 mBrush.mGradient->linear.y2});
1522 mCNode->mGradient.start.x = s.x();
1523 mCNode->mGradient.start.y = s.y();
1524 mCNode->mGradient.end.x = e.x();
1525 mCNode->mGradient.end.y = e.y();
1526 updateGStops(mCNode.get(), mBrush.mGradient);
1529 case VBrush::Type::RadialGradient: {
1530 mCNode->mBrushType = LOTBrushType::BrushGradient;
1531 mCNode->mGradient.type = LOTGradientType::GradientRadial;
1532 VPointF c = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.cx,
1533 mBrush.mGradient->radial.cy});
1534 VPointF f = mBrush.mGradient->mMatrix.map({mBrush.mGradient->radial.fx,
1535 mBrush.mGradient->radial.fy});
1536 mCNode->mGradient.center.x = c.x();
1537 mCNode->mGradient.center.y = c.y();
1538 mCNode->mGradient.focal.x = f.x();
1539 mCNode->mGradient.focal.y = f.y();
1541 float scale = getScale(mBrush.mGradient->mMatrix);
1542 mCNode->mGradient.cradius = mBrush.mGradient->radial.cradius * scale;
1543 mCNode->mGradient.fradius = mBrush.mGradient->radial.fradius * scale;
1544 updateGStops(mCNode.get(), mBrush.mGradient);