From 624a3d0c6d09920343bb6a1e4d15a61c7b510abf Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Wed, 15 May 2024 18:42:40 +0400 Subject: [PATCH] [WIP] --- .../Sources/SoftwareLottieRenderer.mm | 114 +++++++++++- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 163 +++++++++--------- .../CompLayers/ShapeCompositionLayer.cpp | 9 +- 3 files changed, 194 insertions(+), 92 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index a4903b4bb6..2f2189b74a 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -8,8 +8,24 @@ namespace lottie { -static void processRenderContentItem(std::shared_ptr const &contentItem, std::optional &effectiveLocalBounds, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void processRenderContentItem(std::shared_ptr const &contentItem, Vector2D const &globalSize, CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { + auto currentTransform = parentTransform; + + CATransform3D localTransform = contentItem->transform; + currentTransform = localTransform * currentTransform; + + if (!currentTransform.isInvertible()) { + contentItem->renderData.isValid = false; + return; + } + + std::optional effectiveLocalBounds; + + int drawContentDescendants = 0; + for (const auto &shadingVariant : contentItem->shadings) { + drawContentDescendants += 1; + CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, shadingVariant->explicitPath.value()); if (shadingVariant->stroke) { shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); @@ -27,9 +43,86 @@ static void processRenderContentItem(std::shared_ptr } } - for (const auto &subItem : contentItem->subItems) { - processRenderContentItem(subItem, effectiveLocalBounds, bezierPathsBoundingBoxContext); + std::optional effectiveLocalRect; + if (effectiveLocalBounds.has_value()) { + effectiveLocalRect = effectiveLocalBounds; } + + std::optional subnodesGlobalRect; + + for (const auto &subItem : contentItem->subItems) { + processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); + + drawContentDescendants += subItem->renderData.drawContentDescendants; + + if (!subItem->renderData.localRect.empty()) { + if (effectiveLocalRect.has_value()) { + effectiveLocalRect = effectiveLocalRect->unionWith(subItem->renderData.localRect); + } else { + effectiveLocalRect = subItem->renderData.localRect; + } + } + + if (subnodesGlobalRect) { + subnodesGlobalRect = subnodesGlobalRect->unionWith(subItem->renderData.globalRect); + } else { + subnodesGlobalRect = subItem->renderData.globalRect; + } + } + + std::optional fuzzyGlobalRect; + + if (effectiveLocalBounds) { + CGRect effectiveGlobalBounds = effectiveLocalBounds->applyingTransform(currentTransform); + if (fuzzyGlobalRect) { + fuzzyGlobalRect = fuzzyGlobalRect->unionWith(effectiveGlobalBounds); + } else { + fuzzyGlobalRect = effectiveGlobalBounds; + } + } + + if (subnodesGlobalRect) { + if (fuzzyGlobalRect) { + fuzzyGlobalRect = fuzzyGlobalRect->unionWith(subnodesGlobalRect.value()); + } else { + fuzzyGlobalRect = subnodesGlobalRect; + } + } + + if (!fuzzyGlobalRect) { + contentItem->renderData.isValid = false; + return; + } + + CGRect globalRect( + std::floor(fuzzyGlobalRect->x), + std::floor(fuzzyGlobalRect->y), + std::ceil(fuzzyGlobalRect->width + fuzzyGlobalRect->x - floor(fuzzyGlobalRect->x)), + std::ceil(fuzzyGlobalRect->height + fuzzyGlobalRect->y - floor(fuzzyGlobalRect->y)) + ); + + if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(globalRect)) { + contentItem->renderData.isValid = false; + return; + } + + CGRect localRect = effectiveLocalRect.value_or(CGRect(0.0, 0.0, 0.0, 0.0)).applyingTransform(localTransform); + + contentItem->renderData.isValid = true; + + contentItem->renderData.layer._bounds = CGRect(0.0, 0.0, 0.0, 0.0); + contentItem->renderData.layer._position = Vector2D(0.0, 0.0); + contentItem->renderData.layer._transform = contentItem->transform; + contentItem->renderData.layer._opacity = contentItem->alpha; + contentItem->renderData.layer._masksToBounds = false; + contentItem->renderData.layer._isHidden = false; + + contentItem->renderData.globalRect = globalRect; + contentItem->renderData.localRect = localRect; + contentItem->renderData.globalTransform = currentTransform; + contentItem->renderData.drawsContent = effectiveLocalBounds.has_value(); + contentItem->renderData.drawContentDescendants = drawContentDescendants; + contentItem->renderData.isInvertedMatte = false; } static void processRenderTree(std::shared_ptr const &node, Vector2D const &globalSize, CATransform3D const &parentTransform, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { @@ -58,14 +151,12 @@ static void processRenderTree(std::shared_ptr const &node, Vecto return; } - std::optional effectiveLocalBounds; - - double alpha = node->alpha(); - if (node->_contentItem) { - processRenderContentItem(node->_contentItem, effectiveLocalBounds, bezierPathsBoundingBoxContext); + processRenderContentItem(node->_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); } + std::optional effectiveLocalBounds; + bool isInvertedMatte = isInvertedMask; if (isInvertedMatte) { effectiveLocalBounds = node->bounds(); @@ -179,7 +270,7 @@ static void processRenderTree(std::shared_ptr const &node, Vecto node->renderData.layer._bounds = node->bounds(); node->renderData.layer._position = node->position(); node->renderData.layer._transform = node->transform(); - node->renderData.layer._opacity = alpha; + node->renderData.layer._opacity = node->alpha(); node->renderData.layer._masksToBounds = masksToBounds; node->renderData.layer._isHidden = node->isHidden(); @@ -196,6 +287,9 @@ static void processRenderTree(std::shared_ptr const &node, Vecto namespace { static void drawLottieContentItem(std::shared_ptr context, std::shared_ptr item) { + context->saveState(); + context->concatenate(item->transform); + for (const auto &shading : item->shadings) { if (shading->explicitPath->empty()) { continue; @@ -366,6 +460,8 @@ static void drawLottieContentItem(std::shared_ptr conte for (const auto &subItem : item->subItems) { drawLottieContentItem(context, subItem); } + + context->restoreState(); } static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, double parentAlpha) { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index ee22047000..75e4f9d3fa 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -13,6 +13,86 @@ namespace lottie { +class ProcessedRenderTreeNodeData { +public: + struct LayerParams { + CGRect _bounds; + Vector2D _position; + CATransform3D _transform; + double _opacity; + bool _masksToBounds; + bool _isHidden; + + LayerParams( + CGRect bounds_, + Vector2D position_, + CATransform3D transform_, + double opacity_, + bool masksToBounds_, + bool isHidden_ + ) : + _bounds(bounds_), + _position(position_), + _transform(transform_), + _opacity(opacity_), + _masksToBounds(masksToBounds_), + _isHidden(isHidden_) { + } + + CGRect bounds() const { + return _bounds; + } + + Vector2D position() const { + return _position; + } + + CATransform3D transform() const { + return _transform; + } + + double opacity() const { + return _opacity; + } + + bool masksToBounds() const { + return _masksToBounds; + } + + bool isHidden() const { + return _isHidden; + } + }; + + ProcessedRenderTreeNodeData() : + isValid(false), + layer( + CGRect(0.0, 0.0, 0.0, 0.0), + Vector2D(0.0, 0.0), + CATransform3D::identity(), + 1.0, + false, + false + ), + globalRect(CGRect(0.0, 0.0, 0.0, 0.0)), + localRect(CGRect(0.0, 0.0, 0.0, 0.0)), + globalTransform(CATransform3D::identity()), + drawsContent(false), + drawContentDescendants(false), + isInvertedMatte(false) { + + } + + bool isValid = false; + LayerParams layer; + CGRect globalRect; + CGRect localRect; + CATransform3D globalTransform; + bool drawsContent; + int drawContentDescendants; + bool isInvertedMatte; +}; + class RenderableItem { public: enum class Type { @@ -345,8 +425,11 @@ public: public: bool isGroup = false; CATransform3D transform = CATransform3D::identity(); + double alpha = 0.0; std::vector> shadings; std::vector> subItems; + + ProcessedRenderTreeNodeData renderData; }; class RenderTreeNodeContentShadingVariant { @@ -362,86 +445,6 @@ public: size_t subItemLimit = 0; }; -class ProcessedRenderTreeNodeData { -public: - struct LayerParams { - CGRect _bounds; - Vector2D _position; - CATransform3D _transform; - double _opacity; - bool _masksToBounds; - bool _isHidden; - - LayerParams( - CGRect bounds_, - Vector2D position_, - CATransform3D transform_, - double opacity_, - bool masksToBounds_, - bool isHidden_ - ) : - _bounds(bounds_), - _position(position_), - _transform(transform_), - _opacity(opacity_), - _masksToBounds(masksToBounds_), - _isHidden(isHidden_) { - } - - CGRect bounds() const { - return _bounds; - } - - Vector2D position() const { - return _position; - } - - CATransform3D transform() const { - return _transform; - } - - double opacity() const { - return _opacity; - } - - bool masksToBounds() const { - return _masksToBounds; - } - - bool isHidden() const { - return _isHidden; - } - }; - - ProcessedRenderTreeNodeData() : - isValid(false), - layer( - CGRect(0.0, 0.0, 0.0, 0.0), - Vector2D(0.0, 0.0), - CATransform3D::identity(), - 1.0, - false, - false - ), - globalRect(CGRect(0.0, 0.0, 0.0, 0.0)), - localRect(CGRect(0.0, 0.0, 0.0, 0.0)), - globalTransform(CATransform3D::identity()), - drawsContent(false), - drawContentDescendants(false), - isInvertedMatte(false) { - - } - - bool isValid = false; - LayerParams layer; - CGRect globalRect; - CGRect localRect; - CATransform3D globalTransform; - bool drawsContent; - int drawContentDescendants; - bool isInvertedMatte; -}; - class RenderTreeNode { public: RenderTreeNode( diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp index 1ffaff84f8..617fc87ed3 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.cpp @@ -1034,10 +1034,10 @@ public: for (int i = (int)subItems.size() - 1; i >= 0; i--) { subItems[i]->initializeRenderChildren(); subItemNodes.push_back(subItems[i]->_renderTree); - //_renderTree->_contentItem->subItems.push_back(subItems[i]->_renderTree->_contentItem); + _renderTree->_contentItem->subItems.push_back(subItems[i]->_renderTree->_contentItem); } - if (!subItemNodes.empty()) { + /*if (!subItemNodes.empty()) { _renderTree->_subnodes.push_back(std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), @@ -1049,7 +1049,7 @@ public: nullptr, false )); - } + }*/ } } @@ -1087,6 +1087,9 @@ public: containerTransform = transform->transform(); containerOpacity = transform->opacity(); } + _renderTree->_contentItem->transform = containerTransform; + _renderTree->_contentItem->alpha = containerOpacity; + _renderTree->_transform = containerTransform; _renderTree->_alpha = containerOpacity;