From 940619b7cd5b3e9387477d8b68b6ba1afd4b1837 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 00:24:04 +0400 Subject: [PATCH] Lottie --- .../Sources/SoftwareLottieRenderer.mm | 125 ++++-------------- .../Sources/ViewController.swift | 2 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 29 ++-- .../CompLayers/PreCompositionLayer.hpp | 72 +++++----- .../CompLayers/ShapeCompositionLayer.cpp | 5 +- .../Sources/LottieAnimationContainer.mm | 2 - 6 files changed, 74 insertions(+), 161 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index b4c630e469..33fe9c5014 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -8,6 +8,8 @@ namespace { +static constexpr float minVisibleAlpha = 0.5f / 255.0f; + struct TransformedPath { lottie::BezierPath path; lottie::CATransform3D transform; @@ -85,10 +87,6 @@ static std::vector collectPaths(std::shared_ptr getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { - if (!contentItem->renderData.isValid) { - return std::nullopt; - } - auto currentTransform = parentTransform; CATransform3D localTransform = contentItem->transform; currentTransform = localTransform * currentTransform; @@ -185,52 +183,8 @@ static std::optional getRenderNodeGlobalRect(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; - } - - int drawContentDescendants = 0; - for (const auto &shadingVariant : contentItem->shadings) { - if (shadingVariant->stroke) { - } else if (shadingVariant->fill) { - } else { - continue; - } - - drawContentDescendants += 1; - } - - if (contentItem->isGroup) { - for (auto it = contentItem->subItems.rbegin(); it != contentItem->subItems.rend(); it++) { - const auto &subItem = *it; - processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); - - if (subItem->renderData.isValid) { - drawContentDescendants += subItem->renderData.drawContentDescendants; - } - } - } else { - for (const auto &subItem : contentItem->subItems) { - subItem->renderData.isValid = false; - } - } - - contentItem->renderData.isValid = true; - - contentItem->renderData.layer.masksToBounds = false; - - 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) { - if (node->isHidden() || node->alpha() == 0.0f) { +static void processRenderTree(std::shared_ptr const &node, Vector2D const &globalSize, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { + if (node->isHidden() || node->alpha() < minVisibleAlpha) { node->renderData.isValid = false; return; } @@ -242,44 +196,16 @@ static void processRenderTree(std::shared_ptr const &node, Vecto } } - auto currentTransform = parentTransform; - Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y); - CATransform3D localTransform = node->transform(); - localTransform = localTransform.translated(localTranslation); - currentTransform = localTransform * currentTransform; - - if (!currentTransform.isInvertible()) { - node->renderData.isValid = false; - return; - } - - int drawContentDescendants = 0; - if (node->_contentItem) { - processRenderContentItem(node->_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); - if (node->_contentItem->renderData.isValid) { - drawContentDescendants += node->_contentItem->renderData.drawContentDescendants; - } - } - bool isInvertedMatte = isInvertedMask; - for (const auto &item : node->subnodes()) { - processRenderTree(item, globalSize, currentTransform, false, bezierPathsBoundingBoxContext); - if (item->renderData.isValid) { - drawContentDescendants += item->renderData.drawContentDescendants; - } - } - - bool masksToBounds = node->masksToBounds(); - if (masksToBounds) { - CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform); - if (effectiveGlobalBounds.contains(CGRect(0.0, 0.0, globalSize.x, globalSize.y))) { - masksToBounds = false; + for (const auto &subnode : node->subnodes()) { + processRenderTree(subnode, globalSize, false, bezierPathsBoundingBoxContext); + if (subnode->renderData.isValid) { } } if (node->mask()) { - processRenderTree(node->mask(), globalSize, currentTransform, node->invertMask(), bezierPathsBoundingBoxContext); + processRenderTree(node->mask(), globalSize, node->invertMask(), bezierPathsBoundingBoxContext); if (!node->mask()->renderData.isValid) { node->renderData.isValid = false; return; @@ -288,9 +214,6 @@ static void processRenderTree(std::shared_ptr const &node, Vecto node->renderData.isValid = true; - node->renderData.layer.masksToBounds = masksToBounds; - - node->renderData.drawContentDescendants = drawContentDescendants; node->renderData.isInvertedMatte = isInvertedMatte; } @@ -299,10 +222,6 @@ static void processRenderTree(std::shared_ptr const &node, Vecto namespace { static void drawLottieContentItem(std::shared_ptr parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { - if (!item->renderData.isValid) { - return; - } - auto currentTransform = parentTransform; lottie::CATransform3D localTransform = item->transform; currentTransform = localTransform * currentTransform; @@ -320,7 +239,7 @@ static void drawLottieContentItem(std::shared_ptr paren std::shared_ptr tempContext; bool needsTempContext = false; - needsTempContext = layerAlpha != 1.0 && item->renderData.drawContentDescendants > 1; + needsTempContext = layerAlpha != 1.0 && item->drawContentCount > 1; std::optional globalRect; if (needsTempContext) { @@ -552,7 +471,7 @@ static void renderLottieRenderNode(std::shared_ptr node, float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; - if (node->isHidden() || normalizedOpacity == 0.0f) { + if (node->isHidden() || normalizedOpacity < minVisibleAlpha) { return; } @@ -568,11 +487,19 @@ static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr currentContext; std::shared_ptr tempContext; + bool masksToBounds = node->masksToBounds(); + if (masksToBounds) { + lottie::CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform); + if (effectiveGlobalBounds.contains(lottie::CGRect(0.0, 0.0, globalSize.x, globalSize.y))) { + masksToBounds = false; + } + } + bool needsTempContext = false; if (node->mask() && node->mask()->renderData.isValid) { needsTempContext = true; } else { - needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds; + needsTempContext = layerAlpha != 1.0 || masksToBounds; } std::optional globalRect; @@ -583,13 +510,13 @@ static void renderLottieRenderNode(std::shared_ptr node, return; } - if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.masksToBounds) { + if ((node->mask() && node->mask()->renderData.isValid) || masksToBounds) { auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); maskBackingStorage->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); maskBackingStorage->concatenate(currentTransform); - if (node->renderData.layer.masksToBounds) { + if (masksToBounds) { 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)); } if (node->mask() && node->mask()->renderData.isValid) { @@ -679,10 +606,6 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { } - (UIImage * _Nullable)renderForSize:(CGSize)size useReferenceRendering:(bool)useReferenceRendering { - if (!useReferenceRendering) { - return nil; - } - LottieAnimation *animation = _animationContainer.animation; std::shared_ptr renderNode = [_animationContainer internalGetRootRenderTreeNode]; if (!renderNode) { @@ -691,7 +614,11 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { lottie::CATransform3D rootTransform = lottie::CATransform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height)); - processRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), rootTransform, false, *_bezierPathsBoundingBoxContext.get()); + processRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), false, *_bezierPathsBoundingBoxContext.get()); + + if (!useReferenceRendering) { + return nil; + } if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index ca67ee2ad1..47603b86ca 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -78,7 +78,7 @@ private final class ReferenceCompareTest { } var continueFromName: String? - //continueFromName = "1137162165791228042.json" + //continueFromName = "4986037051573928320.json" let _ = await processAnimationFolderAsync(basePath: bundlePath, path: "", stopOnFailure: true, process: { path, name, alwaysDraw in if let continueFromNameValue = continueFromName { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index c401eed0da..5ca3ed4bae 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -14,30 +14,12 @@ namespace lottie { class ProcessedRenderTreeNodeData { -public: - struct LayerParams { - bool masksToBounds; - - LayerParams( - bool masksToBounds_ - ) : - masksToBounds(masksToBounds_) { - } - }; - - ProcessedRenderTreeNodeData() : - isValid(false), - layer( - false - ), - drawContentDescendants(false), - isInvertedMatte(false) { +public: + ProcessedRenderTreeNodeData() { } bool isValid = false; - LayerParams layer; - int drawContentDescendants; - bool isInvertedMatte; + bool isInvertedMatte = false; }; class RenderableItem { @@ -388,6 +370,7 @@ public: std::shared_ptr path; std::vector> shadings; std::vector> subItems; + int drawContentCount = 0; ProcessedRenderTreeNodeData renderData; }; @@ -427,6 +410,9 @@ public: _subnodes(subnodes_), _mask(mask_), _invertMask(invertMask_) { + for (const auto &subnode : _subnodes) { + drawContentCount += subnode->drawContentCount; + } } ~RenderTreeNode() { @@ -477,6 +463,7 @@ public: bool _masksToBounds = false; bool _isHidden = false; std::shared_ptr _contentItem; + int drawContentCount = 0; std::vector> _subnodes; std::shared_ptr _mask; bool _invertMask = false; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.hpp index 4d3a51db1b..f2047a4f34 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/PreCompositionLayer.hpp @@ -102,42 +102,6 @@ public: virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override { if (!_renderTreeNode) { - _contentsTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), - Vector2D(0.0, 0.0), - CATransform3D::identity(), - 1.0, - false, - false, - std::vector>(), - nullptr, - false - ); - - std::vector> subnodes; - subnodes.push_back(_contentsTreeNode); - - std::shared_ptr maskNode; - bool invertMask = false; - if (_matteLayer) { - maskNode = _matteLayer->renderTreeNode(boundingBoxContext); - if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) { - invertMask = true; - } - } - - _renderTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), - Vector2D(0.0, 0.0), - CATransform3D::identity(), - 1.0, - false, - false, - subnodes, - maskNode, - invertMask - ); - std::vector> renderTreeSubnodes; for (const auto &animationLayer : _animationLayers) { bool found = false; @@ -171,7 +135,41 @@ public: renderTreeValue.push_back(renderTreeContentItem); } - _contentsTreeNode->_subnodes = renderTreeValue; + _contentsTreeNode = std::make_shared( + CGRect(0.0, 0.0, 0.0, 0.0), + Vector2D(0.0, 0.0), + CATransform3D::identity(), + 1.0, + false, + false, + renderTreeValue, + nullptr, + false + ); + + std::vector> subnodes; + subnodes.push_back(_contentsTreeNode); + + std::shared_ptr maskNode; + bool invertMask = false; + if (_matteLayer) { + maskNode = _matteLayer->renderTreeNode(boundingBoxContext); + if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) { + invertMask = true; + } + } + + _renderTreeNode = std::make_shared( + CGRect(0.0, 0.0, 0.0, 0.0), + Vector2D(0.0, 0.0), + CATransform3D::identity(), + 1.0, + false, + false, + subnodes, + maskNode, + invertMask + ); } _contentsTreeNode->_bounds = _contentsLayer->bounds(); 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 f0973a1f09..f6419d098d 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 @@ -994,6 +994,8 @@ public: continue; } + _contentItem->drawContentCount++; + auto itemShadingVariant = std::make_shared(); if (shadingVariant.fill) { itemShadingVariant->fill = shadingVariant.fill->fill(); @@ -1011,6 +1013,7 @@ public: std::vector> subItemNodes; for (const auto &subItem : subItems) { subItem->initializeRenderChildren(); + _contentItem->drawContentCount += subItem->_contentItem->drawContentCount; _contentItem->subItems.push_back(subItem->_contentItem); } } @@ -1331,9 +1334,9 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath false ); _contentRenderTreeNode->_contentItem = _contentTree->itemTree->_contentItem; + _contentRenderTreeNode->drawContentCount = _contentTree->itemTree->_contentItem->drawContentCount; std::vector> subnodes; - //subnodes.push_back(_contentTree->itemTree->renderTree()); subnodes.push_back(_contentRenderTreeNode); std::shared_ptr maskNode; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm index 28c00c892a..5f06c01222 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm @@ -58,8 +58,6 @@ result.isValid = node->renderData.isValid; - result.hasSimpleContents = node->renderData.drawContentDescendants <= 1; - result.drawContentDescendants = node->renderData.drawContentDescendants; result.isInvertedMatte = node->renderData.isInvertedMatte; if (node->mask()) { result.maskId = (int64_t)node->mask().get();