From 3a5585c1fdf973d138b499da9f661d2b44177902 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Fri, 17 May 2024 00:20:40 +0400 Subject: [PATCH] Lottie --- .../Sources/SoftwareLottieRenderer.mm | 47 +++++-- .../PublicHeaders/LottieCpp/BezierPath.h | 1 + .../PublicHeaders/LottieCpp/RenderTreeNode.h | 1 + .../CompLayers/CompositionLayer.hpp | 10 +- .../CompLayers/PreCompositionLayer.hpp | 16 +-- .../CompLayers/ShapeCompositionLayer.cpp | 131 ++++++++++++------ .../CompLayers/ShapeCompositionLayer.hpp | 6 +- .../CompLayers/TextCompositionLayer.hpp | 2 +- .../MainThreadAnimationLayer.hpp | 12 +- .../Private/Utility/Primitives/BezierPath.cpp | 52 +++++++ 10 files changed, 201 insertions(+), 77 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 789c95c35a..5e5320cf47 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -18,6 +18,35 @@ struct TransformedPath { } }; +static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) { + //TODO:remove skipApplyTransform + lottie::CATransform3D effectiveTransform = parentTransform; + if (!skipApplyTransform && item->isGroup) { + effectiveTransform = item->transform * effectiveTransform; + } + + size_t maxSubitem = std::min(item->subItems.size(), subItemLimit); + + lottie::CGRect boundingBox(0.0, 0.0, 0.0, 0.0); + if (item->path) { + boundingBox = item->pathBoundingBox.applyingTransform(effectiveTransform); + } + + for (size_t i = 0; i < maxSubitem; i++) { + auto &subItem = item->subItems[i]; + + lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false); + + if (boundingBox.empty()) { + boundingBox = subItemBoundingBox; + } else { + boundingBox = boundingBox.unionWith(subItemBoundingBox); + } + } + + return boundingBox; +} + static std::vector collectPaths(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) { std::vector mappedPaths; @@ -32,6 +61,7 @@ static std::vector collectPaths(std::shared_ptrpath) { mappedPaths.emplace_back(item->path.value(), effectiveTransform); } + assert(!item->trimParams); for (size_t i = 0; i < maxSubitem; i++) { auto &subItem = item->subItems[i]; @@ -66,17 +96,8 @@ static void processRenderContentItem(std::shared_ptr int drawContentDescendants = 0; for (const auto &shadingVariant : contentItem->shadings) { - std::vector itemPaths; - if (shadingVariant->explicitPath) { - itemPaths = shadingVariant->explicitPath.value(); - } else { - auto rawPaths = collectPaths(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true); - for (const auto &rawPath : rawPaths) { - itemPaths.push_back(rawPath.path.copyUsingTransform(rawPath.transform)); - } - } + lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true); - CGRect shapeBounds = bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, itemPaths); if (shadingVariant->stroke) { shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); } else if (shadingVariant->fill) { @@ -95,7 +116,8 @@ static void processRenderContentItem(std::shared_ptr } if (contentItem->isGroup) { - for (const auto &subItem : contentItem->subItems) { + for (auto it = contentItem->subItems.rbegin(); it != contentItem->subItems.rend(); it++) { + const auto &subItem = *it; processRenderContentItem(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); if (subItem->renderData.isValid) { @@ -491,7 +513,8 @@ static void drawLottieContentItem(std::shared_ptr paren } } - for (const auto &subItem : item->subItems) { + for (auto it = item->subItems.rbegin(); it != item->subItems.rend(); it++) { + const auto &subItem = *it; drawLottieContentItem(currentContext, subItem, renderAlpha); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h index 43b7018442..c9ee189b25 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h @@ -144,6 +144,7 @@ public: CGRect bezierPathsBoundingBox(std::vector const &paths); CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, std::vector const &paths); +CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, BezierPath const &path); std::vector trimBezierPaths(std::vector &sourcePaths, double start, double end, double offset, TrimType type); diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 3565fd8212..2e5a8f2155 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -424,6 +424,7 @@ public: double alpha = 0.0; std::optional trimParams; std::optional path; + CGRect pathBoundingBox = CGRect(0.0, 0.0, 0.0, 0.0); std::vector> shadings; std::vector> subItems; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.hpp index 3399646a54..c8a97b55dd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.hpp @@ -82,7 +82,7 @@ public: return _contentsLayer; } - void displayWithFrame(double frame, bool forceUpdates) { + void displayWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { _transformNode->updateTree(frame, forceUpdates); bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame); @@ -93,14 +93,14 @@ public: /// Only update contents if current time is within the layers time bounds. if (layerVisible) { - displayContentsWithFrame(frame, forceUpdates); + displayContentsWithFrame(frame, forceUpdates, boundingBoxContext); if (_maskLayer) { _maskLayer->updateWithFrame(frame, forceUpdates); } } } - virtual void displayContentsWithFrame(double frame, bool forceUpdates) { + virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { /// To be overridden by subclass } @@ -151,11 +151,11 @@ public: return _timeStretch; } - virtual std::shared_ptr renderTreeNode() { + virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) { return nullptr; } - virtual void updateRenderTree() { + virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) { } public: 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 5b310c8571..aff1d21f8b 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 @@ -86,7 +86,7 @@ public: return result; } - virtual void displayContentsWithFrame(double frame, bool forceUpdates) override { + virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override { double localFrame = 0.0; if (_remappingNode) { _remappingNode->update(frame); @@ -96,11 +96,11 @@ public: } for (const auto &animationLayer : _animationLayers) { - animationLayer->displayWithFrame(localFrame, forceUpdates); + animationLayer->displayWithFrame(localFrame, forceUpdates, boundingBoxContext); } } - virtual std::shared_ptr renderTreeNode() override { + virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override { if (!_renderTreeNode) { _contentsTreeNode = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), @@ -120,7 +120,7 @@ public: std::shared_ptr maskNode; bool invertMask = false; if (_matteLayer) { - maskNode = _matteLayer->renderTreeNode(); + maskNode = _matteLayer->renderTreeNode(boundingBoxContext); if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) { invertMask = true; } @@ -148,7 +148,7 @@ public: } } if (found) { - auto node = animationLayer->renderTreeNode(); + auto node = animationLayer->renderTreeNode(boundingBoxContext); if (node) { renderTreeSubnodes.push_back(node); } @@ -177,9 +177,9 @@ public: return _renderTreeNode; } - virtual void updateRenderTree() override { + virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override { if (_matteLayer) { - _matteLayer->updateRenderTree(); + _matteLayer->updateRenderTree(boundingBoxContext); } for (const auto &animationLayer : _animationLayers) { @@ -191,7 +191,7 @@ public: } } if (found) { - animationLayer->updateRenderTree(); + animationLayer->updateRenderTree(boundingBoxContext); } } 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 64f40338d4..02fb2d5665 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 @@ -506,8 +506,9 @@ public: } virtual ~PathOutput() = default; - virtual void update(AnimationFrameTime frameTime) = 0; + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) = 0; virtual BezierPath const *currentPath() = 0; + virtual CGRect const ¤tPathBounds() = 0; }; class StaticPathOutput : public PathOutput { @@ -516,15 +517,26 @@ public: resolvedPath(path) { } - virtual void update(AnimationFrameTime frameTime) override { + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { + if (!isPathBoundsResolved) { + resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); + isPathBoundsResolved = true; + } } virtual BezierPath const *currentPath() override { return &resolvedPath; } + virtual CGRect const ¤tPathBounds() override { + return resolvedPathBounds; + } + private: BezierPath resolvedPath; + + bool isPathBoundsResolved = false; + CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); }; class ShapePathOutput : public PathOutput { @@ -533,9 +545,10 @@ public: path(shape.path.keyframes) { } - virtual void update(AnimationFrameTime frameTime) override { + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { if (!hasValidData || path.hasUpdate(frameTime)) { path.update(frameTime, resolvedPath); + resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); } hasValidData = true; @@ -545,12 +558,17 @@ public: return &resolvedPath; } + virtual CGRect const ¤tPathBounds() override { + return resolvedPathBounds; + } + private: bool hasValidData = false; BezierPathKeyframeInterpolator path; BezierPath resolvedPath; + CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); }; class RectanglePathOutput : public PathOutput { @@ -562,7 +580,7 @@ public: cornerRadius(rectangle.cornerRadius.keyframes) { } - virtual void update(AnimationFrameTime frameTime) override { + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -580,6 +598,7 @@ public: if (hasUpdates) { ValueInterpolator::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath); + resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); } hasValidData = true; @@ -589,6 +608,10 @@ public: return &resolvedPath; } + virtual CGRect const ¤tPathBounds() override { + return resolvedPathBounds; + } + private: bool hasValidData = false; @@ -604,6 +627,7 @@ public: double cornerRadiusValue = 0.0; BezierPath resolvedPath; + CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); }; class EllipsePathOutput : public PathOutput { @@ -614,7 +638,7 @@ public: size(ellipse.size.keyframes) { } - virtual void update(AnimationFrameTime frameTime) override { + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -628,6 +652,7 @@ public: if (hasUpdates) { ValueInterpolator::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath); + resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); } hasValidData = true; @@ -637,6 +662,10 @@ public: return &resolvedPath; } + virtual CGRect const ¤tPathBounds() override { + return resolvedPathBounds; + } + private: bool hasValidData = false; @@ -649,6 +678,7 @@ public: Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0); BezierPath resolvedPath; + CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); }; class StarPathOutput : public PathOutput { @@ -673,7 +703,7 @@ public: } } - virtual void update(AnimationFrameTime frameTime) override { + virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -715,6 +745,7 @@ public: if (hasUpdates) { ValueInterpolator::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath); + resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); } hasValidData = true; @@ -724,6 +755,10 @@ public: return &resolvedPath; } + virtual CGRect const ¤tPathBounds() override { + return resolvedPathBounds; + } + private: bool hasValidData = false; @@ -751,6 +786,7 @@ public: double pointsValue = 0.0; BezierPath resolvedPath; + CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); }; class TransformOutput { @@ -903,11 +939,7 @@ public: std::shared_ptr _contentItem; private: - bool hasTrims(size_t subItemLimit) { - return false; - } - - std::vector collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform, bool &hasTrims) { + std::vector collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform) { std::vector mappedPaths; //TODO:remove skipApplyTransform @@ -930,10 +962,9 @@ public: currentTrim = trims[0]->trimParams(); } - auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false, hasTrims); + auto subItemPaths = subItem->collectPaths(INT32_MAX, effectiveTransform, false); if (currentTrim) { - hasTrims = true; CompoundBezierPath tempPath; for (auto &path : subItemPaths) { tempPath.appendPath(path.path.copyUsingTransform(path.transform)); @@ -1007,23 +1038,22 @@ public: if (isGroup && !subItems.empty()) { std::vector> subItemNodes; - for (int i = (int)subItems.size() - 1; i >= 0; i--) { - subItems[i]->initializeRenderChildren(); - _contentItem->subItems.push_back(subItems[i]->_contentItem); + for (const auto &subItem : subItems) { + subItem->initializeRenderChildren(); + _contentItem->subItems.push_back(subItem->_contentItem); } } } public: - void updateFrame(AnimationFrameTime frameTime) { + void updateFrame(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) { if (transform) { transform->update(frameTime); } if (path) { - path->update(frameTime); - } else { - _contentItem->path = std::nullopt; + path->update(frameTime, boundingBoxContext); + _contentItem->pathBoundingBox = path->currentPathBounds(); } for (const auto &trim : trims) { trim->update(frameTime); @@ -1039,10 +1069,24 @@ public: } for (const auto &subItem : subItems) { - subItem->updateFrame(frameTime); + subItem->updateFrame(frameTime, boundingBoxContext); } } + bool hasTrims() { + if (!trims.empty()) { + return true; + } + + for (const auto &subItem : subItems) { + if (subItem->hasTrims()) { + return true; + } + } + + return false; + } + void updateContents(std::optional parentTrim) { CATransform3D containerTransform = CATransform3D::identity(); double containerOpacity = 1.0; @@ -1053,6 +1097,10 @@ public: _contentItem->transform = containerTransform; _contentItem->alpha = containerOpacity; + if (!trims.empty()) { + _contentItem->trimParams = trims[0]->trimParams(); + } + for (int i = 0; i < shadings.size(); i++) { const auto &shadingVariant = shadings[i]; @@ -1066,11 +1114,9 @@ public: currentTrim = trims[0]; }*/ - bool hasTrims = false; if (parentTrim) { CompoundBezierPath compoundPath; - hasTrims = true; - auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims); + auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true); for (const auto &path : paths) { compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } @@ -1083,21 +1129,20 @@ public: } _contentItem->shadings[i]->explicitPath = resultPaths; } else { - CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true, hasTrims); - for (const auto &path : paths) { - compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); - } - std::vector resultPaths; - for (const auto &path : compoundPath.paths) { - resultPaths.push_back(path); - } - - if (hasTrims) { + if (hasTrims()) { + CompoundBezierPath compoundPath; + auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true); + for (const auto &path : paths) { + compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); + } + std::vector resultPaths; + for (const auto &path : compoundPath.paths) { + resultPaths.push_back(path); + } + _contentItem->shadings[i]->explicitPath = resultPaths; } else { _contentItem->shadings[i]->explicitPath = std::nullopt; - _contentItem->shadings[i]->explicitPath = resultPaths; } } } @@ -1288,18 +1333,18 @@ CompositionLayer(solidLayer, Vector2D::Zero()) { _contentTree = std::make_shared(solidLayer); } -void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates) { +void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { _frameTime = frame; _frameTimeInitialized = true; - _contentTree->itemTree->updateFrame(_frameTime); + _contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext); _contentTree->itemTree->updateContents(std::nullopt); } -std::shared_ptr ShapeCompositionLayer::renderTreeNode() { +std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) { if (!_frameTimeInitialized) { _frameTime = 0.0; _frameTimeInitialized = true; - _contentTree->itemTree->updateFrame(_frameTime); + _contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext); _contentTree->itemTree->updateContents(std::nullopt); } @@ -1324,7 +1369,7 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode() { std::shared_ptr maskNode; bool invertMask = false; if (_matteLayer) { - maskNode = _matteLayer->renderTreeNode(); + maskNode = _matteLayer->renderTreeNode(boundingBoxContext); if (maskNode && _matteType.has_value() && _matteType.value() == MatteType::Invert) { invertMask = true; } @@ -1346,9 +1391,9 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode() { return _renderTreeNode; } -void ShapeCompositionLayer::updateRenderTree() { +void ShapeCompositionLayer::updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) { if (_matteLayer) { - _matteLayer->updateRenderTree(); + _matteLayer->updateRenderTree(boundingBoxContext); } _contentRenderTreeNode->_bounds = _contentsLayer->bounds(); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.hpp index f36e4a241c..27fed46a4e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeCompositionLayer.hpp @@ -16,9 +16,9 @@ public: ShapeCompositionLayer(std::shared_ptr const &shapeLayer); ShapeCompositionLayer(std::shared_ptr const &solidLayer); - virtual void displayContentsWithFrame(double frame, bool forceUpdates) override; - virtual std::shared_ptr renderTreeNode() override; - virtual void updateRenderTree() override; + virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override; + virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override; + virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override; private: std::shared_ptr _contentTree; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.hpp index d2b9076b2d..9d7e916705 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/TextCompositionLayer.hpp @@ -42,7 +42,7 @@ public: _fontProvider = fontProvider; } - virtual void displayContentsWithFrame(double frame, bool forceUpdates) override { + virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override { if (!_textDocument) { return; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.hpp index c3c26a5352..3d6f999f6b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/MainThreadAnimationLayer.hpp @@ -103,7 +103,7 @@ public: newFrame = floor(newFrame); } for (const auto &layer : _animationLayers) { - layer->displayWithFrame(newFrame, false); + layer->displayWithFrame(newFrame, false, _boundingBoxContext); } } @@ -118,7 +118,7 @@ public: /// Forces the view to update its drawing. void forceDisplayUpdate() { for (const auto &layer : _animationLayers) { - layer->displayWithFrame(currentFrame(), true); + layer->displayWithFrame(currentFrame(), true, _boundingBoxContext); } } @@ -193,7 +193,7 @@ public: _currentFrame = currentFrame; for (size_t i = 0; i < _animationLayers.size(); i++) { - _animationLayers[i]->displayWithFrame(_currentFrame, false); + _animationLayers[i]->displayWithFrame(_currentFrame, false, _boundingBoxContext); } } @@ -230,7 +230,7 @@ public: } } if (found) { - auto node = animationLayer->renderTreeNode(); + auto node = animationLayer->renderTreeNode(_boundingBoxContext); if (node) { subnodes.push_back(node); } @@ -264,7 +264,7 @@ public: } } if (found) { - animationLayer->updateRenderTree(); + animationLayer->updateRenderTree(_boundingBoxContext); } } } @@ -288,6 +288,8 @@ private: std::shared_ptr _layerFontProvider; std::shared_ptr _renderTreeNode; + + BezierPathsBoundingBoxContext _boundingBoxContext; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp index 5985865ef2..b5c6065ecd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/BezierPath.cpp @@ -582,6 +582,58 @@ CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, st return calculateBoundingRectOpt(pointsX, pointsY, pointCount); } +CGRect bezierPathsBoundingBoxParallel(BezierPathsBoundingBoxContext &context, BezierPath const &path) { + int pointCount = 0; + + float *pointsX = context.pointsX; + float *pointsY = context.pointsY; + int pointsSize = context.pointsSize; + + PathElement const *pathElements = path.elements().data(); + int pathElementCount = (int)path.elements().size(); + + for (int i = 0; i < pathElementCount; i++) { + const auto &element = pathElements[i]; + + if (pointsSize < pointCount + 1) { + pointsSize = (pointCount + 1) * 2; + pointsX = (float *)realloc(pointsX, pointsSize * 4); + pointsY = (float *)realloc(pointsY, pointsSize * 4); + } + pointsX[pointCount] = (float)element.vertex.point.x; + pointsY[pointCount] = (float)element.vertex.point.y; + pointCount++; + + if (i != 0) { + const auto &previousElement = pathElements[i - 1]; + if (previousElement.vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { + } else { + if (pointsSize < pointCount + 1) { + pointsSize = (pointCount + 2) * 2; + pointsX = (float *)realloc(pointsX, pointsSize * 4); + pointsY = (float *)realloc(pointsY, pointsSize * 4); + } + pointsX[pointCount] = (float)previousElement.vertex.outTangent.x; + pointsY[pointCount] = (float)previousElement.vertex.outTangent.y; + pointCount++; + pointsX[pointCount] = (float)element.vertex.inTangent.x; + pointsY[pointCount] = (float)element.vertex.inTangent.y; + pointCount++; + } + } + } + + context.pointsX = pointsX; + context.pointsY = pointsY; + context.pointsSize = pointsSize; + + if (pointCount == 0) { + return CGRect(0.0, 0.0, 0.0, 0.0); + } + + return calculateBoundingRectOpt(pointsX, pointsY, pointCount); +} + CGRect bezierPathsBoundingBox(std::vector const &paths) { int pointCount = 0;