8484
8585namespace lottie {
8686
87- static std::optional<CGRect> getRenderContentItemGlobalRect (std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
87+ static std::optional<CGRect> getRenderContentItemGlobalRect (std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
8888 if (!contentItem->renderData .isValid ) {
8989 return std::nullopt ;
9090 }
9191
92- auto currentTransform = contentItem->renderData .globalTransform ;
92+ auto currentTransform = parentTransform;
93+ CATransform3D localTransform = contentItem->transform ;
94+ currentTransform = localTransform * currentTransform;
9395
9496 std::optional<CGRect> globalRect;
9597 for (const auto &shadingVariant : contentItem->shadings ) {
111113 }
112114
113115 for (const auto &subItem : contentItem->subItems ) {
114- auto subGlobalRect = getRenderContentItemGlobalRect (subItem, globalSize, bezierPathsBoundingBoxContext);
116+ auto subGlobalRect = getRenderContentItemGlobalRect (subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext);
115117 if (subGlobalRect) {
116118 if (globalRect) {
117119 globalRect = globalRect->unionWith (subGlobalRect.value ());
@@ -134,15 +136,20 @@ CGRect integralGlobalRect(
134136 }
135137}
136138
137- static std::optional<CGRect> getRenderNodeGlobalRect (std::shared_ptr<RenderTreeNode> const &node, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
139+ static std::optional<CGRect> getRenderNodeGlobalRect (std::shared_ptr<RenderTreeNode> const &node, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
138140 if (!node->renderData .isValid ) {
139141 return std::nullopt ;
140142 }
141- auto currentTransform = node->renderData .globalTransform ;
143+
144+ auto currentTransform = parentTransform;
145+ Vector2D localTranslation (node->position ().x + -node->bounds ().x , node->position ().y + -node->bounds ().y );
146+ CATransform3D localTransform = node->transform ();
147+ localTransform = localTransform.translated (localTranslation);
148+ currentTransform = localTransform * currentTransform;
142149
143150 std::optional<CGRect> globalRect;
144151 if (node->_contentItem ) {
145- globalRect = getRenderContentItemGlobalRect (node->_contentItem , globalSize, bezierPathsBoundingBoxContext);
152+ globalRect = getRenderContentItemGlobalRect (node->_contentItem , globalSize, currentTransform, bezierPathsBoundingBoxContext);
146153 }
147154
148155 if (node->renderData .isInvertedMatte ) {
@@ -155,7 +162,7 @@ CGRect integralGlobalRect(
155162 }
156163
157164 for (const auto &subNode : node->subnodes ()) {
158- auto subGlobalRect = getRenderNodeGlobalRect (subNode, globalSize, bezierPathsBoundingBoxContext);
165+ auto subGlobalRect = getRenderNodeGlobalRect (subNode, globalSize, currentTransform, bezierPathsBoundingBoxContext);
159166 if (subGlobalRect) {
160167 if (globalRect) {
161168 globalRect = globalRect->unionWith (subGlobalRect.value ());
@@ -180,7 +187,6 @@ CGRect integralGlobalRect(
180187
181188static void processRenderContentItem (std::shared_ptr<RenderTreeNodeContentItem> const &contentItem, Vector2D const &globalSize, CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
182189 auto currentTransform = parentTransform;
183-
184190 CATransform3D localTransform = contentItem->transform ;
185191 currentTransform = localTransform * currentTransform;
186192
@@ -217,14 +223,8 @@ static void processRenderContentItem(std::shared_ptr<RenderTreeNodeContentItem>
217223
218224 contentItem->renderData .isValid = true ;
219225
220- contentItem->renderData .layer ._bounds = CGRect (0.0 , 0.0 , 0.0 , 0.0 );
221- contentItem->renderData .layer ._position = Vector2D (0.0 , 0.0 );
222- contentItem->renderData .layer ._transform = contentItem->transform ;
223- contentItem->renderData .layer ._opacity = contentItem->alpha ;
224- contentItem->renderData .layer ._masksToBounds = false ;
225- contentItem->renderData .layer ._isHidden = false ;
226+ contentItem->renderData .layer .masksToBounds = false ;
226227
227- contentItem->renderData .globalTransform = currentTransform;
228228 contentItem->renderData .drawContentDescendants = drawContentDescendants;
229229 contentItem->renderData .isInvertedMatte = false ;
230230}
@@ -243,11 +243,9 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
243243 }
244244
245245 auto currentTransform = parentTransform;
246-
247246 Vector2D localTranslation (node->position ().x + -node->bounds ().x , node->position ().y + -node->bounds ().y );
248247 CATransform3D localTransform = node->transform ();
249248 localTransform = localTransform.translated (localTranslation);
250-
251249 currentTransform = localTransform * currentTransform;
252250
253251 if (!currentTransform.isInvertible ()) {
@@ -290,14 +288,8 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
290288
291289 node->renderData .isValid = true ;
292290
293- node->renderData .layer ._bounds = node->bounds ();
294- node->renderData .layer ._position = node->position ();
295- node->renderData .layer ._transform = node->transform ();
296- node->renderData .layer ._opacity = node->alpha ();
297- node->renderData .layer ._masksToBounds = masksToBounds;
298- node->renderData .layer ._isHidden = node->isHidden ();
291+ node->renderData .layer .masksToBounds = masksToBounds;
299292
300- node->renderData .globalTransform = currentTransform;
301293 node->renderData .drawContentDescendants = drawContentDescendants;
302294 node->renderData .isInvertedMatte = isInvertedMatte;
303295}
@@ -306,15 +298,19 @@ static void processRenderTree(std::shared_ptr<RenderTreeNode> const &node, Vecto
306298
307299namespace {
308300
309- static void drawLottieContentItem (std::shared_ptr<lottieRendering::Canvas> parentContext, std::shared_ptr<lottie::RenderTreeNodeContentItem> item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
301+ static void drawLottieContentItem (std::shared_ptr<lottieRendering::Canvas> parentContext, std::shared_ptr<lottie::RenderTreeNodeContentItem> item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie:: BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
310302 if (!item->renderData .isValid ) {
311303 return ;
312304 }
313305
314- float normalizedOpacity = item->renderData .layer .opacity ();
306+ auto currentTransform = parentTransform;
307+ lottie::CATransform3D localTransform = item->transform ;
308+ currentTransform = localTransform * currentTransform;
309+
310+ float normalizedOpacity = item->alpha ;
315311 float layerAlpha = ((float )normalizedOpacity) * parentAlpha;
316312
317- if (item-> renderData . layer . isHidden () || normalizedOpacity == 0 .0f ) {
313+ if (normalizedOpacity == 0 .0f ) {
318314 return ;
319315 }
320316
@@ -328,7 +324,7 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
328324
329325 std::optional<lottie::CGRect> globalRect;
330326 if (needsTempContext) {
331- globalRect = lottie::getRenderContentItemGlobalRect (item, globalSize, bezierPathsBoundingBoxContext);
327+ globalRect = lottie::getRenderContentItemGlobalRect (item, globalSize, parentTransform, bezierPathsBoundingBoxContext);
332328 if (!globalRect || globalRect->width <= 0 .0f || globalRect->height <= 0 .0f ) {
333329 parentContext->restoreState ();
334330 return ;
@@ -341,14 +337,12 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
341337 currentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-globalRect->x , -globalRect->y )));
342338
343339 currentContext->saveState ();
344- currentContext->concatenate (item-> renderData . globalTransform );
340+ currentContext->concatenate (currentTransform );
345341 } else {
346342 currentContext = parentContext;
347343 }
348344
349- parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (item->renderData .layer .position ().x , item->renderData .layer .position ().y )));
350- parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-item->renderData .layer .bounds ().x , -item->renderData .layer .bounds ().y )));
351- parentContext->concatenate (item->renderData .layer .transform ());
345+ parentContext->concatenate (item->transform );
352346
353347 float renderAlpha = 1.0 ;
354348 if (tempContext) {
@@ -536,13 +530,13 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
536530
537531 for (auto it = item->subItems .rbegin (); it != item->subItems .rend (); it++) {
538532 const auto &subItem = *it;
539- drawLottieContentItem (currentContext, subItem, renderAlpha, globalSize, bezierPathsBoundingBoxContext);
533+ drawLottieContentItem (currentContext, subItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext);
540534 }
541535
542536 if (tempContext) {
543537 tempContext->restoreState ();
544538
545- parentContext->concatenate (item-> renderData . globalTransform .inverted ());
539+ parentContext->concatenate (currentTransform .inverted ());
546540 parentContext->setAlpha (layerAlpha);
547541 parentContext->draw (tempContext, globalRect.value ());
548542 parentContext->setAlpha (1.0 );
@@ -551,17 +545,23 @@ static void drawLottieContentItem(std::shared_ptr<lottieRendering::Canvas> paren
551545 parentContext->restoreState ();
552546}
553547
554- static void renderLottieRenderNode (std::shared_ptr<lottie::RenderTreeNode> node, std::shared_ptr<lottieRendering::Canvas> parentContext, lottie::Vector2D const &globalSize, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
548+ static void renderLottieRenderNode (std::shared_ptr<lottie::RenderTreeNode> node, std::shared_ptr<lottieRendering::Canvas> parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) {
555549 if (!node->renderData .isValid ) {
556550 return ;
557551 }
558- float normalizedOpacity = node->renderData . layer . opacity ();
552+ float normalizedOpacity = node->alpha ();
559553 float layerAlpha = ((float )normalizedOpacity) * parentAlpha;
560554
561- if (node->renderData . layer . isHidden () || normalizedOpacity == 0 .0f ) {
555+ if (node->isHidden () || normalizedOpacity == 0 .0f ) {
562556 return ;
563557 }
564558
559+ auto currentTransform = parentTransform;
560+ lottie::Vector2D localTranslation (node->position ().x + -node->bounds ().x , node->position ().y + -node->bounds ().y );
561+ lottie::CATransform3D localTransform = node->transform ();
562+ localTransform = localTransform.translated (localTranslation);
563+ currentTransform = localTransform * currentTransform;
564+
565565 parentContext->saveState ();
566566
567567 std::shared_ptr<lottieRendering::Canvas> maskContext;
@@ -572,28 +572,28 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
572572 if (node->mask () && node->mask ()->renderData .isValid ) {
573573 needsTempContext = true ;
574574 } else {
575- needsTempContext = layerAlpha != 1.0 || node->renderData .layer .masksToBounds () ;
575+ needsTempContext = layerAlpha != 1.0 || node->renderData .layer .masksToBounds ;
576576 }
577577
578578 std::optional<lottie::CGRect> globalRect;
579579 if (needsTempContext) {
580- globalRect = lottie::getRenderNodeGlobalRect (node, globalSize, bezierPathsBoundingBoxContext);
580+ globalRect = lottie::getRenderNodeGlobalRect (node, globalSize, parentTransform, bezierPathsBoundingBoxContext);
581581 if (!globalRect || globalRect->width <= 0 .0f || globalRect->height <= 0 .0f ) {
582582 parentContext->restoreState ();
583583 return ;
584584 }
585585
586- if ((node->mask () && node->mask ()->renderData .isValid ) || node->renderData .layer .masksToBounds () ) {
586+ if ((node->mask () && node->mask ()->renderData .isValid ) || node->renderData .layer .masksToBounds ) {
587587 auto maskBackingStorage = parentContext->makeLayer ((int )(globalRect->width ), (int )(globalRect->height ));
588588
589589 maskBackingStorage->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-globalRect->x , -globalRect->y )));
590- maskBackingStorage->concatenate (node-> renderData . globalTransform );
590+ maskBackingStorage->concatenate (currentTransform );
591591
592- if (node->renderData .layer .masksToBounds () ) {
593- maskBackingStorage->fill (lottie::CGRect (node->renderData . layer . bounds ().x , node->renderData . layer . bounds ().y , node->renderData . layer . bounds ().width , node->renderData . layer . bounds ().height ), lottie::Color (1.0 , 1.0 , 1.0 , 1.0 ));
592+ if (node->renderData .layer .masksToBounds ) {
593+ maskBackingStorage->fill (lottie::CGRect (node->bounds ().x , node->bounds ().y , node->bounds ().width , node->bounds ().height ), lottie::Color (1.0 , 1.0 , 1.0 , 1.0 ));
594594 }
595595 if (node->mask () && node->mask ()->renderData .isValid ) {
596- renderLottieRenderNode (node->mask (), maskBackingStorage, globalSize, 1.0 , bezierPathsBoundingBoxContext);
596+ renderLottieRenderNode (node->mask (), maskBackingStorage, globalSize, currentTransform, 1.0 , bezierPathsBoundingBoxContext);
597597 }
598598
599599 maskContext = maskBackingStorage;
@@ -606,14 +606,14 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
606606 currentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-globalRect->x , -globalRect->y )));
607607
608608 currentContext->saveState ();
609- currentContext->concatenate (node-> renderData . globalTransform );
609+ currentContext->concatenate (currentTransform );
610610 } else {
611611 currentContext = parentContext;
612612 }
613613
614- parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (node->renderData . layer . position ().x , node->renderData . layer . position ().y )));
615- parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-node->renderData . layer . bounds ().x , -node->renderData . layer . bounds ().y )));
616- parentContext->concatenate (node->renderData . layer . transform ());
614+ parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (node->position ().x , node->position ().y )));
615+ parentContext->concatenate (lottie::CATransform3D::identity ().translated (lottie::Vector2D (-node->bounds ().x , -node->bounds ().y )));
616+ parentContext->concatenate (node->transform ());
617617
618618 float renderAlpha = 1.0 ;
619619 if (tempContext) {
@@ -623,17 +623,17 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
623623 }
624624
625625 if (node->_contentItem ) {
626- drawLottieContentItem (currentContext, node->_contentItem , renderAlpha, globalSize, bezierPathsBoundingBoxContext);
626+ drawLottieContentItem (currentContext, node->_contentItem , renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext);
627627 }
628628
629629 if (node->renderData .isInvertedMatte ) {
630- currentContext->fill (lottie::CGRect (node->renderData . layer . bounds ().x , node->renderData . layer . bounds ().y , node->renderData . layer . bounds ().width , node->renderData . layer . bounds ().height ), lottie::Color (0.0 , 0.0 , 0.0 , 1.0 ));
630+ currentContext->fill (lottie::CGRect (node->bounds ().x , node->bounds ().y , node->bounds ().width , node->bounds ().height ), lottie::Color (0.0 , 0.0 , 0.0 , 1.0 ));
631631 currentContext->setBlendMode (lottieRendering::BlendMode::DestinationOut);
632632 }
633633
634634 for (const auto &subnode : node->subnodes ()) {
635635 if (subnode->renderData .isValid ) {
636- renderLottieRenderNode (subnode, currentContext, globalSize, renderAlpha, bezierPathsBoundingBoxContext);
636+ renderLottieRenderNode (subnode, currentContext, globalSize, currentTransform, renderAlpha, bezierPathsBoundingBoxContext);
637637 }
638638 }
639639
@@ -645,7 +645,7 @@ static void renderLottieRenderNode(std::shared_ptr<lottie::RenderTreeNode> node,
645645 tempContext->draw (maskContext, lottie::CGRect (globalRect->x , globalRect->y , globalRect->width , globalRect->height ));
646646 }
647647
648- parentContext->concatenate (node-> renderData . globalTransform .inverted ());
648+ parentContext->concatenate (currentTransform .inverted ());
649649 parentContext->setAlpha (layerAlpha);
650650 parentContext->draw (tempContext, globalRect.value ());
651651 }
@@ -689,15 +689,17 @@ - (UIImage * _Nullable)renderForSize:(CGSize)size useReferenceRendering:(bool)us
689689 return nil ;
690690 }
691691
692- processRenderTree (renderNode, lottie::Vector2D ((int )size.width , (int )size.height ), lottie::CATransform3D::identity ().scaled (lottie::Vector2D (size.width / (float )animation.size .width , size.height / (float )animation.size .height )), false , *_bezierPathsBoundingBoxContext.get ());
692+ lottie::CATransform3D rootTransform = lottie::CATransform3D::identity ().scaled (lottie::Vector2D (size.width / (float )animation.size .width , size.height / (float )animation.size .height ));
693+
694+ processRenderTree (renderNode, lottie::Vector2D ((int )size.width , (int )size.height ), rootTransform, false , *_bezierPathsBoundingBoxContext.get ());
693695
694696 if (useReferenceRendering) {
695697 auto context = std::make_shared<lottieRendering::CanvasImpl>((int )size.width , (int )size.height );
696698
697699 CGPoint scale = CGPointMake (size.width / (CGFloat)animation.size .width , size.height / (CGFloat)animation.size .height );
698700 context->concatenate (lottie::CATransform3D::makeScale (scale.x , scale.y , 1.0 ));
699701
700- renderLottieRenderNode (renderNode, context, lottie::Vector2D (context->width (), context->height ()), 1.0 , *_bezierPathsBoundingBoxContext.get ());
702+ renderLottieRenderNode (renderNode, context, lottie::Vector2D (context->width (), context->height ()), rootTransform, 1.0 , *_bezierPathsBoundingBoxContext.get ());
701703
702704 auto image = context->makeImage ();
703705
0 commit comments