From 8b20aea99b9e533c39ee86aa0bb0da9818d24dfd Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 11:22:53 +0400 Subject: [PATCH 01/32] Lottie --- .../Sources/SoftwareLottieRenderer.mm | 8 ++-- .../Sources/ViewController.swift | 2 +- .../ChatSendMessageContextScreen.swift | 5 ++- .../Sources/ChatMessageBubbleItemNode.swift | 5 ++- .../CompLayers/CompositionLayer.cpp | 1 - .../CompLayers/CompositionLayer.hpp | 18 +++++---- .../CompLayers/PreCompositionLayer.hpp | 38 ++++--------------- .../CompLayers/ShapeCompositionLayer.cpp | 25 +++++------- .../CompLayers/ShapeCompositionLayer.hpp | 3 +- .../MainThreadAnimationLayer.hpp | 19 +--------- .../Protocols/AnimatorNode.hpp | 9 +++-- .../Lottie/Public/Primitives/CALayer.hpp | 8 ---- 12 files changed, 50 insertions(+), 91 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 90dd8f4c1e..367c31e5e2 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -651,13 +651,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { } - (UIImage * _Nullable)renderForSize:(CGSize)size useReferenceRendering:(bool)useReferenceRendering { - LottieAnimation *animation = _animationContainer.animation; - std::shared_ptr renderNode = [_animationContainer internalGetRootRenderTreeNode]; - if (!renderNode) { + if (!useReferenceRendering) { return nil; } - if (!useReferenceRendering) { + LottieAnimation *animation = _animationContainer.animation; + std::shared_ptr renderNode = [_animationContainer internalGetRootRenderTreeNode]; + if (!renderNode) { return nil; } diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index ff9c7fe159..f4ca69a3c5 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -119,7 +119,7 @@ public final class ViewController: UIViewController { self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if !"".isEmpty { + if "".isEmpty { if #available(iOS 13.0, *) { self.test = ReferenceCompareTest(view: self.view) } diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift index c895bc1284..e4cf552b8e 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift @@ -802,11 +802,14 @@ final class ChatSendMessageContextScreenComponent: Component { } let standaloneReactionAnimation: AnimatedStickerNode + var effectiveScale: CGFloat = 1.0 #if targetEnvironment(simulator) standaloneReactionAnimation = DirectAnimatedStickerNode() + effectiveScale = 1.4 #else if "".isEmpty { standaloneReactionAnimation = DirectAnimatedStickerNode() + effectiveScale = 1.4 } else { standaloneReactionAnimation = LottieMetalAnimatedStickerNode() } @@ -823,7 +826,7 @@ final class ChatSendMessageContextScreenComponent: Component { let pathPrefix = component.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(customEffectResource.id) let source = AnimatedStickerResourceSource(account: component.context.account, resource: customEffectResource, fitzModifier: nil) - standaloneReactionAnimation.setup(source: source, width: Int(effectSize.width), height: Int(effectSize.height), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix)) + standaloneReactionAnimation.setup(source: source, width: Int(effectSize.width * effectiveScale), height: Int(effectSize.height * effectiveScale), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix)) standaloneReactionAnimation.completed = { [weak self, weak standaloneReactionAnimation] _ in guard let self else { return diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index db570aef1f..85183d2692 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -5922,17 +5922,20 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI let pathPrefix = item.context.account.postbox.mediaBox.shortLivedResourceCachePathPrefix(resource.id) let additionalAnimationNode: AnimatedStickerNode + var effectiveScale: CGFloat = 1.0 #if targetEnvironment(simulator) additionalAnimationNode = DirectAnimatedStickerNode() + effectiveScale = 1.4 #else if "".isEmpty { additionalAnimationNode = DirectAnimatedStickerNode() + effectiveScale = 1.4 } else { additionalAnimationNode = LottieMetalAnimatedStickerNode() } #endif additionalAnimationNode.updateLayout(size: animationSize) - additionalAnimationNode.setup(source: source, width: Int(animationSize.width), height: Int(animationSize.height), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix)) + additionalAnimationNode.setup(source: source, width: Int(animationSize.width * effectiveScale), height: Int(animationSize.height * effectiveScale), playbackMode: .once, mode: .direct(cachePathPrefix: pathPrefix)) var animationFrame: CGRect if isStickerEffect { let offsetScale: CGFloat = 0.3 diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp index db9e76ffc7..0765e43a96 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp @@ -7,7 +7,6 @@ namespace lottie { InvertedMatteLayer::InvertedMatteLayer(std::shared_ptr inputMatte) : _inputMatte(inputMatte) { setBounds(inputMatte->bounds()); - setNeedsDisplay(true); addSublayer(_inputMatte); } 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 c8a97b55dd..1d795079fe 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 @@ -83,13 +83,15 @@ public: } void displayWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { - _transformNode->updateTree(frame, forceUpdates); - bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame); - _contentsLayer->setTransform(_transformNode->globalTransform()); - _contentsLayer->setOpacity(_transformNode->opacity()); - _contentsLayer->setIsHidden(!layerVisible); + if (_transformNode->updateTree(frame, forceUpdates) || _contentsLayer->isHidden() != !layerVisible) { + _contentsLayer->setTransform(_transformNode->globalTransform()); + _contentsLayer->setOpacity(_transformNode->opacity()); + _contentsLayer->setIsHidden(!layerVisible); + + updateContentsLayerParameters(); + } /// Only update contents if current time is within the layers time bounds. if (layerVisible) { @@ -100,6 +102,9 @@ public: } } + virtual void updateContentsLayerParameters() { + } + virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { /// To be overridden by subclass } @@ -155,9 +160,6 @@ public: return nullptr; } - virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) { - } - public: std::shared_ptr const transformNode() const { return _transformNode; 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 aff1d21f8b..adacde6a4b 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 @@ -174,39 +174,9 @@ public: _contentsTreeNode->_subnodes = renderTreeValue; } - return _renderTreeNode; - } - - virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override { - if (_matteLayer) { - _matteLayer->updateRenderTree(boundingBoxContext); - } - - for (const auto &animationLayer : _animationLayers) { - bool found = false; - for (const auto &sublayer : contentsLayer()->sublayers()) { - if (animationLayer == sublayer) { - found = true; - break; - } - } - if (found) { - animationLayer->updateRenderTree(boundingBoxContext); - } - } - - assert(opacity() == 1.0); - assert(!isHidden()); - assert(!masksToBounds()); - assert(transform().isIdentity()); - assert(position() == Vector2D::Zero()); - _contentsTreeNode->_bounds = _contentsLayer->bounds(); _contentsTreeNode->_position = _contentsLayer->position(); - _contentsTreeNode->_transform = _contentsLayer->transform(); - _contentsTreeNode->_alpha = _contentsLayer->opacity(); _contentsTreeNode->_masksToBounds = _contentsLayer->masksToBounds(); - _contentsTreeNode->_isHidden = _contentsLayer->isHidden(); _renderTreeNode->_bounds = bounds(); _renderTreeNode->_position = position(); @@ -214,6 +184,14 @@ public: _renderTreeNode->_alpha = opacity(); _renderTreeNode->_masksToBounds = masksToBounds(); _renderTreeNode->_isHidden = isHidden(); + + return _renderTreeNode; + } + + virtual void updateContentsLayerParameters() override { + _contentsTreeNode->_transform = _contentsLayer->transform(); + _contentsTreeNode->_alpha = _contentsLayer->opacity(); + _contentsTreeNode->_isHidden = _contentsLayer->isHidden(); } private: 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 e9b2afb90e..8a7759e4b0 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 @@ -1388,27 +1388,22 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath ); } - return _renderTreeNode; -} - -void ShapeCompositionLayer::updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) { - if (_matteLayer) { - _matteLayer->updateRenderTree(boundingBoxContext); - } - _contentRenderTreeNode->_bounds = _contentsLayer->bounds(); _contentRenderTreeNode->_position = _contentsLayer->position(); - _contentRenderTreeNode->_transform = _contentsLayer->transform(); - _contentRenderTreeNode->_alpha = _contentsLayer->opacity(); _contentRenderTreeNode->_masksToBounds = _contentsLayer->masksToBounds(); - _contentRenderTreeNode->_isHidden = _contentsLayer->isHidden(); + + _renderTreeNode->_masksToBounds = masksToBounds(); _renderTreeNode->_bounds = bounds(); _renderTreeNode->_position = position(); - _renderTreeNode->_transform = transform(); - _renderTreeNode->_alpha = opacity(); - _renderTreeNode->_masksToBounds = masksToBounds(); - _renderTreeNode->_isHidden = isHidden(); + + return _renderTreeNode; +} + +void ShapeCompositionLayer::updateContentsLayerParameters() { + _contentRenderTreeNode->_transform = _contentsLayer->transform(); + _contentRenderTreeNode->_alpha = _contentsLayer->opacity(); + _contentRenderTreeNode->_isHidden = _contentsLayer->isHidden(); } } 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 27fed46a4e..2be2f974f2 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 @@ -18,7 +18,8 @@ public: virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override; virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override; - virtual void updateRenderTree(BezierPathsBoundingBoxContext &boundingBoxContext) override; + void initializeContentsLayerParameters(); + virtual void updateContentsLayerParameters() override; private: std::shared_ptr _contentTree; 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 3d6f999f6b..7f95758791 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 @@ -90,7 +90,7 @@ public: _layerFontProvider->addTextLayers(textLayers); _layerFontProvider->reloadTexts(); - setNeedsDisplay(true); + renderTreeNode(); } void setRespectAnimationFrameRate(bool respectAnimationFrameRate) { @@ -249,26 +249,9 @@ public: ); } - updateRenderTree(); - return _renderTreeNode; } - void updateRenderTree() { - for (const auto &animationLayer : _animationLayers) { - bool found = false; - for (const auto &sublayer : sublayers()) { - if (animationLayer == sublayer) { - found = true; - break; - } - } - if (found) { - animationLayer->updateRenderTree(_boundingBoxContext); - } - } - } - private: // MARK: Internal diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp index 1d62e6c8d2..8c07963fcd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp @@ -185,9 +185,12 @@ public: return localUpdatesPermeateDownstream() ? hasUpstreamUpdates() || hasLocalUpdates() : hasUpstreamUpdates(); } - virtual void updateTree(double frame, bool forceUpdates) { - updateContents(frame, forceUpdates); - updateOutputs(frame, forceUpdates); + bool updateTree(double frame, bool forceUpdates) { + if (updateContents(frame, forceUpdates)) { + return updateOutputs(frame, forceUpdates); + } else { + return false; + } } /// The name of the Keypath diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp index 9f9fd4641f..171c75f850 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp @@ -45,13 +45,6 @@ public: } } - bool needsDisplay() const { - return _needsDisplay; - } - void setNeedsDisplay(bool needsDisplay) { - _needsDisplay = true; - } - virtual bool implementsDraw() const { return false; } @@ -148,7 +141,6 @@ private: private: CALayer *_superlayer = nullptr; std::vector> _sublayers; - bool _needsDisplay = false; bool _isHidden = false; float _opacity = 1.0; Vector2D _position = Vector2D(0.0, 0.0); From 054b2ffcb8ce775159d6e3b3f4b273bb8b3efbe7 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 14:40:08 +0400 Subject: [PATCH 02/32] Lottie --- .../LottieMetalTest/Sources/ViewController.swift | 2 +- .../Sources/Lottie/Public/Primitives/Vectors.mm | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index f4ca69a3c5..ff9c7fe159 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -119,7 +119,7 @@ public final class ViewController: UIViewController { self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if "".isEmpty { + if !"".isEmpty { if #available(iOS 13.0, *) { self.test = ReferenceCompareTest(view: self.view) } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 81a0f4ff73..699f0fec89 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -555,7 +555,21 @@ bool CATransform3D::isInvertible() const { } CATransform3D CATransform3D::inverted() const { - return fromNativeTransform(CATransform3DMakeAffineTransform(CGAffineTransformInvert(CATransform3DGetAffineTransform(nativeTransform(*this))))); + simd_double4x4 matrix = { + simd_make_double4(m11, m21, m31, m41), + simd_make_double4(m12, m22, m32, m42), + simd_make_double4(m13, m23, m33, m43), + simd_make_double4(m14, m24, m34, m44) + }; + simd_double4x4 result = simd_inverse(matrix); + CATransform3D nativeResult = CATransform3D( + result.columns[0][0], result.columns[1][0], result.columns[2][0], result.columns[3][0], + result.columns[0][1], result.columns[1][1], result.columns[2][1], result.columns[3][1], + result.columns[0][2], result.columns[1][2], result.columns[2][2], result.columns[3][2], + result.columns[0][3], result.columns[1][3], result.columns[2][3], result.columns[3][3] + ); + + return nativeResult; } bool CGRect::intersects(CGRect const &other) const { From 942ddc013f1c630f63a9642641b8bb60fd091344 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 15:19:48 +0400 Subject: [PATCH 03/32] Lottie --- .../Lottie/Public/Primitives/Vectors.mm | 118 ++++++------------ 1 file changed, 39 insertions(+), 79 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 699f0fec89..0cd9eb8659 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -523,31 +523,26 @@ CATransform3D CATransform3D::operator*(CATransform3D const &b) const { return *this; } - const CATransform3D lhs = b; - const CATransform3D &rhs = *this; - CATransform3D result = CATransform3D::identity(); + simd_double4x4 simdLhs = { + simd_make_double4(b.m11, b.m21, b.m31, b.m41), + simd_make_double4(b.m12, b.m22, b.m32, b.m42), + simd_make_double4(b.m13, b.m23, b.m33, b.m43), + simd_make_double4(b.m14, b.m24, b.m34, b.m44) + }; + simd_double4x4 simdRhs = { + simd_make_double4(m11, m21, m31, m41), + simd_make_double4(m12, m22, m32, m42), + simd_make_double4(m13, m23, m33, m43), + simd_make_double4(m14, m24, m34, m44) + }; - result.m11 = (lhs.m11*rhs.m11)+(lhs.m21*rhs.m12)+(lhs.m31*rhs.m13)+(lhs.m41*rhs.m14); - result.m12 = (lhs.m12*rhs.m11)+(lhs.m22*rhs.m12)+(lhs.m32*rhs.m13)+(lhs.m42*rhs.m14); - result.m13 = (lhs.m13*rhs.m11)+(lhs.m23*rhs.m12)+(lhs.m33*rhs.m13)+(lhs.m43*rhs.m14); - result.m14 = (lhs.m14*rhs.m11)+(lhs.m24*rhs.m12)+(lhs.m34*rhs.m13)+(lhs.m44*rhs.m14); - - result.m21 = (lhs.m11*rhs.m21)+(lhs.m21*rhs.m22)+(lhs.m31*rhs.m23)+(lhs.m41*rhs.m24); - result.m22 = (lhs.m12*rhs.m21)+(lhs.m22*rhs.m22)+(lhs.m32*rhs.m23)+(lhs.m42*rhs.m24); - result.m23 = (lhs.m13*rhs.m21)+(lhs.m23*rhs.m22)+(lhs.m33*rhs.m23)+(lhs.m43*rhs.m24); - result.m24 = (lhs.m14*rhs.m21)+(lhs.m24*rhs.m22)+(lhs.m34*rhs.m23)+(lhs.m44*rhs.m24); - - result.m31 = (lhs.m11*rhs.m31)+(lhs.m21*rhs.m32)+(lhs.m31*rhs.m33)+(lhs.m41*rhs.m34); - result.m32 = (lhs.m12*rhs.m31)+(lhs.m22*rhs.m32)+(lhs.m32*rhs.m33)+(lhs.m42*rhs.m34); - result.m33 = (lhs.m13*rhs.m31)+(lhs.m23*rhs.m32)+(lhs.m33*rhs.m33)+(lhs.m43*rhs.m34); - result.m34 = (lhs.m14*rhs.m31)+(lhs.m24*rhs.m32)+(lhs.m34*rhs.m33)+(lhs.m44*rhs.m34); - - result.m41 = (lhs.m11*rhs.m41)+(lhs.m21*rhs.m42)+(lhs.m31*rhs.m43)+(lhs.m41*rhs.m44); - result.m42 = (lhs.m12*rhs.m41)+(lhs.m22*rhs.m42)+(lhs.m32*rhs.m43)+(lhs.m42*rhs.m44); - result.m43 = (lhs.m13*rhs.m41)+(lhs.m23*rhs.m42)+(lhs.m33*rhs.m43)+(lhs.m43*rhs.m44); - result.m44 = (lhs.m14*rhs.m41)+(lhs.m24*rhs.m42)+(lhs.m34*rhs.m43)+(lhs.m44*rhs.m44); - - return result; + simd_double4x4 simdResult = simd_mul(simdRhs, simdLhs); + return CATransform3D( + simdResult.columns[0][0], simdResult.columns[1][0], simdResult.columns[2][0], simdResult.columns[3][0], + simdResult.columns[0][1], simdResult.columns[1][1], simdResult.columns[2][1], simdResult.columns[3][1], + simdResult.columns[0][2], simdResult.columns[1][2], simdResult.columns[2][2], simdResult.columns[3][2], + simdResult.columns[0][3], simdResult.columns[1][3], simdResult.columns[2][3], simdResult.columns[3][3] + ); } bool CATransform3D::isInvertible() const { @@ -590,71 +585,36 @@ CGRect CGRect::unionWith(CGRect const &other) const { return CGRect(result.origin.x, result.origin.y, result.size.width, result.size.height); } -static inline Vector2D applyingTransformToPoint(CATransform3D const &transform, Vector2D const &point) { - double newX = point.x * transform.m11 + point.y * transform.m21 + transform.m41; - double newY = point.x * transform.m12 + point.y * transform.m22 + transform.m42; - double newW = point.x * transform.m14 + point.y * transform.m24 + transform.m44; - - return Vector2D(newX / newW, newY / newW); -} - CGRect CGRect::applyingTransform(CATransform3D const &transform) const { if (transform.isIdentity()) { return *this; } - Vector2D topLeft = applyingTransformToPoint(transform, Vector2D(x, y)); - Vector2D topRight = applyingTransformToPoint(transform, Vector2D(x + width, y)); - Vector2D bottomLeft = applyingTransformToPoint(transform, Vector2D(x, y + height)); - Vector2D bottomRight = applyingTransformToPoint(transform, Vector2D(x + width, y + height)); + simd_double3 simdRow1 = simd_make_double3(transform.m11, transform.m12, transform.m14); + simd_double3 simdRow2 = simd_make_double3(transform.m21, transform.m22, transform.m24); + simd_double3 simdRow3 = simd_make_double3(transform.m41, transform.m42, transform.m44); - double minX = topLeft.x; - if (topRight.x < minX) { - minX = topRight.x; - } - if (bottomLeft.x < minX) { - minX = bottomLeft.x; - } - if (bottomRight.x < minX) { - minX = bottomRight.x; - } + Vector2D sourceTopLeft = Vector2D(x, y); + Vector2D sourceTopRight = Vector2D(x + width, y); + Vector2D sourceBottomLeft = Vector2D(x, y + height); + Vector2D sourceBottomRight = Vector2D(x + width, y + height); - double minY = topLeft.y; - if (topRight.y < minY) { - minY = topRight.y; - } - if (bottomLeft.y < minY) { - minY = bottomLeft.y; - } - if (bottomRight.y < minY) { - minY = bottomRight.y; - } + simd_double3 simdTopLeft = sourceTopLeft.x * simdRow1 + sourceTopLeft.y * simdRow2 + simdRow3; + simd_double3 simdTopRight = sourceTopRight.x * simdRow1 + sourceTopRight.y * simdRow2 + simdRow3; + simd_double3 simdBottomLeft = sourceBottomLeft.x * simdRow1 + sourceBottomLeft.y * simdRow2 + simdRow3; + simd_double3 simdBottomRight = sourceBottomRight.x * simdRow1 + sourceBottomRight.y * simdRow2 + simdRow3; - double maxX = topLeft.x; - if (topRight.x > maxX) { - maxX = topRight.x; - } - if (bottomLeft.x > maxX) { - maxX = bottomLeft.x; - } - if (bottomRight.x > maxX) { - maxX = bottomRight.x; - } + Vector2D topLeft = Vector2D(simdTopLeft[0] / simdTopLeft[2], simdTopLeft[1] / simdTopLeft[2]); + Vector2D topRight = Vector2D(simdTopRight[0] / simdTopRight[2], simdTopRight[1] / simdTopRight[2]); + Vector2D bottomLeft = Vector2D(simdBottomLeft[0] / simdBottomLeft[2], simdBottomLeft[1] / simdBottomLeft[2]); + Vector2D bottomRight = Vector2D(simdBottomRight[0] / simdBottomRight[2], simdBottomRight[1] / simdBottomRight[2]); - double maxY = topLeft.y; - if (topRight.y > maxY) { - maxY = topRight.y; - } - if (bottomLeft.y > maxY) { - maxY = bottomLeft.y; - } - if (bottomRight.y > maxY) { - maxY = bottomRight.y; - } + double minX = simd_reduce_min(simd_make_double4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); + double minY = simd_reduce_min(simd_make_double4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); + double maxX = simd_reduce_max(simd_make_double4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); + double maxY = simd_reduce_max(simd_make_double4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); - CGRect result(minX, minY, maxX - minX, maxY - minY); - - return result; + return CGRect(minX, minY, maxX - minX, maxY - minY); } } From 9c4fdec38d62b3d2636fa2278ce0443488612e21 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 17:51:59 +0400 Subject: [PATCH 04/32] Refactoring --- .../Sources/ViewController.swift | 2 +- .../PublicHeaders/LottieCpp/Vectors.h | 28 +++++++++---------- .../CompLayers/ShapeUtils/BezierPathUtils.cpp | 2 +- .../Public/Keyframes/ValueInterpolators.cpp | 5 ++-- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index ff9c7fe159..f4ca69a3c5 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -119,7 +119,7 @@ public final class ViewController: UIViewController { self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if !"".isEmpty { + if "".isEmpty { if #available(iOS 13.0, *) { self.test = ReferenceCompareTest(view: self.view) } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 19105a01dc..006c40917e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -16,16 +16,16 @@ struct Vector1D { Array }; - explicit Vector1D(double value_) : + explicit Vector1D(float value_) : value(value_) { } explicit Vector1D(lottiejson11::Json const &json) noexcept(false); lottiejson11::Json toJson() const; - double value; + float value; - double distanceTo(Vector1D const &to) const { + float distanceTo(Vector1D const &to) const { return abs(to.value - value); } }; @@ -48,7 +48,7 @@ struct __attribute__((packed)) Vector2D { y(0.0) { } - explicit Vector2D(double x_, double y_) : + explicit Vector2D(float x_, float y_) : x(x_), y(y_) { } @@ -56,8 +56,8 @@ struct __attribute__((packed)) Vector2D { explicit Vector2D(lottiejson11::Json const &json) noexcept(false); lottiejson11::Json toJson() const; - double x; - double y; + float x; + float y; Vector2D operator+(Vector2D const &rhs) const { return Vector2D(x + rhs.x, y + rhs.y); @@ -67,7 +67,7 @@ struct __attribute__((packed)) Vector2D { return Vector2D(x - rhs.x, y - rhs.y); } - Vector2D operator*(double scalar) const { + Vector2D operator*(float scalar) const { return Vector2D(x * scalar, y * scalar); } @@ -83,15 +83,15 @@ struct __attribute__((packed)) Vector2D { return x == 0.0 && y == 0.0; } - double distanceTo(Vector2D const &to) const { + float distanceTo(Vector2D const &to) const { auto deltaX = to.x - x; auto deltaY = to.y - y; return sqrt(deltaX * deltaX + deltaY * deltaY); } bool colinear(Vector2D const &a, Vector2D const &b) const { - double area = x * (a.y - b.y) + a.x * (b.y - y) + b.x * (y - a.y); - double accuracy = 0.05; + float area = x * (a.y - b.y) + a.x * (b.y - y) + b.x * (y - a.y); + float accuracy = 0.05; if (area < accuracy && area > -accuracy) { return true; } @@ -120,7 +120,7 @@ Vector2D interpolate( ); struct Vector3D { - explicit Vector3D(double x_, double y_, double z_) : + explicit Vector3D(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { @@ -129,9 +129,9 @@ struct Vector3D { explicit Vector3D(lottiejson11::Json const &json) noexcept(false); lottiejson11::Json toJson() const; - double x = 0.0; - double y = 0.0; - double z = 0.0; + float x = 0.0; + float y = 0.0; + float z = 0.0; }; Vector3D interpolate( diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp index cd129d2606..a94aec3081 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp @@ -57,7 +57,7 @@ BezierPath makeRectangleBezierPath( const double ControlPointConstant = 0.55228; Vector2D size = inputSize * 0.5; - double radius = std::min(std::min(cornerRadius, size.x), size.y); + double radius = std::min(std::min(cornerRadius, (double)size.x), (double)size.y); BezierPath bezierPath; std::vector points; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp index 6e91c29572..7a92fd80ba 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp @@ -10,10 +10,11 @@ void batchInterpolate(std::vector const &from, std::vector Date: Sat, 18 May 2024 17:58:34 +0400 Subject: [PATCH 05/32] Refactoring --- .../PublicHeaders/LottieCpp/Vectors.h | 40 ++++++------- .../Lottie/Public/Primitives/Vectors.mm | 60 +++++++++---------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 006c40917e..5046d96b10 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -149,16 +149,16 @@ inline double radiansToDegrees(double value) { } struct CATransform3D { - double m11, m12, m13, m14; - double m21, m22, m23, m24; - double m31, m32, m33, m34; - double m41, m42, m43, m44; + float m11, m12, m13, m14; + float m21, m22, m23, m24; + float m31, m32, m33, m34; + float m41, m42, m43, m44; CATransform3D( - double m11_, double m12_, double m13_, double m14_, - double m21_, double m22_, double m23_, double m24_, - double m31_, double m32_, double m33_, double m34_, - double m41_, double m42_, double m43_, double m44_ + float m11_, float m12_, float m13_, float m14_, + float m21_, float m22_, float m23_, float m24_, + float m31_, float m32_, float m33_, float m34_, + float m41_, float m42_, float m43_, float m44_ ) : m11(m11_), m12(m12_), m13(m13_), m14(m14_), m21(m21_), m22(m22_), m23(m23_), m24(m24_), @@ -184,7 +184,7 @@ struct CATransform3D { m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0; } - static CATransform3D makeTranslation(double tx, double ty, double tz) { + static CATransform3D makeTranslation(float tx, float ty, float tz) { return CATransform3D( 1, 0, 0, 0, 0, 1, 0, 0, @@ -193,7 +193,7 @@ struct CATransform3D { ); } - static CATransform3D makeScale(double sx, double sy, double sz) { + static CATransform3D makeScale(float sx, float sy, float sz) { return CATransform3D( sx, 0, 0, 0, 0, sy, 0, 0, @@ -202,12 +202,12 @@ struct CATransform3D { ); } - static CATransform3D makeRotation(double radians, double x, double y, double z); + static CATransform3D makeRotation(float radians, float x, float y, float z); - static CATransform3D makeSkew(double skew, double skewAxis) { - double mCos = cos(degreesToRadians(skewAxis)); - double mSin = sin(degreesToRadians(skewAxis)); - double aTan = tan(degreesToRadians(skew)); + static CATransform3D makeSkew(float skew, float skewAxis) { + float mCos = cos(degreesToRadians(skewAxis)); + float mSin = sin(degreesToRadians(skewAxis)); + float aTan = tan(degreesToRadians(skew)); CATransform3D transform1( mCos, @@ -273,9 +273,9 @@ struct CATransform3D { Vector2D const &anchor, Vector2D const &position, Vector2D const &scale, - double rotation, - std::optional skew, - std::optional skewAxis + float rotation, + std::optional skew, + std::optional skewAxis ) { CATransform3D result = CATransform3D::identity(); if (skew.has_value() && skewAxis.has_value()) { @@ -287,13 +287,13 @@ struct CATransform3D { return result; } - CATransform3D rotated(double degrees) const; + CATransform3D rotated(float degrees) const; CATransform3D translated(Vector2D const &translation) const; CATransform3D scaled(Vector2D const &scale) const; - CATransform3D skewed(double skew, double skewAxis) const { + CATransform3D skewed(float skew, float skewAxis) const { return CATransform3D::makeSkew(skew, skewAxis) * (*this); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 0cd9eb8659..40e93bad15 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -463,7 +463,7 @@ CATransform3D fromNativeTransform(::CATransform3D const &value) { return result; } -CATransform3D CATransform3D::makeRotation(double radians, double x, double y, double z) { +CATransform3D CATransform3D::makeRotation(float radians, float x, float y, float z) { return fromNativeTransform(CATransform3DMakeRotation(radians, x, y, z)); /*if (x == 0.0 && y == 0.0 && z == 0.0) { @@ -501,7 +501,7 @@ CATransform3D CATransform3D::makeRotation(double radians, double x, double y, do return returnValue;*/ } -CATransform3D CATransform3D::rotated(double degrees) const { +CATransform3D CATransform3D::rotated(float degrees) const { return fromNativeTransform(CATransform3DRotate(nativeTransform(*this), degreesToRadians(degrees), 0.0, 0.0, 1.0)); //return CATransform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this); } @@ -523,20 +523,20 @@ CATransform3D CATransform3D::operator*(CATransform3D const &b) const { return *this; } - simd_double4x4 simdLhs = { - simd_make_double4(b.m11, b.m21, b.m31, b.m41), - simd_make_double4(b.m12, b.m22, b.m32, b.m42), - simd_make_double4(b.m13, b.m23, b.m33, b.m43), - simd_make_double4(b.m14, b.m24, b.m34, b.m44) + simd_float4x4 simdLhs = { + simd_make_float4(b.m11, b.m21, b.m31, b.m41), + simd_make_float4(b.m12, b.m22, b.m32, b.m42), + simd_make_float4(b.m13, b.m23, b.m33, b.m43), + simd_make_float4(b.m14, b.m24, b.m34, b.m44) }; - simd_double4x4 simdRhs = { - simd_make_double4(m11, m21, m31, m41), - simd_make_double4(m12, m22, m32, m42), - simd_make_double4(m13, m23, m33, m43), - simd_make_double4(m14, m24, m34, m44) + simd_float4x4 simdRhs = { + simd_make_float4(m11, m21, m31, m41), + simd_make_float4(m12, m22, m32, m42), + simd_make_float4(m13, m23, m33, m43), + simd_make_float4(m14, m24, m34, m44) }; - simd_double4x4 simdResult = simd_mul(simdRhs, simdLhs); + simd_float4x4 simdResult = simd_mul(simdRhs, simdLhs); return CATransform3D( simdResult.columns[0][0], simdResult.columns[1][0], simdResult.columns[2][0], simdResult.columns[3][0], simdResult.columns[0][1], simdResult.columns[1][1], simdResult.columns[2][1], simdResult.columns[3][1], @@ -550,13 +550,13 @@ bool CATransform3D::isInvertible() const { } CATransform3D CATransform3D::inverted() const { - simd_double4x4 matrix = { - simd_make_double4(m11, m21, m31, m41), - simd_make_double4(m12, m22, m32, m42), - simd_make_double4(m13, m23, m33, m43), - simd_make_double4(m14, m24, m34, m44) + simd_float4x4 matrix = { + simd_make_float4(m11, m21, m31, m41), + simd_make_float4(m12, m22, m32, m42), + simd_make_float4(m13, m23, m33, m43), + simd_make_float4(m14, m24, m34, m44) }; - simd_double4x4 result = simd_inverse(matrix); + simd_float4x4 result = simd_inverse(matrix); CATransform3D nativeResult = CATransform3D( result.columns[0][0], result.columns[1][0], result.columns[2][0], result.columns[3][0], result.columns[0][1], result.columns[1][1], result.columns[2][1], result.columns[3][1], @@ -590,29 +590,29 @@ CGRect CGRect::applyingTransform(CATransform3D const &transform) const { return *this; } - simd_double3 simdRow1 = simd_make_double3(transform.m11, transform.m12, transform.m14); - simd_double3 simdRow2 = simd_make_double3(transform.m21, transform.m22, transform.m24); - simd_double3 simdRow3 = simd_make_double3(transform.m41, transform.m42, transform.m44); + simd_float3 simdRow1 = simd_make_float3(transform.m11, transform.m12, transform.m14); + simd_float3 simdRow2 = simd_make_float3(transform.m21, transform.m22, transform.m24); + simd_float3 simdRow3 = simd_make_float3(transform.m41, transform.m42, transform.m44); Vector2D sourceTopLeft = Vector2D(x, y); Vector2D sourceTopRight = Vector2D(x + width, y); Vector2D sourceBottomLeft = Vector2D(x, y + height); Vector2D sourceBottomRight = Vector2D(x + width, y + height); - simd_double3 simdTopLeft = sourceTopLeft.x * simdRow1 + sourceTopLeft.y * simdRow2 + simdRow3; - simd_double3 simdTopRight = sourceTopRight.x * simdRow1 + sourceTopRight.y * simdRow2 + simdRow3; - simd_double3 simdBottomLeft = sourceBottomLeft.x * simdRow1 + sourceBottomLeft.y * simdRow2 + simdRow3; - simd_double3 simdBottomRight = sourceBottomRight.x * simdRow1 + sourceBottomRight.y * simdRow2 + simdRow3; + simd_float3 simdTopLeft = sourceTopLeft.x * simdRow1 + sourceTopLeft.y * simdRow2 + simdRow3; + simd_float3 simdTopRight = sourceTopRight.x * simdRow1 + sourceTopRight.y * simdRow2 + simdRow3; + simd_float3 simdBottomLeft = sourceBottomLeft.x * simdRow1 + sourceBottomLeft.y * simdRow2 + simdRow3; + simd_float3 simdBottomRight = sourceBottomRight.x * simdRow1 + sourceBottomRight.y * simdRow2 + simdRow3; Vector2D topLeft = Vector2D(simdTopLeft[0] / simdTopLeft[2], simdTopLeft[1] / simdTopLeft[2]); Vector2D topRight = Vector2D(simdTopRight[0] / simdTopRight[2], simdTopRight[1] / simdTopRight[2]); Vector2D bottomLeft = Vector2D(simdBottomLeft[0] / simdBottomLeft[2], simdBottomLeft[1] / simdBottomLeft[2]); Vector2D bottomRight = Vector2D(simdBottomRight[0] / simdBottomRight[2], simdBottomRight[1] / simdBottomRight[2]); - double minX = simd_reduce_min(simd_make_double4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); - double minY = simd_reduce_min(simd_make_double4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); - double maxX = simd_reduce_max(simd_make_double4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); - double maxY = simd_reduce_max(simd_make_double4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); + float minX = simd_reduce_min(simd_make_float4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); + float minY = simd_reduce_min(simd_make_float4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); + float maxX = simd_reduce_max(simd_make_float4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); + float maxY = simd_reduce_max(simd_make_float4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); return CGRect(minX, minY, maxX - minX, maxY - minY); } From c034abeca8c02514d8b7e4c5d7f9263686da8db9 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 18:12:54 +0400 Subject: [PATCH 06/32] Refactoring --- .../Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h | 2 +- .../LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 5046d96b10..f4498aad23 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -30,7 +30,7 @@ struct Vector1D { } }; -double interpolate(double value, double to, double amount); +float interpolate(float value, float to, float amount); Vector1D interpolate( Vector1D const &from, diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 40e93bad15..005204843b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -147,7 +147,7 @@ CATransform3D CATransform3D::_identity = CATransform3D( 0.0, 0.0, 0.0, 1.0 ); -double interpolate(double value, double to, double amount) { +float interpolate(float value, float to, float amount) { return value + ((to - value) * amount); } From 6188ec971c150d5985ff21ff88af492f9f4dbdd2 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 18:25:36 +0400 Subject: [PATCH 07/32] Refactoring --- .../PublicHeaders/LottieCpp/Vectors.h | 12 ++++++------ .../Lottie/Public/Primitives/Vectors.mm | 18 +++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index f4498aad23..601bb3912a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -35,7 +35,7 @@ float interpolate(float value, float to, float amount); Vector1D interpolate( Vector1D const &from, Vector1D const &to, - double amount + float amount ); struct __attribute__((packed)) Vector2D { @@ -100,23 +100,23 @@ struct __attribute__((packed)) Vector2D { Vector2D pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, double amount) const; - Vector2D interpolate(Vector2D const &to, double amount) const; + Vector2D interpolate(Vector2D const &to, float amount) const; Vector2D interpolate( Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, - double amount, + float amount, int maxIterations = 3, int samples = 20, - double accuracy = 1.0 + float accuracy = 1.0 ) const; }; Vector2D interpolate( Vector2D const &from, Vector2D const &to, - double amount + float amount ); struct Vector3D { @@ -137,7 +137,7 @@ struct Vector3D { Vector3D interpolate( Vector3D const &from, Vector3D const &to, - double amount + float amount ); inline double degreesToRadians(double value) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 005204843b..074c23fb35 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -154,7 +154,7 @@ float interpolate(float value, float to, float amount) { Vector1D interpolate( Vector1D const &from, Vector1D const &to, - double amount + float amount ) { return Vector1D(interpolate(from.value, to.value, amount)); } @@ -162,7 +162,7 @@ Vector1D interpolate( Vector2D interpolate( Vector2D const &from, Vector2D const &to, - double amount + float amount ) { return Vector2D(interpolate(from.x, to.x, amount), interpolate(from.y, to.y, amount)); } @@ -171,7 +171,7 @@ Vector2D interpolate( Vector3D interpolate( Vector3D const &from, Vector3D const &to, - double amount + float amount ) { return Vector3D(interpolate(from.x, to.x, amount), interpolate(from.y, to.y, amount), interpolate(from.z, to.z, amount)); } @@ -289,7 +289,7 @@ struct InterpolationPoint2D { }; namespace { - double interpolateDouble(double value, double to, double amount) { + double interpolateFloat(float value, float to, float amount) { return value + ((to - value) * amount); } } @@ -304,10 +304,10 @@ Vector2D Vector2D::pointOnPath(Vector2D const &to, Vector2D const &outTangent, V return f; } -Vector2D Vector2D::interpolate(Vector2D const &to, double amount) const { +Vector2D Vector2D::interpolate(Vector2D const &to, float amount) const { return Vector2D( - interpolateDouble(x, to.x, amount), - interpolateDouble(y, to.y, amount) + interpolateFloat(x, to.x, amount), + interpolateFloat(y, to.y, amount) ); } @@ -315,10 +315,10 @@ Vector2D Vector2D::interpolate( Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, - double amount, + float amount, int maxIterations, int samples, - double accuracy + float accuracy ) const { if (amount == 0.0) { return *this; From 8e43e2641587a553b1699d3ca25c6ee04f7ecb70 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 18:37:18 +0400 Subject: [PATCH 08/32] Refactoring --- .../PublicHeaders/LottieCpp/Vectors.h | 34 +++++++++---------- .../Lottie/Public/Primitives/Vectors.mm | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 601bb3912a..4a5fda711f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -98,7 +98,7 @@ struct __attribute__((packed)) Vector2D { return false; } - Vector2D pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, double amount) const; + Vector2D pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, float amount) const; Vector2D interpolate(Vector2D const &to, float amount) const; @@ -140,12 +140,12 @@ Vector3D interpolate( float amount ); -inline double degreesToRadians(double value) { - return value * M_PI / 180.0; +inline float degreesToRadians(float value) { + return value * M_PI / 180.0f; } -inline double radiansToDegrees(double value) { - return value * 180.0 / M_PI; +inline float radiansToDegrees(float value) { + return value * 180.0f / M_PI; } struct CATransform3D { @@ -312,21 +312,21 @@ private: }; struct CGRect { - explicit CGRect(double x_, double y_, double width_, double height_) : + explicit CGRect(float x_, float y_, float width_, float height_) : x(x_), y(y_), width(width_), height(height_) { } - double x = 0.0; - double y = 0.0; - double width = 0.0; - double height = 0.0; + float x = 0.0f; + float y = 0.0f; + float width = 0.0f; + float height = 0.0f; static CGRect veryLarge() { return CGRect( - -100000000.0, - -100000000.0, - 200000000.0, - 200000000.0 + -100000000.0f, + -100000000.0f, + 200000000.0f, + 200000000.0f ); } @@ -342,13 +342,13 @@ struct CGRect { return width <= 0.0 || height <= 0.0; } - CGRect insetBy(double dx, double dy) const { + CGRect insetBy(float dx, float dy) const { CGRect result = *this; result.x += dx; result.y += dy; - result.width -= dx * 2.0; - result.height -= dy * 2.0; + result.width -= dx * 2.0f; + result.height -= dy * 2.0f; return result; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 074c23fb35..ba1fccbfa7 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -294,7 +294,7 @@ namespace { } } -Vector2D Vector2D::pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, double amount) const { +Vector2D Vector2D::pointOnPath(Vector2D const &to, Vector2D const &outTangent, Vector2D const &inTangent, float amount) const { auto a = interpolate(outTangent, amount); auto b = outTangent.interpolate(inTangent, amount); auto c = inTangent.interpolate(to, amount); From ca50ed466a39a37042bcca107b32d78696552d08 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 18:40:24 +0400 Subject: [PATCH 09/32] Refactoring --- .../Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h | 2 +- .../LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 4a5fda711f..edd1ce0121 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -370,7 +370,7 @@ inline bool isInRange(double value, double from, double to) { return from < value && value < to; } -double cubicBezierInterpolate(double value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3); +float cubicBezierInterpolate(float value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index ba1fccbfa7..6f5a3bc878 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -254,7 +254,7 @@ static double SolveCubic(double a, double b, double c, double d) { return -1; } -double cubicBezierInterpolate(double value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3) { +float cubicBezierInterpolate(float value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3) { double t = 0.0; if (value == P0.x) { // Handle corner cases explicitly to prevent rounding errors From 422447656b52a548f5752f71b28fc7e4e7ebb35f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 19:10:50 +0400 Subject: [PATCH 10/32] Refactoring --- .../Sources/ViewController.swift | 2 +- .../RenderLayers/GetGradientParameters.cpp | 4 +- .../Public/Keyframes/Interpolatable.cpp | 8 +- .../Public/Keyframes/Interpolatable.hpp | 4 +- .../Lottie/Public/Keyframes/Keyframe.hpp | 4 +- .../Lottie/Public/Primitives/Vectors.mm | 88 ++++++++++--------- 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index f4ca69a3c5..da7d22c2b2 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 = "778160933443732778.json" + //continueFromName = "569118802063655905.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/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp index a1b830cf13..749cdd6825 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp @@ -64,7 +64,7 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s if (location >= colorLocations[i] && location <= colorLocations[i + 1]) { double localLocation = 0.0; if (colorLocations[i] != colorLocations[i + 1]) { - localLocation = remapDouble(location, colorLocations[i], colorLocations[i + 1], 0.0, 1.0); + localLocation = remapFloat(location, colorLocations[i], colorLocations[i + 1], 0.0, 1.0); } color = ValueInterpolator::interpolate(gradientColors[i], gradientColors[i + 1], localLocation, std::nullopt, std::nullopt); break; @@ -76,7 +76,7 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s if (location >= alphaLocations[i] && location <= alphaLocations[i + 1]) { double localLocation = 0.0; if (alphaLocations[i] != alphaLocations[i + 1]) { - localLocation = remapDouble(location, alphaLocations[i], alphaLocations[i + 1], 0.0, 1.0); + localLocation = remapFloat(location, alphaLocations[i], alphaLocations[i + 1], 0.0, 1.0); } alpha = ValueInterpolator::interpolate(alphaValues[i], alphaValues[i + 1], localLocation, std::nullopt, std::nullopt); break; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.cpp index 8165d87d71..aa58f615c4 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.cpp @@ -2,13 +2,13 @@ namespace lottie { -double remapDouble(double value, double fromLow, double fromHigh, double toLow, double toHigh) { +float remapFloat(float value, float fromLow, float fromHigh, float toLow, float toHigh) { return toLow + (value - fromLow) * (toHigh - toLow) / (fromHigh - fromLow); } -double clampDouble(double value, double a, double b) { - double minValue = a <= b ? a : b; - double maxValue = a <= b ? b : a; +float clampFloat(float value, float a, float b) { + float minValue = a <= b ? a : b; + float maxValue = a <= b ? b : a; return std::max(std::min(value, maxValue), minValue); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.hpp index 2bfabdca07..b57223466a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Interpolatable.hpp @@ -5,9 +5,9 @@ namespace lottie { -double remapDouble(double value, double fromLow, double fromHigh, double toLow, double toHigh); +float remapFloat(float value, float fromLow, float fromHigh, float toLow, float toHigh); -double clampDouble(double value, double a, double b); +float clampFloat(float value, float a, float b); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp index 3d5797db3f..124d629c4f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp @@ -100,8 +100,8 @@ public: if (to.inTangent.has_value()) { inTanPoint = to.inTangent.value(); } - double progress = remapDouble(keyTime, startTime, endTime, 0.0, 1.0); - if (!outTanPoint.isZero() || inTanPoint != Vector2D(1.0, 1.0)) { + double progress = remapFloat(keyTime, startTime, endTime, 0.0f, 1.0f); + if (!outTanPoint.isZero() || inTanPoint != Vector2D(1.0f, 1.0f)) { /// Cubic interpolation progress = cubicBezierInterpolate(progress, Vector2D::Zero(), outTanPoint, inTanPoint, Vector2D(1.0, 1.0)); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 6f5a3bc878..4f4cfa7e8f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -176,12 +176,12 @@ Vector3D interpolate( return Vector3D(interpolate(from.x, to.x, amount), interpolate(from.y, to.y, amount), interpolate(from.z, to.z, amount)); } -static double cubicRoot(double value) { +static float cubicRoot(float value) { return pow(value, 1.0 / 3.0); } -static double SolveQuadratic(double a, double b, double c) { - double result = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a); +static float SolveQuadratic(float a, float b, float c) { + float result = (-b + sqrt((b * b) - 4 * a * c)) / (2 * a); if (isInRangeOrEqual(result, 0.0, 1.0)) { return result; } @@ -194,35 +194,39 @@ static double SolveQuadratic(double a, double b, double c) { return -1.0; } -static double SolveCubic(double a, double b, double c, double d) { - if (a == 0.0) { +inline bool isApproximatelyEqual(float value, float other) { + return std::abs(value - other) <= FLT_EPSILON; +} + +static float SolveCubic(double a, double b, double c, double d) { + if (isApproximatelyEqual(a, 0.0f)) { return SolveQuadratic(b, c, d); } - if (d == 0.0) { + if (isApproximatelyEqual(d, 0.0f)) { return 0.0; } b /= a; c /= a; d /= a; - double q = (3.0 * c - (b * b)) / 9.0; - double r = (-27.0 * d + b * (9.0 * c - 2.0 * (b * b))) / 54.0; - double disc = (q * q * q) + (r * r); - double term1 = b / 3.0; + float q = (3.0 * c - (b * b)) / 9.0; + float r = (-27.0 * d + b * (9.0 * c - 2.0 * (b * b))) / 54.0; + float disc = (q * q * q) + (r * r); + float term1 = b / 3.0; if (disc > 0.0) { - double s = r + sqrt(disc); + float s = r + sqrt(disc); s = (s < 0) ? -cubicRoot(-s) : cubicRoot(s); - double t = r - sqrt(disc); + float t = r - sqrt(disc); t = (t < 0) ? -cubicRoot(-t) : cubicRoot(t); - double result = -term1 + s + t; + float result = -term1 + s + t; if (isInRangeOrEqual(result, 0.0, 1.0)) { return result; } - } else if (disc == 0) { - double r13 = (r < 0) ? -cubicRoot(-r) : cubicRoot(r); + } else if (isApproximatelyEqual(disc, 0.0f)) { + float r13 = (r < 0) ? -cubicRoot(-r) : cubicRoot(r); - double result = -term1 + 2.0 * r13; + float result = -term1 + 2.0 * r13; if (isInRangeOrEqual(result, 0.0, 1.0)) { return result; } @@ -233,11 +237,11 @@ static double SolveCubic(double a, double b, double c, double d) { } } else { q = -q; - double dum1 = q * q * q; + float dum1 = q * q * q; dum1 = acos(r / sqrt(dum1)); - double r13 = 2.0 * sqrt(q); + float r13 = 2.0 * sqrt(q); - double result = -term1 + r13 * cos(dum1 / 3.0); + float result = -term1 + r13 * cos(dum1 / 3.0); if (isInRangeOrEqual(result, 0.0, 1.0)) { return result; } @@ -251,45 +255,45 @@ static double SolveCubic(double a, double b, double c, double d) { } } - return -1; + return -1.0; } float cubicBezierInterpolate(float value, Vector2D const &P0, Vector2D const &P1, Vector2D const &P2, Vector2D const &P3) { - double t = 0.0; - if (value == P0.x) { + float t = 0.0; + if (isApproximatelyEqual(value, P0.x)) { // Handle corner cases explicitly to prevent rounding errors t = 0.0; - } else if (value == P3.x) { + } else if (isApproximatelyEqual(value, P3.x)) { t = 1.0; } else { // Calculate t - double a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x; - double b = 3 * P0.x - 6 * P1.x + 3 * P2.x; - double c = -3 * P0.x + 3 * P1.x; - double d = P0.x - value; - double tTemp = SolveCubic(a, b, c, d); - if (tTemp == -1.0) { + float a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x; + float b = 3 * P0.x - 6 * P1.x + 3 * P2.x; + float c = -3 * P0.x + 3 * P1.x; + float d = P0.x - value; + float tTemp = SolveCubic(a, b, c, d); + if (isApproximatelyEqual(tTemp, -1.0f)) { return -1.0; } t = tTemp; } // Calculate y from t - double oneMinusT = 1.0 - t; + float oneMinusT = 1.0 - t; return (oneMinusT * oneMinusT * oneMinusT) * P0.y + 3 * t * (oneMinusT * oneMinusT) * P1.y + 3 * (t * t) * (1 - t) * P2.y + (t * t * t) * P3.y; } struct InterpolationPoint2D { - InterpolationPoint2D(Vector2D const point_, double distance_) : + InterpolationPoint2D(Vector2D const point_, float distance_) : point(point_), distance(distance_) { } Vector2D point; - double distance; + float distance; }; namespace { - double interpolateFloat(float value, float to, float amount) { + float interpolateFloat(float value, float to, float amount) { return value + ((to - value) * amount); } } @@ -331,14 +335,14 @@ Vector2D Vector2D::interpolate( return interpolate(to, amount); } - double step = 1.0 / (double)samples; + float step = 1.0 / (float)samples; std::vector points; points.push_back(InterpolationPoint2D(*this, 0.0)); - double totalLength = 0.0; + float totalLength = 0.0; Vector2D previousPoint = *this; - double previousAmount = 0.0; + float previousAmount = 0.0; int closestPoint = 0; @@ -356,13 +360,13 @@ Vector2D Vector2D::interpolate( previousPoint = newPoint; } - double accurateDistance = amount * totalLength; + float accurateDistance = amount * totalLength; auto point = points[closestPoint]; bool foundPoint = false; - double pointAmount = ((double)closestPoint) * step; - double nextPointAmount = pointAmount + step; + float pointAmount = ((float)closestPoint) * step; + float nextPointAmount = pointAmount + step; int refineIterations = 0; while (!foundPoint) { @@ -372,7 +376,7 @@ Vector2D Vector2D::interpolate( if (nextPoint.distance < accurateDistance) { point = nextPoint; closestPoint = closestPoint + 1; - pointAmount = ((double)closestPoint) * step; + pointAmount = ((float)closestPoint) * step; nextPointAmount = pointAmount + step; if (closestPoint == (int)points.size()) { foundPoint = true; @@ -386,14 +390,14 @@ Vector2D Vector2D::interpolate( continue; } point = points[closestPoint]; - pointAmount = ((double)closestPoint) * step; + pointAmount = ((float)closestPoint) * step; nextPointAmount = pointAmount + step; continue; } /// Now we are certain the point is the closest point under the distance auto pointDiff = nextPoint.distance - point.distance; - auto proposedPointAmount = remapDouble((accurateDistance - point.distance) / pointDiff, 0.0, 1.0, pointAmount, nextPointAmount); + auto proposedPointAmount = remapFloat((accurateDistance - point.distance) / pointDiff, 0.0, 1.0, pointAmount, nextPointAmount); auto newPoint = pointOnPath(to, outTangent, inTangent, proposedPointAmount); auto newDistance = point.distance + point.point.distanceTo(newPoint); From 566bce1a9d3bff9d74aea030c36797039e66f71b Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 19:13:31 +0400 Subject: [PATCH 11/32] Refactoring --- .../LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 4f4cfa7e8f..277f8f5d69 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -198,7 +198,7 @@ inline bool isApproximatelyEqual(float value, float other) { return std::abs(value - other) <= FLT_EPSILON; } -static float SolveCubic(double a, double b, double c, double d) { +static float SolveCubic(float a, float b, float c, float d) { if (isApproximatelyEqual(a, 0.0f)) { return SolveQuadratic(b, c, d); } From 148d0a95e7c370708ebadcd8e160356dcef08ed0 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 19:23:08 +0400 Subject: [PATCH 12/32] Refactoring --- .../Private/Utility/Extensions/CGFloatExtensions.swift | 8 ++++++-- .../LottieCpp/PublicHeaders/LottieCpp/Vectors.h | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Tests/LottieMetalTest/LottieSwift/Sources/Private/Utility/Extensions/CGFloatExtensions.swift b/Tests/LottieMetalTest/LottieSwift/Sources/Private/Utility/Extensions/CGFloatExtensions.swift index 015a10ce45..939725a331 100644 --- a/Tests/LottieMetalTest/LottieSwift/Sources/Private/Utility/Extensions/CGFloatExtensions.swift +++ b/Tests/LottieMetalTest/LottieSwift/Sources/Private/Utility/Extensions/CGFloatExtensions.swift @@ -25,11 +25,15 @@ extension CGFloat { } func isInRangeOrEqual(_ from: CGFloat, _ to: CGFloat) -> Bool { - from <= self && self <= to + let from = Float(from) + let to = Float(to) + return from <= Float(self) && Float(self) <= to } func isInRange(_ from: CGFloat, _ to: CGFloat) -> Bool { - from < self && self < to + let from = Float(from) + let to = Float(to) + return from < Float(self) && Float(self) < to } func cubicBezierInterpolate(_ P0: CGPoint, _ P1: CGPoint, _ P2: CGPoint, _ P3: CGPoint) -> CGFloat { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index edd1ce0121..4f42fe9407 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -362,11 +362,11 @@ struct CGRect { CGRect applyingTransform(CATransform3D const &transform) const; }; -inline bool isInRangeOrEqual(double value, double from, double to) { +inline bool isInRangeOrEqual(float value, float from, float to) { return from <= value && value <= to; } -inline bool isInRange(double value, double from, double to) { +inline bool isInRange(float value, float from, float to) { return from < value && value < to; } From 04b69499a6eaa2978e47cb24dad29a0cb44ec143 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 19:26:46 +0400 Subject: [PATCH 13/32] Refactoring --- .../PublicHeaders/LottieCpp/BezierPath.h | 18 +++++++++--------- .../Private/Utility/Primitives/BezierPath.cpp | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h index c9ee189b25..7fa0b83ffe 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h @@ -13,10 +13,10 @@ namespace lottie { struct BezierTrimPathPosition { - double start; - double end; + float start; + float end; - explicit BezierTrimPathPosition(double start_, double end_); + explicit BezierTrimPathPosition(float start_, float end_); }; class BezierPathContents: public std::enable_shared_from_this { @@ -38,10 +38,10 @@ public: std::vector elements; std::optional closed; - double length(); + float length(); private: - std::optional _length; + std::optional _length; public: void moveToStartPoint(CurveVertex const &vertex); @@ -72,7 +72,7 @@ public: /// oooooooooooooooooo--------------------~~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooo /// | toLength| |Offset |fromLength | /// - std::vector> trim(double fromLength, double toLength, double offsetLength); + std::vector> trim(float fromLength, float toLength, float offsetLength); // MARK: Private @@ -87,7 +87,7 @@ public: lottiejson11::Json toJson() const; - double length(); + float length(); void moveToStartPoint(CurveVertex const &vertex); void addVertex(CurveVertex const &vertex); @@ -115,7 +115,7 @@ public: /// oooooooooooooooooo--------------------~~~~~~~~~~~~~~~~ooooooooooooooooooooooooooooo /// | toLength| |Offset |fromLength | /// - std::vector trim(double fromLength, double toLength, double offsetLength); + std::vector trim(float fromLength, float toLength, float offsetLength); std::vector const &elements() const; std::vector &mutableElements(); @@ -146,7 +146,7 @@ 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); +std::vector trimBezierPaths(std::vector &sourcePaths, float start, float end, float offset, TrimType type); } 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 b5c6065ecd..44240211e7 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 @@ -7,7 +7,7 @@ namespace lottie { -BezierTrimPathPosition::BezierTrimPathPosition(double start_, double end_) : +BezierTrimPathPosition::BezierTrimPathPosition(float start_, float end_) : start(start_), end(end_) { } @@ -129,11 +129,11 @@ std::shared_ptr BezierPathContents::cgPath() const { return cgPath; } -double BezierPathContents::length() { +float BezierPathContents::length() { if (_length.has_value()) { return _length.value(); } else { - double result = 0.0; + float result = 0.0; for (size_t i = 1; i < elements.size(); i++) { result += elements[i].length(elements[i - 1]); } @@ -221,7 +221,7 @@ void BezierPathContents::updateVertex(CurveVertex const &vertex, int atIndex, bo } } -std::vector> BezierPathContents::trim(double fromLength, double toLength, double offsetLength) { +std::vector> BezierPathContents::trim(float fromLength, float toLength, float offsetLength) { if (elements.size() <= 1) { return {}; } @@ -230,7 +230,7 @@ std::vector> BezierPathContents::trim(double return {}; } - double lengthValue = length(); + float lengthValue = length(); /// Normalize lengths to the curve length. auto start = fmod(fromLength + offsetLength, lengthValue); @@ -284,7 +284,7 @@ std::vector> BezierPathContents::trimPathAtL std::vector> paths; - double runningLength = 0.0; + float runningLength = 0.0; bool finishedTrimming = false; auto pathElements = elements; @@ -301,7 +301,7 @@ std::vector> BezierPathContents::trimPathAtL /// Loop through and add elements within start->end range. /// Get current element auto element = pathElements[i]; - double elementLength = 0.0; + float elementLength = 0.0; if (i != 0) { elementLength = element.length(pathElements[i - 1]); } @@ -412,7 +412,7 @@ lottiejson11::Json BezierPath::toJson() const { return _contents->toJson(); } -double BezierPath::length() { +float BezierPath::length() { return _contents->length(); } @@ -456,7 +456,7 @@ void BezierPath::updateVertex(CurveVertex const &vertex, int atIndex, bool remea _contents->updateVertex(vertex, atIndex, remeasure); } -std::vector BezierPath::trim(double fromLength, double toLength, double offsetLength) { +std::vector BezierPath::trim(float fromLength, float toLength, float offsetLength) { std::vector result; auto resultContents = _contents->trim(fromLength, toLength, offsetLength); From 1ee81d91d276909ffb451a4c56dec793ef201cd0 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 19:30:38 +0400 Subject: [PATCH 14/32] Refactoring --- .../LottieCpp/PublicHeaders/LottieCpp/Color.h | 10 +++++----- .../Sources/Lottie/Public/Primitives/Color.cpp | 18 +++++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Color.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Color.h index 9f20d05a3e..6e404f29ae 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Color.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Color.h @@ -15,10 +15,10 @@ enum class ColorFormatDenominator { }; struct Color { - double r; - double g; - double b; - double a; + float r; + float g; + float b; + float a; bool operator==(Color const &rhs) const { if (r != rhs.r) { @@ -40,7 +40,7 @@ struct Color { return !(*this == rhs); } - explicit Color(double r_, double g_, double b_, double a_, ColorFormatDenominator denominator = ColorFormatDenominator::One); + explicit Color(float r_, float g_, float b_, float a_, ColorFormatDenominator denominator = ColorFormatDenominator::One); explicit Color(lottiejson11::Json const &jsonAny) noexcept(false); lottiejson11::Json toJson() const; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Color.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Color.cpp index e7c6b9996e..d627e71da4 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Color.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Color.cpp @@ -6,8 +6,8 @@ namespace lottie { -Color::Color(double r_, double g_, double b_, double a_, ColorFormatDenominator denominator) { - double denominatorValue = 1.0; +Color::Color(float r_, float g_, float b_, float a_, ColorFormatDenominator denominator) { + float denominatorValue = 1.0; switch (denominator) { case ColorFormatDenominator::One: { denominatorValue = 1.0; @@ -43,7 +43,7 @@ Color::Color(lottiejson11::Json const &jsonAny) noexcept(false) : size_t index = 0; - double r1 = 0.0; + float r1 = 0.0; if (index < jsonAny.array_items().size()) { if (!jsonAny.array_items()[index].is_number()) { throw LottieParsingException(); @@ -52,7 +52,7 @@ Color::Color(lottiejson11::Json const &jsonAny) noexcept(false) : index++; } - double g1 = 0.0; + float g1 = 0.0; if (index < jsonAny.array_items().size()) { if (!jsonAny.array_items()[index].is_number()) { throw LottieParsingException(); @@ -61,7 +61,7 @@ Color::Color(lottiejson11::Json const &jsonAny) noexcept(false) : index++; } - double b1 = 0.0; + float b1 = 0.0; if (index < jsonAny.array_items().size()) { if (!jsonAny.array_items()[index].is_number()) { throw LottieParsingException(); @@ -70,7 +70,7 @@ Color::Color(lottiejson11::Json const &jsonAny) noexcept(false) : index++; } - double a1 = 0.0; + float a1 = 0.0; if (index < jsonAny.array_items().size()) { if (!jsonAny.array_items()[index].is_number()) { throw LottieParsingException(); @@ -118,9 +118,9 @@ Color Color::fromString(std::string const &string) { converter >> std::hex >> rgbValue; return Color( - ((double)((rgbValue & 0xFF0000) >> 16)) / 255.0, - ((double)((rgbValue & 0x00FF00) >> 8)) / 255.0, - ((double)(rgbValue & 0x0000FF)) / 255.0, + ((float)((rgbValue & 0xFF0000) >> 16)) / 255.0, + ((float)((rgbValue & 0x00FF00) >> 8)) / 255.0, + ((float)(rgbValue & 0x0000FF)) / 255.0, 1.0 ); } From 6f63e9a6249a0765b186e4082ff890b158a3c77d Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 20:10:28 +0400 Subject: [PATCH 15/32] Refactoring --- .../SoftwareLottieRenderer/Sources/Canvas.h | 89 ++++----------- .../Sources/CoreGraphicsCanvasImpl.h | 16 +-- .../Sources/CoreGraphicsCanvasImpl.mm | 106 +++++++++++------- .../Sources/SoftwareLottieRenderer.mm | 38 +++---- .../Sources/ThorVGCanvasImpl.h | 19 ++-- .../Sources/ThorVGCanvasImpl.mm | 34 +++--- .../PublicHeaders/LottieCpp/CurveVertex.h | 14 +-- .../LottieCpp/LottieAnimationContainer.h | 2 +- .../PublicHeaders/LottieCpp/PathElement.h | 8 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 58 +++++----- .../PublicHeaders/LottieCpp/ShapeAttributes.h | 8 +- .../CompLayers/CompositionLayer.hpp | 20 ++-- .../CompLayers/MaskContainerLayer.hpp | 4 +- .../CompLayers/PreCompositionLayer.hpp | 8 +- .../CompLayers/ShapeCompositionLayer.cpp | 80 ++++++------- .../CompLayers/ShapeCompositionLayer.hpp | 2 +- .../CompLayers/TextCompositionLayer.hpp | 2 +- .../MainThreadAnimationLayer.hpp | 12 +- .../Utility/CompositionLayersInitializer.cpp | 2 +- .../Utility/CompositionLayersInitializer.hpp | 2 +- .../Utility/LayerTransformNode.hpp | 4 +- .../NodeProperties/NodeProperty.hpp | 4 +- .../Protocols/AnyNodeProperty.hpp | 4 +- .../Protocols/AnyValueContainer.hpp | 2 +- .../Protocols/HasRenderUpdates.hpp | 2 +- .../Protocols/NodePropertyMap.hpp | 4 +- .../NodeProperties/ValueContainer.hpp | 6 +- .../DashPatternInterpolator.hpp | 4 +- .../ValueProviders/KeyframeInterpolator.hpp | 14 +-- .../ValueProviders/SingleValueProvider.hpp | 2 +- .../OutputNodes/PassThroughOutputNode.hpp | 4 +- .../Nodes/Text/TextAnimatorNode.hpp | 34 +++--- .../Protocols/AnimatorNode.hpp | 20 ++-- .../NodeRenderSystem/Protocols/NodeOutput.hpp | 2 +- .../RenderLayers/GetGradientParameters.cpp | 20 ++-- .../RenderLayers/GetGradientParameters.hpp | 2 +- .../Lottie/Private/Model/Animation.hpp | 6 +- .../Private/Model/Assets/ImageAsset.hpp | 8 +- .../Private/Model/Assets/PrecompAsset.hpp | 2 +- .../Private/Model/Layers/LayerModel.hpp | 10 +- .../Model/Layers/PreCompLayerModel.hpp | 4 +- .../Private/Model/Layers/SolidLayerModel.hpp | 4 +- .../Private/Model/Objects/FitzModifier.hpp | 24 ++-- .../Model/ShapeItems/GradientStroke.hpp | 2 +- .../Private/Model/ShapeItems/Stroke.hpp | 2 +- .../Lottie/Private/Model/Text/Font.hpp | 4 +- .../Lottie/Private/Model/Text/Glyph.hpp | 8 +- .../Private/Model/Text/TextDocument.hpp | 16 +-- .../Utility/Primitives/CompoundBezierPath.hpp | 18 +-- .../FontProvider/AnimationFontProvider.hpp | 4 +- .../Lottie/Public/Keyframes/Keyframe.hpp | 10 +- .../Public/Keyframes/ValueInterpolators.cpp | 2 +- .../Public/Keyframes/ValueInterpolators.hpp | 56 ++++----- .../Public/Primitives/AnimationTime.hpp | 4 +- .../Lottie/Public/Primitives/AnyValue.hpp | 24 ++-- .../Lottie/Public/Primitives/CALayer.hpp | 18 +-- .../Lottie/Public/Primitives/DashPattern.hpp | 4 +- .../Public/Primitives/GradientColorSet.hpp | 2 +- .../LottieMetalAnimatedStickerNode.swift | 4 +- 59 files changed, 433 insertions(+), 455 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h index 28b5e69fb9..88eaf678fb 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h @@ -9,63 +9,6 @@ namespace lottieRendering { -struct Color { - double r; - double g; - double b; - double a; - - Color(double r_, double g_, double b_, double a_) : - r(r_), g(g_), b(b_), a(a_) { - } - - bool operator==(Color const &rhs) const { - if (r != rhs.r) { - return false; - } - if (g != rhs.g) { - return false; - } - if (b != rhs.b) { - return false; - } - if (a != rhs.a) { - return false; - } - return true; - } - - bool operator!=(Color const &rhs) const { - return !(*this == rhs); - } -}; - -enum class BlendMode { - Normal, - DestinationIn, - DestinationOut -}; - -enum class FillRule: int { - None = 0, - NonZeroWinding = 1, - EvenOdd = 2 -}; - -enum class LineCap: int { - None = 0, - Butt = 1, - Round = 2, - Square = 3 -}; - -enum class LineJoin: int { - None = 0, - Miter = 1, - Round = 2, - Bevel = 3 -}; - class Image { public: virtual ~Image() = default; @@ -73,23 +16,29 @@ public: class Gradient { public: - Gradient(std::vector const &colors, std::vector const &locations) : + Gradient(std::vector const &colors, std::vector const &locations) : _colors(colors), _locations(locations) { assert(_colors.size() == _locations.size()); } - std::vector const &colors() const { + std::vector const &colors() const { return _colors; } - std::vector const &locations() const { + std::vector const &locations() const { return _locations; } private: - std::vector _colors; - std::vector _locations; + std::vector _colors; + std::vector _locations; +}; + +enum class BlendMode { + Normal, + DestinationIn, + DestinationOut }; class Canvas { @@ -104,18 +53,18 @@ public: virtual void saveState() = 0; virtual void restoreState() = 0; - virtual void fillPath(std::shared_ptr const &path, FillRule fillRule, Color const &color) = 0; - virtual void linearGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; - virtual void radialGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) = 0; + virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) = 0; + virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; + virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; - virtual void strokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Color const &color) = 0; - virtual void linearGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; - virtual void radialGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) = 0; + virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) = 0; + virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; + virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; - virtual void fill(lottie::CGRect const &rect, Color const &fillColor) = 0; + virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) = 0; virtual void setBlendMode(BlendMode blendMode) = 0; - virtual void setAlpha(double alpha) = 0; + virtual void setAlpha(float alpha) = 0; virtual void concatenate(lottie::CATransform3D const &transform) = 0; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h index fc572306cd..dbc9ad5814 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h @@ -29,17 +29,17 @@ public: virtual void saveState() override; virtual void restoreState() override; - virtual void fillPath(std::shared_ptr const &path, FillRule fillRule, Color const &color) override; - virtual void linearGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) override; + virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; - virtual void strokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Color const &color) override; - virtual void linearGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) override; + virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; - virtual void fill(lottie::CGRect const &rect, Color const &fillColor) override; + virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; - virtual void setAlpha(double alpha) override; + virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; virtual std::shared_ptr makeImage() const; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm index 5a915888c6..2eb352c5c9 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm @@ -1,7 +1,5 @@ #include "CoreGraphicsCanvasImpl.h" - - namespace lottieRendering { namespace { @@ -89,7 +87,7 @@ void CanvasImpl::restoreState() { CGContextRestoreGState(_context); } -void CanvasImpl::fillPath(std::shared_ptr const &path, FillRule fillRule, Color const &color) { +void CanvasImpl::fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) { CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { CGContextAddPath(context, nativePath); @@ -101,7 +99,7 @@ void CanvasImpl::fillPath(std::shared_ptr const &path, FillRule CFRelease(nativeColor); switch (fillRule) { - case FillRule::EvenOdd: { + case lottie::FillRule::EvenOdd: { CGContextEOFillPath(_context); break; } @@ -112,7 +110,7 @@ void CanvasImpl::fillPath(std::shared_ptr const &path, FillRule } } -void CanvasImpl::linearGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void CanvasImpl::linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { CGContextSaveGState(_context); CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { @@ -120,7 +118,7 @@ void CanvasImpl::linearGradientFillPath(std::shared_ptr const &p }); switch (fillRule) { - case FillRule::EvenOdd: { + case lottie::FillRule::EvenOdd: { CGContextEOClip(_context); break; } @@ -142,7 +140,12 @@ void CanvasImpl::linearGradientFillPath(std::shared_ptr const &p assert(gradient.colors().size() == gradient.locations().size()); - CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), gradient.locations().data(), gradient.locations().size()); + std::vector locations; + for (const auto location : gradient.locations()) { + locations.push_back(location); + } + + CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); if (nativeGradient) { CGContextDrawLinearGradient(_context, nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CFRelease(nativeGradient); @@ -152,7 +155,7 @@ void CanvasImpl::linearGradientFillPath(std::shared_ptr const &p CGContextRestoreGState(_context); } -void CanvasImpl::radialGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) { +void CanvasImpl::radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { CGContextSaveGState(_context); CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { @@ -160,7 +163,7 @@ void CanvasImpl::radialGradientFillPath(std::shared_ptr const &p }); switch (fillRule) { - case FillRule::EvenOdd: { + case lottie::FillRule::EvenOdd: { CGContextEOClip(_context); break; } @@ -182,7 +185,12 @@ void CanvasImpl::radialGradientFillPath(std::shared_ptr const &p assert(gradient.colors().size() == gradient.locations().size()); - CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), gradient.locations().data(), gradient.locations().size()); + std::vector locations; + for (const auto location : gradient.locations()) { + locations.push_back(location); + } + + CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); if (nativeGradient) { CGContextDrawRadialGradient(_context, nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CFRelease(nativeGradient); @@ -192,7 +200,7 @@ void CanvasImpl::radialGradientFillPath(std::shared_ptr const &p CGContextRestoreGState(_context); } -void CanvasImpl::strokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Color const &color) { +void CanvasImpl::strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { CGContextAddPath(context, nativePath); @@ -206,15 +214,15 @@ void CanvasImpl::strokePath(std::shared_ptr const &path, double CGContextSetLineWidth(_context, lineWidth); switch (lineJoin) { - case LineJoin::Miter: { + case lottie::LineJoin::Miter: { CGContextSetLineJoin(_context, kCGLineJoinMiter); break; } - case LineJoin::Round: { + case lottie::LineJoin::Round: { CGContextSetLineJoin(_context, kCGLineJoinRound); break; } - case LineJoin::Bevel: { + case lottie::LineJoin::Bevel: { CGContextSetLineJoin(_context, kCGLineJoinBevel); break; } @@ -225,15 +233,15 @@ void CanvasImpl::strokePath(std::shared_ptr const &path, double } switch (lineCap) { - case LineCap::Butt: { + case lottie::LineCap::Butt: { CGContextSetLineCap(_context, kCGLineCapButt); break; } - case LineCap::Round: { + case lottie::LineCap::Round: { CGContextSetLineCap(_context, kCGLineCapRound); break; } - case LineCap::Square: { + case lottie::LineCap::Square: { CGContextSetLineCap(_context, kCGLineCapSquare); break; } @@ -244,12 +252,16 @@ void CanvasImpl::strokePath(std::shared_ptr const &path, double } if (!dashPattern.empty()) { - CGContextSetLineDash(_context, dashPhase, dashPattern.data(), dashPattern.size()); + std::vector mappedDashPattern; + for (const auto value : dashPattern) { + mappedDashPattern.push_back(value); + } + CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); } CGContextStrokePath(_context); } -void CanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void CanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { CGContextSaveGState(_context); CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { @@ -259,15 +271,15 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const CGContextSetLineWidth(_context, lineWidth); switch (lineJoin) { - case LineJoin::Miter: { + case lottie::LineJoin::Miter: { CGContextSetLineJoin(_context, kCGLineJoinMiter); break; } - case LineJoin::Round: { + case lottie::LineJoin::Round: { CGContextSetLineJoin(_context, kCGLineJoinRound); break; } - case LineJoin::Bevel: { + case lottie::LineJoin::Bevel: { CGContextSetLineJoin(_context, kCGLineJoinBevel); break; } @@ -278,15 +290,15 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const } switch (lineCap) { - case LineCap::Butt: { + case lottie::LineCap::Butt: { CGContextSetLineCap(_context, kCGLineCapButt); break; } - case LineCap::Round: { + case lottie::LineCap::Round: { CGContextSetLineCap(_context, kCGLineCapRound); break; } - case LineCap::Square: { + case lottie::LineCap::Square: { CGContextSetLineCap(_context, kCGLineCapSquare); break; } @@ -297,7 +309,11 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const } if (!dashPattern.empty()) { - CGContextSetLineDash(_context, dashPhase, dashPattern.data(), dashPattern.size()); + std::vector mappedDashPattern; + for (const auto value : dashPattern) { + mappedDashPattern.push_back(value); + } + CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); } CGContextReplacePathWithStrokedPath(_context); @@ -315,7 +331,12 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const assert(gradient.colors().size() == gradient.locations().size()); - CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), gradient.locations().data(), gradient.locations().size()); + std::vector locations; + for (const auto location : gradient.locations()) { + locations.push_back(location); + } + + CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); if (nativeGradient) { CGContextDrawLinearGradient(_context, nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CFRelease(nativeGradient); @@ -325,7 +346,7 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const CGContextRestoreGState(_context); } -void CanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) { +void CanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { CGContextSaveGState(_context); CGContextBeginPath(_context); lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { @@ -335,15 +356,15 @@ void CanvasImpl::radialGradientStrokePath(std::shared_ptr const CGContextSetLineWidth(_context, lineWidth); switch (lineJoin) { - case LineJoin::Miter: { + case lottie::LineJoin::Miter: { CGContextSetLineJoin(_context, kCGLineJoinMiter); break; } - case LineJoin::Round: { + case lottie::LineJoin::Round: { CGContextSetLineJoin(_context, kCGLineJoinRound); break; } - case LineJoin::Bevel: { + case lottie::LineJoin::Bevel: { CGContextSetLineJoin(_context, kCGLineJoinBevel); break; } @@ -354,15 +375,15 @@ void CanvasImpl::radialGradientStrokePath(std::shared_ptr const } switch (lineCap) { - case LineCap::Butt: { + case lottie::LineCap::Butt: { CGContextSetLineCap(_context, kCGLineCapButt); break; } - case LineCap::Round: { + case lottie::LineCap::Round: { CGContextSetLineCap(_context, kCGLineCapRound); break; } - case LineCap::Square: { + case lottie::LineCap::Square: { CGContextSetLineCap(_context, kCGLineCapSquare); break; } @@ -373,7 +394,11 @@ void CanvasImpl::radialGradientStrokePath(std::shared_ptr const } if (!dashPattern.empty()) { - CGContextSetLineDash(_context, dashPhase, dashPattern.data(), dashPattern.size()); + std::vector mappedDashPattern; + for (const auto value : dashPattern) { + mappedDashPattern.push_back(value); + } + CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); } CGContextReplacePathWithStrokedPath(_context); @@ -391,7 +416,12 @@ void CanvasImpl::radialGradientStrokePath(std::shared_ptr const assert(gradient.colors().size() == gradient.locations().size()); - CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), gradient.locations().data(), gradient.locations().size()); + std::vector locations; + for (const auto location : gradient.locations()) { + locations.push_back(location); + } + + CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); if (nativeGradient) { CGContextDrawRadialGradient(_context, nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CFRelease(nativeGradient); @@ -401,7 +431,7 @@ void CanvasImpl::radialGradientStrokePath(std::shared_ptr const CGContextRestoreGState(_context); } -void CanvasImpl::fill(lottie::CGRect const &rect, Color const &fillColor) { +void CanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { CGFloat components[4] = { fillColor.r, fillColor.g, fillColor.b, fillColor.a }; CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); CGContextSetFillColorWithColor(_context, nativeColor); @@ -429,7 +459,7 @@ void CanvasImpl::setBlendMode(BlendMode blendMode) { CGContextSetBlendMode(_context, nativeMode); } -void CanvasImpl::setAlpha(double alpha) { +void CanvasImpl::setAlpha(float alpha) { CGContextSetAlpha(_context, alpha); } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 367c31e5e2..3a843f08cf 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -411,18 +411,18 @@ static void drawLottieContentItem(std::shared_ptr paren lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->stroke->shading.get(); if (solidShading->opacity != 0.0) { - lottieRendering::LineJoin lineJoin = lottieRendering::LineJoin::Bevel; + lottie::LineJoin lineJoin = lottie::LineJoin::Bevel; switch (shading->stroke->lineJoin) { case lottie::LineJoin::Bevel: { - lineJoin = lottieRendering::LineJoin::Bevel; + lineJoin = lottie::LineJoin::Bevel; break; } case lottie::LineJoin::Round: { - lineJoin = lottieRendering::LineJoin::Round; + lineJoin = lottie::LineJoin::Round; break; } case lottie::LineJoin::Miter: { - lineJoin = lottieRendering::LineJoin::Miter; + lineJoin = lottie::LineJoin::Miter; break; } default: { @@ -430,18 +430,18 @@ static void drawLottieContentItem(std::shared_ptr paren } } - lottieRendering::LineCap lineCap = lottieRendering::LineCap::Square; + lottie::LineCap lineCap = lottie::LineCap::Square; switch (shading->stroke->lineCap) { case lottie::LineCap::Butt: { - lineCap = lottieRendering::LineCap::Butt; + lineCap = lottie::LineCap::Butt; break; } case lottie::LineCap::Round: { - lineCap = lottieRendering::LineCap::Round; + lineCap = lottie::LineCap::Round; break; } case lottie::LineCap::Square: { - lineCap = lottieRendering::LineCap::Square; + lineCap = lottie::LineCap::Square; break; } default: { @@ -449,25 +449,25 @@ static void drawLottieContentItem(std::shared_ptr paren } } - std::vector dashPattern; + std::vector dashPattern; if (!shading->stroke->dashPattern.empty()) { dashPattern = shading->stroke->dashPattern; } - currentContext->strokePath(path, shading->stroke->lineWidth, lineJoin, lineCap, shading->stroke->dashPhase, dashPattern, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); + currentContext->strokePath(path, shading->stroke->lineWidth, lineJoin, lineCap, shading->stroke->dashPhase, dashPattern, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); } else if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) { //TODO:gradient stroke } } } else if (shading->fill) { - lottieRendering::FillRule rule = lottieRendering::FillRule::NonZeroWinding; + lottie::FillRule rule = lottie::FillRule::NonZeroWinding; switch (shading->fill->rule) { case lottie::FillRule::EvenOdd: { - rule = lottieRendering::FillRule::EvenOdd; + rule = lottie::FillRule::EvenOdd; break; } case lottie::FillRule::NonZeroWinding: { - rule = lottieRendering::FillRule::NonZeroWinding; + rule = lottie::FillRule::NonZeroWinding; break; } default: { @@ -478,16 +478,16 @@ static void drawLottieContentItem(std::shared_ptr paren if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) { lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->fill->shading.get(); if (solidShading->opacity != 0.0) { - currentContext->fillPath(path, rule, lottieRendering::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); + currentContext->fillPath(path, rule, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); } } else if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) { lottie::RenderTreeNodeContentItem::GradientShading *gradientShading = (lottie::RenderTreeNodeContentItem::GradientShading *)shading->fill->shading.get(); if (gradientShading->opacity != 0.0) { - std::vector colors; - std::vector locations; + std::vector colors; + std::vector locations; for (const auto &color : gradientShading->colors) { - colors.push_back(lottieRendering::Color(color.r, color.g, color.b, color.a * gradientShading->opacity * renderAlpha)); + colors.push_back(lottie::Color(color.r, color.g, color.b, color.a * gradientShading->opacity * renderAlpha)); } locations = gradientShading->locations; @@ -562,7 +562,7 @@ static void renderLottieRenderNode(std::shared_ptr node, maskBackingStorage->concatenate(node->renderData.globalTransform); if (node->renderData.layer.masksToBounds()) { - maskBackingStorage->fill(lottie::CGRect(node->renderData.layer.bounds().x, node->renderData.layer.bounds().y, node->renderData.layer.bounds().width, node->renderData.layer.bounds().height), lottieRendering::Color(1.0, 1.0, 1.0, 1.0)); + 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)); } if (node->mask() && node->mask()->renderData.isValid) { renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, 1.0); @@ -599,7 +599,7 @@ static void renderLottieRenderNode(std::shared_ptr node, } if (node->renderData.isInvertedMatte) { - currentContext->fill(lottie::CGRect(node->renderData.layer.bounds().x, node->renderData.layer.bounds().y, node->renderData.layer.bounds().width, node->renderData.layer.bounds().height), lottieRendering::Color(0.0, 0.0, 0.0, 1.0)); + 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)); currentContext->setBlendMode(lottieRendering::BlendMode::DestinationOut); } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h index 6166b708a0..5c5bcba396 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h @@ -20,17 +20,17 @@ public: virtual void saveState() override; virtual void restoreState() override; - virtual void fillPath(std::shared_ptr const &path, FillRule fillRule, Color const &color) override; - virtual void linearGradientFillPath(std::shared_ptr const &path, FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientFillPath(std::shared_ptr const &path, FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) override; - virtual void strokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Color const &color) override; - virtual void linearGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) override; - virtual void fill(lottie::CGRect const &rect, Color const &fillColor) override; + virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; - virtual void setAlpha(double alpha) override; + virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; @@ -51,8 +51,7 @@ private: int _height = 0; std::unique_ptr _canvas; - //SkBlendMode _blendMode = SkBlendMode::kSrcOver; - double _alpha = 1.0; + float _alpha = 1.0; lottie::CATransform3D _transform; std::vector _stateStack; int _bytesPerRow = 0; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm index 0c81b5c8a7..f2d642f1c7 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm @@ -89,19 +89,19 @@ void ThorVGCanvasImpl::restoreState() { _stateStack.pop_back(); } -void ThorVGCanvasImpl::fillPath(std::shared_ptr const &path, FillRule fillRule, Color const &color) { +void ThorVGCanvasImpl::fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) { auto shape = tvg::Shape::gen(); tvgPath(path, shape.get()); shape->transform(tvgTransform(_transform)); shape->fill((int)(color.r * 255.0), (int)(color.g * 255.0), (int)(color.b * 255.0), (int)(color.a * _alpha * 255.0)); - shape->fill(fillRule == FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); + shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::linearGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void ThorVGCanvasImpl::linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { auto shape = tvg::Shape::gen(); tvgPath(path, shape.get()); @@ -124,12 +124,12 @@ void ThorVGCanvasImpl::linearGradientFillPath(std::shared_ptr co fill->colorStops(colors.data(), (uint32_t)colors.size()); shape->fill(std::move(fill)); - shape->fill(fillRule == FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); + shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::radialGradientFillPath(std::shared_ptr const &path, FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) { +void ThorVGCanvasImpl::radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { auto shape = tvg::Shape::gen(); tvgPath(path, shape.get()); @@ -152,12 +152,12 @@ void ThorVGCanvasImpl::radialGradientFillPath(std::shared_ptr co fill->colorStops(colors.data(), (uint32_t)colors.size()); shape->fill(std::move(fill)); - shape->fill(fillRule == FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); + shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding); _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Color const &color) { +void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { auto shape = tvg::Shape::gen(); tvgPath(path, shape.get()); @@ -167,15 +167,15 @@ void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, d shape->strokeWidth(lineWidth); switch (lineJoin) { - case LineJoin::Miter: { + case lottie::LineJoin::Miter: { shape->strokeJoin(tvg::StrokeJoin::Miter); break; } - case LineJoin::Round: { + case lottie::LineJoin::Round: { shape->strokeJoin(tvg::StrokeJoin::Round); break; } - case LineJoin::Bevel: { + case lottie::LineJoin::Bevel: { shape->strokeJoin(tvg::StrokeJoin::Bevel); break; } @@ -186,15 +186,15 @@ void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, d } switch (lineCap) { - case LineCap::Butt: { + case lottie::LineCap::Butt: { shape->strokeCap(tvg::StrokeCap::Butt); break; } - case LineCap::Round: { + case lottie::LineCap::Round: { shape->strokeCap(tvg::StrokeCap::Round); break; } - case LineCap::Square: { + case lottie::LineCap::Square: { shape->strokeCap(tvg::StrokeCap::Square); break; } @@ -217,15 +217,15 @@ void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, d _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void ThorVGCanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { assert(false); } -void ThorVGCanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, double lineWidth, LineJoin lineJoin, LineCap lineCap, double dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, double startRadius, lottie::Vector2D const &endCenter, double endRadius) { +void ThorVGCanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { assert(false); } -void ThorVGCanvasImpl::fill(lottie::CGRect const &rect, Color const &fillColor) { +void ThorVGCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { auto shape = tvg::Shape::gen(); shape->appendRect(rect.x, rect.y, rect.width, rect.height, 0.0f, 0.0f); @@ -257,7 +257,7 @@ void ThorVGCanvasImpl::setBlendMode(BlendMode blendMode) { }*/ } -void ThorVGCanvasImpl::setAlpha(double alpha) { +void ThorVGCanvasImpl::setAlpha(float alpha) { _alpha = alpha; } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h index 58f5ba3ae0..5b15a6eeab 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h @@ -91,7 +91,7 @@ public: /// TI and TO are the new tangents for the trimPoint /// NO and NI are the new tangents for the startPoint and endPoints /// S==NO=========TI==T==TO=======NI==E - CurveVertexSplitResult splitCurve(CurveVertex const &toVertex, double position) const { + CurveVertexSplitResult splitCurve(CurveVertex const &toVertex, float position) const { /// If position is less than or equal to 0, trim at start. if (position <= 0.0) { return CurveVertexSplitResult( @@ -147,8 +147,8 @@ public: /// /// This function should probably live in PathElement, since it deals with curve /// lengths. - CurveVertexSplitResult trimCurve(CurveVertex const &toVertex, double atLength, double curveLength, int maxSamples, double accuracy = 1.0) const { - double currentPosition = atLength / curveLength; + CurveVertexSplitResult trimCurve(CurveVertex const &toVertex, float atLength, float curveLength, int maxSamples, float accuracy = 1.0f) const { + float currentPosition = atLength / curveLength; auto results = splitCurve(toVertex, currentPosition); if (maxSamples == 0) { @@ -162,7 +162,7 @@ public: if (lengthDiff < accuracy) { return results; } - auto diffPosition = std::max(std::min((currentPosition / length) * lengthDiff, currentPosition * 0.5), currentPosition * (-0.5)); + auto diffPosition = std::max(std::min((currentPosition / length) * lengthDiff, currentPosition * 0.5f), currentPosition * (-0.5f)); currentPosition = diffPosition + currentPosition; results = splitCurve(toVertex, currentPosition); } @@ -174,17 +174,17 @@ public: /// For lines (zeroed tangents) the distance between the two points is measured. /// For curves the curve is iterated over by sample count and the points are measured. /// This is ~99% accurate at a sample count of 30 - double distanceTo(CurveVertex const &toVertex, int sampleCount = 25) const { + float distanceTo(CurveVertex const &toVertex, int sampleCount = 25) const { if (outTangentRelative().isZero() && toVertex.inTangentRelative().isZero()) { /// Return a linear distance. return point.distanceTo(toVertex.point); } - double distance = 0.0; + float distance = 0.0; auto previousPoint = point; for (int i = 0; i < sampleCount; i++) { - auto pointOnCurve = splitCurve(toVertex, ((double)(i)) / ((double)(sampleCount))).trimPoint; + auto pointOnCurve = splitCurve(toVertex, ((float)(i)) / ((float)(sampleCount))).trimPoint; distance = distance + previousPoint.distanceTo(pointOnCurve.point); previousPoint = pointOnCurve.point; } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/LottieAnimationContainer.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/LottieAnimationContainer.h index f92e613cee..f4e936e77d 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/LottieAnimationContainer.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/LottieAnimationContainer.h @@ -25,7 +25,7 @@ typedef struct { CGRect bounds; CGPoint position; CATransform3D transform; - double opacity; + float opacity; bool masksToBounds; bool isHidden; } LottieRenderNodeLayerData; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/PathElement.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/PathElement.h index 766fb21435..bb8a8f578d 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/PathElement.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/PathElement.h @@ -41,7 +41,7 @@ struct __attribute__((packed)) PathElement { } /// Initializes a new path with length - explicit PathElement(std::optional length_, CurveVertex const &vertex_) : + explicit PathElement(std::optional length_, CurveVertex const &vertex_) : vertex(vertex_) { } @@ -58,7 +58,7 @@ struct __attribute__((packed)) PathElement { } /// Splits an element span defined by the receiver and fromElement to a position 0-1 - PathSplitResult splitElementAtPosition(PathElement const &fromElement, double atLength) { + PathSplitResult splitElementAtPosition(PathElement const &fromElement, float atLength) { /// Trim the span. Start and trim go into the first, trim and end go into second. auto trimResults = fromElement.vertex.trimCurve(vertex, atLength, length(fromElement), 3); @@ -81,8 +81,8 @@ struct __attribute__((packed)) PathElement { ); } - double length(PathElement const &previous) { - double result = previous.vertex.distanceTo(vertex); + float length(PathElement const &previous) { + float result = previous.vertex.distanceTo(vertex); return result; } }; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 2e5a8f2155..a19f2a9d10 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -19,7 +19,7 @@ public: CGRect _bounds; Vector2D _position; CATransform3D _transform; - double _opacity; + float _opacity; bool _masksToBounds; bool _isHidden; @@ -27,7 +27,7 @@ public: CGRect bounds_, Vector2D position_, CATransform3D transform_, - double opacity_, + float opacity_, bool masksToBounds_, bool isHidden_ ) : @@ -51,7 +51,7 @@ public: return _transform; } - double opacity() const { + float opacity() const { return _opacity; } @@ -135,19 +135,19 @@ public: struct Stroke { Color color; - double lineWidth = 0.0; + float lineWidth = 0.0; LineJoin lineJoin = LineJoin::Round; LineCap lineCap = LineCap::Square; - double dashPhase = 0.0; - std::vector dashPattern; + float dashPhase = 0.0; + std::vector dashPattern; Stroke( Color color_, - double lineWidth_, + float lineWidth_, LineJoin lineJoin_, LineCap lineCap_, - double dashPhase_, - std::vector dashPattern_ + float dashPhase_, + std::vector dashPattern_ ) : color(color_), lineWidth(lineWidth_), @@ -245,7 +245,7 @@ public: FillRule pathFillRule_, GradientType gradientType_, std::vector const &colors_, - std::vector const &locations_, + std::vector const &locations_, Vector2D const &start_, Vector2D const &end_, CGRect bounds_ @@ -301,7 +301,7 @@ public: FillRule pathFillRule; GradientType gradientType; std::vector colors; - std::vector locations; + std::vector locations; Vector2D start; Vector2D end; CGRect bounds; @@ -328,7 +328,7 @@ public: class SolidShading: public Shading { public: - SolidShading(Color const &color_, double opacity_) : + SolidShading(Color const &color_, float opacity_) : color(color_), opacity(opacity_) { } @@ -339,16 +339,16 @@ public: public: Color color; - double opacity = 0.0; + float opacity = 0.0; }; class GradientShading: public Shading { public: GradientShading( - double opacity_, + float opacity_, GradientType gradientType_, std::vector const &colors_, - std::vector const &locations_, + std::vector const &locations_, Vector2D const &start_, Vector2D const &end_ ) : @@ -365,31 +365,31 @@ public: } public: - double opacity = 0.0; + float opacity = 0.0; GradientType gradientType; std::vector colors; - std::vector locations; + std::vector locations; Vector2D start; Vector2D end; }; struct Stroke { std::shared_ptr shading; - double lineWidth = 0.0; + float lineWidth = 0.0; LineJoin lineJoin = LineJoin::Round; LineCap lineCap = LineCap::Square; - double miterLimit = 4.0; - double dashPhase = 0.0; - std::vector dashPattern; + float miterLimit = 4.0; + float dashPhase = 0.0; + std::vector dashPattern; Stroke( std::shared_ptr shading_, - double lineWidth_, + float lineWidth_, LineJoin lineJoin_, LineCap lineCap_, - double miterLimit_, - double dashPhase_, - std::vector dashPattern_ + float miterLimit_, + float dashPhase_, + std::vector dashPattern_ ) : shading(shading_), lineWidth(lineWidth_), @@ -421,7 +421,7 @@ public: public: bool isGroup = false; CATransform3D transform = CATransform3D::identity(); - double alpha = 0.0; + float alpha = 0.0; std::optional trimParams; std::optional path; CGRect pathBoundingBox = CGRect(0.0, 0.0, 0.0, 0.0); @@ -450,7 +450,7 @@ public: CGRect bounds_, Vector2D position_, CATransform3D transform_, - double alpha_, + float alpha_, bool masksToBounds_, bool isHidden_, std::vector> subnodes_, @@ -484,7 +484,7 @@ public: return _transform; } - double alpha() const { + float alpha() const { return _alpha; } @@ -512,7 +512,7 @@ public: CGRect _bounds; Vector2D _position; CATransform3D _transform = CATransform3D::identity(); - double _alpha = 1.0; + float _alpha = 1.0f; bool _masksToBounds = false; bool _isHidden = false; std::shared_ptr _contentItem; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/ShapeAttributes.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/ShapeAttributes.h index c6f11afa4c..c3c6f9d809 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/ShapeAttributes.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/ShapeAttributes.h @@ -37,12 +37,12 @@ enum class TrimType: int { }; struct TrimParams { - double start = 0.0; - double end = 0.0; - double offset = 0.0; + float start = 0.0; + float end = 0.0; + float offset = 0.0; TrimType type = TrimType::Simultaneously; - TrimParams(double start_, double end_, double offset_, TrimType type_) : + TrimParams(float start_, float end_, float offset_, TrimType type_) : start(start_), end(end_), offset(offset_), 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 1d795079fe..13e275dfdd 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, BezierPathsBoundingBoxContext &boundingBoxContext) { + void displayWithFrame(float frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { bool layerVisible = isInRangeOrEqual(frame, _inFrame, _outFrame); if (_transformNode->updateTree(frame, forceUpdates) || _contentsLayer->isHidden() != !layerVisible) { @@ -105,7 +105,7 @@ public: virtual void updateContentsLayerParameters() { } - virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { + virtual void displayContentsWithFrame(float frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { /// To be overridden by subclass } @@ -143,16 +143,16 @@ public: return _matteType; } - double inFrame() const { + float inFrame() const { return _inFrame; } - double outFrame() const { + float outFrame() const { return _outFrame; } - double startFrame() const { + float startFrame() const { return _startFrame; } - double timeStretch() const { + float timeStretch() const { return _timeStretch; } @@ -174,10 +174,10 @@ private: std::shared_ptr _maskLayer; - double _inFrame = 0.0; - double _outFrame = 0.0; - double _startFrame = 0.0; - double _timeStretch = 0.0; + float _inFrame = 0.0; + float _outFrame = 0.0; + float _startFrame = 0.0; + float _timeStretch = 0.0; // MARK: Keypath Searchable diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.hpp index 9c4e46f41b..95bdb3f093 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/MaskContainerLayer.hpp @@ -106,7 +106,7 @@ public: virtual ~MaskLayer() = default; - void updateWithFrame(double frame, bool forceUpdates) { + void updateWithFrame(float frame, bool forceUpdates) { if (_properties.opacity()->needsUpdate(frame) || forceUpdates) { _properties.opacity()->update(frame); setOpacity(_properties.opacity()->value().value); @@ -163,7 +163,7 @@ public: // MARK: Internal - void updateWithFrame(double frame, bool forceUpdates) { + void updateWithFrame(float frame, bool forceUpdates) { for (const auto &maskLayer : _maskLayers) { maskLayer->updateWithFrame(frame, forceUpdates); } 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 adacde6a4b..4d3a51db1b 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 @@ -23,7 +23,7 @@ public: std::shared_ptr const &textProvider, std::shared_ptr const &fontProvider, std::shared_ptr const &assetLibrary, - double frameRate + float frameRate ) : CompositionLayer(precomp, Vector2D(precomp->width, precomp->height)) { if (precomp->timeRemapping) { _remappingNode = std::make_shared>(std::make_shared>(precomp->timeRemapping->keyframes)); @@ -86,8 +86,8 @@ public: return result; } - virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override { - double localFrame = 0.0; + virtual void displayContentsWithFrame(float frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override { + float localFrame = 0.0; if (_remappingNode) { _remappingNode->update(frame); localFrame = _remappingNode->value().value * _frameRate; @@ -195,7 +195,7 @@ public: } private: - double _frameRate = 0.0; + float _frameRate = 0.0; std::shared_ptr> _remappingNode; std::vector> _animationLayers; 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 8a7759e4b0..1c63b5b6c5 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 @@ -74,7 +74,7 @@ public: Color colorValue = Color(0.0, 0.0, 0.0, 0.0); KeyframeInterpolator opacity; - double opacityValue = 0.0; + float opacityValue = 0.0; std::shared_ptr _fill; }; @@ -93,7 +93,7 @@ public: 0.0, gradientType, std::vector(), - std::vector(), + std::vector(), Vector2D(0.0, 0.0), Vector2D(0.0, 0.0) ); @@ -130,7 +130,7 @@ public: if (hasUpdates) { std::vector colors; - std::vector locations; + std::vector locations; getGradientParameters(numberOfColors, colorsValue, colors, locations); RenderTreeNodeContentItem::GradientShading *gradient = ((RenderTreeNodeContentItem::GradientShading *)_fill->shading.get()); @@ -161,7 +161,7 @@ public: Vector3D endPointValue = Vector3D(0.0, 0.0, 0.0); KeyframeInterpolator opacity; - double opacityValue = 0.0; + float opacityValue = 0.0; std::shared_ptr _fill; }; @@ -202,7 +202,7 @@ public: lineCap, miterLimit, 0.0, - std::vector() + std::vector() ); } @@ -257,7 +257,7 @@ public: _stroke->lineWidth = widthValue; _stroke->dashPhase = hasNonZeroDashes ? dashPhaseValue : 0.0; - _stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector(); + _stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector(); } } @@ -268,22 +268,22 @@ public: private: LineJoin lineJoin; LineCap lineCap; - double miterLimit = 4.0; + float miterLimit = 4.0; KeyframeInterpolator color; Color colorValue = Color(0.0, 0.0, 0.0, 0.0); KeyframeInterpolator opacity; - double opacityValue = 0.0; + float opacityValue = 0.0; KeyframeInterpolator width; - double widthValue = 0.0; + float widthValue = 0.0; std::unique_ptr dashPattern; DashPattern dashPatternValue = DashPattern({}); std::unique_ptr> dashPhase; - double dashPhaseValue = 0.0; + float dashPhaseValue = 0.0; std::shared_ptr _stroke; }; @@ -314,7 +314,7 @@ public: 0.0, gradientType, std::vector(), - std::vector(), + std::vector(), Vector2D(0.0, 0.0), Vector2D(0.0, 0.0) ); @@ -325,7 +325,7 @@ public: lineCap, miterLimit, 0.0, - std::vector() + std::vector() ); } @@ -385,7 +385,7 @@ public: } std::vector colors; - std::vector locations; + std::vector locations; getGradientParameters(numberOfColors, colorsValue, colors, locations); RenderTreeNodeContentItem::GradientShading *gradient = ((RenderTreeNodeContentItem::GradientShading *)_stroke->shading.get()); @@ -397,7 +397,7 @@ public: _stroke->lineWidth = widthValue; _stroke->dashPhase = hasNonZeroDashes ? dashPhaseValue : 0.0; - _stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector(); + _stroke->dashPattern = hasNonZeroDashes ? dashPatternValue.values : std::vector(); } } @@ -408,7 +408,7 @@ public: private: LineJoin lineJoin; LineCap lineCap; - double miterLimit = 4.0; + float miterLimit = 4.0; int numberOfColors = 0; GradientType gradientType; @@ -423,16 +423,16 @@ public: Vector3D endPointValue = Vector3D(0.0, 0.0, 0.0); KeyframeInterpolator opacity; - double opacityValue = 0.0; + float opacityValue = 0.0; KeyframeInterpolator width; - double widthValue = 0.0; + float widthValue = 0.0; std::unique_ptr dashPattern; DashPattern dashPatternValue = DashPattern({}); std::unique_ptr> dashPhase; - double dashPhaseValue = 0.0; + float dashPhaseValue = 0.0; std::shared_ptr _stroke; }; @@ -461,12 +461,12 @@ public: } TrimParams trimParams() { - double resolvedStartValue = startValue * 0.01; - double resolvedEndValue = endValue * 0.01; - double resolvedStart = std::min(resolvedStartValue, resolvedEndValue); - double resolvedEnd = std::max(resolvedStartValue, resolvedEndValue); + float resolvedStartValue = startValue * 0.01; + float resolvedEndValue = endValue * 0.01; + float resolvedStart = std::min(resolvedStartValue, resolvedEndValue); + float resolvedEnd = std::max(resolvedStartValue, resolvedEndValue); - double resolvedOffset = fmod(offsetValue, 360.0) / 360.0; + float resolvedOffset = fmod(offsetValue, 360.0) / 360.0; return TrimParams(resolvedStart, resolvedEnd, resolvedOffset, type); } @@ -475,13 +475,13 @@ public: TrimType type; KeyframeInterpolator start; - double startValue = 0.0; + float startValue = 0.0; KeyframeInterpolator end; - double endValue = 0.0; + float endValue = 0.0; KeyframeInterpolator offset; - double offsetValue = 0.0; + float offsetValue = 0.0; }; struct ShadingVariant { @@ -624,7 +624,7 @@ public: Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0); KeyframeInterpolator cornerRadius; - double cornerRadiusValue = 0.0; + float cornerRadiusValue = 0.0; BezierPath resolvedPath; CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); @@ -768,22 +768,22 @@ public: Vector3D positionValue = Vector3D(0.0, 0.0, 0.0); KeyframeInterpolator outerRadius; - double outerRadiusValue = 0.0; + float outerRadiusValue = 0.0; KeyframeInterpolator outerRoundedness; - double outerRoundednessValue = 0.0; + float outerRoundednessValue = 0.0; std::unique_ptr> innerRadius; - double innerRadiusValue = 0.0; + float innerRadiusValue = 0.0; std::unique_ptr> innerRoundedness; - double innerRoundednessValue = 0.0; + float innerRoundednessValue = 0.0; KeyframeInterpolator rotation; - double rotationValue = 0.0; + float rotationValue = 0.0; KeyframeInterpolator points; - double pointsValue = 0.0; + float pointsValue = 0.0; BezierPath resolvedPath; CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); @@ -861,17 +861,17 @@ public: scaleValue = _scale->value(frameTime); } - double rotationValue = 0.0; + float rotationValue = 0.0; if (_rotation) { rotationValue = _rotation->value(frameTime).value; } - double skewValue = 0.0; + float skewValue = 0.0; if (_skew) { skewValue = _skew->value(frameTime).value; } - double skewAxisValue = 0.0; + float skewAxisValue = 0.0; if (_skewAxis) { skewAxisValue = _skewAxis->value(frameTime).value; } @@ -892,7 +892,7 @@ public: return _transformValue; } - double opacity() { + float opacity() { return _opacityValue; } @@ -908,7 +908,7 @@ public: std::unique_ptr> _opacity; CATransform3D _transformValue = CATransform3D::identity(); - double _opacityValue = 1.0; + float _opacityValue = 1.0; }; class ContentItem { @@ -1089,7 +1089,7 @@ public: void updateContents(std::optional parentTrim) { CATransform3D containerTransform = CATransform3D::identity(); - double containerOpacity = 1.0; + float containerOpacity = 1.0; if (transform) { containerTransform = transform->transform(); containerOpacity = transform->opacity(); @@ -1333,7 +1333,7 @@ CompositionLayer(solidLayer, Vector2D::Zero()) { _contentTree = std::make_shared(solidLayer); } -void ShapeCompositionLayer::displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { +void ShapeCompositionLayer::displayContentsWithFrame(float frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) { _frameTime = frame; _frameTimeInitialized = true; _contentTree->itemTree->updateFrame(_frameTime, boundingBoxContext); 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 2be2f974f2..8aff5c77a6 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,7 +16,7 @@ public: ShapeCompositionLayer(std::shared_ptr const &shapeLayer); ShapeCompositionLayer(std::shared_ptr const &solidLayer); - virtual void displayContentsWithFrame(double frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override; + virtual void displayContentsWithFrame(float frame, bool forceUpdates, BezierPathsBoundingBoxContext &boundingBoxContext) override; virtual std::shared_ptr renderTreeNode(BezierPathsBoundingBoxContext &boundingBoxContext) override; void initializeContentsLayerParameters(); virtual void updateContentsLayerParameters() override; 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 9d7e916705..98d7edbbc2 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, BezierPathsBoundingBoxContext &boundingBoxContext) override { + virtual void displayContentsWithFrame(float 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 7f95758791..5a8467e0b0 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 @@ -98,7 +98,7 @@ public: } void display() { - double newFrame = currentFrame(); + float newFrame = currentFrame(); if (_respectAnimationFrameRate) { newFrame = floor(newFrame); } @@ -140,7 +140,7 @@ public: }*/ } - std::optional getValue(AnimationKeypath const &keypath, std::optional atFrame) { + std::optional getValue(AnimationKeypath const &keypath, std::optional atFrame) { /*for (const auto &layer : _animationLayers) { assert(false); if @@ -153,7 +153,7 @@ public: return std::nullopt; } - std::optional getOriginalValue(AnimationKeypath const &keypath, std::optional atFrame) { + std::optional getOriginalValue(AnimationKeypath const &keypath, std::optional atFrame) { /*for (const auto &layer : _animationLayers) { assert(false); if @@ -186,10 +186,10 @@ public: return results; } - double currentFrame() const { + float currentFrame() const { return _currentFrame; } - void setCurrentFrame(double currentFrame) { + void setCurrentFrame(float currentFrame) { _currentFrame = currentFrame; for (size_t i = 0; i < _animationLayers.size(); i++) { @@ -256,7 +256,7 @@ private: // MARK: Internal /// The animatable Current Frame Property - double _currentFrame = 0.0; + float _currentFrame = 0.0; std::shared_ptr _imageProvider; std::shared_ptr _textProvider; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.cpp index 1685790650..8a98548bd3 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.cpp @@ -14,7 +14,7 @@ std::vector> initializeCompositionLayers( std::shared_ptr const &layerImageProvider, std::shared_ptr const &textProvider, std::shared_ptr const &fontProvider, - double frameRate + float frameRate ) { std::vector> compositionLayers; std::map> layerMap; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.hpp index 247d8d5631..74df3529d3 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.hpp @@ -15,7 +15,7 @@ std::vector> initializeCompositionLayers( std::shared_ptr const &layerImageProvider, std::shared_ptr const &textProvider, std::shared_ptr const &fontProvider, - double frameRate + float frameRate ); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp index de22024145..d064a16782 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp @@ -136,11 +136,11 @@ public: return _transformProperties; } - virtual bool shouldRebuildOutputs(double frame) override { + virtual bool shouldRebuildOutputs(float frame) override { return hasLocalUpdates() || hasUpstreamUpdates(); } - virtual void rebuildOutputs(double frame) override { + virtual void rebuildOutputs(float frame) override { _opacity = ((float)_transformProperties->opacity()->value().value) * 0.01f; Vector2D position(0.0, 0.0); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.hpp index 1a6f995c1f..f77cdc470b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.hpp @@ -27,7 +27,7 @@ public: return _typedContainer.outputValue(); } - virtual bool needsUpdate(double frame) const override { + virtual bool needsUpdate(float frame) const override { return _typedContainer.needsUpdate() || _valueProvider->hasUpdate(frame); } @@ -39,7 +39,7 @@ public: _typedContainer.setNeedsUpdate();*/ } - virtual void update(double frame) override { + virtual void update(float frame) override { _typedContainer.setValue(_valueProvider->value(frame), frame); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.hpp index f317e68b9f..ec682ceb93 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyNodeProperty.hpp @@ -16,10 +16,10 @@ public: public: /// Returns true if the property needs to recompute its stored value - virtual bool needsUpdate(double frame) const = 0; + virtual bool needsUpdate(float frame) const = 0; /// Updates the property for the frame - virtual void update(double frame) = 0; + virtual void update(float frame) = 0; /// The Type of the value provider virtual AnyValue::Type valueType() const = 0; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.hpp index 55e6dac2e2..1776bd13bc 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/AnyValueContainer.hpp @@ -17,7 +17,7 @@ public: virtual bool needsUpdate() const = 0; /// The frame time of the last provided update - virtual double lastUpdateFrame() const = 0; + virtual float lastUpdateFrame() const = 0; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/HasRenderUpdates.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/HasRenderUpdates.hpp index cf30fec769..bc6a13261e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/HasRenderUpdates.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/HasRenderUpdates.hpp @@ -5,7 +5,7 @@ namespace lottie { class HasRenderUpdates { public: - virtual bool hasRenderUpdates(double forFrame) = 0; + virtual bool hasRenderUpdates(float forFrame) = 0; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.hpp index 64eff898f4..0879d00c82 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.hpp @@ -13,7 +13,7 @@ class NodePropertyMap: virtual public HasChildKeypaths { public: virtual std::vector> &properties() = 0; - bool needsLocalUpdate(double frame) { + bool needsLocalUpdate(float frame) { for (auto &property : properties()) { if (property->needsUpdate(frame)) { return true; @@ -22,7 +22,7 @@ public: return false; } - void updateNodeProperties(double frame) { + void updateNodeProperties(float frame) { for (auto &property : properties()) { property->update(frame); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueContainer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueContainer.hpp index 54f1548b14..563de16de5 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueContainer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueContainer.hpp @@ -15,7 +15,7 @@ public: } public: - double _lastUpdateFrame = std::numeric_limits::infinity(); + float _lastUpdateFrame = std::numeric_limits::infinity(); bool _needsUpdate = true; virtual AnyValue value() const override { @@ -26,7 +26,7 @@ public: return _needsUpdate; } - virtual double lastUpdateFrame() const override { + virtual float lastUpdateFrame() const override { return _lastUpdateFrame; } @@ -40,7 +40,7 @@ public: _needsUpdate = false; } - void setValue(AnyValue value, double forFrame) { + void setValue(AnyValue value, float forFrame) { if (value.type() == AnyValueType::type()) { _needsUpdate = false; _lastUpdateFrame = forFrame; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/DashPatternInterpolator.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/DashPatternInterpolator.hpp index 2200c4c113..5e744386dd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/DashPatternInterpolator.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/DashPatternInterpolator.hpp @@ -23,14 +23,14 @@ public: } virtual DashPattern value(AnimationFrameTime frame) override { - std::vector values; + std::vector values; for (const auto &interpolator : _keyframeInterpolators) { values.push_back(interpolator->value(frame).value); } return DashPattern(std::move(values)); } - virtual bool hasUpdate(double frame) const override { + virtual bool hasUpdate(float frame) const override { for (const auto &interpolator : _keyframeInterpolators) { if (interpolator->hasUpdate(frame)) { return true; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.hpp index 4ea16e331a..de35d4887a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.hpp @@ -62,7 +62,7 @@ public: /// - If time is outside of the span, and there are more keyframes /// - If a value delegate is set /// - If leading and trailing are both nil. - virtual bool hasUpdate(double frame) const override { + virtual bool hasUpdate(float frame) const override { if (!lastUpdatedFrame.has_value()) { return true; } @@ -94,7 +94,7 @@ public: // MARK: Fileprivate - std::optional lastUpdatedFrame; + std::optional lastUpdatedFrame; std::optional leadingIndex; std::optional trailingIndex; @@ -102,7 +102,7 @@ public: std::optional> trailingKeyframe; /// Finds the appropriate Leading and Trailing keyframe index for the given time. - void updateSpanIndices(double frame) { + void updateSpanIndices(float frame) { if (keyframes.empty()) { leadingIndex = std::nullopt; trailingIndex = std::nullopt; @@ -270,7 +270,7 @@ public: /// - If time is outside of the span, and there are more keyframes /// - If a value delegate is set /// - If leading and trailing are both nil. - bool hasUpdate(double frame) const { + bool hasUpdate(float frame) const { if (!lastUpdatedFrame.has_value()) { return true; } @@ -302,7 +302,7 @@ public: // MARK: Fileprivate - std::optional lastUpdatedFrame; + std::optional lastUpdatedFrame; std::optional leadingIndex; std::optional trailingIndex; @@ -310,7 +310,7 @@ public: std::optional> trailingKeyframe; /// Finds the appropriate Leading and Trailing keyframe index for the given time. - void updateSpanIndices(double frame) { + void updateSpanIndices(float frame) { if (keyframes.empty()) { leadingIndex = std::nullopt; trailingIndex = std::nullopt; @@ -434,7 +434,7 @@ private: ValueInterpolator::setInplace(from.value, outPath); } - void interpolateInplace(Keyframe const &from, Keyframe const &to, double progress, BezierPath &outPath) { + void interpolateInplace(Keyframe const &from, Keyframe const &to, float progress, BezierPath &outPath) { std::optional spatialOutTangent2d; if (from.spatialOutTangent) { spatialOutTangent2d = Vector2D(from.spatialOutTangent->x, from.spatialOutTangent->y); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.hpp index dc1212ba28..b6c3fe6a1f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/SingleValueProvider.hpp @@ -28,7 +28,7 @@ public: return AnyValueType::type(); } - virtual bool hasUpdate(double frame) const override { + virtual bool hasUpdate(float frame) const override { return _hasUpdate; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.hpp index c1d2db8959..f7b73a550d 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/OutputNodes/PassThroughOutputNode.hpp @@ -40,7 +40,7 @@ public: return nullptr; } - virtual bool hasOutputUpdates(double forFrame) override { + virtual bool hasOutputUpdates(float forFrame) override { /// Changes to this node do not affect downstream nodes. bool parentUpdate = false; if (_parent) { @@ -51,7 +51,7 @@ public: return parentUpdate; } - virtual bool hasRenderUpdates(double forFrame) override { + virtual bool hasRenderUpdates(float forFrame) override { /// Return true if there are upstream updates or if this node has updates bool upstreamUpdates = false; if (_parent) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp index 6acdb2ee20..d5f6e2556a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp @@ -112,16 +112,16 @@ public: scale = Vector2D(scale3d.x, scale3d.y); } - double rotation = 0.0; + float rotation = 0.0; if (_rotation) { rotation = _rotation->value().value; } - std::optional skew; + std::optional skew; if (_skew) { skew = _skew->value().value; } - std::optional skewAxis; + std::optional skewAxis; if (_skewAxis) { skewAxis = _skewAxis->value().value; } @@ -140,7 +140,7 @@ public: return nullptr; } - double opacity() { + float opacity() { if (_opacity) { return _opacity->value().value; } else { @@ -164,7 +164,7 @@ public: } } - double tracking() { + float tracking() { if (_tracking) { return _tracking->value().value; } else { @@ -172,7 +172,7 @@ public: } } - double strokeWidth() { + float strokeWidth() { if (_strokeWidth) { return _strokeWidth->value().value; } else { @@ -225,7 +225,7 @@ public: _xform = xform; } - double opacity() { + float opacity() { if (_opacity.has_value()) { return _opacity.value(); } else if (_parentTextNode) { @@ -234,7 +234,7 @@ public: return 1.0; } } - void setOpacity(double opacity) { + void setOpacity(float opacity) { _opacity = opacity; } @@ -264,7 +264,7 @@ public: _fillColor = fillColor; } - double tracking() { + float tracking() { if (_tracking.has_value()) { return _tracking.value(); } else if (_parentTextNode) { @@ -273,11 +273,11 @@ public: return 0.0; } } - void setTracking(double tracking) { + void setTracking(float tracking) { _tracking = tracking; } - double strokeWidth() { + float strokeWidth() { if (_strokeWidth.has_value()) { return _strokeWidth.value(); } else if (_parentTextNode) { @@ -286,11 +286,11 @@ public: return 0.0; } } - void setStrokeWidth(double strokeWidth) { + void setStrokeWidth(float strokeWidth) { _strokeWidth = strokeWidth; } - virtual bool hasOutputUpdates(double frame) override { + virtual bool hasOutputUpdates(float frame) override { // TODO Fix This return true; } @@ -313,11 +313,11 @@ private: std::shared_ptr _outputPath; std::optional _xform; - std::optional _opacity; + std::optional _opacity; std::optional _strokeColor; std::optional _fillColor; - std::optional _tracking; - std::optional _strokeWidth; + std::optional _tracking; + std::optional _strokeWidth; }; class TextAnimatorNode: public AnimatorNode { @@ -347,7 +347,7 @@ public: return true; } - virtual void rebuildOutputs(double frame) override { + virtual void rebuildOutputs(float frame) override { _textOutputNode->setXform(_textAnimatorProperties->caTransform()); _textOutputNode->setOpacity(((float)_textAnimatorProperties->opacity()) * 0.01f); _textOutputNode->setStrokeColor(_textAnimatorProperties->strokeColor()); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp index 8c07963fcd..249c055665 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/AnimatorNode.hpp @@ -55,7 +55,7 @@ public: virtual std::shared_ptr outputNode() = 0; /// Update the outputs of the node. Called if local contents were update or if outputsNeedUpdate returns true. - virtual void rebuildOutputs(double frame) = 0; + virtual void rebuildOutputs(float frame) = 0; /// Setters for marking current node state. bool isEnabled() { @@ -79,10 +79,10 @@ public: _hasUpstreamUpdates = hasUpstreamUpdates; } - std::optional lastUpdateFrame() { + std::optional lastUpdateFrame() { return _lastUpdateFrame; } - virtual void setLastUpdateFrame(std::optional lastUpdateFrame) { + virtual void setLastUpdateFrame(std::optional lastUpdateFrame) { _lastUpdateFrame = lastUpdateFrame; } @@ -97,20 +97,20 @@ public: } /// Called at the end of this nodes update cycle. Always called. Optional. - virtual bool performAdditionalLocalUpdates(double frame, bool forceLocalUpdate) { + virtual bool performAdditionalLocalUpdates(float frame, bool forceLocalUpdate) { /// Optional return forceLocalUpdate; } - virtual void performAdditionalOutputUpdates(double frame, bool forceOutputUpdate) { + virtual void performAdditionalOutputUpdates(float frame, bool forceOutputUpdate) { /// Optional } /// The default simply returns `hasLocalUpdates` - virtual bool shouldRebuildOutputs(double frame) { + virtual bool shouldRebuildOutputs(float frame) { return hasLocalUpdates(); } - virtual bool updateOutputs(double frame, bool forceOutputUpdate) { + virtual bool updateOutputs(float frame, bool forceOutputUpdate) { if (!isEnabled()) { setLastUpdateFrame(frame); if (const auto parentNodeValue = parentNode()) { @@ -147,7 +147,7 @@ public: } /// Rebuilds the content of this node, and upstream nodes if necessary. - virtual bool updateContents(double frame, bool forceLocalUpdate) { + virtual bool updateContents(float frame, bool forceLocalUpdate) { if (!isEnabled()) { // Disabled node, pass through. if (const auto parentNodeValue = parentNode()) { @@ -185,7 +185,7 @@ public: return localUpdatesPermeateDownstream() ? hasUpstreamUpdates() || hasLocalUpdates() : hasUpstreamUpdates(); } - bool updateTree(double frame, bool forceUpdates) { + bool updateTree(float frame, bool forceUpdates) { if (updateContents(frame, forceUpdates)) { return updateOutputs(frame, forceUpdates); } else { @@ -230,7 +230,7 @@ private: bool _isEnabled = true; bool _hasLocalUpdates = false; bool _hasUpstreamUpdates = false; - std::optional _lastUpdateFrame; + std::optional _lastUpdateFrame; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/NodeOutput.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/NodeOutput.hpp index ee400acf20..69d5dd81d5 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/NodeOutput.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Protocols/NodeOutput.hpp @@ -15,7 +15,7 @@ public: virtual std::shared_ptr parent() = 0; /// Returns true if there are any updates upstream. OutputPath must be built before returning. - virtual bool hasOutputUpdates(double forFrame) = 0; + virtual bool hasOutputUpdates(float forFrame) = 0; virtual std::shared_ptr outputPath() = 0; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp index 749cdd6825..a1ec1b2bed 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.cpp @@ -2,13 +2,13 @@ namespace lottie { -void getGradientParameters(int numberOfColors, GradientColorSet const &colors, std::vector &outColors, std::vector &outLocations) { +void getGradientParameters(int numberOfColors, GradientColorSet const &colors, std::vector &outColors, std::vector &outLocations) { std::vector alphaColors; - std::vector alphaValues; - std::vector alphaLocations; + std::vector alphaValues; + std::vector alphaLocations; std::vector gradientColors; - std::vector colorLocations; + std::vector colorLocations; for (int i = 0; i < numberOfColors; i++) { int ix = i * 4; @@ -26,7 +26,7 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s bool drawMask = false; for (int i = numberOfColors * 4; i < (int)colors.colors.size(); i += 2) { - double alpha = colors.colors[i + 1]; + float alpha = colors.colors[i + 1]; if (alpha < 1.0) { drawMask = true; } @@ -36,7 +36,7 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s } if (drawMask) { - std::vector locations; + std::vector locations; for (size_t i = 0; i < std::min(gradientColors.size(), colorLocations.size()); i++) { if (std::find(locations.begin(), locations.end(), colorLocations[i]) == locations.end()) { locations.push_back(colorLocations[i]); @@ -62,7 +62,7 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s Color color = gradientColors[0]; for (size_t i = 0; i < std::min(gradientColors.size(), colorLocations.size()) - 1; i++) { if (location >= colorLocations[i] && location <= colorLocations[i + 1]) { - double localLocation = 0.0; + float localLocation = 0.0; if (colorLocations[i] != colorLocations[i + 1]) { localLocation = remapFloat(location, colorLocations[i], colorLocations[i + 1], 0.0, 1.0); } @@ -71,14 +71,14 @@ void getGradientParameters(int numberOfColors, GradientColorSet const &colors, s } } - double alpha = 1.0; + float alpha = 1.0; for (size_t i = 0; i < std::min(alphaValues.size(), alphaLocations.size()) - 1; i++) { if (location >= alphaLocations[i] && location <= alphaLocations[i + 1]) { - double localLocation = 0.0; + float localLocation = 0.0; if (alphaLocations[i] != alphaLocations[i + 1]) { localLocation = remapFloat(location, alphaLocations[i], alphaLocations[i + 1], 0.0, 1.0); } - alpha = ValueInterpolator::interpolate(alphaValues[i], alphaValues[i + 1], localLocation, std::nullopt, std::nullopt); + alpha = ValueInterpolator::interpolate(alphaValues[i], alphaValues[i + 1], localLocation, std::nullopt, std::nullopt); break; } } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.hpp index f14c578644..8319b8505b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/RenderLayers/GetGradientParameters.hpp @@ -6,7 +6,7 @@ namespace lottie { -void getGradientParameters(int numberOfColors, GradientColorSet const &colors, std::vector &outColors, std::vector &outLocations); +void getGradientParameters(int numberOfColors, GradientColorSet const &colors, std::vector &outColors, std::vector &outLocations); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp index 289ef8cc09..f34c23aed9 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp @@ -32,7 +32,7 @@ public: std::optional tgs_, AnimationFrameTime startFrame_, AnimationFrameTime endFrame_, - double framerate_, + float framerate_, std::string const &version_, std::optional type_, int width_, @@ -93,7 +93,7 @@ public: AnimationFrameTime startFrame = getDouble(json, "ip"); AnimationFrameTime endFrame = getDouble(json, "op"); - double framerate = getDouble(json, "fr"); + float framerate = getDouble(json, "fr"); int width = getInt(json, "w"); int height = getInt(json, "h"); @@ -256,7 +256,7 @@ public: AnimationFrameTime endFrame; /// The frame rate of the composition. - double framerate; + float framerate; /// Return all marker names, in order, or an empty list if none are specified std::vector markerNames() { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp index 992f8534df..134b82961e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp @@ -18,8 +18,8 @@ public: std::string id_, std::string name_, std::string directory_, - double width_, - double height_ + float width_, + float height_ ) : Asset(id_), name(name_), directory(directory_), @@ -64,8 +64,8 @@ public: std::string directory; /// Image Size - double width; - double height; + float width; + float height; std::optional _e; std::optional _t; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp index d79f0016b8..c79dca0066 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp @@ -56,7 +56,7 @@ public: /// Layers of the precomp std::vector> layers; - std::optional frameRate; + std::optional frameRate; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp index 2670043c44..3508400786 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp @@ -255,7 +255,7 @@ public: } } - double timeStretch() { + float timeStretch() { if (_timeStretch.has_value()) { return _timeStretch.value(); } else { @@ -279,12 +279,12 @@ public: std::optional coordinateSpace; /// The in time of the layer in frames. - double inFrame; + float inFrame; /// The out time of the layer in frames. - double outFrame; + float outFrame; /// The start time of the layer in frames. - double startTime; + float startTime; /// The transform of the layer std::shared_ptr transform; @@ -299,7 +299,7 @@ public: std::optional>> masks; /// A number that stretches time by a multiplier - std::optional _timeStretch; + std::optional _timeStretch; /// The type of matte if any. std::optional matte; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp index 44ca30ea0f..3f861b569c 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp @@ -46,10 +46,10 @@ public: std::optional> timeRemapping; /// Precomp Width - double width; + float width; /// Precomp Height - double height; + float height; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp index 922d3e6342..399105e454 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp @@ -31,10 +31,10 @@ public: std::string colorHex; /// The Width of the color layer - double width; + float width; /// The height of the color layer - double height; + float height; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/FitzModifier.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/FitzModifier.hpp index 06553a99b6..632a2374f9 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/FitzModifier.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/FitzModifier.hpp @@ -19,33 +19,33 @@ public: lottiejson11::Json::object toJson() const { lottiejson11::Json::object result; - result.insert(std::make_pair("o", (double)original)); + result.insert(std::make_pair("o", (float)original)); if (type12.has_value()) { - result.insert(std::make_pair("f12", (double)type12.value())); + result.insert(std::make_pair("f12", (float)type12.value())); } if (type3.has_value()) { - result.insert(std::make_pair("f3", (double)type3.value())); + result.insert(std::make_pair("f3", (float)type3.value())); } if (type4.has_value()) { - result.insert(std::make_pair("f4", (double)type4.value())); + result.insert(std::make_pair("f4", (float)type4.value())); } if (type5.has_value()) { - result.insert(std::make_pair("f5", (double)type5.value())); + result.insert(std::make_pair("f5", (float)type5.value())); } if (type6.has_value()) { - result.insert(std::make_pair("f6", (double)type6.value())); + result.insert(std::make_pair("f6", (float)type6.value())); } return result; } public: - double original; - std::optional type12; - std::optional type3; - std::optional type4; - std::optional type5; - std::optional type6; + float original; + std::optional type12; + std::optional type3; + std::optional type4; + std::optional type5; + std::optional type6; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp index 4110e7cf78..36319fb11e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp @@ -182,7 +182,7 @@ public: LineJoin lineJoin; /// Miter Limit - std::optional miterLimit; + std::optional miterLimit; /// The dash pattern of the stroke std::optional> dashPattern; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp index b3d8ff3066..56920e52fa 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp @@ -128,7 +128,7 @@ public: LineJoin lineJoin; /// Miter Limit - std::optional miterLimit; + std::optional miterLimit; /// The dash pattern of the stroke std::optional> dashPattern; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp index 229a85c9dd..065f3c34fe 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp @@ -14,7 +14,7 @@ public: std::string const &name_, std::string const &familyName_, std::string const &style_, - double ascent_ + float ascent_ ) : name(name_), familyName(familyName_), @@ -63,7 +63,7 @@ public: std::optional weight; std::optional fontClass; std::string style; - double ascent; + float ascent; std::optional origin; }; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp index 17b09c0ce0..44d5e771e0 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp @@ -13,10 +13,10 @@ class Glyph { public: Glyph( std::string const &character_, - double fontSize_, + float fontSize_, std::string const &fontFamily_, std::string const &fontStyle_, - double width_, + float width_, std::optional>> shapes_ ) : character(character_), @@ -86,7 +86,7 @@ public: std::string character; /// The font size of the character - double fontSize; + float fontSize; /// The font family of the character std::string fontFamily; @@ -95,7 +95,7 @@ public: std::string fontStyle; /// The Width of the character - double width; + float width; /// The Shape Data of the Character std::optional>> shapes; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp index d692dd0d77..abb85dda71 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp @@ -20,15 +20,15 @@ class TextDocument { public: TextDocument( std::string const &text_, - double fontSize_, + float fontSize_, std::string const &fontFamily_, TextJustification justification_, int tracking_, - double lineHeight_, - std::optional baseline_, + float lineHeight_, + std::optional baseline_, std::optional fillColorData_, std::optional strokeColorData_, - std::optional strokeWidth_, + std::optional strokeWidth_, std::optional strokeOverFill_, std::optional textFramePosition_, std::optional textFrameSize_ @@ -145,7 +145,7 @@ public: std::string text; /// The Font size - double fontSize; + float fontSize; /// The Font Family std::string fontFamily; @@ -157,10 +157,10 @@ public: int tracking; /// Line Height - double lineHeight; + float lineHeight; /// Baseline - std::optional baseline; + std::optional baseline; /// Fill Color data std::optional fillColorData; @@ -169,7 +169,7 @@ public: std::optional strokeColorData; /// Stroke Width - std::optional strokeWidth; + std::optional strokeWidth; /// Stroke Over Fill std::optional strokeOverFill; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp index 5a95f4f02d..3b85cc8af0 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp @@ -17,7 +17,7 @@ public: paths({ path }) { } - CompoundBezierPath(std::vector paths_, std::optional length_) : + CompoundBezierPath(std::vector paths_, std::optional length_) : paths(paths_), _length(length_) { } @@ -28,11 +28,11 @@ public: public: std::vector paths; - double length() { + float length() { if (_length.has_value()) { return _length.value(); } else { - double l = 0.0; + float l = 0.0; for (auto &path : paths) { l += path.length(); } @@ -42,7 +42,7 @@ public: } private: - std::optional _length; + std::optional _length; public: std::shared_ptr addingPath(BezierPath const &path) const { @@ -64,7 +64,7 @@ public: return std::make_shared(newPaths); } - std::shared_ptr trim(double fromPosition, double toPosition, double offset) { + std::shared_ptr trim(float fromPosition, float toPosition, float offset) { if (fromPosition == toPosition) { return std::make_shared(); } @@ -82,11 +82,11 @@ public: return std::make_shared(newPaths); }*/ - double lengthValue = length(); + float lengthValue = length(); /// Normalize lengths to the curve length. - double startPosition = fmod(fromPosition + offset, 1.0); - double endPosition = fmod(toPosition + offset, 1.0); + float startPosition = fmod(fromPosition + offset, 1.0); + float endPosition = fmod(toPosition + offset, 1.0); if (startPosition < 0.0) { startPosition = 1.0 + startPosition; @@ -123,7 +123,7 @@ public: auto compoundPath = std::make_shared(); auto trim = positions[0]; positions.erase(positions.begin()); - double pathStartPosition = 0.0; + float pathStartPosition = 0.0; bool finishedTrimming = false; int i = 0; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/FontProvider/AnimationFontProvider.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/FontProvider/AnimationFontProvider.hpp index f03a137db4..c14d46f8d9 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/FontProvider/AnimationFontProvider.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/FontProvider/AnimationFontProvider.hpp @@ -11,7 +11,7 @@ namespace lottie { /// class AnimationFontProvider { public: - virtual std::shared_ptr fontFor(std::string const &family, double size) = 0; + virtual std::shared_ptr fontFor(std::string const &family, float size) = 0; }; /// Default Font provider. @@ -22,7 +22,7 @@ public: virtual ~DefaultFontProvider() = default; - virtual std::shared_ptr fontFor(std::string const &family, double size) override { + virtual std::shared_ptr fontFor(std::string const &family, float size) override { //CTFontCreateWithName(family as CFString, size, nil) return nullptr; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp index 124d629c4f..c4adf10367 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp @@ -65,7 +65,7 @@ public: } public: - T interpolate(Keyframe const &to, double progress) { + T interpolate(Keyframe const &to, float progress) { std::optional spatialOutTangent2d; if (spatialOutTangent) { spatialOutTangent2d = Vector2D(spatialOutTangent->x, spatialOutTangent->y); @@ -78,9 +78,9 @@ public: } /// Interpolates the keyTime into a value from 0-1 - double interpolatedProgress(Keyframe const &to, double keyTime) { - double startTime = time; - double endTime = to.time; + float interpolatedProgress(Keyframe const &to, float keyTime) { + float startTime = time; + float endTime = to.time; if (keyTime <= startTime) { return 0.0; } @@ -100,7 +100,7 @@ public: if (to.inTangent.has_value()) { inTanPoint = to.inTangent.value(); } - double progress = remapFloat(keyTime, startTime, endTime, 0.0f, 1.0f); + float progress = remapFloat(keyTime, startTime, endTime, 0.0f, 1.0f); if (!outTanPoint.isZero() || inTanPoint != Vector2D(1.0f, 1.0f)) { /// Cubic interpolation progress = cubicBezierInterpolate(progress, Vector2D::Zero(), outTanPoint, inTanPoint, Vector2D(1.0, 1.0)); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp index 7a92fd80ba..650d25e041 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.cpp @@ -4,7 +4,7 @@ namespace lottie { -void batchInterpolate(std::vector const &from, std::vector const &to, BezierPath &resultPath, double amount) { +void batchInterpolate(std::vector const &from, std::vector const &to, BezierPath &resultPath, float amount) { int elementCount = (int)from.size(); if (elementCount > (int)to.size()) { elementCount = (int)to.size(); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.hpp index d3c098a6a3..4cc473bfc9 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/ValueInterpolators.hpp @@ -18,9 +18,9 @@ struct ValueInterpolator { }; template<> -struct ValueInterpolator { +struct ValueInterpolator { public: - static double interpolate(double value, double to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static float interpolate(float value, float to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { return value + ((to - value) * amount); } }; @@ -28,22 +28,22 @@ public: template<> struct ValueInterpolator { public: - static Vector1D interpolate(Vector1D const &value, Vector1D const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { - return Vector1D(ValueInterpolator::interpolate(value.value, to.value, amount, spatialOutTangent, spatialInTangent)); + static Vector1D interpolate(Vector1D const &value, Vector1D const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + return Vector1D(ValueInterpolator::interpolate(value.value, to.value, amount, spatialOutTangent, spatialInTangent)); } }; template<> struct ValueInterpolator { public: - static Vector2D interpolate(Vector2D const &value, Vector2D const &to, double amount, Vector2D spatialOutTangent, Vector2D spatialInTangent) { + static Vector2D interpolate(Vector2D const &value, Vector2D const &to, float amount, Vector2D spatialOutTangent, Vector2D spatialInTangent) { auto cp1 = value + spatialOutTangent; auto cp2 = to + spatialInTangent; return value.interpolate(to, cp1, cp2, amount); } - static Vector2D interpolate(Vector2D const &value, Vector2D const &to, double amount) { + static Vector2D interpolate(Vector2D const &value, Vector2D const &to, float amount) { return value.interpolate(to, amount); } }; @@ -51,7 +51,7 @@ public: template<> struct ValueInterpolator { public: - static Vector3D interpolate(Vector3D const &value, Vector3D const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static Vector3D interpolate(Vector3D const &value, Vector3D const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { if (spatialOutTangent && spatialInTangent) { Vector2D from2d(value.x, value.y); Vector2D to2d(to.x, to.y); @@ -64,14 +64,14 @@ public: return Vector3D( result2d.x, result2d.y, - ValueInterpolator::interpolate(value.z, to.z, amount, spatialOutTangent, spatialInTangent) + ValueInterpolator::interpolate(value.z, to.z, amount, spatialOutTangent, spatialInTangent) ); } return Vector3D( - ValueInterpolator::interpolate(value.x, to.x, amount, spatialOutTangent, spatialInTangent), - ValueInterpolator::interpolate(value.y, to.y, amount, spatialOutTangent, spatialInTangent), - ValueInterpolator::interpolate(value.z, to.z, amount, spatialOutTangent, spatialInTangent) + ValueInterpolator::interpolate(value.x, to.x, amount, spatialOutTangent, spatialInTangent), + ValueInterpolator::interpolate(value.y, to.y, amount, spatialOutTangent, spatialInTangent), + ValueInterpolator::interpolate(value.z, to.z, amount, spatialOutTangent, spatialInTangent) ); } }; @@ -79,22 +79,22 @@ public: template<> struct ValueInterpolator { public: - static Color interpolate(Color const &value, Color const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static Color interpolate(Color const &value, Color const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { return Color( - ValueInterpolator::interpolate(value.r, to.r, amount, spatialOutTangent, spatialInTangent), - ValueInterpolator::interpolate(value.g, to.g, amount, spatialOutTangent, spatialInTangent), - ValueInterpolator::interpolate(value.b, to.b, amount, spatialOutTangent, spatialInTangent), - ValueInterpolator::interpolate(value.a, to.a, amount, spatialOutTangent, spatialInTangent) + ValueInterpolator::interpolate(value.r, to.r, amount, spatialOutTangent, spatialInTangent), + ValueInterpolator::interpolate(value.g, to.g, amount, spatialOutTangent, spatialInTangent), + ValueInterpolator::interpolate(value.b, to.b, amount, spatialOutTangent, spatialInTangent), + ValueInterpolator::interpolate(value.a, to.a, amount, spatialOutTangent, spatialInTangent) ); } }; -void batchInterpolate(std::vector const &from, std::vector const &to, BezierPath &resultPath, double amount); +void batchInterpolate(std::vector const &from, std::vector const &to, BezierPath &resultPath, float amount); template<> struct ValueInterpolator { public: - static CurveVertex interpolate(CurveVertex const &value, CurveVertex const &to, double amount, Vector2D spatialOutTangent, Vector2D spatialInTangent) { + static CurveVertex interpolate(CurveVertex const &value, CurveVertex const &to, float amount, Vector2D spatialOutTangent, Vector2D spatialInTangent) { return CurveVertex::absolute( ValueInterpolator::interpolate(value.point, to.point, amount, spatialOutTangent, spatialInTangent), ValueInterpolator::interpolate(value.inTangent, to.inTangent, amount, spatialOutTangent, spatialInTangent), @@ -102,7 +102,7 @@ public: ); } - static CurveVertex interpolate(CurveVertex const &value, CurveVertex const &to, double amount) { + static CurveVertex interpolate(CurveVertex const &value, CurveVertex const &to, float amount) { return CurveVertex::absolute( ValueInterpolator::interpolate(value.point, to.point, amount), ValueInterpolator::interpolate(value.inTangent, to.inTangent, amount), @@ -114,7 +114,7 @@ public: template<> struct ValueInterpolator { public: - static BezierPath interpolate(BezierPath const &value, BezierPath const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static BezierPath interpolate(BezierPath const &value, BezierPath const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { BezierPath newPath; newPath.reserveCapacity(std::max(value.elements().size(), to.elements().size())); //TODO:probably a bug in the upstream code, uncomment @@ -150,7 +150,7 @@ public: memcpy(resultPath.mutableElements().data(), value.elements().data(), value.elements().size() * sizeof(PathElement)); } - static void interpolateInplace(BezierPath const &value, BezierPath const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent, BezierPath &resultPath) { + static void interpolateInplace(BezierPath const &value, BezierPath const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent, BezierPath &resultPath) { /*if (value.elements().size() != to.elements().size()) { return to; }*/ @@ -185,7 +185,7 @@ public: template<> struct ValueInterpolator { public: - static TextDocument interpolate(TextDocument const &value, TextDocument const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static TextDocument interpolate(TextDocument const &value, TextDocument const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { if (amount == 1.0) { return to; } else { @@ -197,12 +197,12 @@ public: template<> struct ValueInterpolator { public: - static GradientColorSet interpolate(GradientColorSet const &value, GradientColorSet const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static GradientColorSet interpolate(GradientColorSet const &value, GradientColorSet const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { assert(value.colors.size() == to.colors.size()); - std::vector colors; + std::vector colors; size_t colorCount = std::min(value.colors.size(), to.colors.size()); for (size_t i = 0; i < colorCount; i++) { - colors.push_back(ValueInterpolator::interpolate(value.colors[i], to.colors[i], amount, spatialOutTangent, spatialInTangent)); + colors.push_back(ValueInterpolator::interpolate(value.colors[i], to.colors[i], amount, spatialOutTangent, spatialInTangent)); } return GradientColorSet(colors); } @@ -211,12 +211,12 @@ public: template<> struct ValueInterpolator { public: - static DashPattern interpolate(DashPattern const &value, DashPattern const &to, double amount, std::optional spatialOutTangent, std::optional spatialInTangent) { + static DashPattern interpolate(DashPattern const &value, DashPattern const &to, float amount, std::optional spatialOutTangent, std::optional spatialInTangent) { assert(value.values.size() == to.values.size()); - std::vector values; + std::vector values; size_t colorCount = std::min(value.values.size(), to.values.size()); for (size_t i = 0; i < colorCount; i++) { - values.push_back(ValueInterpolator::interpolate(value.values[i], to.values[i], amount, spatialOutTangent, spatialInTangent)); + values.push_back(ValueInterpolator::interpolate(value.values[i], to.values[i], amount, spatialOutTangent, spatialInTangent)); } return DashPattern(std::move(values)); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnimationTime.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnimationTime.hpp index 02a3e558d3..a2a7ec3fdd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnimationTime.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnimationTime.hpp @@ -4,10 +4,10 @@ namespace lottie { /// Defines animation time in Frames (Seconds * Framerate). -typedef double AnimationFrameTime; +typedef float AnimationFrameTime; /// Defines animation time by a progress from 0 (beginning of the animation) to 1 (end of the animation) -typedef double AnimationProgressTime; +typedef float AnimationProgressTime; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnyValue.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnyValue.hpp index 0cd69ea851..f730fad652 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnyValue.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/AnyValue.hpp @@ -16,7 +16,7 @@ namespace lottie { class AnyValue { public: enum class Type { - Double, + Float, Vector1D, Vector2D, Vector3D, @@ -28,9 +28,9 @@ public: }; public: - AnyValue(double value) : - _type(Type::Double), - _doubleValue(value) { + AnyValue(float value) : + _type(Type::Float), + _floatValue(value) { } AnyValue(Vector1D const &value) : @@ -73,9 +73,9 @@ public: _dashPatternValue(value) { } - template::value>> - double get() { - return asDouble(); + template::value>> + float get() { + return asFloat(); } template::value>> @@ -123,8 +123,8 @@ public: return _type; } - double asDouble() { - return _doubleValue.value(); + float asFloat() { + return _floatValue.value(); } Vector1D asVector1D() { @@ -162,7 +162,7 @@ public: private: Type _type; - std::optional _doubleValue; + std::optional _floatValue; std::optional _vector1DValue; std::optional _vector2DValue; std::optional _vector3DValue; @@ -178,9 +178,9 @@ struct AnyValueType { }; template<> -struct AnyValueType { +struct AnyValueType { static AnyValue::Type type() { - return AnyValue::Type::Double; + return AnyValue::Type::Float; } }; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp index 171c75f850..dbc7006416 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp @@ -186,10 +186,10 @@ public: _path = path; } - double lineWidth() const { + float lineWidth() const { return _lineWidth; } - void setLineWidth(double lineWidth) { + void setLineWidth(float lineWidth) { _lineWidth = lineWidth; } @@ -207,17 +207,17 @@ public: _lineCap = lineCap; } - double lineDashPhase() const { + float lineDashPhase() const { return _lineDashPhase; } - void setLineDashPhase(double lineDashPhase) { + void setLineDashPhase(float lineDashPhase) { _lineDashPhase = lineDashPhase; } - std::vector const &dashPattern() const { + std::vector const &dashPattern() const { return _dashPattern; } - void setDashPattern(std::vector const &dashPattern) { + void setDashPattern(std::vector const &dashPattern) { _dashPattern = dashPattern; } @@ -243,11 +243,11 @@ private: std::optional _fillColor = Color(0.0, 0.0, 0.0, 1.0); FillRule _fillRule = FillRule::NonZeroWinding; std::shared_ptr _path; - double _lineWidth = 1.0; + float _lineWidth = 1.0; LineJoin _lineJoin = LineJoin::Miter; LineCap _lineCap = LineCap::Butt; - double _lineDashPhase = 0.0; - std::vector _dashPattern; + float _lineDashPhase = 0.0; + std::vector _dashPattern; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/DashPattern.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/DashPattern.hpp index d22ee1e4d8..e6255ec821 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/DashPattern.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/DashPattern.hpp @@ -6,11 +6,11 @@ namespace lottie { struct DashPattern { - DashPattern(std::vector &&values_) : + DashPattern(std::vector &&values_) : values(std::move(values_)) { } - std::vector values; + std::vector values; }; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/GradientColorSet.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/GradientColorSet.hpp index d37448999a..abf576690f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/GradientColorSet.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/GradientColorSet.hpp @@ -34,7 +34,7 @@ struct GradientColorSet { return result; } - std::vector colors; + std::vector colors; }; } diff --git a/submodules/TelegramUI/Components/LottieMetal/Sources/LottieMetalAnimatedStickerNode.swift b/submodules/TelegramUI/Components/LottieMetal/Sources/LottieMetalAnimatedStickerNode.swift index 8b4ea6042e..9e08a17d94 100644 --- a/submodules/TelegramUI/Components/LottieMetal/Sources/LottieMetalAnimatedStickerNode.swift +++ b/submodules/TelegramUI/Components/LottieMetal/Sources/LottieMetalAnimatedStickerNode.swift @@ -468,7 +468,7 @@ private final class RenderFrameState { restoreState() } - func renderNode(animationContainer: LottieAnimationContainer, node: LottieRenderNodeProxy, globalSize: CGSize, parentAlpha: CGFloat) { + func renderNode(animationContainer: LottieAnimationContainer, node: LottieRenderNodeProxy, globalSize: CGSize, parentAlpha: Float) { let normalizedOpacity = node.layer.opacity let layerAlpha = normalizedOpacity * parentAlpha @@ -534,7 +534,7 @@ private final class RenderFrameState { concat(node.layer.transform) } - var renderAlpha: CGFloat = 1.0 + var renderAlpha: Float = 1.0 if needsTempContext { renderAlpha = 1.0 } else { From 6bebe70b4ab37f20d550a68b890084c3b4d11d0a Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 20:13:30 +0400 Subject: [PATCH 16/32] Refactoring --- .../CompLayers/ShapeUtils/BezierPathUtils.cpp | 108 +++++++++--------- .../CompLayers/ShapeUtils/BezierPathUtils.hpp | 16 +-- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp index a94aec3081..71fc3cc1d7 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.cpp @@ -7,7 +7,7 @@ BezierPath makeEllipseBezierPath( Vector2D const ¢er, PathDirection direction ) { - const double ControlPointConstant = 0.55228; + const float ControlPointConstant = 0.55228; Vector2D half = size * 0.5; if (direction == PathDirection::CounterClockwise) { @@ -51,13 +51,13 @@ BezierPath makeEllipseBezierPath( BezierPath makeRectangleBezierPath( Vector2D const &position, Vector2D const &inputSize, - double cornerRadius, + float cornerRadius, PathDirection direction ) { - const double ControlPointConstant = 0.55228; + const float ControlPointConstant = 0.55228; Vector2D size = inputSize * 0.5; - double radius = std::min(std::min(cornerRadius, (double)size.x), (double)size.y); + float radius = std::min(std::min(cornerRadius, (float)size.x), (float)size.y); BezierPath bezierPath; std::vector points; @@ -97,7 +97,7 @@ BezierPath makeRectangleBezierPath( .translated(position) }; } else { - double controlPoint = radius * ControlPointConstant; + float controlPoint = radius * ControlPointConstant; points = { /// Lead In CurveVertex::absolute( @@ -184,28 +184,28 @@ BezierPath makeRectangleBezierPath( } /// Magic number needed for building path data -static constexpr double StarNodePolystarConstant = 0.47829; +static constexpr float StarNodePolystarConstant = 0.47829; BezierPath makeStarBezierPath( Vector2D const &position, - double outerRadius, - double innerRadius, - double inputOuterRoundedness, - double inputInnerRoundedness, - double numberOfPoints, - double rotation, + float outerRadius, + float innerRadius, + float inputOuterRoundedness, + float inputInnerRoundedness, + float numberOfPoints, + float rotation, PathDirection direction ) { - double currentAngle = degreesToRadians(rotation - 90.0); - double anglePerPoint = (2.0 * M_PI) / numberOfPoints; - double halfAnglePerPoint = anglePerPoint / 2.0; - double partialPointAmount = numberOfPoints - floor(numberOfPoints); - double outerRoundedness = inputOuterRoundedness * 0.01; - double innerRoundedness = inputInnerRoundedness * 0.01; + float currentAngle = degreesToRadians(rotation - 90.0); + float anglePerPoint = (2.0 * M_PI) / numberOfPoints; + float halfAnglePerPoint = anglePerPoint / 2.0; + float partialPointAmount = numberOfPoints - floor(numberOfPoints); + float outerRoundedness = inputOuterRoundedness * 0.01; + float innerRoundedness = inputInnerRoundedness * 0.01; Vector2D point = Vector2D::Zero(); - double partialPointRadius = 0.0; + float partialPointRadius = 0.0; if (partialPointAmount != 0.0) { currentAngle += halfAnglePerPoint * (1 - partialPointAmount); partialPointRadius = innerRadius + partialPointAmount * (outerRadius - innerRadius); @@ -225,8 +225,8 @@ BezierPath makeStarBezierPath( bool longSegment = false; int numPoints = (int)(ceil(numberOfPoints) * 2.0); for (int i = 0; i < numPoints; i++) { - double radius = longSegment ? outerRadius : innerRadius; - double dTheta = halfAnglePerPoint; + float radius = longSegment ? outerRadius : innerRadius; + float dTheta = halfAnglePerPoint; if (partialPointRadius != 0.0 && i == numPoints - 2) { dTheta = anglePerPoint * partialPointAmount / 2; } @@ -240,18 +240,18 @@ BezierPath makeStarBezierPath( if (innerRoundedness == 0.0 && outerRoundedness == 0.0) { vertices.push_back(CurveVertex::relative(point + position, Vector2D::Zero(), Vector2D::Zero())); } else { - double cp1Theta = (atan2(previousPoint.y, previousPoint.x) - M_PI / 2.0); - double cp1Dx = cos(cp1Theta); - double cp1Dy = sin(cp1Theta); + float cp1Theta = (atan2(previousPoint.y, previousPoint.x) - M_PI / 2.0); + float cp1Dx = cos(cp1Theta); + float cp1Dy = sin(cp1Theta); - double cp2Theta = (atan2(point.y, point.x) - M_PI / 2.0); - double cp2Dx = cos(cp2Theta); - double cp2Dy = sin(cp2Theta); + float cp2Theta = (atan2(point.y, point.x) - M_PI / 2.0); + float cp2Dx = cos(cp2Theta); + float cp2Dy = sin(cp2Theta); - double cp1Roundedness = longSegment ? innerRoundedness : outerRoundedness; - double cp2Roundedness = longSegment ? outerRoundedness : innerRoundedness; - double cp1Radius = longSegment ? innerRadius : outerRadius; - double cp2Radius = longSegment ? outerRadius : innerRadius; + float cp1Roundedness = longSegment ? innerRoundedness : outerRoundedness; + float cp2Roundedness = longSegment ? outerRoundedness : innerRoundedness; + float cp1Radius = longSegment ? innerRadius : outerRadius; + float cp2Radius = longSegment ? outerRadius : innerRadius; Vector2D cp1( cp1Radius * cp1Roundedness * StarNodePolystarConstant * cp1Dx, @@ -295,7 +295,7 @@ BezierPath makeStarBezierPath( return path; } -CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, double end, double offset, TrimType type) { +CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, float start, float end, float offset, TrimType type) { /// No need to trim, it's a full path if (start == 0.0 && end == 1.0) { return sourcePath; @@ -328,8 +328,8 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, /// Brace yourself for the below code. /// Normalize lengths with offset. - double startPosition = fmod(start + offset, 1.0); - double endPosition = fmod(end + offset, 1.0); + float startPosition = fmod(start + offset, 1.0); + float endPosition = fmod(end + offset, 1.0); if (startPosition < 0.0) { startPosition = 1.0 + startPosition; @@ -346,15 +346,15 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, } /// First get the total length of all paths. - double totalLength = 0.0; + float totalLength = 0.0; for (auto &upstreamPath : sourcePath.paths) { totalLength += upstreamPath.length(); } /// Now determine the start and end cut lengths - double startLength = startPosition * totalLength; - double endLength = endPosition * totalLength; - double pathStart = 0.0; + float startLength = startPosition * totalLength; + float endLength = endPosition * totalLength; + float pathStart = 0.0; CompoundBezierPath result; @@ -367,12 +367,12 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, // pathStart|=======E----------------------|pathEnd // Cut path components, removing after end. - double pathCutLength = endLength - pathStart; - double subpathStart = 0.0; - double subpathEnd = subpathStart + pathContainer.length(); + float pathCutLength = endLength - pathStart; + float subpathStart = 0.0; + float subpathEnd = subpathStart + pathContainer.length(); if (pathCutLength < subpathEnd) { /// This is the subpath that needs to be cut. - double cutLength = pathCutLength - subpathStart; + float cutLength = pathCutLength - subpathStart; CompoundBezierPath tempPath; tempPath.appendPath(pathContainer); @@ -395,14 +395,14 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, // // Cut path components, removing before beginning. - double pathCutLength = startLength - pathStart; + float pathCutLength = startLength - pathStart; // Clear paths from container - double subpathStart = 0.0; - double subpathEnd = subpathStart + pathContainer.length(); + float subpathStart = 0.0; + float subpathEnd = subpathStart + pathContainer.length(); if (subpathStart < pathCutLength && pathCutLength < subpathEnd) { /// This is the subpath that needs to be cut. - double cutLength = pathCutLength - subpathStart; + float cutLength = pathCutLength - subpathStart; CompoundBezierPath tempPath; tempPath.appendPath(pathContainer); auto newPaths = tempPath.trim(cutLength / pathContainer.length(), 1, 0); @@ -420,12 +420,12 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, // trim from path beginning to endLength. // Cut path components, removing before beginnings. - double startCutLength = startLength - pathStart; - double endCutLength = endLength - pathStart; + float startCutLength = startLength - pathStart; + float endCutLength = endLength - pathStart; - double subpathStart = 0.0; + float subpathStart = 0.0; - double subpathEnd = subpathStart + pathContainer.length(); + float subpathEnd = subpathStart + pathContainer.length(); if (!isInRange(startCutLength, subpathStart, subpathEnd) && !isInRange(endCutLength, subpathStart, subpathEnd)) @@ -437,7 +437,7 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, !isInRange(endCutLength, subpathStart, subpathEnd)) { /// The start of the path needs to be trimmed // |-------S======================|E - double cutLength = startCutLength - subpathStart; + float cutLength = startCutLength - subpathStart; CompoundBezierPath tempPath; tempPath.appendPath(pathContainer); auto newPaths = tempPath.trim(cutLength / pathContainer.length(), 1, 0); @@ -447,7 +447,7 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, } else if (!isInRange(startCutLength, subpathStart, subpathEnd) && isInRange(endCutLength, subpathStart, subpathEnd)) { // S|=======E----------------------| - double cutLength = endCutLength - subpathStart; + float cutLength = endCutLength - subpathStart; CompoundBezierPath tempPath; tempPath.appendPath(pathContainer); auto newPaths = tempPath.trim(0, cutLength / pathContainer.length(), 0); @@ -457,8 +457,8 @@ CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, } else if (isInRange(startCutLength, subpathStart, subpathEnd) && isInRange(endCutLength, subpathStart, subpathEnd)) { // |-------S============E---------| - double cutFromLength = startCutLength - subpathStart; - double cutToLength = endCutLength - subpathStart; + float cutFromLength = startCutLength - subpathStart; + float cutToLength = endCutLength - subpathStart; CompoundBezierPath tempPath; tempPath.appendPath(pathContainer); auto newPaths = tempPath.trim( diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.hpp index 6b223adc3d..677dbe96bd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/ShapeUtils/BezierPathUtils.hpp @@ -17,22 +17,22 @@ BezierPath makeEllipseBezierPath( BezierPath makeRectangleBezierPath( Vector2D const &position, Vector2D const &inputSize, - double cornerRadius, + float cornerRadius, PathDirection direction ); BezierPath makeStarBezierPath( Vector2D const &position, - double outerRadius, - double innerRadius, - double inputOuterRoundedness, - double inputInnerRoundedness, - double numberOfPoints, - double rotation, + float outerRadius, + float innerRadius, + float inputOuterRoundedness, + float inputInnerRoundedness, + float numberOfPoints, + float rotation, PathDirection direction ); -CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, double start, double end, double offset, TrimType type); +CompoundBezierPath trimCompoundPath(CompoundBezierPath sourcePath, float start, float end, float offset, TrimType type); } From b6ba0d8f9912ce13e1e181885bec70a68101b3bd Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 20:23:38 +0400 Subject: [PATCH 17/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 14 +- .../CompLayers/CompositionLayer.cpp | 2 - .../Lottie/Private/Model/Animation.hpp | 6 +- .../Private/Model/Assets/ImageAsset.hpp | 4 +- .../Private/Model/Assets/PrecompAsset.hpp | 4 +- .../Private/Model/Layers/LayerModel.hpp | 8 +- .../Model/Layers/PreCompLayerModel.hpp | 4 +- .../Private/Model/Layers/SolidLayerModel.hpp | 4 +- .../Lottie/Private/Model/Objects/Marker.hpp | 2 +- .../Model/ShapeItems/GradientStroke.hpp | 2 +- .../Private/Model/ShapeItems/Stroke.hpp | 2 +- .../Lottie/Private/Model/Text/Font.hpp | 2 +- .../Lottie/Private/Model/Text/Glyph.hpp | 4 +- .../Private/Model/Text/TextDocument.hpp | 16 +- .../Lottie/Public/Keyframes/Keyframe.hpp | 5 +- .../Lottie/Public/Primitives/RenderTree.cpp | 140 -------------- .../Lottie/Public/Primitives/RenderTree.hpp | 172 ------------------ 17 files changed, 44 insertions(+), 347 deletions(-) delete mode 100644 submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.cpp delete mode 100644 submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.hpp diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 3a843f08cf..738a4c02b5 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -292,13 +292,13 @@ static void processRenderTree(std::shared_ptr const &node, Vecto namespace { -static void drawLottieContentItem(std::shared_ptr parentContext, std::shared_ptr item, double parentAlpha) { +static void drawLottieContentItem(std::shared_ptr parentContext, std::shared_ptr item, float parentAlpha) { if (!item->renderData.isValid) { return; } float normalizedOpacity = item->renderData.layer.opacity(); - double layerAlpha = ((double)normalizedOpacity) * parentAlpha; + float layerAlpha = ((float)normalizedOpacity) * parentAlpha; if (item->renderData.layer.isHidden() || normalizedOpacity == 0.0f) { return; @@ -329,7 +329,7 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-item->renderData.layer.bounds().x, -item->renderData.layer.bounds().y))); parentContext->concatenate(item->renderData.layer.transform()); - double renderAlpha = 1.0; + float renderAlpha = 1.0; if (tempContext) { renderAlpha = 1.0; } else { @@ -530,12 +530,12 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, double parentAlpha) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, float parentAlpha) { if (!node->renderData.isValid) { return; } float normalizedOpacity = node->renderData.layer.opacity(); - double layerAlpha = ((double)normalizedOpacity) * parentAlpha; + float layerAlpha = ((float)normalizedOpacity) * parentAlpha; if (node->renderData.layer.isHidden() || normalizedOpacity == 0.0f) { return; @@ -587,7 +587,7 @@ static void renderLottieRenderNode(std::shared_ptr node, parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->renderData.layer.bounds().x, -node->renderData.layer.bounds().y))); parentContext->concatenate(node->renderData.layer.transform()); - double renderAlpha = 1.0; + float renderAlpha = 1.0; if (tempContext) { renderAlpha = 1.0; } else { @@ -661,7 +661,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return nil; } - processRenderTree(renderNode, lottie::Vector2D((int)size.width, (int)size.height), lottie::CATransform3D::identity().scaled(lottie::Vector2D(size.width / (double)animation.size.width, size.height / (double)animation.size.height)), false, *_bezierPathsBoundingBoxContext.get()); + 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()); if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp index 0765e43a96..1a87942244 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp @@ -1,7 +1,5 @@ #include "CompositionLayer.hpp" -#include "Lottie/Public/Primitives/RenderTree.hpp" - namespace lottie { InvertedMatteLayer::InvertedMatteLayer(std::shared_ptr inputMatte) : diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp index f34c23aed9..ffeb67964b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Animation.hpp @@ -90,10 +90,10 @@ public: } } - AnimationFrameTime startFrame = getDouble(json, "ip"); - AnimationFrameTime endFrame = getDouble(json, "op"); + AnimationFrameTime startFrame = (float)getDouble(json, "ip"); + AnimationFrameTime endFrame = (float)getDouble(json, "op"); - float framerate = getDouble(json, "fr"); + float framerate = (float)getDouble(json, "fr"); int width = getInt(json, "w"); int height = getInt(json, "h"); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp index 134b82961e..5eea900309 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/ImageAsset.hpp @@ -33,8 +33,8 @@ public: Asset(json) { name = getString(json, "p"); directory = getString(json, "u"); - width = getDouble(json, "w"); - height = getDouble(json, "h"); + width = (float)getDouble(json, "w"); + height = (float)getDouble(json, "h"); _e = getOptionalInt(json, "e"); _t = getOptionalString(json, "t"); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp index c79dca0066..af169474a7 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Assets/PrecompAsset.hpp @@ -23,7 +23,9 @@ public: explicit PrecompAsset(lottiejson11::Json::object const &json) noexcept(false) : Asset(json) { - frameRate = getOptionalDouble(json, "fr"); + if (const auto frameRateValue = getOptionalDouble(json, "fr")) { + frameRate = (float)frameRateValue.value(); + } auto layerDictionaries = getObjectArray(json, "layers"); for (size_t i = 0; i < layerDictionaries.size(); i++) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp index 3508400786..16f84d0f10 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/LayerModel.hpp @@ -71,9 +71,9 @@ public: coordinateSpace = std::nullopt; } - inFrame = getDouble(json, "ip"); - outFrame = getDouble(json, "op"); - startTime = getDouble(json, "st"); + inFrame = (float)getDouble(json, "ip"); + outFrame = (float)getDouble(json, "op"); + startTime = (float)getDouble(json, "st"); transform = std::make_shared(getObject(json, "ks")); parent = getOptionalInt(json, "parent"); @@ -141,7 +141,7 @@ public: } if (const auto timeStretchData = getOptionalDouble(json, "sr")) { - _timeStretch = timeStretchData.value(); + _timeStretch = (float)timeStretchData.value(); } if (const auto matteRawValue = getOptionalInt(json, "tt")) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp index 3f861b569c..16a2195a0b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/PreCompLayerModel.hpp @@ -19,8 +19,8 @@ public: if (const auto timeRemappingData = getOptionalObject(json, "tm")) { timeRemapping = KeyframeGroup(timeRemappingData.value()); } - width = getDouble(json, "w"); - height = getDouble(json, "h"); + width = (float)getDouble(json, "w"); + height = (float)getDouble(json, "h"); } virtual ~PreCompLayerModel() = default; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp index 399105e454..cea0671b49 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Layers/SolidLayerModel.hpp @@ -12,8 +12,8 @@ public: explicit SolidLayerModel(lottiejson11::Json::object const &json) noexcept(false) : LayerModel(json) { colorHex = getString(json, "sc"); - width = getDouble(json, "sw"); - height = getDouble(json, "sh"); + width = (float)getDouble(json, "sw"); + height = (float)getDouble(json, "sh"); } virtual ~SolidLayerModel() = default; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/Marker.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/Marker.hpp index 4f715e9ac7..bd1e5584dd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/Marker.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Objects/Marker.hpp @@ -20,7 +20,7 @@ public: explicit Marker(lottiejson11::Json::object const &json) noexcept(false) { name = getString(json, "cm"); - frameTime = getDouble(json, "tm"); + frameTime = (float)getDouble(json, "tm"); dr = getOptionalInt(json, "dr"); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp index 36319fb11e..95bed62b51 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/GradientStroke.hpp @@ -91,7 +91,7 @@ public: } if (const auto miterLimitData = getOptionalDouble(json, "ml")) { - miterLimit = miterLimitData.value(); + miterLimit = (float)miterLimitData.value(); } auto colorsContainer = getObject(json, "g"); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp index 56920e52fa..e306fcd6a6 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/ShapeItems/Stroke.hpp @@ -65,7 +65,7 @@ public: } if (const auto miterLimitData = getOptionalDouble(json, "ml")) { - miterLimit = miterLimitData.value(); + miterLimit = (float)miterLimitData.value(); } if (const auto dashElementsData = getOptionalObjectArray(json, "d")) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp index 065f3c34fe..b321983e71 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Font.hpp @@ -29,7 +29,7 @@ public: weight = getOptionalString(json, "fWeight"); fontClass = getOptionalString(json, "fClass"); style = getString(json, "fStyle"); - ascent = getDouble(json, "ascent"); + ascent = (float)getDouble(json, "ascent"); origin = getOptionalInt(json, "origin"); } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp index 44d5e771e0..d5c1522850 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/Glyph.hpp @@ -34,10 +34,10 @@ public: fontStyle(""), width(0.0) { character = getString(json, "ch"); - fontSize = getDouble(json, "size"); + fontSize = (float)getDouble(json, "size"); fontFamily = getString(json, "fFamily"); fontStyle = getString(json, "style"); - width = getDouble(json, "w"); + width = (float)getDouble(json, "w"); if (const auto shapeContainer = getOptionalObject(json, "data")) { internalHasData = true; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp index abb85dda71..83ea39ebcc 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Model/Text/TextDocument.hpp @@ -62,7 +62,7 @@ public: lottiejson11::Json::object const &json = jsonAny.object_items(); text = getString(json, "t"); - fontSize = getDouble(json, "s"); + fontSize = (float)getDouble(json, "s"); fontFamily = getString(json, "f"); auto justificationRawValue = getInt(json, "j"); @@ -81,8 +81,10 @@ public: } tracking = getInt(json, "tr"); - lineHeight = getDouble(json, "lh"); - baseline = getOptionalDouble(json, "ls"); + lineHeight = (float)getDouble(json, "lh"); + if (const auto baselineValue = getOptionalDouble(json, "ls")) { + baseline = (float)baselineValue.value(); + } if (const auto fillColorDataValue = getOptionalAny(json, "fc")) { fillColorData = Color(fillColorDataValue.value()); @@ -92,8 +94,12 @@ public: strokeColorData = Color(strokeColorDataValue.value()); } - strokeWidth = getOptionalDouble(json, "sw"); - strokeOverFill = getOptionalBool(json, "of"); + if (const auto strokeWidthValue = getOptionalDouble(json, "sw")) { + strokeWidth = (float)strokeWidthValue.value(); + } + if (const auto strokeOverFillValue = getOptionalBool(json, "of")) { + strokeOverFill = (float)strokeOverFillValue.value(); + } if (const auto textFramePositionData = getOptionalAny(json, "ps")) { textFramePosition = Vector3D(textFramePositionData.value()); diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp index c4adf10367..a3d12b226f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Keyframes/Keyframe.hpp @@ -162,7 +162,10 @@ public: endValue = T(endValueData.value()); } - time = getOptionalDouble(json.object_items(), "t"); + if (const auto timeValue = getOptionalDouble(json.object_items(), "t")) { + time = (float)timeValue.value(); + } + hold = getOptionalInt(json.object_items(), "h"); if (const auto inTangentData = getOptionalObject(json.object_items(), "i")) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.cpp deleted file mode 100644 index eec19c1596..0000000000 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "RenderTree.hpp" - -namespace lottie { - -BoundingBoxNode::BoundingBoxNode( - LayerParams const &layer_, - CGRect const &globalRect_, - CGRect const &localRect_, - CATransform3D const &globalTransform_, - bool drawsContent_, - std::shared_ptr renderableItem_, - bool isInvertedMatte_, - std::vector> const &subnodes_, - std::shared_ptr const &mask_ -) : -layer(layer_), -globalRect(globalRect_), -localRect(localRect_), -globalTransform(globalTransform_), -drawsContent(drawsContent_), -renderableItem(renderableItem_), -isInvertedMatte(isInvertedMatte_), -subnodes(subnodes_), -mask(mask_) { -} - -std::shared_ptr boundingBoxTree(std::shared_ptr const &layer, Vector2D const &globalSize, CATransform3D const &parentTransform) { - if (layer->isHidden() || layer->opacity() == 0.0f) { - return nullptr; - } - - if (layer->masksToBounds()) { - if (layer->bounds().empty()) { - return nullptr; - } - } - - auto currentTransform = parentTransform; - - currentTransform = currentTransform.translated(Vector2D(layer->position().x, layer->position().y)); - currentTransform = currentTransform.translated(Vector2D(-layer->bounds().x, -layer->bounds().y)); - currentTransform = layer->transform() * currentTransform; - - if (!currentTransform.isInvertible()) { - return nullptr; - } - - std::optional effectiveLocalBounds; - - auto renderableItem = layer->renderableItem(); - if (renderableItem) { - effectiveLocalBounds = renderableItem->boundingRect(); - } else if (layer->implementsDraw()) { - effectiveLocalBounds = layer->bounds(); - } - - bool isInvertedMatte = layer->isInvertedMatte(); - if (isInvertedMatte) { - effectiveLocalBounds = layer->bounds(); - } - - if (effectiveLocalBounds && effectiveLocalBounds->empty()) { - effectiveLocalBounds = std::nullopt; - } - - std::vector> subnodes; - std::optional subnodesGlobalRect; - - for (const auto &sublayer : layer->sublayers()) { - if (const auto subnode = boundingBoxTree(sublayer, globalSize, currentTransform)) { - subnodes.push_back(subnode); - - if (subnodesGlobalRect) { - subnodesGlobalRect = subnodesGlobalRect->unionWith(subnode->globalRect); - } else { - subnodesGlobalRect = subnode->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) { - return nullptr; - } - - 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)) { - return nullptr; - } - - std::shared_ptr maskNode; - if (layer->mask()) { - if (const auto maskNodeValue = boundingBoxTree(layer->mask(), globalSize, currentTransform)) { - if (!maskNodeValue->globalRect.intersects(globalRect)) { - return nullptr; - } - maskNode = maskNodeValue; - } else { - return nullptr; - } - } - - return std::make_shared( - layer, - globalRect, - CGRect(0.0, 0.0, 0.0, 0.0), - currentTransform, - effectiveLocalBounds.has_value(), - renderableItem, - isInvertedMatte, - subnodes, - maskNode - ); -} - -} diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.hpp deleted file mode 100644 index 3f190522a0..0000000000 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/RenderTree.hpp +++ /dev/null @@ -1,172 +0,0 @@ -#ifndef RenderTree_hpp -#define RenderTree_hpp - -#include - -#include -#include "Lottie/Public/Primitives/CALayer.hpp" - -namespace lottie { - -struct BoundingBoxNode { - 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_) { - } - - LayerParams(std::shared_ptr const &layer) : - _bounds(layer->bounds()), - _position(layer->position()), - _transform(layer->transform()), - _opacity(layer->opacity()), - _masksToBounds(layer->masksToBounds()), - _isHidden(layer->isHidden()) { - } - - bool operator==(LayerParams const &rhs) const { - if (_bounds != rhs._bounds) { - return false; - } - if (_position != rhs._position) { - return false; - } - if (_transform != rhs._transform) { - return false; - } - if (_opacity != rhs._opacity) { - return false; - } - if (_masksToBounds != rhs._masksToBounds) { - return false; - } - if (_isHidden != rhs._isHidden) { - return false; - } - return true; - } - - bool operator!=(LayerParams const &rhs) const { - return !(*this == rhs); - } - - 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; - } - }; - - LayerParams layer; - CGRect globalRect; - CGRect localRect; - CATransform3D globalTransform; - bool drawsContent; - std::shared_ptr renderableItem; - bool isInvertedMatte; - std::vector> subnodes; - std::shared_ptr mask; - - explicit BoundingBoxNode( - LayerParams const &layer_, - CGRect const &globalRect_, - CGRect const &localRect_, - CATransform3D const &globalTransform_, - bool drawsContent_, - std::shared_ptr renderableItem_, - bool isInvertedMatte_, - std::vector> const &subnodes_, - std::shared_ptr const &mask_ - ); - - bool operator==(BoundingBoxNode const &rhs) const { - if (layer != rhs.layer) { - return false; - } - if (globalRect != rhs.globalRect) { - return false; - } - if (localRect != rhs.localRect) { - return false; - } - if (globalTransform != rhs.globalTransform) { - return false; - } - if (drawsContent != rhs.drawsContent) { - return false; - } - if ((renderableItem == nullptr) != (rhs.renderableItem == nullptr)) { - return false; - } else if (renderableItem) { - if (!renderableItem->isEqual(rhs.renderableItem)) { - return false; - } - } - if (isInvertedMatte != rhs.isInvertedMatte) { - return false; - } - if (subnodes.size() != rhs.subnodes.size()) { - return false; - } else { - for (size_t i = 0; i < subnodes.size(); i++) { - if ((*subnodes[i].get()) != (*rhs.subnodes[i].get())) { - return false; - } - } - } - if ((mask == nullptr) != (rhs.mask == nullptr)) { - return false; - } else if (mask) { - if ((*mask.get()) != *(rhs.mask.get())) { - return false; - } - } - return true; - } - - bool operator!=(BoundingBoxNode const &rhs) const { - return !(*this == rhs); - } -}; - -std::shared_ptr boundingBoxTree(std::shared_ptr const &layer, Vector2D const &globalSize, CATransform3D const &parentTransform); - -} - -#endif /* RenderTree_hpp */ From 7f88fb6ffa99fc92239b20e6d2060eb3000819fd Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 20:51:38 +0400 Subject: [PATCH 18/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 14 ++- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 14 ++- .../CompLayers/ShapeCompositionLayer.cpp | 114 +++++++----------- 3 files changed, 63 insertions(+), 79 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 738a4c02b5..9053ea3fa2 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -18,7 +18,7 @@ struct TransformedPath { } }; -static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) { +static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { //TODO:remove skipApplyTransform lottie::CATransform3D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { @@ -29,13 +29,17 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptrpath) { - boundingBox = item->pathBoundingBox.applyingTransform(effectiveTransform); + if (item->path->needsBoundsRecalculation) { + item->path->bounds = lottie::bezierPathsBoundingBoxParallel(bezierPathsBoundingBoxContext, item->path->path); + item->path->needsBoundsRecalculation = false; + } + boundingBox = item->path->bounds.applyingTransform(effectiveTransform); } for (size_t i = 0; i < maxSubitem; i++) { auto &subItem = item->subItems[i]; - lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false); + lottie::CGRect subItemBoundingBox = collectPathBoundingBoxes(subItem, INT32_MAX, effectiveTransform, false, bezierPathsBoundingBoxContext); if (boundingBox.empty()) { boundingBox = subItemBoundingBox; @@ -59,7 +63,7 @@ static std::vector collectPaths(std::shared_ptrsubItems.size(), subItemLimit); if (item->path) { - mappedPaths.emplace_back(item->path.value(), effectiveTransform); + mappedPaths.emplace_back(item->path->path, effectiveTransform); } assert(!item->trimParams); @@ -96,7 +100,7 @@ static void processRenderContentItem(std::shared_ptr int drawContentDescendants = 0; for (const auto &shadingVariant : contentItem->shadings) { - lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true); + lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true, bezierPathsBoundingBoxContext); if (shadingVariant->stroke) { shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index a19f2a9d10..e504de3175 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -309,6 +309,17 @@ public: class RenderTreeNodeContentShadingVariant; +struct RenderTreeNodeContentPath { +public: + explicit RenderTreeNodeContentPath(BezierPath path_) : + path(path_) { + } + + BezierPath path; + CGRect bounds = CGRect(0.0, 0.0, 0.0, 0.0); + bool needsBoundsRecalculation = true; +}; + class RenderTreeNodeContentItem { public: enum class ShadingType { @@ -423,8 +434,7 @@ public: CATransform3D transform = CATransform3D::identity(); float alpha = 0.0; std::optional trimParams; - std::optional path; - CGRect pathBoundingBox = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr path; std::vector> shadings; std::vector> subItems; 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 1c63b5b6c5..f0973a1f09 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,60 +506,44 @@ public: } virtual ~PathOutput() = default; - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) = 0; - virtual BezierPath const *currentPath() = 0; - virtual CGRect const ¤tPathBounds() = 0; + virtual void update(AnimationFrameTime frameTime) = 0; + virtual std::shared_ptr ¤tPath() = 0; }; class StaticPathOutput : public PathOutput { public: explicit StaticPathOutput(BezierPath const &path) : - resolvedPath(path) { + resolvedPath(std::make_shared(path)) { } - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { - if (!isPathBoundsResolved) { - resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); - isPathBoundsResolved = true; - } + virtual void update(AnimationFrameTime frameTime) override { } - virtual BezierPath const *currentPath() override { - return &resolvedPath; - } - - virtual CGRect const ¤tPathBounds() override { - return resolvedPathBounds; + virtual std::shared_ptr ¤tPath() override { + return resolvedPath; } private: - BezierPath resolvedPath; - - bool isPathBoundsResolved = false; - CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr resolvedPath; }; class ShapePathOutput : public PathOutput { public: explicit ShapePathOutput(Shape const &shape) : - path(shape.path.keyframes) { + path(shape.path.keyframes), + resolvedPath(std::make_shared(BezierPath())) { } - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { + virtual void update(AnimationFrameTime frameTime) override { if (!hasValidData || path.hasUpdate(frameTime)) { - path.update(frameTime, resolvedPath); - resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); + path.update(frameTime, resolvedPath->path); + resolvedPath->needsBoundsRecalculation = true; } - hasValidData = true; } - virtual BezierPath const *currentPath() override { - return &resolvedPath; - } - - virtual CGRect const ¤tPathBounds() override { - return resolvedPathBounds; + virtual std::shared_ptr ¤tPath() override { + return resolvedPath; } private: @@ -567,8 +551,7 @@ public: BezierPathKeyframeInterpolator path; - BezierPath resolvedPath; - CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr resolvedPath; }; class RectanglePathOutput : public PathOutput { @@ -577,10 +560,11 @@ public: direction(rectangle.direction.value_or(PathDirection::Clockwise)), position(rectangle.position.keyframes), size(rectangle.size.keyframes), - cornerRadius(rectangle.cornerRadius.keyframes) { + cornerRadius(rectangle.cornerRadius.keyframes), + resolvedPath(std::make_shared(BezierPath())) { } - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { + virtual void update(AnimationFrameTime frameTime) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -597,19 +581,15 @@ public: } if (hasUpdates) { - ValueInterpolator::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath); - resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); + ValueInterpolator::setInplace(makeRectangleBezierPath(Vector2D(positionValue.x, positionValue.y), Vector2D(sizeValue.x, sizeValue.y), cornerRadiusValue, direction), resolvedPath->path); + resolvedPath->needsBoundsRecalculation = true; } hasValidData = true; } - virtual BezierPath const *currentPath() override { - return &resolvedPath; - } - - virtual CGRect const ¤tPathBounds() override { - return resolvedPathBounds; + virtual std::shared_ptr ¤tPath() override { + return resolvedPath; } private: @@ -626,8 +606,7 @@ public: KeyframeInterpolator cornerRadius; float cornerRadiusValue = 0.0; - BezierPath resolvedPath; - CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr resolvedPath; }; class EllipsePathOutput : public PathOutput { @@ -635,10 +614,11 @@ public: explicit EllipsePathOutput(Ellipse const &ellipse) : direction(ellipse.direction.value_or(PathDirection::Clockwise)), position(ellipse.position.keyframes), - size(ellipse.size.keyframes) { + size(ellipse.size.keyframes), + resolvedPath(std::make_shared(BezierPath())) { } - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { + virtual void update(AnimationFrameTime frameTime) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -651,19 +631,15 @@ public: } if (hasUpdates) { - ValueInterpolator::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath); - resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); + ValueInterpolator::setInplace(makeEllipseBezierPath(Vector2D(sizeValue.x, sizeValue.y), Vector2D(positionValue.x, positionValue.y), direction), resolvedPath->path); + resolvedPath->needsBoundsRecalculation = true; } hasValidData = true; } - virtual BezierPath const *currentPath() override { - return &resolvedPath; - } - - virtual CGRect const ¤tPathBounds() override { - return resolvedPathBounds; + virtual std::shared_ptr ¤tPath() override { + return resolvedPath; } private: @@ -677,8 +653,7 @@ public: KeyframeInterpolator size; Vector3D sizeValue = Vector3D(0.0, 0.0, 0.0); - BezierPath resolvedPath; - CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr resolvedPath; }; class StarPathOutput : public PathOutput { @@ -689,7 +664,8 @@ public: outerRadius(star.outerRadius.keyframes), outerRoundedness(star.outerRoundness.keyframes), rotation(star.rotation.keyframes), - points(star.points.keyframes) { + points(star.points.keyframes), + resolvedPath(std::make_shared(BezierPath())) { if (star.innerRadius.has_value()) { innerRadius = std::make_unique>(std::make_shared>(star.innerRadius->keyframes)); } else { @@ -703,7 +679,7 @@ public: } } - virtual void update(AnimationFrameTime frameTime, BezierPathsBoundingBoxContext &boundingBoxContext) override { + virtual void update(AnimationFrameTime frameTime) override { bool hasUpdates = false; if (!hasValidData || position.hasUpdate(frameTime)) { @@ -744,19 +720,15 @@ public: } if (hasUpdates) { - ValueInterpolator::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath); - resolvedPathBounds = bezierPathsBoundingBoxParallel(boundingBoxContext, resolvedPath); + ValueInterpolator::setInplace(makeStarBezierPath(Vector2D(positionValue.x, positionValue.y), outerRadiusValue, innerRadiusValue, outerRoundednessValue, innerRoundednessValue, pointsValue, rotationValue, direction), resolvedPath->path); + resolvedPath->needsBoundsRecalculation = true; } hasValidData = true; } - virtual BezierPath const *currentPath() override { - return &resolvedPath; - } - - virtual CGRect const ¤tPathBounds() override { - return resolvedPathBounds; + virtual std::shared_ptr ¤tPath() override { + return resolvedPath; } private: @@ -785,8 +757,7 @@ public: KeyframeInterpolator points; float pointsValue = 0.0; - BezierPath resolvedPath; - CGRect resolvedPathBounds = CGRect(0.0, 0.0, 0.0, 0.0); + std::shared_ptr resolvedPath; }; class TransformOutput { @@ -951,7 +922,7 @@ public: size_t maxSubitem = std::min(subItems.size(), subItemLimit); if (_contentItem->path) { - mappedPaths.emplace_back(_contentItem->path.value(), effectiveTransform); + mappedPaths.emplace_back(_contentItem->path->path, effectiveTransform); } for (size_t i = 0; i < maxSubitem; i++) { @@ -1012,7 +983,7 @@ public: _contentItem->isGroup = isGroup; if (path) { - _contentItem->path = *path->currentPath(); + _contentItem->path = path->currentPath(); } if (!shadings.empty()) { @@ -1052,8 +1023,7 @@ public: } if (path) { - path->update(frameTime, boundingBoxContext); - _contentItem->pathBoundingBox = path->currentPathBounds(); + path->update(frameTime); } for (const auto &trim : trims) { trim->update(frameTime); From 4f506dd47648a8954b8e4a116ab59648c563d24f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 21:27:05 +0400 Subject: [PATCH 19/32] Refactoring --- Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h | 1 + .../SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h | 1 + .../SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm | 4 ++++ .../SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h | 1 + .../SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm | 4 ++++ 5 files changed, 11 insertions(+) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h index 88eaf678fb..1aa1c930bb 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h @@ -67,6 +67,7 @@ public: virtual void setAlpha(float alpha) = 0; virtual void concatenate(lottie::CATransform3D const &transform) = 0; + virtual lottie::CATransform3D currentTransform() = 0; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) = 0; }; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h index dbc9ad5814..de7c1f39dd 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h @@ -41,6 +41,7 @@ public: virtual void setBlendMode(BlendMode blendMode) override; virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; + virtual lottie::CATransform3D currentTransform() override; virtual std::shared_ptr makeImage() const; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm index 2eb352c5c9..db48fd2f4b 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm @@ -467,6 +467,10 @@ void CanvasImpl::concatenate(lottie::CATransform3D const &transform) { CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform))); } +lottie::CATransform3D CanvasImpl::currentTransform() { + return lottie::fromNativeTransform(CATransform3DMakeAffineTransform(CGContextGetCTM(_context))); +} + std::shared_ptr CanvasImpl::makeImage() const { ::CGImageRef nativeImage = CGBitmapContextCreateImage(_context); if (nativeImage) { diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h index 5c5bcba396..39126f6036 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h @@ -33,6 +33,7 @@ public: virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; + virtual lottie::CATransform3D currentTransform() override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm index f2d642f1c7..a6561c587a 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm @@ -271,6 +271,10 @@ void ThorVGCanvasImpl::concatenate(lottie::CATransform3D const &transform) { ));*/ } +lottie::CATransform3D ThorVGCanvasImpl::currentTransform() { + return _transform; +} + void ThorVGCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { /*ThorVGCanvasImpl *impl = (ThorVGCanvasImpl *)other.get(); auto image = impl->surface()->makeImageSnapshot(); From 5aa4b29f7bf8a2b236f41dd2f0c34425a86b7a16 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 22:01:56 +0400 Subject: [PATCH 20/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 129 ++++++++++++++++-- 1 file changed, 119 insertions(+), 10 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 9053ea3fa2..34b4e30729 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -84,6 +84,100 @@ static std::vector collectPaths(std::shared_ptr getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { + if (!contentItem->renderData.isValid) { + return std::nullopt; + } + + auto currentTransform = contentItem->renderData.globalTransform; + + std::optional globalRect; + for (const auto &shadingVariant : contentItem->shadings) { + lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true, bezierPathsBoundingBoxContext); + + if (shadingVariant->stroke) { + shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); + } else if (shadingVariant->fill) { + } else { + continue; + } + + CGRect shapeGlobalBounds = shapeBounds.applyingTransform(currentTransform); + if (globalRect) { + globalRect = globalRect->unionWith(shapeGlobalBounds); + } else { + globalRect = shapeGlobalBounds; + } + } + + for (const auto &subItem : contentItem->subItems) { + auto subGlobalRect = getRenderContentItemGlobalRect(subItem, globalSize, bezierPathsBoundingBoxContext); + if (subGlobalRect) { + if (globalRect) { + globalRect = globalRect->unionWith(subGlobalRect.value()); + } else { + globalRect = subGlobalRect.value(); + } + } + } + + if (globalRect) { + CGRect integralGlobalRect( + std::floor(globalRect->x), + std::floor(globalRect->y), + std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), + std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) + ); + return integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); + } else { + return std::nullopt; + } +} + +static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { + if (!node->renderData.isValid) { + return std::nullopt; + } + auto currentTransform = node->renderData.globalTransform; + + std::optional globalRect; + if (node->_contentItem) { + globalRect = getRenderContentItemGlobalRect(node->_contentItem, globalSize, bezierPathsBoundingBoxContext); + } + + if (node->renderData.isInvertedMatte) { + CGRect globalBounds = node->bounds().applyingTransform(currentTransform); + if (globalRect) { + globalRect = globalRect->unionWith(globalBounds); + } else { + globalRect = globalBounds; + } + } + + for (const auto &subNode : node->subnodes()) { + auto subGlobalRect = getRenderNodeGlobalRect(subNode, globalSize, bezierPathsBoundingBoxContext); + if (subGlobalRect) { + if (globalRect) { + globalRect = globalRect->unionWith(subGlobalRect.value()); + } else { + globalRect = subGlobalRect.value(); + } + } + } + + if (globalRect) { + CGRect integralGlobalRect( + std::floor(globalRect->x), + std::floor(globalRect->y), + std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), + std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) + ); + return integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); + } else { + return std::nullopt; + } +} + static void processRenderContentItem(std::shared_ptr const &contentItem, Vector2D const &globalSize, CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; @@ -150,6 +244,7 @@ static void processRenderContentItem(std::shared_ptr std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) ); + integralGlobalRect = integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(integralGlobalRect)) { contentItem->renderData.isValid = false; @@ -245,6 +340,7 @@ static void processRenderTree(std::shared_ptr const &node, Vecto std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) ); + integralGlobalRect = integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(integralGlobalRect)) { node->renderData.isValid = false; @@ -296,7 +392,7 @@ static void processRenderTree(std::shared_ptr const &node, Vecto namespace { -static void drawLottieContentItem(std::shared_ptr parentContext, std::shared_ptr item, float parentAlpha) { +static void drawLottieContentItem(std::shared_ptr parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (!item->renderData.isValid) { return; } @@ -316,12 +412,19 @@ static void drawLottieContentItem(std::shared_ptr paren bool needsTempContext = false; needsTempContext = layerAlpha != 1.0 && item->renderData.drawContentDescendants > 1; + std::optional globalRect; if (needsTempContext) { - auto tempContextValue = parentContext->makeLayer((int)(item->renderData.globalRect.width), (int)(item->renderData.globalRect.height)); + globalRect = lottie::getRenderContentItemGlobalRect(item, globalSize, bezierPathsBoundingBoxContext); + if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { + parentContext->restoreState(); + return; + } + + auto tempContextValue = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); tempContext = tempContextValue; currentContext = tempContextValue; - currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-item->renderData.globalRect.x, -item->renderData.globalRect.y))); + currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); currentContext->concatenate(item->renderData.globalTransform); @@ -519,7 +622,7 @@ static void drawLottieContentItem(std::shared_ptr paren for (auto it = item->subItems.rbegin(); it != item->subItems.rend(); it++) { const auto &subItem = *it; - drawLottieContentItem(currentContext, subItem, renderAlpha); + drawLottieContentItem(currentContext, subItem, renderAlpha, globalSize, bezierPathsBoundingBoxContext); } if (tempContext) { @@ -527,14 +630,14 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->concatenate(item->renderData.globalTransform.inverted()); parentContext->setAlpha(layerAlpha); - parentContext->draw(tempContext, item->renderData.globalRect); + parentContext->draw(tempContext, globalRect.value()); parentContext->setAlpha(1.0); } parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, float parentAlpha) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (!node->renderData.isValid) { return; } @@ -558,6 +661,12 @@ static void renderLottieRenderNode(std::shared_ptr node, needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds(); } + auto testGlobalRect = lottie::getRenderNodeGlobalRect(node, globalSize, bezierPathsBoundingBoxContext); + auto actualGlobalRect = node->renderData.globalRect; + if (testGlobalRect != actualGlobalRect) { + //printf("getRenderNodeGlobalRect!\n"); + } + if (needsTempContext) { if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.masksToBounds()) { auto maskBackingStorage = parentContext->makeLayer((int)(node->renderData.globalRect.width), (int)(node->renderData.globalRect.height)); @@ -569,7 +678,7 @@ static void renderLottieRenderNode(std::shared_ptr node, 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)); } if (node->mask() && node->mask()->renderData.isValid) { - renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, 1.0); + renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, 1.0, bezierPathsBoundingBoxContext); } maskContext = maskBackingStorage; @@ -599,7 +708,7 @@ static void renderLottieRenderNode(std::shared_ptr node, } if (node->_contentItem) { - drawLottieContentItem(currentContext, node->_contentItem, renderAlpha); + drawLottieContentItem(currentContext, node->_contentItem, renderAlpha, globalSize, bezierPathsBoundingBoxContext); } if (node->renderData.isInvertedMatte) { @@ -609,7 +718,7 @@ static void renderLottieRenderNode(std::shared_ptr node, for (const auto &subnode : node->subnodes()) { if (subnode->renderData.isValid) { - renderLottieRenderNode(subnode, currentContext, globalSize, renderAlpha); + renderLottieRenderNode(subnode, currentContext, globalSize, renderAlpha, bezierPathsBoundingBoxContext); } } @@ -673,7 +782,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); - renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), 1.0); + renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), 1.0, *_bezierPathsBoundingBoxContext.get()); auto image = context->makeImage(); From 734658393910416c526a77c53e0546793485931f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 22:07:17 +0400 Subject: [PATCH 21/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 34b4e30729..3b198fae47 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -661,17 +661,18 @@ static void renderLottieRenderNode(std::shared_ptr node, needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds(); } - auto testGlobalRect = lottie::getRenderNodeGlobalRect(node, globalSize, bezierPathsBoundingBoxContext); - auto actualGlobalRect = node->renderData.globalRect; - if (testGlobalRect != actualGlobalRect) { - //printf("getRenderNodeGlobalRect!\n"); - } - + std::optional globalRect; if (needsTempContext) { + globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, bezierPathsBoundingBoxContext); + if (!globalRect) { + parentContext->restoreState(); + return; + } + if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.masksToBounds()) { - auto maskBackingStorage = parentContext->makeLayer((int)(node->renderData.globalRect.width), (int)(node->renderData.globalRect.height)); + auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); - maskBackingStorage->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->renderData.globalRect.x, -node->renderData.globalRect.y))); + maskBackingStorage->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); maskBackingStorage->concatenate(node->renderData.globalTransform); if (node->renderData.layer.masksToBounds()) { @@ -684,11 +685,11 @@ static void renderLottieRenderNode(std::shared_ptr node, maskContext = maskBackingStorage; } - auto tempContextValue = parentContext->makeLayer((int)(node->renderData.globalRect.width), (int)(node->renderData.globalRect.height)); + auto tempContextValue = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); tempContext = tempContextValue; currentContext = tempContextValue; - currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->renderData.globalRect.x, -node->renderData.globalRect.y))); + currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); currentContext->concatenate(node->renderData.globalTransform); @@ -727,12 +728,12 @@ static void renderLottieRenderNode(std::shared_ptr node, if (maskContext) { tempContext->setBlendMode(lottieRendering::BlendMode::DestinationIn); - tempContext->draw(maskContext, lottie::CGRect(node->renderData.globalRect.x, node->renderData.globalRect.y, node->renderData.globalRect.width, node->renderData.globalRect.height)); + tempContext->draw(maskContext, lottie::CGRect(globalRect->x, globalRect->y, globalRect->width, globalRect->height)); } parentContext->concatenate(node->renderData.globalTransform.inverted()); parentContext->setAlpha(layerAlpha); - parentContext->draw(tempContext, node->renderData.globalRect); + parentContext->draw(tempContext, globalRect.value()); } parentContext->restoreState(); From c0ef6478f75c3bd13d548306c39c92c6e97cce66 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 22:14:10 +0400 Subject: [PATCH 22/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 90 +------------------ .../Sources/ViewController.swift | 2 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 2 - .../Sources/LottieAnimationContainer.mm | 1 - 4 files changed, 3 insertions(+), 92 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 3b198fae47..e56e5bb349 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -189,28 +189,15 @@ static void processRenderContentItem(std::shared_ptr return; } - std::optional globalRect; - int drawContentDescendants = 0; - for (const auto &shadingVariant : contentItem->shadings) { - lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true, bezierPathsBoundingBoxContext); - if (shadingVariant->stroke) { - shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); } else if (shadingVariant->fill) { } else { continue; } drawContentDescendants += 1; - - CGRect shapeGlobalBounds = shapeBounds.applyingTransform(currentTransform); - if (globalRect) { - globalRect = globalRect->unionWith(shapeGlobalBounds); - } else { - globalRect = shapeGlobalBounds; - } } if (contentItem->isGroup) { @@ -220,11 +207,6 @@ static void processRenderContentItem(std::shared_ptr if (subItem->renderData.isValid) { drawContentDescendants += subItem->renderData.drawContentDescendants; - if (globalRect) { - globalRect = globalRect->unionWith(subItem->renderData.globalRect); - } else { - globalRect = subItem->renderData.globalRect; - } } } } else { @@ -233,28 +215,6 @@ static void processRenderContentItem(std::shared_ptr } } - if (!globalRect) { - contentItem->renderData.isValid = false; - return; - } - - CGRect integralGlobalRect( - std::floor(globalRect->x), - std::floor(globalRect->y), - std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), - std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) - ); - integralGlobalRect = integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); - - if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(integralGlobalRect)) { - contentItem->renderData.isValid = false; - return; - } - if (integralGlobalRect.width <= 0.0 || integralGlobalRect.height <= 0.0) { - contentItem->renderData.isValid = false; - return; - } - contentItem->renderData.isValid = true; contentItem->renderData.layer._bounds = CGRect(0.0, 0.0, 0.0, 0.0); @@ -264,7 +224,6 @@ static void processRenderContentItem(std::shared_ptr contentItem->renderData.layer._masksToBounds = false; contentItem->renderData.layer._isHidden = false; - contentItem->renderData.globalRect = integralGlobalRect; contentItem->renderData.globalTransform = currentTransform; contentItem->renderData.drawContentDescendants = drawContentDescendants; contentItem->renderData.isInvertedMatte = false; @@ -297,56 +256,22 @@ static void processRenderTree(std::shared_ptr const &node, Vecto } int drawContentDescendants = 0; - std::optional globalRect; if (node->_contentItem) { processRenderContentItem(node->_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); if (node->_contentItem->renderData.isValid) { drawContentDescendants += node->_contentItem->renderData.drawContentDescendants; - globalRect = node->_contentItem->renderData.globalRect; } } bool isInvertedMatte = isInvertedMask; - if (isInvertedMatte) { - CGRect globalBounds = node->bounds().applyingTransform(currentTransform); - if (globalRect) { - globalRect = globalRect->unionWith(globalBounds); - } else { - globalRect = globalBounds; - } - } for (const auto &item : node->subnodes()) { processRenderTree(item, globalSize, currentTransform, false, bezierPathsBoundingBoxContext); if (item->renderData.isValid) { drawContentDescendants += item->renderData.drawContentDescendants; - - if (globalRect) { - globalRect = globalRect->unionWith(item->renderData.globalRect); - } else { - globalRect = item->renderData.globalRect; - } } } - if (!globalRect) { - node->renderData.isValid = false; - return; - } - - CGRect integralGlobalRect( - std::floor(globalRect->x), - std::floor(globalRect->y), - std::ceil(globalRect->width + globalRect->x - floor(globalRect->x)), - std::ceil(globalRect->height + globalRect->y - floor(globalRect->y)) - ); - integralGlobalRect = integralGlobalRect.intersection(CGRect(0.0, 0.0, globalSize.x, globalSize.y)); - - if (!CGRect(0.0, 0.0, globalSize.x, globalSize.y).intersects(integralGlobalRect)) { - node->renderData.isValid = false; - return; - } - bool masksToBounds = node->masksToBounds(); if (masksToBounds) { CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform); @@ -357,22 +282,12 @@ static void processRenderTree(std::shared_ptr const &node, Vecto if (node->mask()) { processRenderTree(node->mask(), globalSize, currentTransform, node->invertMask(), bezierPathsBoundingBoxContext); - if (node->mask()->renderData.isValid) { - if (!node->mask()->renderData.globalRect.intersects(integralGlobalRect)) { - node->renderData.isValid = false; - return; - } - } else { + if (!node->mask()->renderData.isValid) { node->renderData.isValid = false; return; } } - if (integralGlobalRect.width <= 0.0 || integralGlobalRect.height <= 0.0) { - node->renderData.isValid = false; - return; - } - node->renderData.isValid = true; node->renderData.layer._bounds = node->bounds(); @@ -382,7 +297,6 @@ static void processRenderTree(std::shared_ptr const &node, Vecto node->renderData.layer._masksToBounds = masksToBounds; node->renderData.layer._isHidden = node->isHidden(); - node->renderData.globalRect = integralGlobalRect; node->renderData.globalTransform = currentTransform; node->renderData.drawContentDescendants = drawContentDescendants; node->renderData.isInvertedMatte = isInvertedMatte; @@ -664,7 +578,7 @@ static void renderLottieRenderNode(std::shared_ptr node, std::optional globalRect; if (needsTempContext) { globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, bezierPathsBoundingBoxContext); - if (!globalRect) { + if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; } diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index da7d22c2b2..b8464a9117 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 = "569118802063655905.json" + //continueFromName = "1471004892762996754.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 e504de3175..9b26390a23 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -74,7 +74,6 @@ public: false, false ), - globalRect(CGRect(0.0, 0.0, 0.0, 0.0)), globalTransform(CATransform3D::identity()), drawContentDescendants(false), isInvertedMatte(false) { @@ -83,7 +82,6 @@ public: bool isValid = false; LayerParams layer; - CGRect globalRect; CATransform3D globalTransform; int drawContentDescendants; bool isInvertedMatte; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm index d155af122e..283a1cdf0f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm @@ -64,7 +64,6 @@ result.layer.masksToBounds = node->renderData.layer._masksToBounds; result.layer.isHidden = node->renderData.layer._isHidden; - result.globalRect = CGRectMake(node->renderData.globalRect.x, node->renderData.globalRect.y, node->renderData.globalRect.width, node->renderData.globalRect.height); result.globalTransform = lottie::nativeTransform(node->renderData.globalTransform); result.hasSimpleContents = node->renderData.drawContentDescendants <= 1; result.drawContentDescendants = node->renderData.drawContentDescendants; From a5377fd4f5f3d9048b0c34b76c2d619f72da009f Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 May 2024 23:06:02 +0400 Subject: [PATCH 23/32] Lottie --- .../Sources/SoftwareLottieRenderer.mm | 110 +++++++++--------- .../Sources/ViewController.swift | 2 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 53 +-------- .../Sources/LottieAnimationContainer.mm | 9 +- 4 files changed, 61 insertions(+), 113 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index e56e5bb349..b4c630e469 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -84,12 +84,14 @@ static std::vector collectPaths(std::shared_ptr getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional 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 = contentItem->renderData.globalTransform; + auto currentTransform = parentTransform; + CATransform3D localTransform = contentItem->transform; + currentTransform = localTransform * currentTransform; std::optional globalRect; for (const auto &shadingVariant : contentItem->shadings) { @@ -111,7 +113,7 @@ static std::optional getRenderContentItemGlobalRect(std::shared_ptrsubItems) { - auto subGlobalRect = getRenderContentItemGlobalRect(subItem, globalSize, bezierPathsBoundingBoxContext); + auto subGlobalRect = getRenderContentItemGlobalRect(subItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); if (subGlobalRect) { if (globalRect) { globalRect = globalRect->unionWith(subGlobalRect.value()); @@ -134,15 +136,20 @@ static std::optional getRenderContentItemGlobalRect(std::shared_ptr getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (!node->renderData.isValid) { return std::nullopt; } - auto currentTransform = node->renderData.globalTransform; + + 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; std::optional globalRect; if (node->_contentItem) { - globalRect = getRenderContentItemGlobalRect(node->_contentItem, globalSize, bezierPathsBoundingBoxContext); + globalRect = getRenderContentItemGlobalRect(node->_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); } if (node->renderData.isInvertedMatte) { @@ -155,7 +162,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptrsubnodes()) { - auto subGlobalRect = getRenderNodeGlobalRect(subNode, globalSize, bezierPathsBoundingBoxContext); + auto subGlobalRect = getRenderNodeGlobalRect(subNode, globalSize, currentTransform, bezierPathsBoundingBoxContext); if (subGlobalRect) { if (globalRect) { globalRect = globalRect->unionWith(subGlobalRect.value()); @@ -180,7 +187,6 @@ 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; @@ -217,14 +223,8 @@ static void processRenderContentItem(std::shared_ptr 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.layer.masksToBounds = false; - contentItem->renderData.globalTransform = currentTransform; contentItem->renderData.drawContentDescendants = drawContentDescendants; contentItem->renderData.isInvertedMatte = false; } @@ -243,11 +243,9 @@ 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()) { @@ -290,14 +288,8 @@ static void processRenderTree(std::shared_ptr const &node, Vecto node->renderData.isValid = true; - node->renderData.layer._bounds = node->bounds(); - node->renderData.layer._position = node->position(); - node->renderData.layer._transform = node->transform(); - node->renderData.layer._opacity = node->alpha(); - node->renderData.layer._masksToBounds = masksToBounds; - node->renderData.layer._isHidden = node->isHidden(); + node->renderData.layer.masksToBounds = masksToBounds; - node->renderData.globalTransform = currentTransform; node->renderData.drawContentDescendants = drawContentDescendants; node->renderData.isInvertedMatte = isInvertedMatte; } @@ -306,15 +298,19 @@ 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::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +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; } - float normalizedOpacity = item->renderData.layer.opacity(); + auto currentTransform = parentTransform; + lottie::CATransform3D localTransform = item->transform; + currentTransform = localTransform * currentTransform; + + float normalizedOpacity = item->alpha; float layerAlpha = ((float)normalizedOpacity) * parentAlpha; - if (item->renderData.layer.isHidden() || normalizedOpacity == 0.0f) { + if (normalizedOpacity == 0.0f) { return; } @@ -328,7 +324,7 @@ static void drawLottieContentItem(std::shared_ptr paren std::optional globalRect; if (needsTempContext) { - globalRect = lottie::getRenderContentItemGlobalRect(item, globalSize, bezierPathsBoundingBoxContext); + globalRect = lottie::getRenderContentItemGlobalRect(item, globalSize, parentTransform, bezierPathsBoundingBoxContext); if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; @@ -341,14 +337,12 @@ static void drawLottieContentItem(std::shared_ptr paren currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); - currentContext->concatenate(item->renderData.globalTransform); + currentContext->concatenate(currentTransform); } else { currentContext = parentContext; } - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(item->renderData.layer.position().x, item->renderData.layer.position().y))); - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-item->renderData.layer.bounds().x, -item->renderData.layer.bounds().y))); - parentContext->concatenate(item->renderData.layer.transform()); + parentContext->concatenate(item->transform); float renderAlpha = 1.0; if (tempContext) { @@ -536,13 +530,13 @@ static void drawLottieContentItem(std::shared_ptr paren for (auto it = item->subItems.rbegin(); it != item->subItems.rend(); it++) { const auto &subItem = *it; - drawLottieContentItem(currentContext, subItem, renderAlpha, globalSize, bezierPathsBoundingBoxContext); + drawLottieContentItem(currentContext, subItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext); } if (tempContext) { tempContext->restoreState(); - parentContext->concatenate(item->renderData.globalTransform.inverted()); + parentContext->concatenate(currentTransform.inverted()); parentContext->setAlpha(layerAlpha); parentContext->draw(tempContext, globalRect.value()); parentContext->setAlpha(1.0); @@ -551,17 +545,23 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (!node->renderData.isValid) { return; } - float normalizedOpacity = node->renderData.layer.opacity(); + float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; - if (node->renderData.layer.isHidden() || normalizedOpacity == 0.0f) { + if (node->isHidden() || normalizedOpacity == 0.0f) { return; } + auto currentTransform = parentTransform; + lottie::Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y); + lottie::CATransform3D localTransform = node->transform(); + localTransform = localTransform.translated(localTranslation); + currentTransform = localTransform * currentTransform; + parentContext->saveState(); std::shared_ptr maskContext; @@ -572,28 +572,28 @@ static void renderLottieRenderNode(std::shared_ptr node, if (node->mask() && node->mask()->renderData.isValid) { needsTempContext = true; } else { - needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds(); + needsTempContext = layerAlpha != 1.0 || node->renderData.layer.masksToBounds; } std::optional globalRect; if (needsTempContext) { - globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, bezierPathsBoundingBoxContext); + globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, parentTransform, bezierPathsBoundingBoxContext); if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; } - if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.masksToBounds()) { + if ((node->mask() && node->mask()->renderData.isValid) || node->renderData.layer.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(node->renderData.globalTransform); + maskBackingStorage->concatenate(currentTransform); - if (node->renderData.layer.masksToBounds()) { - 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)); + if (node->renderData.layer.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) { - renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, 1.0, bezierPathsBoundingBoxContext); + renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, currentTransform, 1.0, bezierPathsBoundingBoxContext); } maskContext = maskBackingStorage; @@ -606,14 +606,14 @@ static void renderLottieRenderNode(std::shared_ptr node, currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); - currentContext->concatenate(node->renderData.globalTransform); + currentContext->concatenate(currentTransform); } else { currentContext = parentContext; } - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(node->renderData.layer.position().x, node->renderData.layer.position().y))); - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->renderData.layer.bounds().x, -node->renderData.layer.bounds().y))); - parentContext->concatenate(node->renderData.layer.transform()); + parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(node->position().x, node->position().y))); + parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->bounds().x, -node->bounds().y))); + parentContext->concatenate(node->transform()); float renderAlpha = 1.0; if (tempContext) { @@ -623,17 +623,17 @@ static void renderLottieRenderNode(std::shared_ptr node, } if (node->_contentItem) { - drawLottieContentItem(currentContext, node->_contentItem, renderAlpha, globalSize, bezierPathsBoundingBoxContext); + drawLottieContentItem(currentContext, node->_contentItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext); } if (node->renderData.isInvertedMatte) { - 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)); + 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)); currentContext->setBlendMode(lottieRendering::BlendMode::DestinationOut); } for (const auto &subnode : node->subnodes()) { if (subnode->renderData.isValid) { - renderLottieRenderNode(subnode, currentContext, globalSize, renderAlpha, bezierPathsBoundingBoxContext); + renderLottieRenderNode(subnode, currentContext, globalSize, currentTransform, renderAlpha, bezierPathsBoundingBoxContext); } } @@ -645,7 +645,7 @@ static void renderLottieRenderNode(std::shared_ptr node, tempContext->draw(maskContext, lottie::CGRect(globalRect->x, globalRect->y, globalRect->width, globalRect->height)); } - parentContext->concatenate(node->renderData.globalTransform.inverted()); + parentContext->concatenate(currentTransform.inverted()); parentContext->setAlpha(layerAlpha); parentContext->draw(tempContext, globalRect.value()); } @@ -689,7 +689,9 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return nil; } - 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()); + 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()); if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); @@ -697,7 +699,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); - renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), 1.0, *_bezierPathsBoundingBoxContext.get()); + renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, *_bezierPathsBoundingBoxContext.get()); auto image = context->makeImage(); diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index b8464a9117..ca67ee2ad1 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 = "1471004892762996754.json" + //continueFromName = "1137162165791228042.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 9b26390a23..c401eed0da 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -16,73 +16,26 @@ namespace lottie { class ProcessedRenderTreeNodeData { public: struct LayerParams { - CGRect _bounds; - Vector2D _position; - CATransform3D _transform; - float _opacity; - bool _masksToBounds; - bool _isHidden; + bool masksToBounds; LayerParams( - CGRect bounds_, - Vector2D position_, - CATransform3D transform_, - float opacity_, - bool masksToBounds_, - bool isHidden_ + bool masksToBounds_ ) : - _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; - } - - float opacity() const { - return _opacity; - } - - bool masksToBounds() const { - return _masksToBounds; - } - - bool isHidden() const { - return _isHidden; + masksToBounds(masksToBounds_) { } }; ProcessedRenderTreeNodeData() : isValid(false), layer( - CGRect(0.0, 0.0, 0.0, 0.0), - Vector2D(0.0, 0.0), - CATransform3D::identity(), - 1.0, - false, false ), - globalTransform(CATransform3D::identity()), drawContentDescendants(false), isInvertedMatte(false) { - } bool isValid = false; LayerParams layer; - CATransform3D globalTransform; int drawContentDescendants; bool isInvertedMatte; }; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm index 283a1cdf0f..28c00c892a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/LottieAnimationContainer.mm @@ -56,15 +56,8 @@ result.internalId = nodeId; result.isValid = node->renderData.isValid; + - result.layer.bounds = CGRectMake(node->renderData.layer._bounds.x, node->renderData.layer._bounds.y, node->renderData.layer._bounds.width, node->renderData.layer._bounds.height); - result.layer.position = CGPointMake(node->renderData.layer._position.x, node->renderData.layer._position.y); - result.layer.transform = lottie::nativeTransform(node->renderData.layer._transform); - result.layer.opacity = node->renderData.layer._opacity; - result.layer.masksToBounds = node->renderData.layer._masksToBounds; - result.layer.isHidden = node->renderData.layer._isHidden; - - result.globalTransform = lottie::nativeTransform(node->renderData.globalTransform); result.hasSimpleContents = node->renderData.drawContentDescendants <= 1; result.drawContentDescendants = node->renderData.drawContentDescendants; result.isInvertedMatte = node->renderData.isInvertedMatte; From 940619b7cd5b3e9387477d8b68b6ba1afd4b1837 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 00:24:04 +0400 Subject: [PATCH 24/32] 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(); From 41c88223b498249895b4c495b908ac3f6b384d10 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 00:42:02 +0400 Subject: [PATCH 25/32] Lottie --- .../Sources/SoftwareLottieRenderer.mm | 74 +++++-------------- 1 file changed, 18 insertions(+), 56 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 33fe9c5014..39433d22d5 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -134,8 +134,8 @@ static std::optional getRenderContentItemGlobalRect(std::shared_ptr getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { - if (!node->renderData.isValid) { +static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { + if (node->isHidden() || node->alpha() < minVisibleAlpha) { return std::nullopt; } @@ -150,7 +150,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptr_contentItem, globalSize, currentTransform, bezierPathsBoundingBoxContext); } - if (node->renderData.isInvertedMatte) { + if (isInvertedMatte) { CGRect globalBounds = node->bounds().applyingTransform(currentTransform); if (globalRect) { globalRect = globalRect->unionWith(globalBounds); @@ -160,7 +160,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptrsubnodes()) { - auto subGlobalRect = getRenderNodeGlobalRect(subNode, globalSize, currentTransform, bezierPathsBoundingBoxContext); + auto subGlobalRect = getRenderNodeGlobalRect(subNode, globalSize, currentTransform, false, bezierPathsBoundingBoxContext); if (subGlobalRect) { if (globalRect) { globalRect = globalRect->unionWith(subGlobalRect.value()); @@ -183,40 +183,6 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, Vector2D const &globalSize, bool isInvertedMask, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { - if (node->isHidden() || node->alpha() < minVisibleAlpha) { - node->renderData.isValid = false; - return; - } - - if (node->masksToBounds()) { - if (node->bounds().empty()) { - node->renderData.isValid = false; - return; - } - } - - bool isInvertedMatte = isInvertedMask; - - for (const auto &subnode : node->subnodes()) { - processRenderTree(subnode, globalSize, false, bezierPathsBoundingBoxContext); - if (subnode->renderData.isValid) { - } - } - - if (node->mask()) { - processRenderTree(node->mask(), globalSize, node->invertMask(), bezierPathsBoundingBoxContext); - if (!node->mask()->renderData.isValid) { - node->renderData.isValid = false; - return; - } - } - - node->renderData.isValid = true; - - node->renderData.isInvertedMatte = isInvertedMatte; -} - } namespace { @@ -464,10 +430,7 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { - if (!node->renderData.isValid) { - return; - } +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; @@ -481,8 +444,6 @@ static void renderLottieRenderNode(std::shared_ptr node, localTransform = localTransform.translated(localTranslation); currentTransform = localTransform * currentTransform; - parentContext->saveState(); - std::shared_ptr maskContext; std::shared_ptr currentContext; std::shared_ptr tempContext; @@ -490,13 +451,18 @@ static void renderLottieRenderNode(std::shared_ptr node, bool masksToBounds = node->masksToBounds(); if (masksToBounds) { lottie::CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform); + if (effectiveGlobalBounds.width <= 0.0f || effectiveGlobalBounds.height <= 0.0f) { + return; + } if (effectiveGlobalBounds.contains(lottie::CGRect(0.0, 0.0, globalSize.x, globalSize.y))) { masksToBounds = false; } } + parentContext->saveState(); + bool needsTempContext = false; - if (node->mask() && node->mask()->renderData.isValid) { + if (node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) { needsTempContext = true; } else { needsTempContext = layerAlpha != 1.0 || masksToBounds; @@ -504,13 +470,13 @@ static void renderLottieRenderNode(std::shared_ptr node, std::optional globalRect; if (needsTempContext) { - globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, parentTransform, bezierPathsBoundingBoxContext); + globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, parentTransform, false, bezierPathsBoundingBoxContext); if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; } - if ((node->mask() && node->mask()->renderData.isValid) || masksToBounds) { + if ((node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) || masksToBounds) { auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); maskBackingStorage->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); @@ -519,8 +485,8 @@ static void renderLottieRenderNode(std::shared_ptr node, 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) { - renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, currentTransform, 1.0, bezierPathsBoundingBoxContext); + if (node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) { + renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, currentTransform, 1.0, node->invertMask(), bezierPathsBoundingBoxContext); } maskContext = maskBackingStorage; @@ -553,15 +519,13 @@ static void renderLottieRenderNode(std::shared_ptr node, drawLottieContentItem(currentContext, node->_contentItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext); } - if (node->renderData.isInvertedMatte) { + if (isInvertedMatte) { 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)); currentContext->setBlendMode(lottieRendering::BlendMode::DestinationOut); } for (const auto &subnode : node->subnodes()) { - if (subnode->renderData.isValid) { - renderLottieRenderNode(subnode, currentContext, globalSize, currentTransform, renderAlpha, bezierPathsBoundingBoxContext); - } + renderLottieRenderNode(subnode, currentContext, globalSize, currentTransform, renderAlpha, false, bezierPathsBoundingBoxContext); } if (tempContext) { @@ -614,8 +578,6 @@ 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), false, *_bezierPathsBoundingBoxContext.get()); - if (!useReferenceRendering) { return nil; } @@ -626,7 +588,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); - renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, *_bezierPathsBoundingBoxContext.get()); + renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); auto image = context->makeImage(); From 814852d902f577f2739c2e00b8ddeba3ed9a52ef Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 02:04:45 +0400 Subject: [PATCH 26/32] Lottie --- .../SoftwareLottieRenderer/Sources/Canvas.h | 28 ++- .../Sources/CoreGraphicsCanvasImpl.h | 13 +- .../Sources/CoreGraphicsCanvasImpl.mm | 110 ++++++--- .../Sources/NullCanvasImpl.h | 47 ++++ .../Sources/NullCanvasImpl.mm | 81 +++++++ .../Sources/SoftwareLottieRenderer.mm | 223 ++++++++++-------- .../Sources/ThorVGCanvasImpl.h | 13 +- .../Sources/ThorVGCanvasImpl.mm | 46 ++-- .../Sources/ViewController.swift | 4 +- 9 files changed, 385 insertions(+), 180 deletions(-) create mode 100644 Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h create mode 100644 Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h index 1aa1c930bb..9eb9bc90d6 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace lottieRendering { @@ -41,6 +42,20 @@ enum class BlendMode { DestinationOut }; +enum class PathCommandType { + MoveTo, + LineTo, + CurveTo, + Close +}; + +typedef struct { + PathCommandType type; + CGPoint points[4]; +} PathCommand; + +typedef std::function)> CanvasPathEnumerator; + class Canvas { public: virtual ~Canvas() = default; @@ -53,13 +68,13 @@ public: virtual void saveState() = 0; virtual void restoreState() = 0; - virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) = 0; - virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; - virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; + virtual void fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) = 0; + virtual void linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; + virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; - virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) = 0; - virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; - virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; + virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) = 0; + virtual void linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) = 0; + virtual void radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) = 0; virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) = 0; virtual void setBlendMode(BlendMode blendMode) = 0; @@ -67,7 +82,6 @@ public: virtual void setAlpha(float alpha) = 0; virtual void concatenate(lottie::CATransform3D const &transform) = 0; - virtual lottie::CATransform3D currentTransform() = 0; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) = 0; }; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h index de7c1f39dd..3a4176ba0d 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h @@ -29,19 +29,18 @@ public: virtual void saveState() override; virtual void restoreState() override; - virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) override; - virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; - virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; - virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; - virtual lottie::CATransform3D currentTransform() override; virtual std::shared_ptr makeImage() const; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm index db48fd2f4b..d9fd69f65c 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm @@ -11,6 +11,52 @@ int alignUp(int size, int align) { return (size + alignmentMask) & ~alignmentMask; } +bool addEnumeratedPath(CGContextRef context, CanvasPathEnumerator const &enumeratePath) { + bool isEmpty = true; + + enumeratePath([&](PathCommand const &command) { + switch (command.type) { + case PathCommandType::MoveTo: { + if (isEmpty) { + isEmpty = false; + CGContextBeginPath(context); + } + CGContextMoveToPoint(context, command.points[0].x, command.points[0].y); + break; + } + case PathCommandType::LineTo: { + if (isEmpty) { + isEmpty = false; + CGContextBeginPath(context); + } + CGContextAddLineToPoint(context, command.points[0].x, command.points[0].y); + break; + } + case PathCommandType::CurveTo: { + if (isEmpty) { + isEmpty = false; + CGContextBeginPath(context); + } + CGContextAddCurveToPoint(context, command.points[0].x, command.points[0].y, command.points[1].x, command.points[1].y, command.points[2].x, command.points[2].y); + break; + } + case PathCommandType::Close: { + if (isEmpty) { + isEmpty = false; + CGContextBeginPath(context); + } + CGContextClosePath(context); + break; + } + default: { + break; + } + } + }); + + return !isEmpty; +} + } ImageImpl::ImageImpl(::CGImageRef image) { @@ -87,11 +133,10 @@ void CanvasImpl::restoreState() { CGContextRestoreGState(_context); } -void CanvasImpl::fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) { - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); +void CanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) { + if (!addEnumeratedPath(_context, enumeratePath)) { + return; + } CGFloat components[4] = { color.r, color.g, color.b, color.a }; CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); @@ -110,12 +155,13 @@ void CanvasImpl::fillPath(std::shared_ptr const &path, lottie::F } } -void CanvasImpl::linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void CanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { CGContextSaveGState(_context); - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); + + if (!addEnumeratedPath(_context, enumeratePath)) { + CGContextRestoreGState(_context); + return; + } switch (fillRule) { case lottie::FillRule::EvenOdd: { @@ -155,12 +201,13 @@ void CanvasImpl::linearGradientFillPath(std::shared_ptr const &p CGContextRestoreGState(_context); } -void CanvasImpl::radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { +void CanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { CGContextSaveGState(_context); - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); + + if (!addEnumeratedPath(_context, enumeratePath)) { + CGContextRestoreGState(_context); + return; + } switch (fillRule) { case lottie::FillRule::EvenOdd: { @@ -200,11 +247,10 @@ void CanvasImpl::radialGradientFillPath(std::shared_ptr const &p CGContextRestoreGState(_context); } -void CanvasImpl::strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); +void CanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { + if (!addEnumeratedPath(_context, enumeratePath)) { + return; + } CGFloat components[4] = { color.r, color.g, color.b, color.a }; CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); @@ -261,12 +307,12 @@ void CanvasImpl::strokePath(std::shared_ptr const &path, float l CGContextStrokePath(_context); } -void CanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void CanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { CGContextSaveGState(_context); - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); + if (!addEnumeratedPath(_context, enumeratePath)) { + CGContextRestoreGState(_context); + return; + } CGContextSetLineWidth(_context, lineWidth); @@ -346,12 +392,12 @@ void CanvasImpl::linearGradientStrokePath(std::shared_ptr const CGContextRestoreGState(_context); } -void CanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { +void CanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { CGContextSaveGState(_context); - CGContextBeginPath(_context); - lottie::CGPathCocoaImpl::withNativePath(path, [context = _context](CGPathRef nativePath) { - CGContextAddPath(context, nativePath); - }); + if (!addEnumeratedPath(_context, enumeratePath)) { + CGContextRestoreGState(_context); + return; + } CGContextSetLineWidth(_context, lineWidth); @@ -467,10 +513,6 @@ void CanvasImpl::concatenate(lottie::CATransform3D const &transform) { CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform))); } -lottie::CATransform3D CanvasImpl::currentTransform() { - return lottie::fromNativeTransform(CATransform3DMakeAffineTransform(CGContextGetCTM(_context))); -} - std::shared_ptr CanvasImpl::makeImage() const { ::CGImageRef nativeImage = CGBitmapContextCreateImage(_context); if (nativeImage) { diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h new file mode 100644 index 0000000000..92e930e316 --- /dev/null +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h @@ -0,0 +1,47 @@ +#ifndef NullCanvasImpl_h +#define NullCanvasImpl_h + +#include "Canvas.h" + +namespace lottieRendering { + +class NullCanvasImpl: public Canvas { +public: + NullCanvasImpl(int width, int height); + virtual ~NullCanvasImpl(); + + virtual int width() const override; + virtual int height() const override; + + virtual std::shared_ptr makeLayer(int width, int height) override; + + virtual void saveState() override; + virtual void restoreState() override; + + virtual void fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; + + virtual void setBlendMode(BlendMode blendMode) override; + + virtual void setAlpha(float alpha) override; + + virtual void concatenate(lottie::CATransform3D const &transform) override; + + virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; + + void flush(); + +private: + float _width = 0.0f; + float _height = 0.0f; + lottie::CATransform3D _transform; +}; + +} + +#endif diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm new file mode 100644 index 0000000000..29d14e03ed --- /dev/null +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm @@ -0,0 +1,81 @@ +#include "NullCanvasImpl.h" + +namespace lottieRendering { + +namespace { + +void addEnumeratedPath(CanvasPathEnumerator const &enumeratePath) { + enumeratePath([&](PathCommand const &command) { + }); +} + +} + +NullCanvasImpl::NullCanvasImpl(int width, int height) : +_width(width), _height(height), _transform(lottie::CATransform3D::identity()) { +} + +NullCanvasImpl::~NullCanvasImpl() { +} + +int NullCanvasImpl::width() const { + return _width; +} + +int NullCanvasImpl::height() const { + return _height; +} + +std::shared_ptr NullCanvasImpl::makeLayer(int width, int height) { + return std::make_shared(width, height); +} + +void NullCanvasImpl::saveState() { +} + +void NullCanvasImpl::restoreState() { +} + +void NullCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { + addEnumeratedPath(enumeratePath); +} + +void NullCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { +} + +void NullCanvasImpl::setBlendMode(BlendMode blendMode) { +} + +void NullCanvasImpl::setAlpha(float alpha) { +} + +void NullCanvasImpl::concatenate(lottie::CATransform3D const &transform) { +} + +void NullCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { +} + +void NullCanvasImpl::flush() { +} + +} diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 39433d22d5..aa4005b252 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -3,6 +3,7 @@ #import "Canvas.h" #import "CoreGraphicsCanvasImpl.h" #import "ThorVGCanvasImpl.h" +#import "NullCanvasImpl.h" #include @@ -10,6 +11,8 @@ namespace { static constexpr float minVisibleAlpha = 0.5f / 255.0f; +static constexpr float minGlobalRectCalculationSize = 200.0f; + struct TransformedPath { lottie::BezierPath path; lottie::CATransform3D transform; @@ -53,9 +56,7 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr collectPaths(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform) { - std::vector mappedPaths; - +static void enumeratePaths(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform, std::function const &onPath) { //TODO:remove skipApplyTransform lottie::CATransform3D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { @@ -65,21 +66,14 @@ static std::vector collectPaths(std::shared_ptrsubItems.size(), subItemLimit); if (item->path) { - mappedPaths.emplace_back(item->path->path, effectiveTransform); + onPath(item->path->path, effectiveTransform); } - assert(!item->trimParams); for (size_t i = 0; i < maxSubitem; i++) { auto &subItem = item->subItems[i]; - auto subItemPaths = collectPaths(subItem, INT32_MAX, effectiveTransform, false); - - for (auto &path : subItemPaths) { - mappedPaths.emplace_back(path.path, path.transform); - } + enumeratePaths(subItem, INT32_MAX, effectiveTransform, false, onPath); } - - return mappedPaths; } } @@ -187,7 +181,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptr parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void drawLottieContentItem(std::shared_ptr const &parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; lottie::CATransform3D localTransform = item->transform; currentTransform = localTransform * currentTransform; @@ -201,7 +195,7 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->saveState(); - std::shared_ptr currentContext; + std::shared_ptr const *currentContext; std::shared_ptr tempContext; bool needsTempContext = false; @@ -209,7 +203,11 @@ static void drawLottieContentItem(std::shared_ptr paren std::optional globalRect; if (needsTempContext) { - globalRect = lottie::getRenderContentItemGlobalRect(item, globalSize, parentTransform, bezierPathsBoundingBoxContext); + if (globalSize.x <= minGlobalRectCalculationSize && globalSize.y <= minGlobalRectCalculationSize) { + globalRect = lottie::CGRect(0.0, 0.0, globalSize.x, globalSize.y); + } else { + globalRect = lottie::getRenderContentItemGlobalRect(item, globalSize, parentTransform, bezierPathsBoundingBoxContext); + } if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; @@ -218,13 +216,13 @@ static void drawLottieContentItem(std::shared_ptr paren auto tempContextValue = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); tempContext = tempContextValue; - currentContext = tempContextValue; - currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + currentContext = &tempContext; + (*currentContext)->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); - currentContext->saveState(); - currentContext->concatenate(currentTransform); + (*currentContext)->saveState(); + (*currentContext)->concatenate(currentTransform); } else { - currentContext = parentContext; + currentContext = &parentContext; } parentContext->concatenate(item->transform); @@ -237,75 +235,105 @@ static void drawLottieContentItem(std::shared_ptr paren } for (const auto &shading : item->shadings) { - std::vector itemPaths; + lottieRendering::CanvasPathEnumerator iteratePaths; if (shading->explicitPath) { - itemPaths = shading->explicitPath.value(); - } else { - auto rawPaths = collectPaths(item, shading->subItemLimit, lottie::CATransform3D::identity(), true); - for (const auto &rawPath : rawPaths) { - itemPaths.push_back(rawPath.path.copyUsingTransform(rawPath.transform)); - } - } - - if (itemPaths.empty()) { - continue; - } - - std::shared_ptr path = lottie::CGPath::makePath(); - - const auto iterate = [&](LottiePathItem const *pathItem) { - switch (pathItem->type) { - case LottiePathItemTypeMoveTo: { - path->moveTo(lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y)); - break; - } - case LottiePathItemTypeLineTo: { - path->addLineTo(lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y)); - break; - } - case LottiePathItemTypeCurveTo: { - path->addCurveTo(lottie::Vector2D(pathItem->points[2].x, pathItem->points[2].y), lottie::Vector2D(pathItem->points[0].x, pathItem->points[0].y), lottie::Vector2D(pathItem->points[1].x, pathItem->points[1].y)); - break; - } - case LottiePathItemTypeClose: { - path->closeSubpath(); - break; - } - default: { - break; - } - } - }; - - LottiePathItem pathItem; - for (const auto &path : itemPaths) { - std::optional previousElement; - for (const auto &element : path.elements()) { - if (previousElement.has_value()) { - if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { - pathItem.type = LottiePathItemTypeLineTo; - pathItem.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(&pathItem); - } else { - pathItem.type = LottiePathItemTypeCurveTo; - pathItem.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - pathItem.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); - pathItem.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); - iterate(&pathItem); + auto itemPaths = shading->explicitPath.value(); + iteratePaths = [itemPaths = itemPaths](std::function iterate) -> void { + lottieRendering::PathCommand pathCommand; + for (const auto &path : itemPaths) { + std::optional previousElement; + for (const auto &element : path.elements()) { + if (previousElement.has_value()) { + if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { + pathCommand.type = lottieRendering::PathCommandType::LineTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } else { + pathCommand.type = lottieRendering::PathCommandType::CurveTo; + pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); + pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); + iterate(pathCommand); + } + } else { + pathCommand.type = lottieRendering::PathCommandType::MoveTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } + previousElement = element; + } + if (path.closed().value_or(true)) { + pathCommand.type = lottieRendering::PathCommandType::Close; + iterate(pathCommand); } - } else { - pathItem.type = LottiePathItemTypeMoveTo; - pathItem.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); - iterate(&pathItem); } - previousElement = element; - } - if (path.closed().value_or(true)) { - pathItem.type = LottiePathItemTypeClose; - iterate(&pathItem); - } + }; + } else { + iteratePaths = [&](std::function iterate) { + enumeratePaths(item, shading->subItemLimit, lottie::CATransform3D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::CATransform3D const &transform) { + auto path = sourcePath.copyUsingTransform(transform); + + lottieRendering::PathCommand pathCommand; + std::optional previousElement; + for (const auto &element : path.elements()) { + if (previousElement.has_value()) { + if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { + pathCommand.type = lottieRendering::PathCommandType::LineTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } else { + pathCommand.type = lottieRendering::PathCommandType::CurveTo; + pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); + pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); + iterate(pathCommand); + } + } else { + pathCommand.type = lottieRendering::PathCommandType::MoveTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } + previousElement = element; + } + if (path.closed().value_or(true)) { + pathCommand.type = lottieRendering::PathCommandType::Close; + iterate(pathCommand); + } + }); + }; } + /*auto iteratePaths = [&](std::function iterate) -> void { + lottieRendering::PathCommand pathCommand; + for (const auto &path : itemPaths) { + std::optional previousElement; + for (const auto &element : path.elements()) { + if (previousElement.has_value()) { + if (previousElement->vertex.outTangentRelative().isZero() && element.vertex.inTangentRelative().isZero()) { + pathCommand.type = lottieRendering::PathCommandType::LineTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } else { + pathCommand.type = lottieRendering::PathCommandType::CurveTo; + pathCommand.points[2] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + pathCommand.points[1] = CGPointMake(element.vertex.inTangent.x, element.vertex.inTangent.y); + pathCommand.points[0] = CGPointMake(previousElement->vertex.outTangent.x, previousElement->vertex.outTangent.y); + iterate(pathCommand); + } + } else { + pathCommand.type = lottieRendering::PathCommandType::MoveTo; + pathCommand.points[0] = CGPointMake(element.vertex.point.x, element.vertex.point.y); + iterate(pathCommand); + } + previousElement = element; + } + if (path.closed().value_or(true)) { + pathCommand.type = lottieRendering::PathCommandType::Close; + iterate(pathCommand); + } + } + };*/ + if (shading->stroke) { if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) { lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->stroke->shading.get(); @@ -354,7 +382,7 @@ static void drawLottieContentItem(std::shared_ptr paren dashPattern = shading->stroke->dashPattern; } - currentContext->strokePath(path, shading->stroke->lineWidth, lineJoin, lineCap, shading->stroke->dashPhase, dashPattern, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); + (*currentContext)->strokePath(iteratePaths, shading->stroke->lineWidth, lineJoin, lineCap, shading->stroke->dashPhase, dashPattern, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); } else if (shading->stroke->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) { //TODO:gradient stroke } @@ -378,7 +406,7 @@ static void drawLottieContentItem(std::shared_ptr paren if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Solid) { lottie::RenderTreeNodeContentItem::SolidShading *solidShading = (lottie::RenderTreeNodeContentItem::SolidShading *)shading->fill->shading.get(); if (solidShading->opacity != 0.0) { - currentContext->fillPath(path, rule, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); + (*currentContext)->fillPath(iteratePaths, rule, lottie::Color(solidShading->color.r, solidShading->color.g, solidShading->color.b, solidShading->color.a * solidShading->opacity * renderAlpha)); } } else if (shading->fill->shading->type() == lottie::RenderTreeNodeContentItem::ShadingType::Gradient) { lottie::RenderTreeNodeContentItem::GradientShading *gradientShading = (lottie::RenderTreeNodeContentItem::GradientShading *)shading->fill->shading.get(); @@ -397,11 +425,11 @@ static void drawLottieContentItem(std::shared_ptr paren switch (gradientShading->gradientType) { case lottie::GradientType::Linear: { - currentContext->linearGradientFillPath(path, rule, gradient, start, end); + (*currentContext)->linearGradientFillPath(iteratePaths, rule, gradient, start, end); break; } case lottie::GradientType::Radial: { - currentContext->radialGradientFillPath(path, rule, gradient, start, 0.0, start, start.distanceTo(end)); + (*currentContext)->radialGradientFillPath(iteratePaths, rule, gradient, start, 0.0, start, start.distanceTo(end)); break; } default: { @@ -415,7 +443,7 @@ static void drawLottieContentItem(std::shared_ptr paren for (auto it = item->subItems.rbegin(); it != item->subItems.rend(); it++) { const auto &subItem = *it; - drawLottieContentItem(currentContext, subItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext); + drawLottieContentItem(*currentContext, subItem, renderAlpha, globalSize, currentTransform, bezierPathsBoundingBoxContext); } if (tempContext) { @@ -430,7 +458,7 @@ static void drawLottieContentItem(std::shared_ptr paren parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr const &parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; @@ -470,7 +498,11 @@ static void renderLottieRenderNode(std::shared_ptr node, std::optional globalRect; if (needsTempContext) { - globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, parentTransform, false, bezierPathsBoundingBoxContext); + if (globalSize.x <= minGlobalRectCalculationSize && globalSize.y <= minGlobalRectCalculationSize) { + globalRect = lottie::CGRect(0.0, 0.0, globalSize.x, globalSize.y); + } else { + globalRect = lottie::getRenderNodeGlobalRect(node, globalSize, parentTransform, false, bezierPathsBoundingBoxContext); + } if (!globalRect || globalRect->width <= 0.0f || globalRect->height <= 0.0f) { parentContext->restoreState(); return; @@ -578,10 +610,6 @@ 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)); - if (!useReferenceRendering) { - return nil; - } - if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); @@ -594,12 +622,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return [[UIImage alloc] initWithCGImage:std::static_pointer_cast(image)->nativeImage()]; } else { - /*auto context = std::make_shared((int)size.width, (int)size.height); + //auto context = std::make_shared((int)size.width, (int)size.height); + auto context = std::make_shared((int)size.width, (int)size.height); CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); - renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), 1.0);*/ + renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); return nil; } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h index 39126f6036..2e822f7ffc 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h @@ -20,12 +20,12 @@ public: virtual void saveState() override; virtual void restoreState() override; - virtual void fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) override; - virtual void linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; - virtual void strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; - virtual void linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; - virtual void radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) override; + virtual void linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottieRendering::Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; + virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) override; + virtual void linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override; + virtual void radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override; virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; @@ -33,7 +33,6 @@ public: virtual void setAlpha(float alpha) override; virtual void concatenate(lottie::CATransform3D const &transform) override; - virtual lottie::CATransform3D currentTransform() override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm index a6561c587a..4573881392 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm @@ -4,22 +4,22 @@ namespace lottieRendering { namespace { -void tvgPath(std::shared_ptr const &path, tvg::Shape *shape) { - path->enumerate([shape](lottie::CGPathItem const &item) { - switch (item.type) { - case lottie::CGPathItem::Type::MoveTo: { - shape->moveTo(item.points[0].x, item.points[0].y); +void tvgPath(CanvasPathEnumerator const &enumeratePath, tvg::Shape *shape) { + enumeratePath([&](PathCommand const &command) { + switch (command.type) { + case PathCommandType::MoveTo: { + shape->moveTo(command.points[0].x, command.points[0].y); break; } - case lottie::CGPathItem::Type::LineTo: { - shape->lineTo(item.points[0].x, item.points[0].y); + case PathCommandType::LineTo: { + shape->lineTo(command.points[0].x, command.points[0].y); break; } - case lottie::CGPathItem::Type::CurveTo: { - shape->cubicTo(item.points[0].x, item.points[0].y, item.points[1].x, item.points[1].y, item.points[2].x, item.points[2].y); + case PathCommandType::CurveTo: { + shape->cubicTo(command.points[0].x, command.points[0].y, command.points[1].x, command.points[1].y, command.points[2].x, command.points[2].y); break; } - case lottie::CGPathItem::Type::Close: { + case PathCommandType::Close: { shape->close(); break; } @@ -89,9 +89,9 @@ void ThorVGCanvasImpl::restoreState() { _stateStack.pop_back(); } -void ThorVGCanvasImpl::fillPath(std::shared_ptr const &path, lottie::FillRule fillRule, lottie::Color const &color) { +void ThorVGCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) { auto shape = tvg::Shape::gen(); - tvgPath(path, shape.get()); + tvgPath(enumeratePath, shape.get()); shape->transform(tvgTransform(_transform)); @@ -101,9 +101,9 @@ void ThorVGCanvasImpl::fillPath(std::shared_ptr const &path, lot _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::linearGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { +void ThorVGCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { auto shape = tvg::Shape::gen(); - tvgPath(path, shape.get()); + tvgPath(enumeratePath, shape.get()); shape->transform(tvgTransform(_transform)); @@ -129,9 +129,9 @@ void ThorVGCanvasImpl::linearGradientFillPath(std::shared_ptr co _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::radialGradientFillPath(std::shared_ptr const &path, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { +void ThorVGCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { auto shape = tvg::Shape::gen(); - tvgPath(path, shape.get()); + tvgPath(enumeratePath, shape.get()); shape->transform(tvgTransform(_transform)); @@ -157,9 +157,9 @@ void ThorVGCanvasImpl::radialGradientFillPath(std::shared_ptr co _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { +void ThorVGCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, lottie::Color const &color) { auto shape = tvg::Shape::gen(); - tvgPath(path, shape.get()); + tvgPath(enumeratePath, shape.get()); shape->transform(tvgTransform(_transform)); @@ -217,12 +217,10 @@ void ThorVGCanvasImpl::strokePath(std::shared_ptr const &path, f _canvas->push(std::move(shape)); } -void ThorVGCanvasImpl::linearGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { - assert(false); +void ThorVGCanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { } -void ThorVGCanvasImpl::radialGradientStrokePath(std::shared_ptr const &path, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { - assert(false); +void ThorVGCanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { } void ThorVGCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { @@ -271,10 +269,6 @@ void ThorVGCanvasImpl::concatenate(lottie::CATransform3D const &transform) { ));*/ } -lottie::CATransform3D ThorVGCanvasImpl::currentTransform() { - return _transform; -} - void ThorVGCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { /*ThorVGCanvasImpl *impl = (ThorVGCanvasImpl *)other.get(); auto image = impl->surface()->makeImageSnapshot(); diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index 47603b86ca..2efe5e5fbf 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 = "4986037051573928320.json" + //continueFromName = "35707580709863498.json" let _ = await processAnimationFolderAsync(basePath: bundlePath, path: "", stopOnFailure: true, process: { path, name, alwaysDraw in if let continueFromNameValue = continueFromName { @@ -119,7 +119,7 @@ public final class ViewController: UIViewController { self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if "".isEmpty { + if !"".isEmpty { if #available(iOS 13.0, *) { self.test = ReferenceCompareTest(view: self.view) } From 59d8d311d428bbcf16ac179a234eccaf5d2313ca Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 02:11:00 +0400 Subject: [PATCH 27/32] Refactoring --- .../SoftwareLottieRenderer/Sources/Canvas.h | 2 +- .../Sources/CoreGraphicsCanvasImpl.h | 2 +- .../Sources/CoreGraphicsCanvasImpl.mm | 2 +- .../Sources/NullCanvasImpl.h | 4 +- .../Sources/NullCanvasImpl.mm | 4 +- .../Sources/SoftwareLottieRenderer.mm | 48 ++++++++--------- .../Sources/ThorVGCanvasImpl.h | 6 +-- .../Sources/ThorVGCanvasImpl.mm | 6 +-- .../Sources/ViewController.swift | 2 +- .../PublicHeaders/LottieCpp/BezierPath.h | 2 +- .../PublicHeaders/LottieCpp/CGPath.h | 4 +- .../PublicHeaders/LottieCpp/CGPathCocoa.h | 2 +- .../PublicHeaders/LottieCpp/CurveVertex.h | 2 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 8 +-- .../PublicHeaders/LottieCpp/Vectors.h | 54 +++++++++---------- .../PublicHeaders/LottieCpp/VectorsCocoa.h | 4 +- .../CompLayers/PreCompositionLayer.hpp | 6 +-- .../CompLayers/ShapeCompositionLayer.cpp | 26 ++++----- .../MainThreadAnimationLayer.hpp | 2 +- .../Utility/LayerTransformNode.hpp | 8 +-- .../Nodes/Text/TextAnimatorNode.hpp | 12 ++--- .../Private/Utility/Primitives/BezierPath.cpp | 4 +- .../Lottie/Public/Primitives/CALayer.hpp | 6 +-- .../Lottie/Public/Primitives/CGPath.cpp | 8 +-- .../Lottie/Public/Primitives/CGPath.mm | 2 +- .../Lottie/Public/Primitives/Vectors.mm | 28 +++++----- 26 files changed, 127 insertions(+), 127 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h index 9eb9bc90d6..25fbffc6da 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h @@ -81,7 +81,7 @@ public: virtual void setAlpha(float alpha) = 0; - virtual void concatenate(lottie::CATransform3D const &transform) = 0; + virtual void concatenate(lottie::Transform3D const &transform) = 0; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) = 0; }; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h index 3a4176ba0d..b7139a8e8f 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h @@ -40,7 +40,7 @@ public: virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::CATransform3D const &transform) override; + virtual void concatenate(lottie::Transform3D const &transform) override; virtual std::shared_ptr makeImage() const; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm index d9fd69f65c..ef9089971f 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm @@ -509,7 +509,7 @@ void CanvasImpl::setAlpha(float alpha) { CGContextSetAlpha(_context, alpha); } -void CanvasImpl::concatenate(lottie::CATransform3D const &transform) { +void CanvasImpl::concatenate(lottie::Transform3D const &transform) { CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform))); } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h index 92e930e316..faba71da5a 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h @@ -30,7 +30,7 @@ public: virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::CATransform3D const &transform) override; + virtual void concatenate(lottie::Transform3D const &transform) override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; @@ -39,7 +39,7 @@ public: private: float _width = 0.0f; float _height = 0.0f; - lottie::CATransform3D _transform; + lottie::Transform3D _transform; }; } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm index 29d14e03ed..1b2cb78bfd 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm @@ -12,7 +12,7 @@ void addEnumeratedPath(CanvasPathEnumerator const &enumeratePath) { } NullCanvasImpl::NullCanvasImpl(int width, int height) : -_width(width), _height(height), _transform(lottie::CATransform3D::identity()) { +_width(width), _height(height), _transform(lottie::Transform3D::identity()) { } NullCanvasImpl::~NullCanvasImpl() { @@ -69,7 +69,7 @@ void NullCanvasImpl::setBlendMode(BlendMode blendMode) { void NullCanvasImpl::setAlpha(float alpha) { } -void NullCanvasImpl::concatenate(lottie::CATransform3D const &transform) { +void NullCanvasImpl::concatenate(lottie::Transform3D const &transform) { } void NullCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index aa4005b252..def813ef48 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -15,17 +15,17 @@ static constexpr float minGlobalRectCalculationSize = 200.0f; struct TransformedPath { lottie::BezierPath path; - lottie::CATransform3D transform; + lottie::Transform3D transform; - TransformedPath(lottie::BezierPath const &path_, lottie::CATransform3D const &transform_) : + TransformedPath(lottie::BezierPath const &path_, lottie::Transform3D const &transform_) : path(path_), transform(transform_) { } }; -static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { //TODO:remove skipApplyTransform - lottie::CATransform3D effectiveTransform = parentTransform; + lottie::Transform3D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { effectiveTransform = item->transform * effectiveTransform; } @@ -56,9 +56,9 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::CATransform3D const &parentTransform, bool skipApplyTransform, std::function const &onPath) { +static void enumeratePaths(std::shared_ptr item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, std::function const &onPath) { //TODO:remove skipApplyTransform - lottie::CATransform3D effectiveTransform = parentTransform; + lottie::Transform3D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { effectiveTransform = item->transform * effectiveTransform; } @@ -80,14 +80,14 @@ static void enumeratePaths(std::shared_ptr it namespace lottie { -static std::optional getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; - CATransform3D localTransform = contentItem->transform; + Transform3D localTransform = contentItem->transform; currentTransform = localTransform * currentTransform; std::optional globalRect; for (const auto &shadingVariant : contentItem->shadings) { - lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::CATransform3D::identity(), true, bezierPathsBoundingBoxContext); + lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::Transform3D::identity(), true, bezierPathsBoundingBoxContext); if (shadingVariant->stroke) { shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); @@ -128,14 +128,14 @@ static std::optional getRenderContentItemGlobalRect(std::shared_ptr getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (node->isHidden() || node->alpha() < minVisibleAlpha) { return std::nullopt; } auto currentTransform = parentTransform; Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y); - CATransform3D localTransform = node->transform(); + Transform3D localTransform = node->transform(); localTransform = localTransform.translated(localTranslation); currentTransform = localTransform * currentTransform; @@ -181,9 +181,9 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptr const &parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void drawLottieContentItem(std::shared_ptr const &parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; - lottie::CATransform3D localTransform = item->transform; + lottie::Transform3D localTransform = item->transform; currentTransform = localTransform * currentTransform; float normalizedOpacity = item->alpha; @@ -217,7 +217,7 @@ static void drawLottieContentItem(std::shared_ptr const tempContext = tempContextValue; currentContext = &tempContext; - (*currentContext)->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + (*currentContext)->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); (*currentContext)->saveState(); (*currentContext)->concatenate(currentTransform); @@ -270,7 +270,7 @@ static void drawLottieContentItem(std::shared_ptr const }; } else { iteratePaths = [&](std::function iterate) { - enumeratePaths(item, shading->subItemLimit, lottie::CATransform3D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::CATransform3D const &transform) { + enumeratePaths(item, shading->subItemLimit, lottie::Transform3D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform3D const &transform) { auto path = sourcePath.copyUsingTransform(transform); lottieRendering::PathCommand pathCommand; @@ -458,7 +458,7 @@ static void drawLottieContentItem(std::shared_ptr const parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr const &parentContext, lottie::Vector2D const &globalSize, lottie::CATransform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr const &parentContext, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; @@ -468,7 +468,7 @@ static void renderLottieRenderNode(std::shared_ptr node, auto currentTransform = parentTransform; lottie::Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y); - lottie::CATransform3D localTransform = node->transform(); + lottie::Transform3D localTransform = node->transform(); localTransform = localTransform.translated(localTranslation); currentTransform = localTransform * currentTransform; @@ -511,7 +511,7 @@ static void renderLottieRenderNode(std::shared_ptr node, if ((node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) || 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(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); maskBackingStorage->concatenate(currentTransform); if (masksToBounds) { @@ -528,7 +528,7 @@ static void renderLottieRenderNode(std::shared_ptr node, tempContext = tempContextValue; currentContext = tempContextValue; - currentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + currentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); currentContext->concatenate(currentTransform); @@ -536,8 +536,8 @@ static void renderLottieRenderNode(std::shared_ptr node, currentContext = parentContext; } - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(node->position().x, node->position().y))); - parentContext->concatenate(lottie::CATransform3D::identity().translated(lottie::Vector2D(-node->bounds().x, -node->bounds().y))); + parentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(node->position().x, node->position().y))); + parentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-node->bounds().x, -node->bounds().y))); parentContext->concatenate(node->transform()); float renderAlpha = 1.0; @@ -608,13 +608,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return nil; } - lottie::CATransform3D rootTransform = lottie::CATransform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height)); + lottie::Transform3D rootTransform = lottie::Transform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height)); if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); - context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); + context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0)); renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); @@ -626,7 +626,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { auto context = std::make_shared((int)size.width, (int)size.height); CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); - context->concatenate(lottie::CATransform3D::makeScale(scale.x, scale.y, 1.0)); + context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0)); renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h index 2e822f7ffc..b3550f2247 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h @@ -32,7 +32,7 @@ public: virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::CATransform3D const &transform) override; + virtual void concatenate(lottie::Transform3D const &transform) override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; @@ -52,8 +52,8 @@ private: std::unique_ptr _canvas; float _alpha = 1.0; - lottie::CATransform3D _transform; - std::vector _stateStack; + lottie::Transform3D _transform; + std::vector _stateStack; int _bytesPerRow = 0; uint32_t *_backingData = nullptr; int _statsNumStrokes = 0; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm index 4573881392..bd8e3d9b6f 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm @@ -27,7 +27,7 @@ void tvgPath(CanvasPathEnumerator const &enumeratePath, tvg::Shape *shape) { }); } -tvg::Matrix tvgTransform(lottie::CATransform3D const &transform) { +tvg::Matrix tvgTransform(lottie::Transform3D const &transform) { CGAffineTransform affineTransform = CATransform3DGetAffineTransform(lottie::nativeTransform(transform)); tvg::Matrix result; result.e11 = affineTransform.a; @@ -45,7 +45,7 @@ tvg::Matrix tvgTransform(lottie::CATransform3D const &transform) { } ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height) : -_width(width), _height(height), _transform(lottie::CATransform3D::identity()) { +_width(width), _height(height), _transform(lottie::Transform3D::identity()) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ tvg::Initializer::init(0); @@ -259,7 +259,7 @@ void ThorVGCanvasImpl::setAlpha(float alpha) { _alpha = alpha; } -void ThorVGCanvasImpl::concatenate(lottie::CATransform3D const &transform) { +void ThorVGCanvasImpl::concatenate(lottie::Transform3D const &transform) { _transform = transform * _transform; /*_canvas->concat(SkM44( transform.m11, transform.m21, transform.m31, transform.m41, diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index 2efe5e5fbf..5bb4863b6a 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -119,7 +119,7 @@ public final class ViewController: UIViewController { self.view.layer.addSublayer(MetalEngine.shared.rootLayer) - if !"".isEmpty { + if "".isEmpty { if #available(iOS 13.0, *) { self.test = ReferenceCompareTest(view: self.view) } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h index 7fa0b83ffe..2fd05533e4 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h @@ -122,7 +122,7 @@ public: std::optional const &closed() const; void setClosed(std::optional const &closed); std::shared_ptr cgPath() const; - BezierPath copyUsingTransform(CATransform3D const &transform) const; + BezierPath copyUsingTransform(Transform3D const &transform) const; public: BezierPath(std::shared_ptr contents); diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h index d7e445e635..4288688ab8 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h @@ -57,7 +57,7 @@ public: virtual bool empty() const = 0; - virtual std::shared_ptr copyUsingTransform(CATransform3D const &transform) const = 0; + virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const = 0; virtual void addLineTo(Vector2D const &point) = 0; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) = 0; @@ -71,7 +71,7 @@ public: virtual bool isEqual(CGPath *other) const = 0; }; -Vector2D transformVector(Vector2D const &v, CATransform3D const &m); +Vector2D transformVector(Vector2D const &v, Transform3D const &m); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h index bf80eea07b..5626825a62 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h @@ -22,7 +22,7 @@ public: virtual bool empty() const override; - virtual std::shared_ptr copyUsingTransform(CATransform3D const &transform) const override; + virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const override; virtual void addLineTo(Vector2D const &point) override; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h index 5b15a6eeab..4f5a8a28d5 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h @@ -64,7 +64,7 @@ public: return CurveVertex(point + translation, inTangent + translation, outTangent + translation, false); } - CurveVertex transformed(CATransform3D const &transform) const { + CurveVertex transformed(Transform3D const &transform) const { return CurveVertex(transformVector(point, transform), transformVector(inTangent, transform), transformVector(outTangent, transform), false); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 5ca3ed4bae..5dec74814f 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -364,7 +364,7 @@ public: public: bool isGroup = false; - CATransform3D transform = CATransform3D::identity(); + Transform3D transform = Transform3D::identity(); float alpha = 0.0; std::optional trimParams; std::shared_ptr path; @@ -393,7 +393,7 @@ public: RenderTreeNode( CGRect bounds_, Vector2D position_, - CATransform3D transform_, + Transform3D transform_, float alpha_, bool masksToBounds_, bool isHidden_, @@ -427,7 +427,7 @@ public: return _position; } - CATransform3D const &transform() const { + Transform3D const &transform() const { return _transform; } @@ -458,7 +458,7 @@ public: public: CGRect _bounds; Vector2D _position; - CATransform3D _transform = CATransform3D::identity(); + Transform3D _transform = Transform3D::identity(); float _alpha = 1.0f; bool _masksToBounds = false; bool _isHidden = false; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 4f42fe9407..14029225d0 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -148,13 +148,13 @@ inline float radiansToDegrees(float value) { return value * 180.0f / M_PI; } -struct CATransform3D { +struct Transform3D { float m11, m12, m13, m14; float m21, m22, m23, m24; float m31, m32, m33, m34; float m41, m42, m43, m44; - CATransform3D( + Transform3D( float m11_, float m12_, float m13_, float m14_, float m21_, float m22_, float m23_, float m24_, float m31_, float m32_, float m33_, float m34_, @@ -166,14 +166,14 @@ struct CATransform3D { m41(m41_), m42(m42_), m43(m43_), m44(m44_) { } - bool operator==(CATransform3D const &rhs) const { + bool operator==(Transform3D const &rhs) const { return m11 == rhs.m11 && m12 == rhs.m12 && m13 == rhs.m13 && m14 == rhs.m14 && m21 == rhs.m21 && m22 == rhs.m22 && m23 == rhs.m23 && m24 == rhs.m24 && m31 == rhs.m31 && m32 == rhs.m32 && m33 == rhs.m33 && m34 == rhs.m34 && m41 == rhs.m41 && m42 == rhs.m42 && m43 == rhs.m43 && m44 == rhs.m44; } - bool operator!=(CATransform3D const &rhs) const { + bool operator!=(Transform3D const &rhs) const { return !(*this == rhs); } @@ -184,8 +184,8 @@ struct CATransform3D { m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0; } - static CATransform3D makeTranslation(float tx, float ty, float tz) { - return CATransform3D( + static Transform3D makeTranslation(float tx, float ty, float tz) { + return Transform3D( 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, @@ -193,8 +193,8 @@ struct CATransform3D { ); } - static CATransform3D makeScale(float sx, float sy, float sz) { - return CATransform3D( + static Transform3D makeScale(float sx, float sy, float sz) { + return Transform3D( sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, @@ -202,14 +202,14 @@ struct CATransform3D { ); } - static CATransform3D makeRotation(float radians, float x, float y, float z); + static Transform3D makeRotation(float radians, float x, float y, float z); - static CATransform3D makeSkew(float skew, float skewAxis) { + static Transform3D makeSkew(float skew, float skewAxis) { float mCos = cos(degreesToRadians(skewAxis)); float mSin = sin(degreesToRadians(skewAxis)); float aTan = tan(degreesToRadians(skew)); - CATransform3D transform1( + Transform3D transform1( mCos, mSin, 0.0, @@ -228,7 +228,7 @@ struct CATransform3D { 1.0 ); - CATransform3D transform2( + Transform3D transform2( 1.0, 0.0, 0.0, @@ -247,7 +247,7 @@ struct CATransform3D { 1.0 ); - CATransform3D transform3( + Transform3D transform3( mCos, -mSin, 0.0, @@ -269,7 +269,7 @@ struct CATransform3D { return transform3 * transform2 * transform1; } - static CATransform3D makeTransform( + static Transform3D makeTransform( Vector2D const &anchor, Vector2D const &position, Vector2D const &scale, @@ -277,38 +277,38 @@ struct CATransform3D { std::optional skew, std::optional skewAxis ) { - CATransform3D result = CATransform3D::identity(); + Transform3D result = Transform3D::identity(); if (skew.has_value() && skewAxis.has_value()) { - result = CATransform3D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + result = Transform3D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); } else { - result = CATransform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + result = Transform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); } return result; } - CATransform3D rotated(float degrees) const; + Transform3D rotated(float degrees) const; - CATransform3D translated(Vector2D const &translation) const; + Transform3D translated(Vector2D const &translation) const; - CATransform3D scaled(Vector2D const &scale) const; + Transform3D scaled(Vector2D const &scale) const; - CATransform3D skewed(float skew, float skewAxis) const { - return CATransform3D::makeSkew(skew, skewAxis) * (*this); + Transform3D skewed(float skew, float skewAxis) const { + return Transform3D::makeSkew(skew, skewAxis) * (*this); } - static CATransform3D const &identity() { + static Transform3D const &identity() { return _identity; } - CATransform3D operator*(CATransform3D const &b) const; + Transform3D operator*(Transform3D const &b) const; bool isInvertible() const; - CATransform3D inverted() const; + Transform3D inverted() const; private: - static CATransform3D _identity; + static Transform3D _identity; }; struct CGRect { @@ -359,7 +359,7 @@ struct CGRect { CGRect intersection(CGRect const &other) const; CGRect unionWith(CGRect const &other) const; - CGRect applyingTransform(CATransform3D const &transform) const; + CGRect applyingTransform(Transform3D const &transform) const; }; inline bool isInRangeOrEqual(float value, float from, float to) { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h index d706e3643b..bf8083f45b 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h @@ -7,8 +7,8 @@ namespace lottie { -::CATransform3D nativeTransform(CATransform3D const &value); -CATransform3D fromNativeTransform(::CATransform3D const &value); +::CATransform3D nativeTransform(Transform3D const &value); +Transform3D fromNativeTransform(::CATransform3D const &value); } 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 f2047a4f34..e1538ec1a9 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 @@ -123,7 +123,7 @@ public: auto renderTreeContentItem = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, @@ -138,7 +138,7 @@ public: _contentsTreeNode = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, @@ -162,7 +162,7 @@ public: _renderTreeNode = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, 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 f6419d098d..7f3968b1c8 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 @@ -492,9 +492,9 @@ public: struct TransformedPath { BezierPath path; - CATransform3D transform; + Transform3D transform; - TransformedPath(BezierPath const &path_, CATransform3D const &transform_) : + TransformedPath(BezierPath const &path_, Transform3D const &transform_) : path(path_), transform(transform_) { } @@ -853,13 +853,13 @@ public: _opacityValue = 1.0; } - _transformValue = CATransform3D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y)); + _transformValue = Transform3D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y)); hasValidData = true; } } - CATransform3D const &transform() { + Transform3D const &transform() { return _transformValue; } @@ -878,7 +878,7 @@ public: std::unique_ptr> _skewAxis; std::unique_ptr> _opacity; - CATransform3D _transformValue = CATransform3D::identity(); + Transform3D _transformValue = Transform3D::identity(); float _opacityValue = 1.0; }; @@ -910,11 +910,11 @@ public: std::shared_ptr _contentItem; private: - std::vector collectPaths(size_t subItemLimit, CATransform3D const &parentTransform, bool skipApplyTransform) { + std::vector collectPaths(size_t subItemLimit, Transform3D const &parentTransform, bool skipApplyTransform) { std::vector mappedPaths; //TODO:remove skipApplyTransform - CATransform3D effectiveTransform = parentTransform; + Transform3D effectiveTransform = parentTransform; if (!skipApplyTransform && isGroup && transform) { effectiveTransform = transform->transform() * effectiveTransform; } @@ -942,7 +942,7 @@ public: } CompoundBezierPath trimmedPath = trimCompoundPath(tempPath, currentTrim->start, currentTrim->end, currentTrim->offset, currentTrim->type); for (auto &path : trimmedPath.paths) { - mappedPaths.emplace_back(path, CATransform3D::identity()); + mappedPaths.emplace_back(path, Transform3D::identity()); } } else { for (auto &path : subItemPaths) { @@ -1061,7 +1061,7 @@ public: } void updateContents(std::optional parentTrim) { - CATransform3D containerTransform = CATransform3D::identity(); + Transform3D containerTransform = Transform3D::identity(); float containerOpacity = 1.0; if (transform) { containerTransform = transform->transform(); @@ -1089,7 +1089,7 @@ public: if (parentTrim) { CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true); + auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true); for (const auto &path : paths) { compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } @@ -1104,7 +1104,7 @@ public: } else { if (hasTrims()) { CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, CATransform3D::identity(), true); + auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true); for (const auto &path : paths) { compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } @@ -1325,7 +1325,7 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath _contentRenderTreeNode = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, @@ -1351,7 +1351,7 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath _renderTreeNode = std::make_shared( CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, 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 5a8467e0b0..2d080d80d6 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 @@ -239,7 +239,7 @@ public: _renderTreeNode = std::make_shared( bounds(), position(), - CATransform3D::identity(), + Transform3D::identity(), 1.0, false, false, diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp index d064a16782..79c9950b2a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp @@ -157,7 +157,7 @@ public: Vector3D anchor = _transformProperties->anchor()->value(); Vector3D scale = _transformProperties->scale()->value(); - _localTransform = CATransform3D::makeTransform( + _localTransform = Transform3D::makeTransform( Vector2D(anchor.x, anchor.y), position, Vector2D(scale.x, scale.y), @@ -181,7 +181,7 @@ public: return _opacity; } - CATransform3D const &globalTransform() { + Transform3D const &globalTransform() { return _globalTransform; } @@ -191,8 +191,8 @@ private: std::shared_ptr _transformProperties; float _opacity = 1.0; - CATransform3D _localTransform = CATransform3D::identity(); - CATransform3D _globalTransform = CATransform3D::identity(); + Transform3D _localTransform = Transform3D::identity(); + Transform3D _globalTransform = Transform3D::identity(); public: virtual LayerTransformNode *asLayerTransformNode() override { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp index d5f6e2556a..dc252f6102 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp @@ -93,7 +93,7 @@ public: return _childKeypaths; } - CATransform3D caTransform() { + Transform3D caTransform() { Vector2D anchor = Vector2D::Zero(); if (_anchor) { auto anchor3d = _anchor->value(); @@ -126,7 +126,7 @@ public: skewAxis = _skewAxis->value().value; } - return CATransform3D::makeTransform( + return Transform3D::makeTransform( anchor, position, scale, @@ -212,16 +212,16 @@ public: return _parentTextNode; } - CATransform3D xform() { + Transform3D xform() { if (_xform.has_value()) { return _xform.value(); } else if (_parentTextNode) { return _parentTextNode->xform(); } else { - return CATransform3D::identity(); + return Transform3D::identity(); } } - void setXform(CATransform3D const &xform) { + void setXform(Transform3D const &xform) { _xform = xform; } @@ -312,7 +312,7 @@ private: std::shared_ptr _outputPath; - std::optional _xform; + std::optional _xform; std::optional _opacity; std::optional _strokeColor; std::optional _fillColor; 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 44240211e7..2ab088e42a 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 @@ -486,8 +486,8 @@ std::shared_ptr BezierPath::cgPath() const { return _contents->cgPath(); } -BezierPath BezierPath::copyUsingTransform(CATransform3D const &transform) const { - if (transform == CATransform3D::identity()) { +BezierPath BezierPath::copyUsingTransform(Transform3D const &transform) const { + if (transform == Transform3D::identity()) { return (*this); } BezierPath result; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp index dbc7006416..5a26454a94 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp @@ -89,10 +89,10 @@ public: return bounds(); } - CATransform3D const &transform() const { + Transform3D const &transform() const { return _transform; } - void setTransform(CATransform3D const &transform) { + void setTransform(Transform3D const &transform) { _transform = transform; } @@ -145,7 +145,7 @@ private: float _opacity = 1.0; Vector2D _position = Vector2D(0.0, 0.0); CGRect _bounds = CGRect(0.0, 0.0, 0.0, 0.0); - CATransform3D _transform = CATransform3D::identity(); + Transform3D _transform = Transform3D::identity(); std::shared_ptr _mask; bool _masksToBounds = false; std::optional _compositingFilter; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp index 9b174bed67..d1cf338111 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp @@ -35,7 +35,7 @@ void addPointToBoundingRect(bool *isFirst, CGRect *rect, Vector2D const *point) } -Vector2D transformVector(Vector2D const &v, CATransform3D const &m) { +Vector2D transformVector(Vector2D const &v, Transform3D const &m) { return Vector2D( m.m11 * v.x + m.m21 * v.y + m.m41 * 1.0, m.m12 * v.x + m.m22 * v.y + m.m42 * 1.0 @@ -51,7 +51,7 @@ public: virtual bool empty() const override; - virtual std::shared_ptr copyUsingTransform(CATransform3D const &transform) const override; + virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const override; virtual void addLineTo(Vector2D const &point) override; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override; @@ -108,10 +108,10 @@ bool CGPathImpl::empty() const { return _items.empty(); } -std::shared_ptr CGPathImpl::copyUsingTransform(CATransform3D const &transform) const { +std::shared_ptr CGPathImpl::copyUsingTransform(Transform3D const &transform) const { auto result = std::make_shared(); - if (transform == CATransform3D::identity()) { + if (transform == Transform3D::identity()) { result->_items = _items; return result; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm index e80342007f..ab13e06922 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm @@ -92,7 +92,7 @@ bool CGPathCocoaImpl::empty() const { return CGPathIsEmpty(_path); } -std::shared_ptr CGPathCocoaImpl::copyUsingTransform(CATransform3D const &transform) const { +std::shared_ptr CGPathCocoaImpl::copyUsingTransform(Transform3D const &transform) const { ::CATransform3D nativeTransform; nativeTransform.m11 = transform.m11; nativeTransform.m12 = transform.m12; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 277f8f5d69..b6f8276bf7 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -140,7 +140,7 @@ lottiejson11::Json Vector3D::toJson() const { return lottiejson11::Json(result); } -CATransform3D CATransform3D::_identity = CATransform3D( +Transform3D Transform3D::_identity = Transform3D( 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, @@ -415,7 +415,7 @@ Vector2D Vector2D::interpolate( return point.point; } -::CATransform3D nativeTransform(CATransform3D const &value) { +::CATransform3D nativeTransform(Transform3D const &value) { ::CATransform3D result; result.m11 = value.m11; @@ -441,8 +441,8 @@ Vector2D Vector2D::interpolate( return result; } -CATransform3D fromNativeTransform(::CATransform3D const &value) { - CATransform3D result = CATransform3D::identity(); +Transform3D fromNativeTransform(::CATransform3D const &value) { + Transform3D result = Transform3D::identity(); result.m11 = value.m11; result.m12 = value.m12; @@ -467,7 +467,7 @@ CATransform3D fromNativeTransform(::CATransform3D const &value) { return result; } -CATransform3D CATransform3D::makeRotation(float radians, float x, float y, float z) { +Transform3D Transform3D::makeRotation(float radians, float x, float y, float z) { return fromNativeTransform(CATransform3DMakeRotation(radians, x, y, z)); /*if (x == 0.0 && y == 0.0 && z == 0.0) { @@ -505,21 +505,21 @@ CATransform3D CATransform3D::makeRotation(float radians, float x, float y, float return returnValue;*/ } -CATransform3D CATransform3D::rotated(float degrees) const { +Transform3D Transform3D::rotated(float degrees) const { return fromNativeTransform(CATransform3DRotate(nativeTransform(*this), degreesToRadians(degrees), 0.0, 0.0, 1.0)); //return CATransform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this); } -CATransform3D CATransform3D::translated(Vector2D const &translation) const { +Transform3D Transform3D::translated(Vector2D const &translation) const { return fromNativeTransform(CATransform3DTranslate(nativeTransform(*this), translation.x, translation.y, 0.0)); } -CATransform3D CATransform3D::scaled(Vector2D const &scale) const { +Transform3D Transform3D::scaled(Vector2D const &scale) const { return fromNativeTransform(CATransform3DScale(nativeTransform(*this), scale.x, scale.y, 1.0)); //return CATransform3D::makeScale(scale.x, scale.y, 1.0) * (*this); } -CATransform3D CATransform3D::operator*(CATransform3D const &b) const { +Transform3D Transform3D::operator*(Transform3D const &b) const { if (isIdentity()) { return b; } @@ -541,7 +541,7 @@ CATransform3D CATransform3D::operator*(CATransform3D const &b) const { }; simd_float4x4 simdResult = simd_mul(simdRhs, simdLhs); - return CATransform3D( + return Transform3D( simdResult.columns[0][0], simdResult.columns[1][0], simdResult.columns[2][0], simdResult.columns[3][0], simdResult.columns[0][1], simdResult.columns[1][1], simdResult.columns[2][1], simdResult.columns[3][1], simdResult.columns[0][2], simdResult.columns[1][2], simdResult.columns[2][2], simdResult.columns[3][2], @@ -549,11 +549,11 @@ CATransform3D CATransform3D::operator*(CATransform3D const &b) const { ); } -bool CATransform3D::isInvertible() const { +bool Transform3D::isInvertible() const { return std::abs(m11 * m22 - m12 * m21) >= 0.00000001; } -CATransform3D CATransform3D::inverted() const { +Transform3D Transform3D::inverted() const { simd_float4x4 matrix = { simd_make_float4(m11, m21, m31, m41), simd_make_float4(m12, m22, m32, m42), @@ -561,7 +561,7 @@ CATransform3D CATransform3D::inverted() const { simd_make_float4(m14, m24, m34, m44) }; simd_float4x4 result = simd_inverse(matrix); - CATransform3D nativeResult = CATransform3D( + Transform3D nativeResult = Transform3D( result.columns[0][0], result.columns[1][0], result.columns[2][0], result.columns[3][0], result.columns[0][1], result.columns[1][1], result.columns[2][1], result.columns[3][1], result.columns[0][2], result.columns[1][2], result.columns[2][2], result.columns[3][2], @@ -589,7 +589,7 @@ CGRect CGRect::unionWith(CGRect const &other) const { return CGRect(result.origin.x, result.origin.y, result.size.width, result.size.height); } -CGRect CGRect::applyingTransform(CATransform3D const &transform) const { +CGRect CGRect::applyingTransform(Transform3D const &transform) const { if (transform.isIdentity()) { return *this; } From 62d58b3bddf1deac0caef0d8ad950276bc37578d Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 02:36:08 +0400 Subject: [PATCH 28/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 18 +++---- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 17 ++---- .../CompLayers/CompositionLayer.cpp | 2 +- .../CompLayers/CompositionLayer.hpp | 2 +- .../CompLayers/PreCompositionLayer.hpp | 15 ++---- .../CompLayers/ShapeCompositionLayer.cpp | 8 +-- .../MainThreadAnimationLayer.hpp | 7 ++- .../Lottie/Public/Primitives/CALayer.hpp | 53 ++----------------- .../Lottie/Public/Primitives/Vectors.mm | 50 ++++++++--------- 9 files changed, 51 insertions(+), 121 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index def813ef48..25fbb49c66 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -134,9 +134,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptrposition().x + -node->bounds().x, node->position().y + -node->bounds().y); Transform3D localTransform = node->transform(); - localTransform = localTransform.translated(localTranslation); currentTransform = localTransform * currentTransform; std::optional globalRect; @@ -145,7 +143,7 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptrbounds().applyingTransform(currentTransform); + CGRect globalBounds = CGRect(0.0f, 0.0f, node->size().x, node->size().y).applyingTransform(currentTransform); if (globalRect) { globalRect = globalRect->unionWith(globalBounds); } else { @@ -467,9 +465,7 @@ static void renderLottieRenderNode(std::shared_ptr node, } auto currentTransform = parentTransform; - lottie::Vector2D localTranslation(node->position().x + -node->bounds().x, node->position().y + -node->bounds().y); lottie::Transform3D localTransform = node->transform(); - localTransform = localTransform.translated(localTranslation); currentTransform = localTransform * currentTransform; std::shared_ptr maskContext; @@ -478,7 +474,7 @@ static void renderLottieRenderNode(std::shared_ptr node, bool masksToBounds = node->masksToBounds(); if (masksToBounds) { - lottie::CGRect effectiveGlobalBounds = node->bounds().applyingTransform(currentTransform); + lottie::CGRect effectiveGlobalBounds = lottie::CGRect(0.0f, 0.0f, node->size().x, node->size().y).applyingTransform(currentTransform); if (effectiveGlobalBounds.width <= 0.0f || effectiveGlobalBounds.height <= 0.0f) { return; } @@ -515,7 +511,7 @@ static void renderLottieRenderNode(std::shared_ptr node, maskBackingStorage->concatenate(currentTransform); 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)); + maskBackingStorage->fill(lottie::CGRect(0.0f, 0.0f, node->size().x, node->size().y), lottie::Color(1.0f, 1.0f, 1.0f, 1.0f)); } if (node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) { renderLottieRenderNode(node->mask(), maskBackingStorage, globalSize, currentTransform, 1.0, node->invertMask(), bezierPathsBoundingBoxContext); @@ -536,13 +532,11 @@ static void renderLottieRenderNode(std::shared_ptr node, currentContext = parentContext; } - parentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(node->position().x, node->position().y))); - parentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-node->bounds().x, -node->bounds().y))); parentContext->concatenate(node->transform()); - float renderAlpha = 1.0; + float renderAlpha = 1.0f; if (tempContext) { - renderAlpha = 1.0; + renderAlpha = 1.0f; } else { renderAlpha = layerAlpha; } @@ -552,7 +546,7 @@ static void renderLottieRenderNode(std::shared_ptr node, } if (isInvertedMatte) { - 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)); + currentContext->fill(lottie::CGRect(0.0f, 0.0f, node->size().x, node->size().y), lottie::Color(0.0f, 0.0f, 0.0f, 1.0f)); currentContext->setBlendMode(lottieRendering::BlendMode::DestinationOut); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 5dec74814f..9d08581009 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -391,8 +391,7 @@ public: class RenderTreeNode { public: RenderTreeNode( - CGRect bounds_, - Vector2D position_, + Vector2D size_, Transform3D transform_, float alpha_, bool masksToBounds_, @@ -401,8 +400,7 @@ public: std::shared_ptr mask_, bool invertMask_ ) : - _bounds(bounds_), - _position(position_), + _size(size_), _transform(transform_), _alpha(alpha_), _masksToBounds(masksToBounds_), @@ -419,12 +417,8 @@ public: } public: - CGRect const &bounds() const { - return _bounds; - } - - Vector2D const &position() const { - return _position; + Vector2D const &size() const { + return _size; } Transform3D const &transform() const { @@ -456,8 +450,7 @@ public: } public: - CGRect _bounds; - Vector2D _position; + Vector2D _size; Transform3D _transform = Transform3D::identity(); float _alpha = 1.0f; bool _masksToBounds = false; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp index 1a87942244..53f850f090 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/CompLayers/CompositionLayer.cpp @@ -4,7 +4,7 @@ namespace lottie { InvertedMatteLayer::InvertedMatteLayer(std::shared_ptr inputMatte) : _inputMatte(inputMatte) { - setBounds(inputMatte->bounds()); + setSize(inputMatte->size()); addSublayer(_inputMatte); } 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 13e275dfdd..e82b9c8b33 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 @@ -57,7 +57,7 @@ public: _childKeypaths.push_back(_transformNode->transformProperties()); - _contentsLayer->setBounds(CGRect(0.0, 0.0, size.x, size.y)); + _contentsLayer->setSize(size); if (layer->blendMode.has_value() && layer->blendMode.value() != BlendMode::Normal) { setCompositingFilter(layer->blendMode); 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 e1538ec1a9..af69491fa3 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 @@ -30,9 +30,9 @@ public: } _frameRate = frameRate; - setBounds(CGRect(0.0, 0.0, precomp->width, precomp->height)); + setSize(Vector2D(precomp->width, precomp->height)); contentsLayer()->setMasksToBounds(true); - contentsLayer()->setBounds(bounds()); + contentsLayer()->setSize(size()); auto layers = initializeCompositionLayers( asset.layers, @@ -49,7 +49,7 @@ public: for (auto layerIt = layers.rbegin(); layerIt != layers.rend(); layerIt++) { std::shared_ptr layer = *layerIt; - layer->setBounds(bounds()); + layer->setSize(size()); _animationLayers.push_back(layer); if (layer->isImageCompositionLayer()) { @@ -121,7 +121,6 @@ public: std::vector> renderTreeValue; auto renderTreeContentItem = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), Transform3D::identity(), 1.0, @@ -136,7 +135,6 @@ public: } _contentsTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), Transform3D::identity(), 1.0, @@ -160,7 +158,6 @@ public: } _renderTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), Transform3D::identity(), 1.0, @@ -172,12 +169,10 @@ public: ); } - _contentsTreeNode->_bounds = _contentsLayer->bounds(); - _contentsTreeNode->_position = _contentsLayer->position(); + _contentsTreeNode->_size = _contentsLayer->size(); _contentsTreeNode->_masksToBounds = _contentsLayer->masksToBounds(); - _renderTreeNode->_bounds = bounds(); - _renderTreeNode->_position = position(); + _renderTreeNode->_size = size(); _renderTreeNode->_transform = transform(); _renderTreeNode->_alpha = opacity(); _renderTreeNode->_masksToBounds = masksToBounds(); 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 7f3968b1c8..19f244e0ee 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 @@ -1323,7 +1323,6 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath if (!_renderTreeNode) { _contentRenderTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), Transform3D::identity(), 1.0, @@ -1349,7 +1348,6 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath } _renderTreeNode = std::make_shared( - CGRect(0.0, 0.0, 0.0, 0.0), Vector2D(0.0, 0.0), Transform3D::identity(), 1.0, @@ -1361,14 +1359,12 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath ); } - _contentRenderTreeNode->_bounds = _contentsLayer->bounds(); - _contentRenderTreeNode->_position = _contentsLayer->position(); + _contentRenderTreeNode->_size = _contentsLayer->size(); _contentRenderTreeNode->_masksToBounds = _contentsLayer->masksToBounds(); _renderTreeNode->_masksToBounds = masksToBounds(); - _renderTreeNode->_bounds = bounds(); - _renderTreeNode->_position = position(); + _renderTreeNode->_size = size(); return _renderTreeNode; } 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 2d080d80d6..b8b288307f 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 @@ -42,7 +42,7 @@ public: _layerTextProvider = std::make_shared(textProvider); _layerFontProvider = std::make_shared(fontProvider); - setBounds(CGRect(0.0, 0.0, animation.width, animation.height)); + setSize(Vector2D(animation.width, animation.height)); auto layers = initializeCompositionLayers( animation.layers, @@ -60,7 +60,7 @@ public: for (auto layerIt = layers.rbegin(); layerIt != layers.rend(); layerIt++) { std::shared_ptr const &layer = *layerIt; - layer->setBounds(bounds()); + layer->setSize(size()); _animationLayers.push_back(layer); if (layer->isImageCompositionLayer()) { @@ -237,8 +237,7 @@ public: } } _renderTreeNode = std::make_shared( - bounds(), - position(), + size(), Transform3D::identity(), 1.0, false, diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp index 5a26454a94..2c40c5667e 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp @@ -24,27 +24,13 @@ public: virtual ~CALayer() = default; void addSublayer(std::shared_ptr layer) { - if (layer->_superlayer) { - layer->_superlayer->removeSublayer(layer.get()); - } - layer->_superlayer = this; _sublayers.push_back(layer); } void insertSublayer(std::shared_ptr layer, int index) { - if (layer->_superlayer) { - layer->_superlayer->removeSublayer(layer.get()); - } - layer->_superlayer = this; _sublayers.insert(_sublayers.begin() + index, layer); } - void removeFromSuperlayer() { - if (_superlayer) { - _superlayer->removeSublayer(this); - } - } - virtual bool implementsDraw() const { return false; } @@ -71,22 +57,11 @@ public: _opacity = opacity; } - Vector2D const &position() const { - return _position; + Vector2D const &size() const { + return _size; } - void setPosition(Vector2D const &position) { - _position = position; - } - - CGRect const &bounds() const { - return _bounds; - } - void setBounds(CGRect const &bounds) { - _bounds = bounds; - } - - virtual CGRect effectiveBounds() const { - return bounds(); + void setSize(Vector2D const &size) { + _size = size; } Transform3D const &transform() const { @@ -131,7 +106,6 @@ private: void removeSublayer(CALayer *layer) { for (auto it = _sublayers.begin(); it != _sublayers.end(); it++) { if (it->get() == layer) { - layer->_superlayer = nullptr; _sublayers.erase(it); break; } @@ -139,12 +113,10 @@ private: } private: - CALayer *_superlayer = nullptr; std::vector> _sublayers; bool _isHidden = false; float _opacity = 1.0; - Vector2D _position = Vector2D(0.0, 0.0); - CGRect _bounds = CGRect(0.0, 0.0, 0.0, 0.0); + Vector2D _size = Vector2D(0.0, 0.0); Transform3D _transform = Transform3D::identity(); std::shared_ptr _mask; bool _masksToBounds = false; @@ -221,21 +193,6 @@ public: _dashPattern = dashPattern; } - virtual CGRect effectiveBounds() const override { - if (_path) { - CGRect boundingBox = _path->boundingBox(); - if (_strokeColor) { - boundingBox.x -= _lineWidth / 2.0; - boundingBox.y -= _lineWidth / 2.0; - boundingBox.width += _lineWidth; - boundingBox.height += _lineWidth; - } - return boundingBox; - } else { - return CGRect(0.0, 0.0, 0.0, 0.0); - } - } - std::shared_ptr renderableItem() override; private: diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index b6f8276bf7..9618e9e289 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -468,10 +468,8 @@ Transform3D fromNativeTransform(::CATransform3D const &value) { } Transform3D Transform3D::makeRotation(float radians, float x, float y, float z) { - return fromNativeTransform(CATransform3DMakeRotation(radians, x, y, z)); - - /*if (x == 0.0 && y == 0.0 && z == 0.0) { - return CATransform3D::identity(); + if (std::abs(radians) <= FLT_EPSILON || (x == 0.0 && y == 0.0 && z == 0.0)) { + return Transform3D::identity(); } float s = sin(radians); @@ -480,43 +478,41 @@ Transform3D Transform3D::makeRotation(float radians, float x, float y, float z) float len = sqrt(x*x + y*y + z*z); x /= len; y /= len; z /= len; - CATransform3D returnValue = CATransform3D::identity(); + Transform3D returnValue = Transform3D::identity(); - returnValue.m11 = c + (1-c) * x*x; - returnValue.m12 = (1-c) * x*y + s*z; - returnValue.m13 = (1-c) * x*z - s*y; - returnValue.m14 = 0; + returnValue.m11 = c + (1.0f - c) * x * x; + returnValue.m12 = (1.0f - c) * x*y + s * z; + returnValue.m13 = (1.0f - c) * x*z - s * y; + returnValue.m14 = 0.0f; - returnValue.m21 = (1-c) * y*x - s*z; - returnValue.m22 = c + (1-c) * y*y; - returnValue.m23 = (1-c) * y*z + s*x; - returnValue.m24 = 0; + returnValue.m21 = (1.0f - c) * y * x - s * z; + returnValue.m22 = c + (1.0f - c) * y * y; + returnValue.m23 = (1.0f - c) * y * z + s * x; + returnValue.m24 = 0.0f; - returnValue.m31 = (1-c) * z*x + s*y; - returnValue.m32 = (1-c) * y*z - s*x; - returnValue.m33 = c + (1-c) * z*z; - returnValue.m34 = 0; + returnValue.m31 = (1.0f - c) * z * x + s * y; + returnValue.m32 = (1.0f - c) * y * z - s * x; + returnValue.m33 = c + (1.0f - c) * z * z; + returnValue.m34 = 0.0f; - returnValue.m41 = 0; - returnValue.m42 = 0; - returnValue.m43 = 0; - returnValue.m44 = 1; + returnValue.m41 = 0.0f; + returnValue.m42 = 0.0f; + returnValue.m43 = 0.0f; + returnValue.m44 = 1.0f; - return returnValue;*/ + return returnValue; } Transform3D Transform3D::rotated(float degrees) const { - return fromNativeTransform(CATransform3DRotate(nativeTransform(*this), degreesToRadians(degrees), 0.0, 0.0, 1.0)); - //return CATransform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this); + return Transform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this); } Transform3D Transform3D::translated(Vector2D const &translation) const { - return fromNativeTransform(CATransform3DTranslate(nativeTransform(*this), translation.x, translation.y, 0.0)); + return Transform3D::makeTranslation(translation.x, translation.y, 0.0f) * (*this); } Transform3D Transform3D::scaled(Vector2D const &scale) const { - return fromNativeTransform(CATransform3DScale(nativeTransform(*this), scale.x, scale.y, 1.0)); - //return CATransform3D::makeScale(scale.x, scale.y, 1.0) * (*this); + return Transform3D::makeScale(scale.x, scale.y, 1.0) * (*this); } Transform3D Transform3D::operator*(Transform3D const &b) const { From 1f1509a64c53b94c63e2beb060bdbba7c8551fd3 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sun, 19 May 2024 12:27:24 +0400 Subject: [PATCH 29/32] Refactoring --- .../SoftwareLottieRenderer/Sources/Canvas.h | 2 +- .../Sources/CoreGraphicsCanvasImpl.h | 2 +- .../Sources/CoreGraphicsCanvasImpl.mm | 2 +- .../Sources/NullCanvasImpl.h | 4 +- .../Sources/NullCanvasImpl.mm | 4 +- .../Sources/SoftwareLottieRenderer.mm | 44 +- .../Sources/ThorVGCanvasImpl.h | 6 +- .../Sources/ThorVGCanvasImpl.mm | 6 +- .../PublicHeaders/LottieCpp/BezierPath.h | 2 +- .../PublicHeaders/LottieCpp/CGPath.h | 4 +- .../PublicHeaders/LottieCpp/CGPathCocoa.h | 2 +- .../PublicHeaders/LottieCpp/CurveVertex.h | 2 +- .../PublicHeaders/LottieCpp/RenderTreeNode.h | 8 +- .../PublicHeaders/LottieCpp/Vectors.h | 175 ++---- .../PublicHeaders/LottieCpp/VectorsCocoa.h | 4 +- .../CompLayers/PreCompositionLayer.hpp | 6 +- .../CompLayers/ShapeCompositionLayer.cpp | 26 +- .../MainThreadAnimationLayer.hpp | 2 +- .../Utility/LayerTransformNode.hpp | 8 +- .../Nodes/Text/TextAnimatorNode.hpp | 12 +- .../Private/Utility/Primitives/BezierPath.cpp | 4 +- .../Lottie/Public/Primitives/CALayer.hpp | 6 +- .../Lottie/Public/Primitives/CGPath.cpp | 15 +- .../Lottie/Public/Primitives/CGPath.mm | 29 +- .../Lottie/Public/Primitives/Vectors.mm | 496 ++++++++++++++---- 25 files changed, 522 insertions(+), 349 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h index 25fbffc6da..32a02bfc8a 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/Canvas.h @@ -81,7 +81,7 @@ public: virtual void setAlpha(float alpha) = 0; - virtual void concatenate(lottie::Transform3D const &transform) = 0; + virtual void concatenate(lottie::Transform2D const &transform) = 0; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) = 0; }; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h index b7139a8e8f..db8064458a 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.h @@ -40,7 +40,7 @@ public: virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void setBlendMode(BlendMode blendMode) override; virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::Transform3D const &transform) override; + virtual void concatenate(lottie::Transform2D const &transform) override; virtual std::shared_ptr makeImage() const; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm index ef9089971f..38f95beb72 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/CoreGraphicsCanvasImpl.mm @@ -509,7 +509,7 @@ void CanvasImpl::setAlpha(float alpha) { CGContextSetAlpha(_context, alpha); } -void CanvasImpl::concatenate(lottie::Transform3D const &transform) { +void CanvasImpl::concatenate(lottie::Transform2D const &transform) { CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform))); } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h index faba71da5a..56918703ae 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.h @@ -30,7 +30,7 @@ public: virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::Transform3D const &transform) override; + virtual void concatenate(lottie::Transform2D const &transform) override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; @@ -39,7 +39,7 @@ public: private: float _width = 0.0f; float _height = 0.0f; - lottie::Transform3D _transform; + lottie::Transform2D _transform; }; } diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm index 1b2cb78bfd..6030201180 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/NullCanvasImpl.mm @@ -12,7 +12,7 @@ void addEnumeratedPath(CanvasPathEnumerator const &enumeratePath) { } NullCanvasImpl::NullCanvasImpl(int width, int height) : -_width(width), _height(height), _transform(lottie::Transform3D::identity()) { +_width(width), _height(height), _transform(lottie::Transform2D::identity()) { } NullCanvasImpl::~NullCanvasImpl() { @@ -69,7 +69,7 @@ void NullCanvasImpl::setBlendMode(BlendMode blendMode) { void NullCanvasImpl::setAlpha(float alpha) { } -void NullCanvasImpl::concatenate(lottie::Transform3D const &transform) { +void NullCanvasImpl::concatenate(lottie::Transform2D const &transform) { } void NullCanvasImpl::draw(std::shared_ptr const &other, lottie::CGRect const &rect) { diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index 25fbb49c66..d53b21787b 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -15,17 +15,17 @@ static constexpr float minGlobalRectCalculationSize = 200.0f; struct TransformedPath { lottie::BezierPath path; - lottie::Transform3D transform; + lottie::Transform2D transform; - TransformedPath(lottie::BezierPath const &path_, lottie::Transform3D const &transform_) : + TransformedPath(lottie::BezierPath const &path_, lottie::Transform2D const &transform_) : path(path_), transform(transform_) { } }; -static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::Transform2D const &parentTransform, bool skipApplyTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { //TODO:remove skipApplyTransform - lottie::Transform3D effectiveTransform = parentTransform; + lottie::Transform2D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { effectiveTransform = item->transform * effectiveTransform; } @@ -56,9 +56,9 @@ static lottie::CGRect collectPathBoundingBoxes(std::shared_ptr item, size_t subItemLimit, lottie::Transform3D const &parentTransform, bool skipApplyTransform, std::function const &onPath) { +static void enumeratePaths(std::shared_ptr item, size_t subItemLimit, lottie::Transform2D const &parentTransform, bool skipApplyTransform, std::function const &onPath) { //TODO:remove skipApplyTransform - lottie::Transform3D effectiveTransform = parentTransform; + lottie::Transform2D effectiveTransform = parentTransform; if (!skipApplyTransform && item->isGroup) { effectiveTransform = item->transform * effectiveTransform; } @@ -80,14 +80,14 @@ static void enumeratePaths(std::shared_ptr it namespace lottie { -static std::optional getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional getRenderContentItemGlobalRect(std::shared_ptr const &contentItem, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; - Transform3D localTransform = contentItem->transform; + Transform2D localTransform = contentItem->transform; currentTransform = localTransform * currentTransform; std::optional globalRect; for (const auto &shadingVariant : contentItem->shadings) { - lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::Transform3D::identity(), true, bezierPathsBoundingBoxContext); + lottie::CGRect shapeBounds = collectPathBoundingBoxes(contentItem, shadingVariant->subItemLimit, lottie::Transform2D::identity(), true, bezierPathsBoundingBoxContext); if (shadingVariant->stroke) { shapeBounds = shapeBounds.insetBy(-shadingVariant->stroke->lineWidth / 2.0, -shadingVariant->stroke->lineWidth / 2.0); @@ -128,13 +128,13 @@ static std::optional getRenderContentItemGlobalRect(std::shared_ptr getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static std::optional getRenderNodeGlobalRect(std::shared_ptr const &node, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, bool isInvertedMatte, BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { if (node->isHidden() || node->alpha() < minVisibleAlpha) { return std::nullopt; } auto currentTransform = parentTransform; - Transform3D localTransform = node->transform(); + Transform2D localTransform = node->transform(); currentTransform = localTransform * currentTransform; std::optional globalRect; @@ -179,9 +179,9 @@ static std::optional getRenderNodeGlobalRect(std::shared_ptr const &parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void drawLottieContentItem(std::shared_ptr const &parentContext, std::shared_ptr item, float parentAlpha, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { auto currentTransform = parentTransform; - lottie::Transform3D localTransform = item->transform; + lottie::Transform2D localTransform = item->transform; currentTransform = localTransform * currentTransform; float normalizedOpacity = item->alpha; @@ -215,7 +215,7 @@ static void drawLottieContentItem(std::shared_ptr const tempContext = tempContextValue; currentContext = &tempContext; - (*currentContext)->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + (*currentContext)->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); (*currentContext)->saveState(); (*currentContext)->concatenate(currentTransform); @@ -268,7 +268,7 @@ static void drawLottieContentItem(std::shared_ptr const }; } else { iteratePaths = [&](std::function iterate) { - enumeratePaths(item, shading->subItemLimit, lottie::Transform3D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform3D const &transform) { + enumeratePaths(item, shading->subItemLimit, lottie::Transform2D::identity(), true, [&](lottie::BezierPath const &sourcePath, lottie::Transform2D const &transform) { auto path = sourcePath.copyUsingTransform(transform); lottieRendering::PathCommand pathCommand; @@ -456,7 +456,7 @@ static void drawLottieContentItem(std::shared_ptr const parentContext->restoreState(); } -static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr const &parentContext, lottie::Vector2D const &globalSize, lottie::Transform3D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { +static void renderLottieRenderNode(std::shared_ptr node, std::shared_ptr const &parentContext, lottie::Vector2D const &globalSize, lottie::Transform2D const &parentTransform, float parentAlpha, bool isInvertedMatte, lottie::BezierPathsBoundingBoxContext &bezierPathsBoundingBoxContext) { float normalizedOpacity = node->alpha(); float layerAlpha = ((float)normalizedOpacity) * parentAlpha; @@ -465,7 +465,7 @@ static void renderLottieRenderNode(std::shared_ptr node, } auto currentTransform = parentTransform; - lottie::Transform3D localTransform = node->transform(); + lottie::Transform2D localTransform = node->transform(); currentTransform = localTransform * currentTransform; std::shared_ptr maskContext; @@ -507,7 +507,7 @@ static void renderLottieRenderNode(std::shared_ptr node, if ((node->mask() && !node->mask()->isHidden() && node->mask()->alpha() >= minVisibleAlpha) || masksToBounds) { auto maskBackingStorage = parentContext->makeLayer((int)(globalRect->width), (int)(globalRect->height)); - maskBackingStorage->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + maskBackingStorage->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); maskBackingStorage->concatenate(currentTransform); if (masksToBounds) { @@ -524,7 +524,7 @@ static void renderLottieRenderNode(std::shared_ptr node, tempContext = tempContextValue; currentContext = tempContextValue; - currentContext->concatenate(lottie::Transform3D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); + currentContext->concatenate(lottie::Transform2D::identity().translated(lottie::Vector2D(-globalRect->x, -globalRect->y))); currentContext->saveState(); currentContext->concatenate(currentTransform); @@ -602,13 +602,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { return nil; } - lottie::Transform3D rootTransform = lottie::Transform3D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height)); + lottie::Transform2D rootTransform = lottie::Transform2D::identity().scaled(lottie::Vector2D(size.width / (float)animation.size.width, size.height / (float)animation.size.height)); if (useReferenceRendering) { auto context = std::make_shared((int)size.width, (int)size.height); CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); - context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0)); + context->concatenate(lottie::Transform2D::makeScale(scale.x, scale.y)); renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); @@ -620,7 +620,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { auto context = std::make_shared((int)size.width, (int)size.height); CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); - context->concatenate(lottie::Transform3D::makeScale(scale.x, scale.y, 1.0)); + context->concatenate(lottie::Transform2D::makeScale(scale.x, scale.y)); renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h index b3550f2247..dbd676b5e9 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.h @@ -32,7 +32,7 @@ public: virtual void setAlpha(float alpha) override; - virtual void concatenate(lottie::Transform3D const &transform) override; + virtual void concatenate(lottie::Transform2D const &transform) override; virtual void draw(std::shared_ptr const &other, lottie::CGRect const &rect) override; @@ -52,8 +52,8 @@ private: std::unique_ptr _canvas; float _alpha = 1.0; - lottie::Transform3D _transform; - std::vector _stateStack; + lottie::Transform2D _transform; + std::vector _stateStack; int _bytesPerRow = 0; uint32_t *_backingData = nullptr; int _statsNumStrokes = 0; diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm index bd8e3d9b6f..4be6064df2 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/ThorVGCanvasImpl.mm @@ -27,7 +27,7 @@ void tvgPath(CanvasPathEnumerator const &enumeratePath, tvg::Shape *shape) { }); } -tvg::Matrix tvgTransform(lottie::Transform3D const &transform) { +tvg::Matrix tvgTransform(lottie::Transform2D const &transform) { CGAffineTransform affineTransform = CATransform3DGetAffineTransform(lottie::nativeTransform(transform)); tvg::Matrix result; result.e11 = affineTransform.a; @@ -45,7 +45,7 @@ tvg::Matrix tvgTransform(lottie::Transform3D const &transform) { } ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height) : -_width(width), _height(height), _transform(lottie::Transform3D::identity()) { +_width(width), _height(height), _transform(lottie::Transform2D::identity()) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ tvg::Initializer::init(0); @@ -259,7 +259,7 @@ void ThorVGCanvasImpl::setAlpha(float alpha) { _alpha = alpha; } -void ThorVGCanvasImpl::concatenate(lottie::Transform3D const &transform) { +void ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) { _transform = transform * _transform; /*_canvas->concat(SkM44( transform.m11, transform.m21, transform.m31, transform.m41, diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h index 2fd05533e4..b17f942761 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/BezierPath.h @@ -122,7 +122,7 @@ public: std::optional const &closed() const; void setClosed(std::optional const &closed); std::shared_ptr cgPath() const; - BezierPath copyUsingTransform(Transform3D const &transform) const; + BezierPath copyUsingTransform(Transform2D const &transform) const; public: BezierPath(std::shared_ptr contents); diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h index 4288688ab8..1ddeeb81ed 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPath.h @@ -57,7 +57,7 @@ public: virtual bool empty() const = 0; - virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const = 0; + virtual std::shared_ptr copyUsingTransform(Transform2D const &transform) const = 0; virtual void addLineTo(Vector2D const &point) = 0; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) = 0; @@ -71,7 +71,7 @@ public: virtual bool isEqual(CGPath *other) const = 0; }; -Vector2D transformVector(Vector2D const &v, Transform3D const &m); +Vector2D transformVector(Vector2D const &v, Transform2D const &m); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h index 5626825a62..af32f20bb5 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CGPathCocoa.h @@ -22,7 +22,7 @@ public: virtual bool empty() const override; - virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const override; + virtual std::shared_ptr copyUsingTransform(Transform2D const &transform) const override; virtual void addLineTo(Vector2D const &point) override; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h index 4f5a8a28d5..90e3ccf305 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/CurveVertex.h @@ -64,7 +64,7 @@ public: return CurveVertex(point + translation, inTangent + translation, outTangent + translation, false); } - CurveVertex transformed(Transform3D const &transform) const { + CurveVertex transformed(Transform2D const &transform) const { return CurveVertex(transformVector(point, transform), transformVector(inTangent, transform), transformVector(outTangent, transform), false); } diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h index 9d08581009..09616bdf0a 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/RenderTreeNode.h @@ -364,7 +364,7 @@ public: public: bool isGroup = false; - Transform3D transform = Transform3D::identity(); + Transform2D transform = Transform2D::identity(); float alpha = 0.0; std::optional trimParams; std::shared_ptr path; @@ -392,7 +392,7 @@ class RenderTreeNode { public: RenderTreeNode( Vector2D size_, - Transform3D transform_, + Transform2D transform_, float alpha_, bool masksToBounds_, bool isHidden_, @@ -421,7 +421,7 @@ public: return _size; } - Transform3D const &transform() const { + Transform2D const &transform() const { return _transform; } @@ -451,7 +451,7 @@ public: public: Vector2D _size; - Transform3D _transform = Transform3D::identity(); + Transform2D _transform = Transform2D::identity(); float _alpha = 1.0f; bool _masksToBounds = false; bool _isHidden = false; diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h index 14029225d0..ffe5596872 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/Vectors.h @@ -8,6 +8,8 @@ #include +#import + namespace lottie { struct Vector1D { @@ -148,167 +150,64 @@ inline float radiansToDegrees(float value) { return value * 180.0f / M_PI; } -struct Transform3D { - float m11, m12, m13, m14; - float m21, m22, m23, m24; - float m31, m32, m33, m34; - float m41, m42, m43, m44; - - Transform3D( - float m11_, float m12_, float m13_, float m14_, - float m21_, float m22_, float m23_, float m24_, - float m31_, float m32_, float m33_, float m34_, - float m41_, float m42_, float m43_, float m44_ - ) : - m11(m11_), m12(m12_), m13(m13_), m14(m14_), - m21(m21_), m22(m22_), m23(m23_), m24(m24_), - m31(m31_), m32(m32_), m33(m33_), m34(m34_), - m41(m41_), m42(m42_), m43(m43_), m44(m44_) { +struct Transform2D { + static Transform2D const &identity() { + return _identity; } - bool operator==(Transform3D const &rhs) const { - return m11 == rhs.m11 && m12 == rhs.m12 && m13 == rhs.m13 && m14 == rhs.m14 && - m21 == rhs.m21 && m22 == rhs.m22 && m23 == rhs.m23 && m24 == rhs.m24 && - m31 == rhs.m31 && m32 == rhs.m32 && m33 == rhs.m33 && m34 == rhs.m34 && - m41 == rhs.m41 && m42 == rhs.m42 && m43 == rhs.m43 && m44 == rhs.m44; + explicit Transform2D(simd_float3x3 const &rows_) : + _rows(rows_) { } - bool operator!=(Transform3D const &rhs) const { - return !(*this == rhs); + Transform2D operator*(Transform2D const &other) const { + return Transform2D(simd_mul(other._rows, _rows)); } - inline bool isIdentity() const { - return m11 == 1.0 && m12 == 0.0 && m13 == 0.0 && m14 == 0.0 && - m21 == 0.0 && m22 == 1.0 && m23 == 0.0 && m24 == 0.0 && - m31 == 0.0 && m32 == 0.0 && m33 == 1.0 && m34 == 0.0 && - m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0; + bool isInvertible() const { + return simd_determinant(_rows) > 0.00000001; } - static Transform3D makeTranslation(float tx, float ty, float tz) { - return Transform3D( - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - tx, ty, tz, 1 - ); + Transform2D inverted() const { + return Transform2D(simd_inverse(_rows)); } - static Transform3D makeScale(float sx, float sy, float sz) { - return Transform3D( - sx, 0, 0, 0, - 0, sy, 0, 0, - 0, 0, sz, 0, - 0, 0, 0, 1 - ); + bool isIdentity() const { + return (*this) == identity(); } - static Transform3D makeRotation(float radians, float x, float y, float z); - - static Transform3D makeSkew(float skew, float skewAxis) { - float mCos = cos(degreesToRadians(skewAxis)); - float mSin = sin(degreesToRadians(skewAxis)); - float aTan = tan(degreesToRadians(skew)); - - Transform3D transform1( - mCos, - mSin, - 0.0, - 0.0, - -mSin, - mCos, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ); - - Transform3D transform2( - 1.0, - 0.0, - 0.0, - 0.0, - aTan, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ); - - Transform3D transform3( - mCos, - -mSin, - 0.0, - 0.0, - mSin, - mCos, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0, - 0.0, - 0.0, - 0.0, - 0.0, - 1.0 - ); - - return transform3 * transform2 * transform1; - } - - static Transform3D makeTransform( + static Transform2D makeTranslation(float tx, float ty); + static Transform2D makeScale(float sx, float sy); + static Transform2D makeRotation(float radians); + static Transform2D makeSkew(float skew, float skewAxis); + static Transform2D makeTransform( Vector2D const &anchor, Vector2D const &position, Vector2D const &scale, float rotation, std::optional skew, std::optional skewAxis - ) { - Transform3D result = Transform3D::identity(); - if (skew.has_value() && skewAxis.has_value()) { - result = Transform3D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); - } else { - result = Transform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); - } - - return result; + ); + + Transform2D rotated(float degrees) const; + Transform2D translated(Vector2D const &translation) const; + Transform2D scaled(Vector2D const &scale) const; + Transform2D skewed(float skew, float skewAxis) const; + + bool operator==(Transform2D const &rhs) const { + return simd_equal(_rows, rhs._rows); } - Transform3D rotated(float degrees) const; - - Transform3D translated(Vector2D const &translation) const; - - Transform3D scaled(Vector2D const &scale) const; - - Transform3D skewed(float skew, float skewAxis) const { - return Transform3D::makeSkew(skew, skewAxis) * (*this); + bool operator!=(Transform2D const &rhs) const { + return !((*this) == rhs); } - static Transform3D const &identity() { - return _identity; + simd_float3x3 const &rows() const { + return _rows; } - - Transform3D operator*(Transform3D const &b) const; - - bool isInvertible() const; - - Transform3D inverted() const; - private: - static Transform3D _identity; + static Transform2D _identity; + + simd_float3x3 _rows; }; struct CGRect { @@ -359,7 +258,7 @@ struct CGRect { CGRect intersection(CGRect const &other) const; CGRect unionWith(CGRect const &other) const; - CGRect applyingTransform(Transform3D const &transform) const; + CGRect applyingTransform(Transform2D const &transform) const; }; inline bool isInRangeOrEqual(float value, float from, float to) { diff --git a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h index bf8083f45b..ef77ad9966 100644 --- a/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h +++ b/submodules/TelegramUI/Components/LottieCpp/PublicHeaders/LottieCpp/VectorsCocoa.h @@ -7,8 +7,8 @@ namespace lottie { -::CATransform3D nativeTransform(Transform3D const &value); -Transform3D fromNativeTransform(::CATransform3D const &value); +::CATransform3D nativeTransform(Transform2D const &value); +Transform2D fromNativeTransform(::CATransform3D const &value); } 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 af69491fa3..c170f21a58 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 @@ -122,7 +122,7 @@ public: std::vector> renderTreeValue; auto renderTreeContentItem = std::make_shared( Vector2D(0.0, 0.0), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, @@ -136,7 +136,7 @@ public: _contentsTreeNode = std::make_shared( Vector2D(0.0, 0.0), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, @@ -159,7 +159,7 @@ public: _renderTreeNode = std::make_shared( Vector2D(0.0, 0.0), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, 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 19f244e0ee..e7ad955e0f 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 @@ -492,9 +492,9 @@ public: struct TransformedPath { BezierPath path; - Transform3D transform; + Transform2D transform; - TransformedPath(BezierPath const &path_, Transform3D const &transform_) : + TransformedPath(BezierPath const &path_, Transform2D const &transform_) : path(path_), transform(transform_) { } @@ -853,13 +853,13 @@ public: _opacityValue = 1.0; } - _transformValue = Transform3D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y)); + _transformValue = Transform2D::identity().translated(Vector2D(positionValue.x, positionValue.y)).rotated(rotationValue).skewed(-skewValue, skewAxisValue).scaled(Vector2D(scaleValue.x * 0.01, scaleValue.y * 0.01)).translated(Vector2D(-anchorValue.x, -anchorValue.y)); hasValidData = true; } } - Transform3D const &transform() { + Transform2D const &transform() { return _transformValue; } @@ -878,7 +878,7 @@ public: std::unique_ptr> _skewAxis; std::unique_ptr> _opacity; - Transform3D _transformValue = Transform3D::identity(); + Transform2D _transformValue = Transform2D::identity(); float _opacityValue = 1.0; }; @@ -910,11 +910,11 @@ public: std::shared_ptr _contentItem; private: - std::vector collectPaths(size_t subItemLimit, Transform3D const &parentTransform, bool skipApplyTransform) { + std::vector collectPaths(size_t subItemLimit, Transform2D const &parentTransform, bool skipApplyTransform) { std::vector mappedPaths; //TODO:remove skipApplyTransform - Transform3D effectiveTransform = parentTransform; + Transform2D effectiveTransform = parentTransform; if (!skipApplyTransform && isGroup && transform) { effectiveTransform = transform->transform() * effectiveTransform; } @@ -942,7 +942,7 @@ public: } CompoundBezierPath trimmedPath = trimCompoundPath(tempPath, currentTrim->start, currentTrim->end, currentTrim->offset, currentTrim->type); for (auto &path : trimmedPath.paths) { - mappedPaths.emplace_back(path, Transform3D::identity()); + mappedPaths.emplace_back(path, Transform2D::identity()); } } else { for (auto &path : subItemPaths) { @@ -1061,7 +1061,7 @@ public: } void updateContents(std::optional parentTrim) { - Transform3D containerTransform = Transform3D::identity(); + Transform2D containerTransform = Transform2D::identity(); float containerOpacity = 1.0; if (transform) { containerTransform = transform->transform(); @@ -1089,7 +1089,7 @@ public: if (parentTrim) { CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true); + auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true); for (const auto &path : paths) { compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } @@ -1104,7 +1104,7 @@ public: } else { if (hasTrims()) { CompoundBezierPath compoundPath; - auto paths = collectPaths(shadingVariant.subItemLimit, Transform3D::identity(), true); + auto paths = collectPaths(shadingVariant.subItemLimit, Transform2D::identity(), true); for (const auto &path : paths) { compoundPath.appendPath(path.path.copyUsingTransform(path.transform)); } @@ -1324,7 +1324,7 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath if (!_renderTreeNode) { _contentRenderTreeNode = std::make_shared( Vector2D(0.0, 0.0), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, @@ -1349,7 +1349,7 @@ std::shared_ptr ShapeCompositionLayer::renderTreeNode(BezierPath _renderTreeNode = std::make_shared( Vector2D(0.0, 0.0), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, 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 b8b288307f..b9e47a7a60 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 @@ -238,7 +238,7 @@ public: } _renderTreeNode = std::make_shared( size(), - Transform3D::identity(), + Transform2D::identity(), 1.0, false, false, diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp index 79c9950b2a..459558a2bd 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/LayerContainers/Utility/LayerTransformNode.hpp @@ -157,7 +157,7 @@ public: Vector3D anchor = _transformProperties->anchor()->value(); Vector3D scale = _transformProperties->scale()->value(); - _localTransform = Transform3D::makeTransform( + _localTransform = Transform2D::makeTransform( Vector2D(anchor.x, anchor.y), position, Vector2D(scale.x, scale.y), @@ -181,7 +181,7 @@ public: return _opacity; } - Transform3D const &globalTransform() { + Transform2D const &globalTransform() { return _globalTransform; } @@ -191,8 +191,8 @@ private: std::shared_ptr _transformProperties; float _opacity = 1.0; - Transform3D _localTransform = Transform3D::identity(); - Transform3D _globalTransform = Transform3D::identity(); + Transform2D _localTransform = Transform2D::identity(); + Transform2D _globalTransform = Transform2D::identity(); public: virtual LayerTransformNode *asLayerTransformNode() override { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp index dc252f6102..6e8b534b48 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/MainThread/NodeRenderSystem/Nodes/Text/TextAnimatorNode.hpp @@ -93,7 +93,7 @@ public: return _childKeypaths; } - Transform3D caTransform() { + Transform2D caTransform() { Vector2D anchor = Vector2D::Zero(); if (_anchor) { auto anchor3d = _anchor->value(); @@ -126,7 +126,7 @@ public: skewAxis = _skewAxis->value().value; } - return Transform3D::makeTransform( + return Transform2D::makeTransform( anchor, position, scale, @@ -212,16 +212,16 @@ public: return _parentTextNode; } - Transform3D xform() { + Transform2D xform() { if (_xform.has_value()) { return _xform.value(); } else if (_parentTextNode) { return _parentTextNode->xform(); } else { - return Transform3D::identity(); + return Transform2D::identity(); } } - void setXform(Transform3D const &xform) { + void setXform(Transform2D const &xform) { _xform = xform; } @@ -312,7 +312,7 @@ private: std::shared_ptr _outputPath; - std::optional _xform; + std::optional _xform; std::optional _opacity; std::optional _strokeColor; std::optional _fillColor; 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 2ab088e42a..85c50dba1e 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 @@ -486,8 +486,8 @@ std::shared_ptr BezierPath::cgPath() const { return _contents->cgPath(); } -BezierPath BezierPath::copyUsingTransform(Transform3D const &transform) const { - if (transform == Transform3D::identity()) { +BezierPath BezierPath::copyUsingTransform(Transform2D const &transform) const { + if (transform == Transform2D::identity()) { return (*this); } BezierPath result; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp index 2c40c5667e..c0c0a61006 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CALayer.hpp @@ -64,10 +64,10 @@ public: _size = size; } - Transform3D const &transform() const { + Transform2D const &transform() const { return _transform; } - void setTransform(Transform3D const &transform) { + void setTransform(Transform2D const &transform) { _transform = transform; } @@ -117,7 +117,7 @@ private: bool _isHidden = false; float _opacity = 1.0; Vector2D _size = Vector2D(0.0, 0.0); - Transform3D _transform = Transform3D::identity(); + Transform2D _transform = Transform2D::identity(); std::shared_ptr _mask; bool _masksToBounds = false; std::optional _compositingFilter; diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp index d1cf338111..e985e18392 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.cpp @@ -35,11 +35,10 @@ void addPointToBoundingRect(bool *isFirst, CGRect *rect, Vector2D const *point) } -Vector2D transformVector(Vector2D const &v, Transform3D const &m) { - return Vector2D( - m.m11 * v.x + m.m21 * v.y + m.m41 * 1.0, - m.m12 * v.x + m.m22 * v.y + m.m42 * 1.0 - ); +Vector2D transformVector(Vector2D const &v, Transform2D const &m) { + float transformedX = m.rows().columns[0][0] * v.x + m.rows().columns[1][0] * v.y + m.rows().columns[2][0] * 1.0f; + float transformedY = m.rows().columns[0][1] * v.x + m.rows().columns[1][1] * v.y + m.rows().columns[2][1] * 1.0f; + return Vector2D(transformedX, transformedY); } class CGPathImpl: public CGPath { @@ -51,7 +50,7 @@ public: virtual bool empty() const override; - virtual std::shared_ptr copyUsingTransform(Transform3D const &transform) const override; + virtual std::shared_ptr copyUsingTransform(Transform2D const &transform) const override; virtual void addLineTo(Vector2D const &point) override; virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override; @@ -108,10 +107,10 @@ bool CGPathImpl::empty() const { return _items.empty(); } -std::shared_ptr CGPathImpl::copyUsingTransform(Transform3D const &transform) const { +std::shared_ptr CGPathImpl::copyUsingTransform(Transform2D const &transform) const { auto result = std::make_shared(); - if (transform == Transform3D::identity()) { + if (transform == Transform2D::identity()) { result->_items = _items; return result; } diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm index ab13e06922..93a5e242a1 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/CGPath.mm @@ -92,29 +92,12 @@ bool CGPathCocoaImpl::empty() const { return CGPathIsEmpty(_path); } -std::shared_ptr CGPathCocoaImpl::copyUsingTransform(Transform3D const &transform) const { - ::CATransform3D nativeTransform; - nativeTransform.m11 = transform.m11; - nativeTransform.m12 = transform.m12; - nativeTransform.m13 = transform.m13; - nativeTransform.m14 = transform.m14; - - nativeTransform.m21 = transform.m21; - nativeTransform.m22 = transform.m22; - nativeTransform.m23 = transform.m23; - nativeTransform.m24 = transform.m24; - - nativeTransform.m31 = transform.m31; - nativeTransform.m32 = transform.m32; - nativeTransform.m33 = transform.m33; - nativeTransform.m34 = transform.m34; - - nativeTransform.m41 = transform.m41; - nativeTransform.m42 = transform.m42; - nativeTransform.m43 = transform.m43; - nativeTransform.m44 = transform.m44; - - auto affineTransform = CATransform3DGetAffineTransform(nativeTransform); +std::shared_ptr CGPathCocoaImpl::copyUsingTransform(Transform2D const &transform) const { + CGAffineTransform affineTransform = CGAffineTransformMake( + transform.rows().columns[0][0], transform.rows().columns[0][1], + transform.rows().columns[1][0], transform.rows().columns[1][1], + transform.rows().columns[2][0], transform.rows().columns[2][1] + ); CGPathRef resultPath = CGPathCreateCopyByTransformingPath(_path, &affineTransform); if (resultPath == nil) { diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm index 9618e9e289..3695d35aa0 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Public/Primitives/Vectors.mm @@ -8,10 +8,261 @@ #import -#import - namespace lottie { +/*explicit Transform2D(Transform3D const &t) { + CGAffineTransform at = CATransform3DGetAffineTransform(nativeTransform(t)); + _rows.columns[0] = simd_make_float3(at.a, at.b, 0.0); + _rows.columns[1] = simd_make_float3(at.c, at.d, 0.0); + _rows.columns[2] = simd_make_float3(at.tx, at.ty, 1.0); +} + + Transform3D transform3D() { + CGAffineTransform at = CGAffineTransformMake( + _rows.columns[0][0], _rows.columns[0][1], + _rows.columns[1][0], _rows.columns[1][1], + _rows.columns[2][0], _rows.columns[2][1] + ); + return fromNativeTransform(CATransform3DMakeAffineTransform(at)); + }*/ + +/*struct Transform3D { + float m11, m12, m13, m14; + float m21, m22, m23, m24; + float m31, m32, m33, m34; + float m41, m42, m43, m44; + + Transform3D( + float m11_, float m12_, float m13_, float m14_, + float m21_, float m22_, float m23_, float m24_, + float m31_, float m32_, float m33_, float m34_, + float m41_, float m42_, float m43_, float m44_ + ) : + m11(m11_), m12(m12_), m13(m13_), m14(m14_), + m21(m21_), m22(m22_), m23(m23_), m24(m24_), + m31(m31_), m32(m32_), m33(m33_), m34(m34_), + m41(m41_), m42(m42_), m43(m43_), m44(m44_) { + } + + bool operator==(Transform3D const &rhs) const { + return m11 == rhs.m11 && m12 == rhs.m12 && m13 == rhs.m13 && m14 == rhs.m14 && + m21 == rhs.m21 && m22 == rhs.m22 && m23 == rhs.m23 && m24 == rhs.m24 && + m31 == rhs.m31 && m32 == rhs.m32 && m33 == rhs.m33 && m34 == rhs.m34 && + m41 == rhs.m41 && m42 == rhs.m42 && m43 == rhs.m43 && m44 == rhs.m44; + } + + bool operator!=(Transform3D const &rhs) const { + return !(*this == rhs); + } + + inline bool isIdentity() const { + return m11 == 1.0 && m12 == 0.0 && m13 == 0.0 && m14 == 0.0 && + m21 == 0.0 && m22 == 1.0 && m23 == 0.0 && m24 == 0.0 && + m31 == 0.0 && m32 == 0.0 && m33 == 1.0 && m34 == 0.0 && + m41 == 0.0 && m42 == 0.0 && m43 == 0.0 && m44 == 1.0; + } + + static Transform3D makeTranslation(float tx, float ty, float tz) { + return Transform3D( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + tx, ty, tz, 1 + ); + } + + static Transform3D makeScale(float sx, float sy, float sz) { + return Transform3D( + sx, 0, 0, 0, + 0, sy, 0, 0, + 0, 0, sz, 0, + 0, 0, 0, 1 + ); + } + + static Transform3D makeRotation(float radians); + + static Transform3D makeSkew(float skew, float skewAxis) { + float mCos = cos(degreesToRadians(skewAxis)); + float mSin = sin(degreesToRadians(skewAxis)); + float aTan = tan(degreesToRadians(skew)); + + Transform3D transform1( + mCos, + mSin, + 0.0, + 0.0, + -mSin, + mCos, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ); + + Transform3D transform2( + 1.0, + 0.0, + 0.0, + 0.0, + aTan, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ); + + Transform3D transform3( + mCos, + -mSin, + 0.0, + 0.0, + mSin, + mCos, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0 + ); + + return transform3 * transform2 * transform1; + } + + static Transform3D makeTransform( + Vector2D const &anchor, + Vector2D const &position, + Vector2D const &scale, + float rotation, + std::optional skew, + std::optional skewAxis + ) { + Transform3D result = Transform3D::identity(); + if (skew.has_value() && skewAxis.has_value()) { + result = Transform3D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + } else { + result = Transform3D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + } + + return result; + } + + Transform3D rotated(float degrees) const; + + Transform3D translated(Vector2D const &translation) const; + + Transform3D scaled(Vector2D const &scale) const; + + Transform3D skewed(float skew, float skewAxis) const { + return Transform3D::makeSkew(skew, skewAxis) * (*this); + } + + static Transform3D identity() { + return Transform3D( + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + ); + } + + Transform3D operator*(Transform3D const &b) const; +};*/ + +/*Transform2D t2d(Transform3D const &testMatrix) { + ::CATransform3D nativeTest; + + nativeTest.m11 = testMatrix.m11; + nativeTest.m12 = testMatrix.m12; + nativeTest.m13 = testMatrix.m13; + nativeTest.m14 = testMatrix.m14; + + nativeTest.m21 = testMatrix.m21; + nativeTest.m22 = testMatrix.m22; + nativeTest.m23 = testMatrix.m23; + nativeTest.m24 = testMatrix.m24; + + nativeTest.m31 = testMatrix.m31; + nativeTest.m32 = testMatrix.m32; + nativeTest.m33 = testMatrix.m33; + nativeTest.m34 = testMatrix.m34; + + nativeTest.m41 = testMatrix.m41; + nativeTest.m42 = testMatrix.m42; + nativeTest.m43 = testMatrix.m43; + nativeTest.m44 = testMatrix.m44; + + CGAffineTransform at = CATransform3DGetAffineTransform(nativeTest); + Transform2D result = Transform2D::identity(); + simd_float3x3 *rows = (simd_float3x3 *)&result.rows(); + rows->columns[0] = simd_make_float3(at.a, at.b, 0.0); + rows->columns[1] = simd_make_float3(at.c, at.d, 0.0); + rows->columns[2] = simd_make_float3(at.tx, at.ty, 1.0); + + return result; +} + +Transform3D t3d(Transform2D const &t) { + CGAffineTransform at = CGAffineTransformMake( + t.rows().columns[0][0], t.rows().columns[0][1], + t.rows().columns[1][0], t.rows().columns[1][1], + t.rows().columns[2][0], t.rows().columns[2][1] + ); + ::CATransform3D value = CATransform3DMakeAffineTransform(at); + + Transform3D result = Transform3D::identity(); + result.m11 = value.m11; + result.m12 = value.m12; + result.m13 = value.m13; + result.m14 = value.m14; + + result.m21 = value.m21; + result.m22 = value.m22; + result.m23 = value.m23; + result.m24 = value.m24; + + result.m31 = value.m31; + result.m32 = value.m32; + result.m33 = value.m33; + result.m34 = value.m34; + + result.m41 = value.m41; + result.m42 = value.m42; + result.m43 = value.m43; + result.m44 = value.m44; + + return result; +} + +Transform3D Transform3D::operator*(Transform3D const &b) const { + if (isIdentity()) { + return b; + } + if (b.isIdentity()) { + return *this; + } + + return t3d((t2d(*this) * t2d(b))); +}*/ + Vector1D::Vector1D(lottiejson11::Json const &json) noexcept(false) { if (json.is_number()) { value = json.number_value(); @@ -140,13 +391,108 @@ lottiejson11::Json Vector3D::toJson() const { return lottiejson11::Json(result); } -Transform3D Transform3D::_identity = Transform3D( - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 +Transform2D Transform2D::_identity = Transform2D( + simd_float3x3({ + simd_make_float3(1.0f, 0.0f, 0.0f), + simd_make_float3(0.0f, 1.0f, 0.0f), + simd_make_float3(0.0f, 0.0f, 1.0f) + }) ); +Transform2D Transform2D::makeTranslation(float tx, float ty) { + return Transform2D(simd_float3x3({ + simd_make_float3(1.0f, 0.0f, 0.0f), + simd_make_float3(0.0f, 1.0f, 0.0f), + simd_make_float3(tx, ty, 1.0f) + })); +} + +Transform2D Transform2D::makeScale(float sx, float sy) { + return Transform2D(simd_float3x3({ + simd_make_float3(sx, 0.0f, 0.0f), + simd_make_float3(0.0f, sy, 0.0f), + simd_make_float3(0.0f, 0.0f, 1.0f) + })); +} + +Transform2D Transform2D::makeRotation(float radians) { + float c = cos(radians); + float s = sin(radians); + + return Transform2D(simd_float3x3({ + simd_make_float3(c, s, 0.0f), + simd_make_float3(-s, c, 0.0f), + simd_make_float3(0.0f, 0.0f, 1.0f) + })); +} + +Transform2D Transform2D::makeSkew(float skew, float skewAxis) { + if (std::abs(skew) <= FLT_EPSILON && std::abs(skewAxis) <= FLT_EPSILON) { + return Transform2D::identity(); + } + + float mCos = cos(degreesToRadians(skewAxis)); + float mSin = sin(degreesToRadians(skewAxis)); + float aTan = tan(degreesToRadians(skew)); + + simd_float3x3 simd1 = simd_float3x3({ + simd_make_float3(mCos, -mSin, 0.0), + simd_make_float3(mSin, mCos, 0.0), + simd_make_float3(0.0, 0.0, 1.0) + }); + + simd_float3x3 simd2 = simd_float3x3({ + simd_make_float3(1.0, 0.0, 0.0), + simd_make_float3(aTan, 1.0, 0.0), + simd_make_float3(0.0, 0.0, 1.0) + }); + + simd_float3x3 simd3 = simd_float3x3({ + simd_make_float3(mCos, mSin, 0.0), + simd_make_float3(-mSin, mCos, 0.0), + simd_make_float3(0.0, 0.0, 1.0) + }); + + simd_float3x3 result = simd_mul(simd_mul(simd3, simd2), simd1); + Transform2D resultTransform(result); + + return resultTransform; +} + +Transform2D Transform2D::makeTransform( + Vector2D const &anchor, + Vector2D const &position, + Vector2D const &scale, + float rotation, + std::optional skew, + std::optional skewAxis +) { + Transform2D result = Transform2D::identity(); + if (skew.has_value() && skewAxis.has_value()) { + result = Transform2D::identity().translated(position).rotated(rotation).skewed(-skew.value(), skewAxis.value()).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + } else { + result = Transform2D::identity().translated(position).rotated(rotation).scaled(Vector2D(scale.x * 0.01, scale.y * 0.01)).translated(Vector2D(-anchor.x, -anchor.y)); + } + + return result; +} + +Transform2D Transform2D::rotated(float degrees) const { + return Transform2D::makeRotation(degreesToRadians(degrees)) * (*this); +} + +Transform2D Transform2D::translated(Vector2D const &translation) const { + return Transform2D::makeTranslation(translation.x, translation.y) * (*this); +} + +Transform2D Transform2D::scaled(Vector2D const &scale) const { + return Transform2D::makeScale(scale.x, scale.y) * (*this); +} + +Transform2D Transform2D::skewed(float skew, float skewAxis) const { + return Transform2D::makeSkew(skew, skewAxis) * (*this); +} + float interpolate(float value, float to, float amount) { return value + ((to - value) * amount); } @@ -415,8 +761,15 @@ Vector2D Vector2D::interpolate( return point.point; } -::CATransform3D nativeTransform(Transform3D const &value) { - ::CATransform3D result; +::CATransform3D nativeTransform(Transform2D const &value) { + CGAffineTransform at = CGAffineTransformMake( + value.rows().columns[0][0], value.rows().columns[0][1], + value.rows().columns[1][0], value.rows().columns[1][1], + value.rows().columns[2][0], value.rows().columns[2][1] + ); + return CATransform3DMakeAffineTransform(at); + + /*::CATransform3D result; result.m11 = value.m11; result.m12 = value.m12; @@ -438,11 +791,20 @@ Vector2D Vector2D::interpolate( result.m43 = value.m43; result.m44 = value.m44; - return result; + return result;*/ } -Transform3D fromNativeTransform(::CATransform3D const &value) { - Transform3D result = Transform3D::identity(); +Transform2D fromNativeTransform(::CATransform3D const &value) { + CGAffineTransform at = CATransform3DGetAffineTransform(value); + return Transform2D( + simd_float3x3({ + simd_make_float3(at.a, at.b, 0.0), + simd_make_float3(at.c, at.d, 0.0), + simd_make_float3(at.tx, at.ty, 1.0) + }) + ); + + /*Transform2D result = Transform2D::identity(); result.m11 = value.m11; result.m12 = value.m12; @@ -464,47 +826,23 @@ Transform3D fromNativeTransform(::CATransform3D const &value) { result.m43 = value.m43; result.m44 = value.m44; - return result; + return result;*/ } -Transform3D Transform3D::makeRotation(float radians, float x, float y, float z) { - if (std::abs(radians) <= FLT_EPSILON || (x == 0.0 && y == 0.0 && z == 0.0)) { +/*Transform3D Transform3D::makeRotation(float radians) { + if (std::abs(radians) <= FLT_EPSILON) { return Transform3D::identity(); } float s = sin(radians); float c = cos(radians); - float len = sqrt(x*x + y*y + z*z); - x /= len; y /= len; z /= len; - - Transform3D returnValue = Transform3D::identity(); - - returnValue.m11 = c + (1.0f - c) * x * x; - returnValue.m12 = (1.0f - c) * x*y + s * z; - returnValue.m13 = (1.0f - c) * x*z - s * y; - returnValue.m14 = 0.0f; - - returnValue.m21 = (1.0f - c) * y * x - s * z; - returnValue.m22 = c + (1.0f - c) * y * y; - returnValue.m23 = (1.0f - c) * y * z + s * x; - returnValue.m24 = 0.0f; - - returnValue.m31 = (1.0f - c) * z * x + s * y; - returnValue.m32 = (1.0f - c) * y * z - s * x; - returnValue.m33 = c + (1.0f - c) * z * z; - returnValue.m34 = 0.0f; - - returnValue.m41 = 0.0f; - returnValue.m42 = 0.0f; - returnValue.m43 = 0.0f; - returnValue.m44 = 1.0f; - - return returnValue; + ::CGAffineTransform t = CGAffineTransformMake(c, s, -s, c, 0.0f, 0.0f); + return fromNativeTransform(CATransform3DMakeAffineTransform(t)); } Transform3D Transform3D::rotated(float degrees) const { - return Transform3D::makeRotation(degreesToRadians(degrees), 0.0, 0.0, 1.0) * (*this); + return Transform3D::makeRotation(degreesToRadians(degrees)) * (*this); } Transform3D Transform3D::translated(Vector2D const &translation) const { @@ -515,57 +853,14 @@ Transform3D Transform3D::scaled(Vector2D const &scale) const { return Transform3D::makeScale(scale.x, scale.y, 1.0) * (*this); } -Transform3D Transform3D::operator*(Transform3D const &b) const { - if (isIdentity()) { - return b; - } - if (b.isIdentity()) { - return *this; - } - - simd_float4x4 simdLhs = { - simd_make_float4(b.m11, b.m21, b.m31, b.m41), - simd_make_float4(b.m12, b.m22, b.m32, b.m42), - simd_make_float4(b.m13, b.m23, b.m33, b.m43), - simd_make_float4(b.m14, b.m24, b.m34, b.m44) - }; - simd_float4x4 simdRhs = { - simd_make_float4(m11, m21, m31, m41), - simd_make_float4(m12, m22, m32, m42), - simd_make_float4(m13, m23, m33, m43), - simd_make_float4(m14, m24, m34, m44) - }; - - simd_float4x4 simdResult = simd_mul(simdRhs, simdLhs); - return Transform3D( - simdResult.columns[0][0], simdResult.columns[1][0], simdResult.columns[2][0], simdResult.columns[3][0], - simdResult.columns[0][1], simdResult.columns[1][1], simdResult.columns[2][1], simdResult.columns[3][1], - simdResult.columns[0][2], simdResult.columns[1][2], simdResult.columns[2][2], simdResult.columns[3][2], - simdResult.columns[0][3], simdResult.columns[1][3], simdResult.columns[2][3], simdResult.columns[3][3] - ); -} - bool Transform3D::isInvertible() const { - return std::abs(m11 * m22 - m12 * m21) >= 0.00000001; + return Transform2D(*this).isInvertible(); + //return std::abs(m11 * m22 - m12 * m21) >= 0.00000001; } Transform3D Transform3D::inverted() const { - simd_float4x4 matrix = { - simd_make_float4(m11, m21, m31, m41), - simd_make_float4(m12, m22, m32, m42), - simd_make_float4(m13, m23, m33, m43), - simd_make_float4(m14, m24, m34, m44) - }; - simd_float4x4 result = simd_inverse(matrix); - Transform3D nativeResult = Transform3D( - result.columns[0][0], result.columns[1][0], result.columns[2][0], result.columns[3][0], - result.columns[0][1], result.columns[1][1], result.columns[2][1], result.columns[3][1], - result.columns[0][2], result.columns[1][2], result.columns[2][2], result.columns[3][2], - result.columns[0][3], result.columns[1][3], result.columns[2][3], result.columns[3][3] - ); - - return nativeResult; -} + return Transform2D(*this).inverted().transform3D(); +}*/ bool CGRect::intersects(CGRect const &other) const { return CGRectIntersectsRect(CGRectMake(x, y, width, height), CGRectMake(other.x, other.y, other.width, other.height)); @@ -585,29 +880,26 @@ CGRect CGRect::unionWith(CGRect const &other) const { return CGRect(result.origin.x, result.origin.y, result.size.width, result.size.height); } -CGRect CGRect::applyingTransform(Transform3D const &transform) const { +CGRect CGRect::applyingTransform(Transform2D const &transform) const { if (transform.isIdentity()) { return *this; } - simd_float3 simdRow1 = simd_make_float3(transform.m11, transform.m12, transform.m14); - simd_float3 simdRow2 = simd_make_float3(transform.m21, transform.m22, transform.m24); - simd_float3 simdRow3 = simd_make_float3(transform.m41, transform.m42, transform.m44); - Vector2D sourceTopLeft = Vector2D(x, y); Vector2D sourceTopRight = Vector2D(x + width, y); Vector2D sourceBottomLeft = Vector2D(x, y + height); Vector2D sourceBottomRight = Vector2D(x + width, y + height); - simd_float3 simdTopLeft = sourceTopLeft.x * simdRow1 + sourceTopLeft.y * simdRow2 + simdRow3; - simd_float3 simdTopRight = sourceTopRight.x * simdRow1 + sourceTopRight.y * simdRow2 + simdRow3; - simd_float3 simdBottomLeft = sourceBottomLeft.x * simdRow1 + sourceBottomLeft.y * simdRow2 + simdRow3; - simd_float3 simdBottomRight = sourceBottomRight.x * simdRow1 + sourceBottomRight.y * simdRow2 + simdRow3; + simd_float4 xs = simd_make_float4(sourceTopLeft.x, sourceTopRight.x, sourceBottomLeft.x, sourceBottomRight.x); + simd_float4 ys = simd_make_float4(sourceTopLeft.y, sourceTopRight.y, sourceBottomLeft.y, sourceBottomRight.y); - Vector2D topLeft = Vector2D(simdTopLeft[0] / simdTopLeft[2], simdTopLeft[1] / simdTopLeft[2]); - Vector2D topRight = Vector2D(simdTopRight[0] / simdTopRight[2], simdTopRight[1] / simdTopRight[2]); - Vector2D bottomLeft = Vector2D(simdBottomLeft[0] / simdBottomLeft[2], simdBottomLeft[1] / simdBottomLeft[2]); - Vector2D bottomRight = Vector2D(simdBottomRight[0] / simdBottomRight[2], simdBottomRight[1] / simdBottomRight[2]); + simd_float4 rx = xs * transform.rows().columns[0][0] + ys * transform.rows().columns[1][0] + transform.rows().columns[2][0]; + simd_float4 ry = xs * transform.rows().columns[0][1] + ys * transform.rows().columns[1][1] + transform.rows().columns[2][1]; + + Vector2D topLeft = Vector2D(rx[0], ry[0]); + Vector2D topRight = Vector2D(rx[1], ry[1]); + Vector2D bottomLeft = Vector2D(rx[2], ry[2]); + Vector2D bottomRight = Vector2D(rx[3], ry[3]); float minX = simd_reduce_min(simd_make_float4(topLeft.x, topRight.x, bottomLeft.x, bottomRight.x)); float minY = simd_reduce_min(simd_make_float4(topLeft.y, topRight.y, bottomLeft.y, bottomRight.y)); From 9c08df83a1120f38147246a56cbbf3f0d6bfba33 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 20 May 2024 15:37:06 +0400 Subject: [PATCH 30/32] Refactoring --- .../Sources/SoftwareLottieRenderer.mm | 2 +- Tests/LottieMetalTest/Sources/ViewController.swift | 4 ++-- .../Utility/Primitives/CompoundBezierPath.hpp | 13 ------------- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm index d53b21787b..c71328ef45 100644 --- a/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm +++ b/Tests/LottieMetalTest/SoftwareLottieRenderer/Sources/SoftwareLottieRenderer.mm @@ -622,7 +622,7 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) { CGPoint scale = CGPointMake(size.width / (CGFloat)animation.size.width, size.height / (CGFloat)animation.size.height); context->concatenate(lottie::Transform2D::makeScale(scale.x, scale.y)); - renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); + //renderLottieRenderNode(renderNode, context, lottie::Vector2D(context->width(), context->height()), rootTransform, 1.0, false, *_bezierPathsBoundingBoxContext.get()); return nil; } diff --git a/Tests/LottieMetalTest/Sources/ViewController.swift b/Tests/LottieMetalTest/Sources/ViewController.swift index 5bb4863b6a..3fa91463c3 100644 --- a/Tests/LottieMetalTest/Sources/ViewController.swift +++ b/Tests/LottieMetalTest/Sources/ViewController.swift @@ -176,7 +176,7 @@ public final class ViewController: UIViewController { let updatesPerSecond = Double(numUpdates) / deltaTime startTime = timestamp numUpdates = 0 - print("updatesPerSecond: \(updatesPerSecond)") + print("Ours: updatesPerSecond: \(updatesPerSecond)") } } }.start() @@ -205,7 +205,7 @@ public final class ViewController: UIViewController { let updatesPerSecond = Double(numUpdates) / deltaTime startTime = timestamp numUpdates = 0 - print("updatesPerSecond: \(updatesPerSecond)") + print("Rlottie: updatesPerSecond: \(updatesPerSecond)") } } }.start() diff --git a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp index 3b85cc8af0..58d02eb283 100644 --- a/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp +++ b/submodules/TelegramUI/Components/LottieCpp/Sources/Lottie/Private/Utility/Primitives/CompoundBezierPath.hpp @@ -69,19 +69,6 @@ public: return std::make_shared(); } - /*bool trimSimultaneously = false; - if (trimSimultaneously) { - /// Trim each path individually. - std::vector newPaths; - for (auto &path : paths) { - auto trimmedPaths = path.trim(fromPosition * path.length(), toPosition * path.length(), offset * path.length()); - for (const auto &trimmedPath : trimmedPaths) { - newPaths.push_back(trimmedPath); - } - } - return std::make_shared(newPaths); - }*/ - float lengthValue = length(); /// Normalize lengths to the curve length. From 6e28b2a4e2e30d862c7f0e9d28d1bc96f5f302dd Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 20 May 2024 18:51:23 +0400 Subject: [PATCH 31/32] Update tgcalls --- submodules/TgVoipWebrtc/tgcalls | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index 9effaaac6b..8721352f45 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit 9effaaac6b3372b12cc183702d839376038cec15 +Subproject commit 8721352f452128adec41c254b8407a4cb18cbbeb From 7c0216b9077eb1c40299a43056020e396da2ba3a Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Mon, 20 May 2024 18:51:35 +0400 Subject: [PATCH 32/32] Effect improvements --- .../Sources/AvatarVideoNode.swift | 4 +- .../ChatSendMessageContextScreen.swift | 41 +- .../Sources/StickerPackEmojisItem.swift | 16 +- .../Sources/ChatMessageBubbleItemNode.swift | 15 + .../Sources/EmojiKeyboardItemLayer.swift | 477 +++ .../Sources/EmojiPagerContentComponent.swift | 2838 +---------------- .../Sources/EmojiSearchHeaderView.swift | 607 ++++ .../EmojiSearchSearchBarComponent.swift | 19 +- .../Sources/EmptySearchResultsView.swift | 101 + .../EntityKeyboardTopPanelComponent.swift | 6 +- .../Sources/GroupEmbeddedView.swift | 209 ++ .../Sources/GroupExpandActionButton.swift | 128 + .../Sources/GroupHeaderActionButton.swift | 149 + .../Sources/GroupHeaderLayer.swift | 525 +++ .../Sources/PassthroughComponents.swift | 346 ++ .../Sources/PremiumBadgeView.swift | 140 + .../EntityKeyboard/Sources/WarpView.swift | 138 + .../Sources/EmojiListInputComponent.swift | 6 +- 18 files changed, 2939 insertions(+), 2826 deletions(-) create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchHeaderView.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/EmptySearchResultsView.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupEmbeddedView.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupExpandActionButton.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderActionButton.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderLayer.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/PassthroughComponents.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/PremiumBadgeView.swift create mode 100644 submodules/TelegramUI/Components/EntityKeyboard/Sources/WarpView.swift diff --git a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift index 6b17989a2e..634438cafa 100644 --- a/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift +++ b/submodules/AvatarVideoNode/Sources/AvatarVideoNode.swift @@ -27,7 +27,7 @@ public final class AvatarVideoNode: ASDisplayNode { private var fileDisposable = MetaDisposable() private var animationFile: TelegramMediaFile? - private var itemLayer: EmojiPagerContentComponent.View.ItemLayer? + private var itemLayer: EmojiKeyboardItemLayer? private var useAnimationNode = false private var animationNode: AnimatedStickerNode? private let stickerFetchedDisposable = MetaDisposable() @@ -101,7 +101,7 @@ public final class AvatarVideoNode: ASDisplayNode { let itemNativeFitSize = self.internalSize.width > 100.0 ? CGSize(width: 192.0, height: 192.0) : CGSize(width: 64.0, height: 64.0) let animationData = EntityKeyboardAnimationData(file: animationFile) - let itemLayer = EmojiPagerContentComponent.View.ItemLayer( + let itemLayer = EmojiKeyboardItemLayer( item: EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData), diff --git a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift index e4cf552b8e..b4d1d47116 100644 --- a/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift +++ b/submodules/ChatSendMessageActionUI/Sources/ChatSendMessageContextScreen.swift @@ -179,6 +179,9 @@ final class ChatSendMessageContextScreenComponent: Component { private var animateOutToEmpty: Bool = false private var initializationDisplayLink: SharedDisplayLinkDriver.Link? + private var updateSourcePositionsDisplayLink: SharedDisplayLinkDriver.Link? + + private var stableSourceSendButtonFrame: CGRect? override init(frame: CGRect) { self.backgroundView = BlurredBackgroundView(color: .clear, enableBlur: true) @@ -273,6 +276,21 @@ final class ChatSendMessageContextScreenComponent: Component { let environment = environment[EnvironmentType.self].value + if let previousEnvironment = self.environment, previousEnvironment.inputHeight != 0.0, environment.inputHeight == 0.0 { + DispatchQueue.main.async { [weak self] in + guard let self, let component = self.component else { + return + } + let stableSourceSendButtonFrame = convertFrame(component.sourceSendButton.bounds, from: component.sourceSendButton.view, to: self) + if self.stableSourceSendButtonFrame != stableSourceSendButtonFrame { + self.stableSourceSendButtonFrame = stableSourceSendButtonFrame + if !self.isUpdating { + self.state?.updated(transition: .spring(duration: 0.35)) + } + } + } + } + var transition = transition var transitionIsImmediate = transition.animation.isImmediate @@ -365,7 +383,19 @@ final class ChatSendMessageContextScreenComponent: Component { self.addSubview(sendButton) } - let sourceSendButtonFrame = convertFrame(component.sourceSendButton.bounds, from: component.sourceSendButton.view, to: self) + let sourceSendButtonFrame: CGRect + switch self.presentationAnimationState { + case .animatedOut: + sourceSendButtonFrame = convertFrame(component.sourceSendButton.bounds, from: component.sourceSendButton.view, to: self) + self.stableSourceSendButtonFrame = sourceSendButtonFrame + default: + if let stableSourceSendButtonFrame = self.stableSourceSendButtonFrame { + sourceSendButtonFrame = stableSourceSendButtonFrame + } else { + sourceSendButtonFrame = convertFrame(component.sourceSendButton.bounds, from: component.sourceSendButton.view, to: self) + self.stableSourceSendButtonFrame = sourceSendButtonFrame + } + } let sendButtonScale: CGFloat switch self.presentationAnimationState { @@ -843,6 +873,8 @@ final class ChatSendMessageContextScreenComponent: Component { if !self.isUpdating { self.state?.updated(transition: .easeInOut(duration: 0.2)) } + + self.endEditing(true) }) })) } @@ -1014,6 +1046,13 @@ final class ChatSendMessageContextScreenComponent: Component { } } + if let standaloneReactionAnimation, let targetView = messageItemView.effectIconView { + let effectSize = CGSize(width: 380.0, height: 380.0) + var effectFrame = effectSize.centered(around: targetView.convert(targetView.bounds.center, to: self)) + effectFrame.origin.x -= effectFrame.width * 0.3 + transition.setFrame(view: standaloneReactionAnimation.view, frame: effectFrame) + } + if let reactionContextNode = self.reactionContextNode { let reactionContextY = environment.statusBarHeight let size = availableSize diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift index 3d629bea08..f47734863d 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackEmojisItem.swift @@ -122,8 +122,8 @@ final class StickerPackEmojisItemNode: GridItemNode { private var boundsChangeTrackerLayer = SimpleLayer() - private var visibleItemLayers: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemLayer] = [:] - private var visibleItemPlaceholderViews: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemPlaceholderView] = [:] + private var visibleItemLayers: [EmojiKeyboardItemLayer.Key: EmojiKeyboardItemLayer] = [:] + private var visibleItemPlaceholderViews: [EmojiKeyboardItemLayer.Key: EmojiPagerContentComponent.View.ItemPlaceholderView] = [:] private let containerNode: ASDisplayNode private let titleNode: ImmediateTextNode @@ -195,7 +195,7 @@ final class StickerPackEmojisItemNode: GridItemNode { func targetItem(at point: CGPoint) -> (TelegramMediaFile, CALayer)? { if let (item, _) = self.item(atPoint: point), let file = item.itemFile { - let itemId = EmojiPagerContentComponent.View.ItemLayer.Key( + let itemId = EmojiKeyboardItemLayer.Key( groupId: 0, itemId: .animation(.file(file.fileId)) ) @@ -237,7 +237,7 @@ final class StickerPackEmojisItemNode: GridItemNode { private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (EmojiPagerContentComponent.Item, CGRect)? { let localPoint = point - var closestItem: (key: EmojiPagerContentComponent.View.ItemLayer.Key, distance: CGFloat)? + var closestItem: (key: EmojiKeyboardItemLayer.Key, distance: CGFloat)? for (key, itemLayer) in self.visibleItemLayers { if extendedHitRange { @@ -308,7 +308,7 @@ final class StickerPackEmojisItemNode: GridItemNode { let animationRenderer = item.animationRenderer let theme = item.theme let items = item.items - var validIds = Set() + var validIds = Set() let itemLayout: ItemLayout if let current = self.itemLayout, current.width == self.size.width && current.itemsCount == items.count { @@ -322,7 +322,7 @@ final class StickerPackEmojisItemNode: GridItemNode { for index in 0 ..< items.count { let item = items[index] - let itemId = EmojiPagerContentComponent.View.ItemLayer.Key( + let itemId = EmojiKeyboardItemLayer.Key( groupId: 0, itemId: .animation(.file(item.file.fileId)) ) @@ -334,7 +334,7 @@ final class StickerPackEmojisItemNode: GridItemNode { var updateItemLayerPlaceholder = false var itemTransition = transition - let itemLayer: EmojiPagerContentComponent.View.ItemLayer + let itemLayer: EmojiKeyboardItemLayer if let current = self.visibleItemLayers[itemId] { itemLayer = current } else { @@ -342,7 +342,7 @@ final class StickerPackEmojisItemNode: GridItemNode { itemTransition = .immediate let animationData = EntityKeyboardAnimationData(file: item.file) - itemLayer = EmojiPagerContentComponent.View.ItemLayer( + itemLayer = EmojiKeyboardItemLayer( item: EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 6d2a04c29c..3b078023ba 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -638,6 +638,8 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var replyRecognizer: ChatSwipeToReplyRecognizer? private var currentSwipeAction: ChatControllerInteractionSwipeAction? + private var fetchEffectDisposable: Disposable? + //private let debugNode: ASDisplayNode override public var visibility: ListViewItemNodeVisibility { @@ -838,6 +840,10 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + deinit { + self.fetchEffectDisposable?.dispose() + } override public func cancelInsertionAnimations() { self.shadowNode.layer.removeAllAnimations() @@ -5877,6 +5883,9 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI private var additionalAnimationNodes: [ChatMessageTransitionNode.DecorationItemNode] = [] private func playPremiumStickerAnimation(effect: AvailableMessageEffects.MessageEffect, force: Bool) { + guard let item = self.item else { + return + } if self.playedPremiumStickerAnimation && !force { return } @@ -5884,10 +5893,16 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let effectAnimation = effect.effectAnimation { self.playEffectAnimation(resource: effectAnimation.resource, isStickerEffect: true) + if self.fetchEffectDisposable == nil { + self.fetchEffectDisposable = freeMediaFileResourceInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: .standalone(media: effectAnimation), resource: effectAnimation.resource).startStrict() + } } else { let effectSticker = effect.effectSticker if let effectFile = effectSticker.videoThumbnails.first { self.playEffectAnimation(resource: effectFile.resource, isStickerEffect: true) + if self.fetchEffectDisposable == nil { + self.fetchEffectDisposable = freeMediaFileResourceInteractiveFetched(account: item.context.account, userLocation: .other, fileReference: .standalone(media: effectSticker), resource: effectFile.resource).startStrict() + } } } } diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift new file mode 100644 index 0000000000..19ee0562ae --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiKeyboardItemLayer.swift @@ -0,0 +1,477 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import MultiAnimationRenderer +import AnimationCache +import SwiftSignalKit +import TelegramCore +import AccountContext +import TelegramPresentationData +import EmojiTextAttachmentView +import EmojiStatusComponent + +final class EmojiKeyboardCloneItemLayer: SimpleLayer { +} + +public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget { + public struct Key: Hashable { + var groupId: AnyHashable + var itemId: EmojiPagerContentComponent.ItemContent.Id + + public init( + groupId: AnyHashable, + itemId: EmojiPagerContentComponent.ItemContent.Id + ) { + self.groupId = groupId + self.itemId = itemId + } + } + + enum Badge: Equatable { + case premium + case locked + case featured + case text(String) + case customFile(TelegramMediaFile) + } + + public let item: EmojiPagerContentComponent.Item + private let context: AccountContext + + private var content: EmojiPagerContentComponent.ItemContent + private var theme: PresentationTheme? + + private let placeholderColor: UIColor + let pixelSize: CGSize + let pointSize: CGSize + private let size: CGSize + private var disposable: Disposable? + private var fetchDisposable: Disposable? + private var premiumBadgeView: PremiumBadgeView? + + private var iconLayer: SimpleLayer? + private var tintIconLayer: SimpleLayer? + + private(set) var tintContentLayer: SimpleLayer? + + private var badge: Badge? + private var validSize: CGSize? + + private var isInHierarchyValue: Bool = false + public var isVisibleForAnimations: Bool = false { + didSet { + if self.isVisibleForAnimations != oldValue { + self.updatePlayback() + } + } + } + public private(set) var displayPlaceholder: Bool = false + public let onUpdateDisplayPlaceholder: (Bool, Double) -> Void + + weak var cloneLayer: EmojiKeyboardCloneItemLayer? { + didSet { + if let cloneLayer = self.cloneLayer { + cloneLayer.contents = self.contents + } + } + } + + override public var contents: Any? { + didSet { + self.onContentsUpdate() + if let cloneLayer = self.cloneLayer { + cloneLayer.contents = self.contents + } + } + } + + override public var position: CGPoint { + get { + return super.position + } set(value) { + if let mirrorLayer = self.tintContentLayer { + mirrorLayer.position = value + } + super.position = value + } + } + + override public var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let mirrorLayer = self.tintContentLayer { + mirrorLayer.bounds = value + } + super.bounds = value + } + } + + override public func add(_ animation: CAAnimation, forKey key: String?) { + if let mirrorLayer = self.tintContentLayer { + mirrorLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override public func removeAllAnimations() { + if let mirrorLayer = self.tintContentLayer { + mirrorLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override public func removeAnimation(forKey: String) { + if let mirrorLayer = self.tintContentLayer { + mirrorLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } + + public var onContentsUpdate: () -> Void = {} + public var onLoop: () -> Void = {} + + public init( + item: EmojiPagerContentComponent.Item, + context: AccountContext, + attemptSynchronousLoad: Bool, + content: EmojiPagerContentComponent.ItemContent, + cache: AnimationCache, + renderer: MultiAnimationRenderer, + placeholderColor: UIColor, + blurredBadgeColor: UIColor, + accentIconColor: UIColor, + pointSize: CGSize, + onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void + ) { + self.item = item + self.context = context + self.content = content + self.placeholderColor = placeholderColor + self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder + + let scale = min(2.0, UIScreenScale) + let pixelSize = CGSize(width: pointSize.width * scale, height: pointSize.height * scale) + self.pixelSize = pixelSize + self.pointSize = pointSize + self.size = CGSize(width: pixelSize.width / scale, height: pixelSize.height / scale) + + super.init() + + switch content { + case let .animation(animationData): + let loadAnimation: () -> Void = { [weak self] in + guard let strongSelf = self else { + return + } + + strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, unique: false, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: pixelSize.width >= 120.0, customColor: animationData.isTemplate ? .white : nil)) + } + + if attemptSynchronousLoad { + if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize) { + self.updateDisplayPlaceholder(displayPlaceholder: true) + + self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in + if !isFinal { + if !success { + Queue.mainQueue().async { + guard let strongSelf = self else { + return + } + + strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) + } + } + return + } + + Queue.mainQueue().async { + loadAnimation() + + if !success { + guard let strongSelf = self else { + return + } + + strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) + } + } + }) + } else { + loadAnimation() + } + } else { + self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in + if !isFinal { + if !success { + Queue.mainQueue().async { + guard let strongSelf = self else { + return + } + + strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) + } + } + return + } + + Queue.mainQueue().async { + loadAnimation() + + if !success { + guard let strongSelf = self else { + return + } + + strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) + } + } + }) + } + case let .staticEmoji(staticEmoji): + let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + let preScaleFactor: CGFloat = 1.0 + let scaledSize = CGSize(width: floor(size.width * preScaleFactor), height: floor(size.height * preScaleFactor)) + let scaleFactor = scaledSize.width / size.width + + context.scaleBy(x: 1.0 / scaleFactor, y: 1.0 / scaleFactor) + + let string = NSAttributedString(string: staticEmoji, font: Font.regular(floor(32.0 * scaleFactor)), textColor: .black) + let boundingRect = string.boundingRect(with: scaledSize, options: .usesLineFragmentOrigin, context: nil) + UIGraphicsPushContext(context) + string.draw(at: CGPoint(x: floorToScreenPixels((scaledSize.width - boundingRect.width) / 2.0 + boundingRect.minX), y: floorToScreenPixels((scaledSize.height - boundingRect.height) / 2.0 + boundingRect.minY))) + UIGraphicsPopContext() + }) + self.contents = image?.cgImage + case let .icon(icon): + let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + UIGraphicsPushContext(context) + + switch icon { + case .premiumStar: + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: accentIconColor) { + let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) + image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) + } + case let .topic(title, color): + let colors = topicIconColors(for: color) + if let image = generateTopicIcon(backgroundColors: colors.0.map { UIColor(rgb: $0) }, strokeColors: colors.1.map { UIColor(rgb: $0) }, title: title) { + let imageSize = image.size//.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) + image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) + } + case .stop: + if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/NoIcon"), color: .white) { + let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) + image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) + } + case .add: + break + } + + UIGraphicsPopContext() + })?.withRenderingMode(icon == .stop ? .alwaysTemplate : .alwaysOriginal) + self.contents = image?.cgImage + } + + if case .icon(.add) = content { + let tintContentLayer = SimpleLayer() + self.tintContentLayer = tintContentLayer + + let iconLayer = SimpleLayer() + self.iconLayer = iconLayer + self.addSublayer(iconLayer) + + let tintIconLayer = SimpleLayer() + self.tintIconLayer = tintIconLayer + tintContentLayer.addSublayer(tintIconLayer) + } + } + + override public init(layer: Any) { + guard let layer = layer as? EmojiKeyboardItemLayer else { + preconditionFailure() + } + + self.context = layer.context + self.item = layer.item + + self.content = layer.content + self.placeholderColor = layer.placeholderColor + self.size = layer.size + self.pixelSize = layer.pixelSize + self.pointSize = layer.pointSize + + self.onUpdateDisplayPlaceholder = { _, _ in } + + super.init(layer: layer) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.disposable?.dispose() + self.fetchDisposable?.dispose() + } + + public override func action(forKey event: String) -> CAAction? { + if event == kCAOnOrderIn { + self.isInHierarchyValue = true + } else if event == kCAOnOrderOut { + self.isInHierarchyValue = false + } + self.updatePlayback() + return nullAction + } + + func update( + content: EmojiPagerContentComponent.ItemContent, + theme: PresentationTheme + ) { + var themeUpdated = false + if self.theme !== theme { + self.theme = theme + themeUpdated = true + } + var contentUpdated = false + if self.content != content { + self.content = content + contentUpdated = true + } + + if themeUpdated || contentUpdated { + if case let .icon(icon) = content, case let .topic(title, color) = icon { + let image = generateImage(self.size, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + UIGraphicsPushContext(context) + + let colors = topicIconColors(for: color) + if let image = generateTopicIcon(backgroundColors: colors.0.map { UIColor(rgb: $0) }, strokeColors: colors.1.map { UIColor(rgb: $0) }, title: title) { + let imageSize = image.size + image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) + } + + UIGraphicsPopContext() + }) + self.contents = image?.cgImage + } else if case .icon(.add) = content { + guard let iconLayer = self.iconLayer, let tintIconLayer = self.tintIconLayer else { + return + } + func generateIcon(color: UIColor) -> UIImage? { + return generateImage(self.pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + UIGraphicsPushContext(context) + + context.setFillColor(color.withMultipliedAlpha(0.2).cgColor) + context.fillEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: 8.0, dy: 8.0)) + context.setFillColor(color.cgColor) + + let plusSize = CGSize(width: 4.5, height: 31.5) + context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height), cornerRadius: plusSize.width / 2.0).cgPath) + context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width), cornerRadius: plusSize.width / 2.0).cgPath) + context.fillPath() + + UIGraphicsPopContext() + }) + } + + let needsVibrancy = !theme.overallDarkAppearance + let color = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor + + iconLayer.contents = generateIcon(color: color)?.cgImage + tintIconLayer.contents = generateIcon(color: .white)?.cgImage + + tintIconLayer.isHidden = !needsVibrancy + } + } + } + + func update( + transition: Transition, + size: CGSize, + badge: Badge?, + blurredBadgeColor: UIColor, + blurredBadgeBackgroundColor: UIColor + ) { + if self.badge != badge || self.validSize != size { + self.badge = badge + self.validSize = size + + if let iconLayer = self.iconLayer, let tintIconLayer = self.tintIconLayer { + transition.setFrame(layer: iconLayer, frame: CGRect(origin: .zero, size: size)) + transition.setFrame(layer: tintIconLayer, frame: CGRect(origin: .zero, size: size)) + } + + if let badge = badge { + var badgeTransition = transition + let premiumBadgeView: PremiumBadgeView + if let current = self.premiumBadgeView { + premiumBadgeView = current + } else { + badgeTransition = .immediate + premiumBadgeView = PremiumBadgeView(context: self.context) + self.premiumBadgeView = premiumBadgeView + self.addSublayer(premiumBadgeView.layer) + } + + let badgeDiameter = min(16.0, floor(size.height * 0.5)) + let badgeSize = CGSize(width: badgeDiameter, height: badgeDiameter) + badgeTransition.setFrame(view: premiumBadgeView, frame: CGRect(origin: CGPoint(x: size.width - badgeSize.width, y: size.height - badgeSize.height), size: badgeSize)) + premiumBadgeView.update(transition: badgeTransition, badge: badge, backgroundColor: blurredBadgeColor, size: badgeSize) + + self.blurredRepresentationBackgroundColor = blurredBadgeBackgroundColor + self.blurredRepresentationTarget = premiumBadgeView.contentLayer + } else { + if let premiumBadgeView = self.premiumBadgeView { + self.premiumBadgeView = nil + premiumBadgeView.removeFromSuperview() + + self.blurredRepresentationBackgroundColor = nil + self.blurredRepresentationTarget = nil + } + } + } + } + + private func updatePlayback() { + let shouldBePlaying = self.isInHierarchyValue && self.isVisibleForAnimations + + self.shouldBeAnimating = shouldBePlaying + } + + public override func updateDisplayPlaceholder(displayPlaceholder: Bool) { + if self.displayPlaceholder == displayPlaceholder { + return + } + + self.displayPlaceholder = displayPlaceholder + self.onUpdateDisplayPlaceholder(displayPlaceholder, 0.0) + } + + public override func transitionToContents(_ contents: AnyObject, didLoop: Bool) { + self.contents = contents + + if self.displayPlaceholder { + self.displayPlaceholder = false + self.onUpdateDisplayPlaceholder(false, 0.2) + self.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) + } + + if didLoop { + self.onLoop() + } + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift index da9153aea9..157b94af2b 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiPagerContentComponent.swift @@ -26,144 +26,6 @@ import EmojiStatusComponent import TelegramNotices import GenerateStickerPlaceholderImage -private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white) -private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white) -private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white) - - -private final class WarpView: UIView { - private final class WarpPartView: UIView { - let cloneView: PortalView - - init?(contentView: PortalSourceView) { - guard let cloneView = PortalView(matchPosition: false) else { - return nil - } - self.cloneView = cloneView - - super.init(frame: CGRect()) - - self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.0) - - self.clipsToBounds = true - self.addSubview(cloneView.view) - contentView.addPortal(view: cloneView) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(containerSize: CGSize, rect: CGRect, transition: Transition) { - transition.setFrame(view: self.cloneView.view, frame: CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: CGSize(width: containerSize.width, height: containerSize.height))) - } - } - - let contentView: PortalSourceView - - private let clippingView: UIView - - private var warpViews: [WarpPartView] = [] - private let warpMaskContainer: UIView - private let warpMaskGradientLayer: SimpleGradientLayer - - override init(frame: CGRect) { - self.contentView = PortalSourceView() - self.clippingView = UIView() - - self.warpMaskContainer = UIView() - self.warpMaskGradientLayer = SimpleGradientLayer() - self.warpMaskContainer.layer.mask = self.warpMaskGradientLayer - - super.init(frame: frame) - - self.clippingView.addSubview(self.contentView) - - self.clippingView.clipsToBounds = true - self.addSubview(self.clippingView) - self.addSubview(self.warpMaskContainer) - - for _ in 0 ..< 8 { - if let warpView = WarpPartView(contentView: self.contentView) { - self.warpViews.append(warpView) - self.warpMaskContainer.addSubview(warpView) - } - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(size: CGSize, topInset: CGFloat, warpHeight: CGFloat, theme: PresentationTheme, transition: Transition) { - transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) - - let allItemsHeight = warpHeight * 0.5 - for i in 0 ..< self.warpViews.count { - let itemHeight = warpHeight / CGFloat(self.warpViews.count) - let itemFraction = CGFloat(i + 1) / CGFloat(self.warpViews.count) - let _ = itemHeight - - let da = CGFloat.pi * 0.5 / CGFloat(self.warpViews.count) - let alpha = CGFloat.pi * 0.5 - itemFraction * CGFloat.pi * 0.5 - let endPoint = CGPoint(x: cos(alpha), y: sin(alpha)) - let prevAngle = alpha + da - let prevPt = CGPoint(x: cos(prevAngle), y: sin(prevAngle)) - var angle: CGFloat - angle = -atan2(endPoint.y - prevPt.y, endPoint.x - prevPt.x) - - let itemLengthVector = CGPoint(x: endPoint.x - prevPt.x, y: endPoint.y - prevPt.y) - let itemLength = sqrt(itemLengthVector.x * itemLengthVector.x + itemLengthVector.y * itemLengthVector.y) * warpHeight * 0.5 - let _ = itemLength - - var transform: CATransform3D - transform = CATransform3DIdentity - transform.m34 = 1.0 / 240.0 - - transform = CATransform3DTranslate(transform, 0.0, prevPt.x * allItemsHeight, (1.0 - prevPt.y) * allItemsHeight) - transform = CATransform3DRotate(transform, angle, 1.0, 0.0, 0.0) - - let positionY = size.height - allItemsHeight + 4.0 + CGFloat(i) * itemLength - let rect = CGRect(origin: CGPoint(x: 0.0, y: positionY), size: CGSize(width: size.width, height: itemLength)) - transition.setPosition(view: self.warpViews[i], position: CGPoint(x: rect.midX, y: 4.0)) - transition.setBounds(view: self.warpViews[i], bounds: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: itemLength))) - transition.setTransform(view: self.warpViews[i], transform: transform) - self.warpViews[i].update(containerSize: size, rect: rect, transition: transition) - } - - let clippingTopInset: CGFloat = topInset - let frame = CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: CGSize(width: size.width, height: -clippingTopInset + size.height - 21.0)) - transition.setPosition(view: self.clippingView, position: frame.center) - transition.setBounds(view: self.clippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: frame.size)) - self.clippingView.clipsToBounds = true - - transition.setFrame(view: self.warpMaskContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - allItemsHeight), size: CGSize(width: size.width, height: allItemsHeight))) - - var locations: [NSNumber] = [] - var colors: [CGColor] = [] - let numStops = 6 - for i in 0 ..< numStops { - let step = CGFloat(i) / CGFloat(numStops - 1) - locations.append(step as NSNumber) - colors.append(UIColor.black.withAlphaComponent(1.0 - step * step).cgColor) - } - - let gradientHeight: CGFloat = 6.0 - self.warpMaskGradientLayer.startPoint = CGPoint(x: 0.0, y: (allItemsHeight - gradientHeight) / allItemsHeight) - self.warpMaskGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) - - self.warpMaskGradientLayer.locations = locations - self.warpMaskGradientLayer.colors = colors - self.warpMaskGradientLayer.type = .axial - - transition.setFrame(layer: self.warpMaskGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: allItemsHeight))) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - return self.contentView.hitTest(point, with: event) - } -} - public struct EmojiComponentReactionItem: Equatable { public var reaction: MessageReaction.Reaction public var file: TelegramMediaFile @@ -260,2177 +122,6 @@ public final class EntityKeyboardAnimationData: Equatable { } } -public class PassthroughLayer: CALayer { - public var mirrorLayer: CALayer? - - override init() { - super.init() - } - - override init(layer: Any) { - super.init(layer: layer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override public var position: CGPoint { - get { - return super.position - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.position = value - } - super.position = value - } - } - - override public var bounds: CGRect { - get { - return super.bounds - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.bounds = value - } - super.bounds = value - } - } - - override public var opacity: Float { - get { - return super.opacity - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.opacity = value - } - super.opacity = value - } - } - - override public var sublayerTransform: CATransform3D { - get { - return super.sublayerTransform - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.sublayerTransform = value - } - super.sublayerTransform = value - } - } - - override public var transform: CATransform3D { - get { - return super.transform - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.transform = value - } - super.transform = value - } - } - - override public func add(_ animation: CAAnimation, forKey key: String?) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.add(animation, forKey: key) - } - - super.add(animation, forKey: key) - } - - override public func removeAllAnimations() { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.removeAllAnimations() - } - - super.removeAllAnimations() - } - - override public func removeAnimation(forKey: String) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.removeAnimation(forKey: forKey) - } - - super.removeAnimation(forKey: forKey) - } -} - -open class PassthroughView: UIView { - override public static var layerClass: AnyClass { - return PassthroughLayer.self - } - - public let passthroughView: UIView - - override public init(frame: CGRect) { - self.passthroughView = UIView() - - super.init(frame: frame) - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.passthroughView.layer - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -private class PassthroughShapeLayer: CAShapeLayer { - var mirrorLayer: CAShapeLayer? - - override init() { - super.init() - } - - override init(layer: Any) { - super.init(layer: layer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override var position: CGPoint { - get { - return super.position - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.position = value - } - super.position = value - } - } - - override var bounds: CGRect { - get { - return super.bounds - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.bounds = value - } - super.bounds = value - } - } - - override var opacity: Float { - get { - return super.opacity - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.opacity = value - } - super.opacity = value - } - } - - override var sublayerTransform: CATransform3D { - get { - return super.sublayerTransform - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.sublayerTransform = value - } - super.sublayerTransform = value - } - } - - override var transform: CATransform3D { - get { - return super.transform - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.transform = value - } - super.transform = value - } - } - - override var path: CGPath? { - get { - return super.path - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.path = value - } - super.path = value - } - } - - override var fillColor: CGColor? { - get { - return super.fillColor - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.fillColor = value - } - super.fillColor = value - } - } - - override var fillRule: CAShapeLayerFillRule { - get { - return super.fillRule - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.fillRule = value - } - super.fillRule = value - } - } - - override var strokeColor: CGColor? { - get { - return super.strokeColor - } set(value) { - /*if let mirrorLayer = self.mirrorLayer { - mirrorLayer.strokeColor = value - }*/ - super.strokeColor = value - } - } - - override var strokeStart: CGFloat { - get { - return super.strokeStart - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.strokeStart = value - } - super.strokeStart = value - } - } - - override var strokeEnd: CGFloat { - get { - return super.strokeEnd - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.strokeEnd = value - } - super.strokeEnd = value - } - } - - override var lineWidth: CGFloat { - get { - return super.lineWidth - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.lineWidth = value - } - super.lineWidth = value - } - } - - override var miterLimit: CGFloat { - get { - return super.miterLimit - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.miterLimit = value - } - super.miterLimit = value - } - } - - override var lineCap: CAShapeLayerLineCap { - get { - return super.lineCap - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.lineCap = value - } - super.lineCap = value - } - } - - override var lineJoin: CAShapeLayerLineJoin { - get { - return super.lineJoin - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.lineJoin = value - } - super.lineJoin = value - } - } - - override var lineDashPhase: CGFloat { - get { - return super.lineDashPhase - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.lineDashPhase = value - } - super.lineDashPhase = value - } - } - - override var lineDashPattern: [NSNumber]? { - get { - return super.lineDashPattern - } set(value) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.lineDashPattern = value - } - super.lineDashPattern = value - } - } - - override func add(_ animation: CAAnimation, forKey key: String?) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.add(animation, forKey: key) - } - - super.add(animation, forKey: key) - } - - override func removeAllAnimations() { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.removeAllAnimations() - } - - super.removeAllAnimations() - } - - override func removeAnimation(forKey: String) { - if let mirrorLayer = self.mirrorLayer { - mirrorLayer.removeAnimation(forKey: forKey) - } - - super.removeAnimation(forKey: forKey) - } -} - -private let itemBadgeTextFont: UIFont = { - return Font.regular(10.0) -}() - -private final class PremiumBadgeView: UIView { - private let context: AccountContext - - private var badge: EmojiPagerContentComponent.View.ItemLayer.Badge? - - let contentLayer: SimpleLayer - private let overlayColorLayer: SimpleLayer - private let iconLayer: SimpleLayer - private var customFileLayer: InlineFileIconLayer? - - init(context: AccountContext) { - self.context = context - - self.contentLayer = SimpleLayer() - self.contentLayer.contentsGravity = .resize - self.contentLayer.masksToBounds = true - - self.overlayColorLayer = SimpleLayer() - self.overlayColorLayer.masksToBounds = true - - self.iconLayer = SimpleLayer() - - super.init(frame: CGRect()) - - self.layer.addSublayer(self.contentLayer) - self.layer.addSublayer(self.overlayColorLayer) - self.layer.addSublayer(self.iconLayer) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(transition: Transition, badge: EmojiPagerContentComponent.View.ItemLayer.Badge, backgroundColor: UIColor, size: CGSize) { - if self.badge != badge { - self.badge = badge - - switch badge { - case .premium: - self.iconLayer.contents = premiumBadgeIcon?.cgImage - case .featured: - self.iconLayer.contents = featuredBadgeIcon?.cgImage - case .locked: - self.iconLayer.contents = lockedBadgeIcon?.cgImage - case let .text(text): - let string = NSAttributedString(string: text, font: itemBadgeTextFont) - let size = CGSize(width: 12.0, height: 12.0) - let stringBounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - let image = generateImage(size, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - string.draw(at: CGPoint(x: floor((size.width - stringBounds.width) * 0.5), y: floor((size.height - stringBounds.height) * 0.5))) - UIGraphicsPopContext() - }) - self.iconLayer.contents = image?.cgImage - case .customFile: - self.iconLayer.contents = nil - } - - if case let .customFile(customFile) = badge { - let customFileLayer: InlineFileIconLayer - if let current = self.customFileLayer { - customFileLayer = current - } else { - customFileLayer = InlineFileIconLayer( - context: self.context, - userLocation: .other, - attemptSynchronousLoad: false, - file: customFile, - cache: self.context.animationCache, - renderer: self.context.animationRenderer, - unique: false, - placeholderColor: .clear, - pointSize: CGSize(width: 18.0, height: 18.0), - dynamicColor: nil - ) - self.customFileLayer = customFileLayer - self.layer.addSublayer(customFileLayer) - } - let _ = customFileLayer - } else { - if let customFileLayer = self.customFileLayer { - self.customFileLayer = nil - customFileLayer.removeFromSuperlayer() - } - } - } - - let iconInset: CGFloat - switch badge { - case .premium: - iconInset = 2.0 - case .featured: - iconInset = 0.0 - case .locked: - iconInset = 0.0 - case .text, .customFile: - iconInset = 0.0 - } - - switch badge { - case .text, .customFile: - self.contentLayer.isHidden = true - self.overlayColorLayer.isHidden = true - default: - self.contentLayer.isHidden = false - self.overlayColorLayer.isHidden = false - } - - self.overlayColorLayer.backgroundColor = backgroundColor.cgColor - - transition.setFrame(layer: self.contentLayer, frame: CGRect(origin: CGPoint(), size: size)) - transition.setCornerRadius(layer: self.contentLayer, cornerRadius: min(size.width / 2.0, size.height / 2.0)) - - transition.setFrame(layer: self.overlayColorLayer, frame: CGRect(origin: CGPoint(), size: size)) - transition.setCornerRadius(layer: self.overlayColorLayer, cornerRadius: min(size.width / 2.0, size.height / 2.0)) - - transition.setFrame(layer: self.iconLayer, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: iconInset, dy: iconInset)) - - if let customFileLayer = self.customFileLayer { - let iconSize = CGSize(width: 18.0, height: 18.0) - transition.setFrame(layer: customFileLayer, frame: CGRect(origin: CGPoint(), size: iconSize)) - } - } -} - -private final class GroupHeaderActionButton: UIButton { - override static var layerClass: AnyClass { - return PassthroughLayer.self - } - - let tintContainerLayer: SimpleLayer - - private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? - private let backgroundLayer: SimpleLayer - private let tintBackgroundLayer: SimpleLayer - private let textLayer: SimpleLayer - private let tintTextLayer: SimpleLayer - private let pressed: () -> Void - - init(pressed: @escaping () -> Void) { - self.pressed = pressed - - self.tintContainerLayer = SimpleLayer() - - self.backgroundLayer = SimpleLayer() - self.backgroundLayer.masksToBounds = true - - self.tintBackgroundLayer = SimpleLayer() - self.tintBackgroundLayer.masksToBounds = true - - self.textLayer = SimpleLayer() - self.tintTextLayer = SimpleLayer() - - super.init(frame: CGRect()) - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerLayer - - self.layer.addSublayer(self.backgroundLayer) - self.layer.addSublayer(self.textLayer) - - self.addTarget(self, action: #selector(self.onPressed), for: .touchUpInside) - - self.tintContainerLayer.addSublayer(self.tintBackgroundLayer) - self.tintContainerLayer.addSublayer(self.tintTextLayer) - } - - required init(coder: NSCoder) { - preconditionFailure() - } - - @objc private func onPressed() { - self.pressed() - } - - override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - self.alpha = 0.6 - - return super.beginTracking(touch, with: event) - } - - override func endTracking(_ touch: UITouch?, with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.endTracking(touch, with: event) - } - - override func cancelTracking(with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.cancelTracking(with: event) - } - - override func touchesCancelled(_ touches: Set, with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.touchesCancelled(touches, with: event) - } - - func update(theme: PresentationTheme, title: String, compact: Bool) -> CGSize { - let textConstrainedWidth: CGFloat = 100.0 - - let needsVibrancy = !theme.overallDarkAppearance && compact - - let foregroundColor: UIColor - let backgroundColor: UIColor - - if compact { - foregroundColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor - backgroundColor = foregroundColor.withMultipliedAlpha(0.2) - } else { - foregroundColor = theme.list.itemCheckColors.foregroundColor - backgroundColor = theme.list.itemCheckColors.fillColor - } - - self.backgroundLayer.backgroundColor = backgroundColor.cgColor - self.tintBackgroundLayer.backgroundColor = UIColor.white.withAlphaComponent(0.2).cgColor - - self.tintContainerLayer.isHidden = !needsVibrancy - - let textSize: CGSize - if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == foregroundColor, currentTextLayout.constrainedWidth == textConstrainedWidth { - textSize = currentTextLayout.size - } else { - let font: UIFont = compact ? Font.medium(11.0) : Font.semibold(15.0) - let string = NSAttributedString(string: title.uppercased(), font: font, textColor: foregroundColor) - let tintString = NSAttributedString(string: title.uppercased(), font: font, textColor: .white) - let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) - self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - string.draw(in: stringBounds) - - UIGraphicsPopContext() - })?.cgImage - self.tintTextLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - tintString.draw(in: stringBounds) - - UIGraphicsPopContext() - })?.cgImage - self.currentTextLayout = (title, foregroundColor, textConstrainedWidth, textSize) - } - - let size = CGSize(width: textSize.width + (compact ? 6.0 : 16.0) * 2.0, height: compact ? 16.0 : 28.0) - - let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) - self.textLayer.frame = textFrame - self.tintTextLayer.frame = textFrame - - self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) - self.backgroundLayer.cornerRadius = min(size.width, size.height) / 2.0 - - self.tintBackgroundLayer.frame = self.backgroundLayer.frame - self.tintBackgroundLayer.cornerRadius = self.backgroundLayer.cornerRadius - - return size - } -} - -private final class GroupHeaderLayer: UIView { - override static var layerClass: AnyClass { - return PassthroughLayer.self - } - - private let actionPressed: () -> Void - private let performItemAction: (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void - - private let textLayer: SimpleLayer - private let tintTextLayer: SimpleLayer - - private var subtitleLayer: SimpleLayer? - private var tintSubtitleLayer: SimpleLayer? - private var lockIconLayer: SimpleLayer? - private var tintLockIconLayer: SimpleLayer? - private var badgeLayer: SimpleLayer? - private var tintBadgeLayer: SimpleLayer? - private(set) var clearIconLayer: SimpleLayer? - private var tintClearIconLayer: SimpleLayer? - private var separatorLayer: SimpleLayer? - private var tintSeparatorLayer: SimpleLayer? - private var actionButton: GroupHeaderActionButton? - - private var groupEmbeddedView: GroupEmbeddedView? - - private var theme: PresentationTheme? - - private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? - private var currentSubtitleLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? - - let tintContentLayer: SimpleLayer - - init(actionPressed: @escaping () -> Void, performItemAction: @escaping (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void) { - self.actionPressed = actionPressed - self.performItemAction = performItemAction - - self.textLayer = SimpleLayer() - self.tintTextLayer = SimpleLayer() - - self.tintContentLayer = SimpleLayer() - - super.init(frame: CGRect()) - - self.layer.addSublayer(self.textLayer) - self.tintContentLayer.addSublayer(self.tintTextLayer) - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContentLayer - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update( - context: AccountContext, - theme: PresentationTheme, - forceNeedsVibrancy: Bool, - layoutType: EmojiPagerContentComponent.ItemLayoutType, - hasTopSeparator: Bool, - actionButtonTitle: String?, - actionButtonIsCompact: Bool, - title: String, - subtitle: String?, - badge: String?, - isPremiumLocked: Bool, - hasClear: Bool, - embeddedItems: [EmojiPagerContentComponent.Item]?, - isStickers: Bool, - constrainedSize: CGSize, - insets: UIEdgeInsets, - cache: AnimationCache, - renderer: MultiAnimationRenderer, - attemptSynchronousLoad: Bool - ) -> (size: CGSize, centralContentWidth: CGFloat) { - var themeUpdated = false - if self.theme !== theme { - self.theme = theme - themeUpdated = true - } - - let needsVibrancy = !theme.overallDarkAppearance || forceNeedsVibrancy - - let textOffsetY: CGFloat - if hasTopSeparator { - textOffsetY = 9.0 - } else { - textOffsetY = 0.0 - } - - let subtitleColor: UIColor - if theme.overallDarkAppearance && forceNeedsVibrancy { - subtitleColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor.withMultipliedAlpha(0.2) - } else { - subtitleColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor - } - - let color: UIColor - let needsTintText: Bool - if subtitle != nil { - color = theme.chat.inputPanel.primaryTextColor - needsTintText = false - } else { - color = subtitleColor - needsTintText = true - } - - let titleHorizontalOffset: CGFloat - if isPremiumLocked { - titleHorizontalOffset = 10.0 + 2.0 - } else { - titleHorizontalOffset = 0.0 - } - - var actionButtonSize: CGSize? - if let actionButtonTitle = actionButtonTitle { - let actionButton: GroupHeaderActionButton - if let current = self.actionButton { - actionButton = current - } else { - actionButton = GroupHeaderActionButton(pressed: self.actionPressed) - self.actionButton = actionButton - self.addSubview(actionButton) - self.tintContentLayer.addSublayer(actionButton.tintContainerLayer) - } - - actionButtonSize = actionButton.update(theme: theme, title: actionButtonTitle, compact: actionButtonIsCompact) - } else { - if let actionButton = self.actionButton { - self.actionButton = nil - actionButton.removeFromSuperview() - } - } - - var clearSize: CGSize = .zero - var clearWidth: CGFloat = 0.0 - if hasClear { - var updateImage = themeUpdated - - let clearIconLayer: SimpleLayer - if let current = self.clearIconLayer { - clearIconLayer = current - } else { - updateImage = true - clearIconLayer = SimpleLayer() - self.clearIconLayer = clearIconLayer - self.layer.addSublayer(clearIconLayer) - } - let tintClearIconLayer: SimpleLayer - if let current = self.tintClearIconLayer { - tintClearIconLayer = current - } else { - updateImage = true - tintClearIconLayer = SimpleLayer() - self.tintClearIconLayer = tintClearIconLayer - self.tintContentLayer.addSublayer(tintClearIconLayer) - } - - tintClearIconLayer.isHidden = !needsVibrancy - - clearSize = clearIconLayer.bounds.size - if updateImage, let image = PresentationResourcesChat.chatInputMediaPanelGridDismissImage(theme, color: subtitleColor) { - clearSize = image.size - clearIconLayer.contents = image.cgImage - } - if updateImage, let image = PresentationResourcesChat.chatInputMediaPanelGridDismissImage(theme, color: .white) { - tintClearIconLayer.contents = image.cgImage - } - - tintClearIconLayer.frame = clearIconLayer.frame - clearWidth = 4.0 + clearSize.width - } else { - if let clearIconLayer = self.clearIconLayer { - self.clearIconLayer = nil - clearIconLayer.removeFromSuperlayer() - } - if let tintClearIconLayer = self.tintClearIconLayer { - self.tintClearIconLayer = nil - tintClearIconLayer.removeFromSuperlayer() - } - } - - var textConstrainedWidth = constrainedSize.width - titleHorizontalOffset - 10.0 - if let actionButtonSize = actionButtonSize { - if actionButtonIsCompact { - textConstrainedWidth -= actionButtonSize.width * 2.0 + 10.0 - } else { - textConstrainedWidth -= actionButtonSize.width + 10.0 - } - } - if clearWidth > 0.0 { - textConstrainedWidth -= clearWidth + 8.0 - } - - let textSize: CGSize - if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == color, currentTextLayout.constrainedWidth == textConstrainedWidth { - textSize = currentTextLayout.size - } else { - let font: UIFont - let stringValue: String - if subtitle == nil { - font = Font.medium(13.0) - stringValue = title.uppercased() - } else { - font = Font.semibold(16.0) - stringValue = title - } - let string = NSAttributedString(string: stringValue, font: font, textColor: color) - let whiteString = NSAttributedString(string: stringValue, font: font, textColor: .white) - let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 18.0), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) - textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) - self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - //string.draw(in: stringBounds) - string.draw(with: stringBounds, options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) - - UIGraphicsPopContext() - })?.cgImage - self.tintTextLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - //whiteString.draw(in: stringBounds) - whiteString.draw(with: stringBounds, options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) - - UIGraphicsPopContext() - })?.cgImage - self.tintTextLayer.isHidden = !needsVibrancy - self.currentTextLayout = (title, color, textConstrainedWidth, textSize) - } - - var badgeSize: CGSize = .zero - if let badge { - func generateBadgeImage(color: UIColor) -> UIImage? { - let string = NSAttributedString(string: badge, font: Font.semibold(11.0), textColor: .white) - let stringBounds = string.boundingRect(with: CGSize(width: 120, height: 18.0), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) - - let badgeSize = CGSize(width: stringBounds.width + 8.0, height: 16.0) - return generateImage(badgeSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - context.setFillColor(color.cgColor) - context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: badgeSize), cornerRadius: badgeSize.height / 2.0).cgPath) - context.fillPath() - - context.setBlendMode(.clear) - - UIGraphicsPushContext(context) - - string.draw(with: CGRect(origin: CGPoint(x: floorToScreenPixels((badgeSize.width - stringBounds.size.width) / 2.0), y: floorToScreenPixels((badgeSize.height - stringBounds.size.height) / 2.0)), size: stringBounds.size), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) - - UIGraphicsPopContext() - }) - } - - let badgeLayer: SimpleLayer - if let current = self.badgeLayer { - badgeLayer = current - } else { - badgeLayer = SimpleLayer() - self.badgeLayer = badgeLayer - self.layer.addSublayer(badgeLayer) - - if let image = generateBadgeImage(color: color.withMultipliedAlpha(0.66)) { - badgeLayer.contents = image.cgImage - badgeLayer.bounds = CGRect(origin: .zero, size: image.size) - } - } - badgeSize = badgeLayer.bounds.size - - let tintBadgeLayer: SimpleLayer - if let current = self.tintBadgeLayer { - tintBadgeLayer = current - } else { - tintBadgeLayer = SimpleLayer() - self.tintBadgeLayer = tintBadgeLayer - self.tintContentLayer.addSublayer(tintBadgeLayer) - - if let image = generateBadgeImage(color: .white) { - tintBadgeLayer.contents = image.cgImage - } - } - } else { - if let badgeLayer = self.badgeLayer { - self.badgeLayer = nil - badgeLayer.removeFromSuperlayer() - } - if let tintBadgeLayer = self.tintBadgeLayer { - self.tintBadgeLayer = nil - tintBadgeLayer.removeFromSuperlayer() - } - } - - let textFrame: CGRect - if subtitle == nil { - textFrame = CGRect(origin: CGPoint(x: titleHorizontalOffset + floor((constrainedSize.width - titleHorizontalOffset - (textSize.width + badgeSize.width)) / 2.0), y: textOffsetY), size: textSize) - } else { - textFrame = CGRect(origin: CGPoint(x: titleHorizontalOffset, y: textOffsetY), size: textSize) - } - self.textLayer.frame = textFrame - self.tintTextLayer.frame = textFrame - self.tintTextLayer.isHidden = !needsTintText - - if let badgeLayer = self.badgeLayer, let tintBadgeLayer = self.tintBadgeLayer { - badgeLayer.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 4.0, y: 0.0), size: badgeLayer.frame.size) - tintBadgeLayer.frame = badgeLayer.frame - } - - if isPremiumLocked { - let lockIconLayer: SimpleLayer - if let current = self.lockIconLayer { - lockIconLayer = current - } else { - lockIconLayer = SimpleLayer() - self.lockIconLayer = lockIconLayer - self.layer.addSublayer(lockIconLayer) - } - if let image = PresentationResourcesChat.chatEntityKeyboardLock(theme, color: color) { - let imageSize = image.size - lockIconLayer.contents = image.cgImage - lockIconLayer.frame = CGRect(origin: CGPoint(x: textFrame.minX - imageSize.width - 3.0, y: 2.0 + UIScreenPixel), size: imageSize) - } else { - lockIconLayer.contents = nil - } - - let tintLockIconLayer: SimpleLayer - if let current = self.tintLockIconLayer { - tintLockIconLayer = current - } else { - tintLockIconLayer = SimpleLayer() - self.tintLockIconLayer = tintLockIconLayer - self.tintContentLayer.addSublayer(tintLockIconLayer) - } - if let image = PresentationResourcesChat.chatEntityKeyboardLock(theme, color: .white) { - tintLockIconLayer.contents = image.cgImage - tintLockIconLayer.frame = lockIconLayer.frame - tintLockIconLayer.isHidden = !needsVibrancy - } else { - tintLockIconLayer.contents = nil - } - } else { - if let lockIconLayer = self.lockIconLayer { - self.lockIconLayer = nil - lockIconLayer.removeFromSuperlayer() - } - if let tintLockIconLayer = self.tintLockIconLayer { - self.tintLockIconLayer = nil - tintLockIconLayer.removeFromSuperlayer() - } - } - - let subtitleSize: CGSize - if let subtitle = subtitle { - var updateSubtitleContents: UIImage? - var updateTintSubtitleContents: UIImage? - if let currentSubtitleLayout = self.currentSubtitleLayout, currentSubtitleLayout.string == subtitle, currentSubtitleLayout.color == subtitleColor, currentSubtitleLayout.constrainedWidth == textConstrainedWidth { - subtitleSize = currentSubtitleLayout.size - } else { - let string = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: subtitleColor) - let whiteString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: .white) - let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - subtitleSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) - updateSubtitleContents = generateImage(subtitleSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - string.draw(in: stringBounds) - - UIGraphicsPopContext() - }) - updateTintSubtitleContents = generateImage(subtitleSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - whiteString.draw(in: stringBounds) - - UIGraphicsPopContext() - }) - self.currentSubtitleLayout = (subtitle, subtitleColor, textConstrainedWidth, subtitleSize) - } - - let subtitleLayer: SimpleLayer - if let current = self.subtitleLayer { - subtitleLayer = current - } else { - subtitleLayer = SimpleLayer() - self.subtitleLayer = subtitleLayer - self.layer.addSublayer(subtitleLayer) - } - - if let updateSubtitleContents = updateSubtitleContents { - subtitleLayer.contents = updateSubtitleContents.cgImage - } - - let tintSubtitleLayer: SimpleLayer - if let current = self.tintSubtitleLayer { - tintSubtitleLayer = current - } else { - tintSubtitleLayer = SimpleLayer() - self.tintSubtitleLayer = tintSubtitleLayer - self.tintContentLayer.addSublayer(tintSubtitleLayer) - } - tintSubtitleLayer.isHidden = !needsVibrancy - - if let updateTintSubtitleContents = updateTintSubtitleContents { - tintSubtitleLayer.contents = updateTintSubtitleContents.cgImage - } - - let subtitleFrame = CGRect(origin: CGPoint(x: 0.0, y: textFrame.maxY + 1.0), size: subtitleSize) - subtitleLayer.frame = subtitleFrame - tintSubtitleLayer.frame = subtitleFrame - } else { - subtitleSize = CGSize() - if let subtitleLayer = self.subtitleLayer { - self.subtitleLayer = nil - subtitleLayer.removeFromSuperlayer() - } - if let tintSubtitleLayer = self.tintSubtitleLayer { - self.tintSubtitleLayer = nil - tintSubtitleLayer.removeFromSuperlayer() - } - } - - self.clearIconLayer?.frame = CGRect(origin: CGPoint(x: constrainedSize.width - clearSize.width, y: floorToScreenPixels((textSize.height - clearSize.height) / 2.0)), size: clearSize) - - var size: CGSize - size = CGSize(width: constrainedSize.width, height: constrainedSize.height) - - if let embeddedItems = embeddedItems { - let groupEmbeddedView: GroupEmbeddedView - if let current = self.groupEmbeddedView { - groupEmbeddedView = current - } else { - groupEmbeddedView = GroupEmbeddedView(performItemAction: self.performItemAction) - self.groupEmbeddedView = groupEmbeddedView - self.addSubview(groupEmbeddedView) - } - - let groupEmbeddedViewSize = CGSize(width: constrainedSize.width + insets.left + insets.right, height: 36.0) - groupEmbeddedView.frame = CGRect(origin: CGPoint(x: -insets.left, y: size.height - groupEmbeddedViewSize.height), size: groupEmbeddedViewSize) - groupEmbeddedView.update( - context: context, - theme: theme, - insets: insets, - size: groupEmbeddedViewSize, - items: embeddedItems, - isStickers: isStickers, - cache: cache, - renderer: renderer, - attemptSynchronousLoad: attemptSynchronousLoad - ) - } else { - if let groupEmbeddedView = self.groupEmbeddedView { - self.groupEmbeddedView = nil - groupEmbeddedView.removeFromSuperview() - } - } - - if let actionButtonSize = actionButtonSize, let actionButton = self.actionButton { - let actionButtonFrame = CGRect(origin: CGPoint(x: size.width - actionButtonSize.width, y: textFrame.minY + (actionButtonIsCompact ? 0.0 : 3.0)), size: actionButtonSize) - actionButton.bounds = CGRect(origin: CGPoint(), size: actionButtonFrame.size) - actionButton.center = actionButtonFrame.center - } - - if hasTopSeparator { - let separatorLayer: SimpleLayer - if let current = self.separatorLayer { - separatorLayer = current - } else { - separatorLayer = SimpleLayer() - self.separatorLayer = separatorLayer - self.layer.addSublayer(separatorLayer) - } - separatorLayer.backgroundColor = subtitleColor.cgColor - separatorLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)) - - let tintSeparatorLayer: SimpleLayer - if let current = self.tintSeparatorLayer { - tintSeparatorLayer = current - } else { - tintSeparatorLayer = SimpleLayer() - self.tintSeparatorLayer = tintSeparatorLayer - self.tintContentLayer.addSublayer(tintSeparatorLayer) - } - tintSeparatorLayer.backgroundColor = UIColor.white.cgColor - tintSeparatorLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)) - - tintSeparatorLayer.isHidden = !needsVibrancy - } else { - if let separatorLayer = self.separatorLayer { - self.separatorLayer = separatorLayer - separatorLayer.removeFromSuperlayer() - } - if let tintSeparatorLayer = self.tintSeparatorLayer { - self.tintSeparatorLayer = tintSeparatorLayer - tintSeparatorLayer.removeFromSuperlayer() - } - } - - return (size, titleHorizontalOffset + textSize.width + clearWidth) - } - - override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { - return super.hitTest(point, with: event) - } - - func tapGesture(point: CGPoint) -> Bool { - if let groupEmbeddedView = self.groupEmbeddedView { - return groupEmbeddedView.tapGesture(point: self.convert(point, to: groupEmbeddedView)) - } else { - return false - } - } -} - -private final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, PagerExpandableScrollView { - private struct ItemLayout { - var itemSize: CGFloat - var itemSpacing: CGFloat - var sideInset: CGFloat - var itemCount: Int - var contentSize: CGSize - - init(height: CGFloat, sideInset: CGFloat, itemCount: Int) { - self.itemSize = 30.0 - self.itemSpacing = 20.0 - self.sideInset = sideInset - self.itemCount = itemCount - - self.contentSize = CGSize(width: self.sideInset * 2.0 + CGFloat(self.itemCount) * self.itemSize + CGFloat(self.itemCount - 1) * self.itemSpacing, height: height) - } - - func frame(at index: Int) -> CGRect { - return CGRect(origin: CGPoint(x: sideInset + CGFloat(index) * (self.itemSize + self.itemSpacing), y: floor((self.contentSize.height - self.itemSize) / 2.0)), size: CGSize(width: self.itemSize, height: self.itemSize)) - } - - func visibleItems(for rect: CGRect) -> Range? { - let offsetRect = rect.offsetBy(dx: -self.sideInset, dy: 0.0) - var minVisibleIndex = Int(floor((offsetRect.minX - self.itemSpacing) / (self.itemSize + self.itemSpacing))) - minVisibleIndex = max(0, minVisibleIndex) - var maxVisibleIndex = Int(ceil((offsetRect.maxX - self.itemSpacing) / (self.itemSize + self.itemSpacing))) - maxVisibleIndex = min(maxVisibleIndex, self.itemCount - 1) - - if minVisibleIndex <= maxVisibleIndex { - return minVisibleIndex ..< (maxVisibleIndex + 1) - } else { - return nil - } - } - } - - private let performItemAction: (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void - - private var visibleItemLayers: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemLayer] = [:] - private var ignoreScrolling: Bool = false - - private var context: AccountContext? - private var theme: PresentationTheme? - private var cache: AnimationCache? - private var renderer: MultiAnimationRenderer? - private var currentInsets: UIEdgeInsets? - private var currentSize: CGSize? - private var items: [EmojiPagerContentComponent.Item]? - private var isStickers: Bool = false - - private var itemLayout: ItemLayout? - - init(performItemAction: @escaping (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void) { - self.performItemAction = performItemAction - - super.init(frame: CGRect()) - - self.delaysContentTouches = false - if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { - self.contentInsetAdjustmentBehavior = .never - } - if #available(iOS 13.0, *) { - self.automaticallyAdjustsScrollIndicatorInsets = false - } - self.showsVerticalScrollIndicator = true - self.showsHorizontalScrollIndicator = false - self.delegate = self - self.clipsToBounds = true - self.scrollsToTop = false - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func tapGesture(point: CGPoint) -> Bool { - guard let itemLayout = self.itemLayout else { - return false - } - - for (_, itemLayer) in self.visibleItemLayers { - if itemLayer.frame.inset(by: UIEdgeInsets(top: -6.0, left: -itemLayout.itemSpacing, bottom: -6.0, right: -itemLayout.itemSpacing)).contains(point) { - self.performItemAction(itemLayer.item, self, itemLayer.frame, itemLayer) - return true - } - } - - return false - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - if !self.ignoreScrolling { - self.updateVisibleItems(transition: .immediate, attemptSynchronousLoad: false) - } - } - - private func updateVisibleItems(transition: Transition, attemptSynchronousLoad: Bool) { - guard let context = self.context, let theme = self.theme, let itemLayout = self.itemLayout, let items = self.items, let cache = self.cache, let renderer = self.renderer else { - return - } - - var validIds = Set() - if let itemRange = itemLayout.visibleItems(for: self.bounds) { - for index in itemRange.lowerBound ..< itemRange.upperBound { - let item = items[index] - let itemId = EmojiPagerContentComponent.View.ItemLayer.Key( - groupId: AnyHashable(0), - itemId: item.content.id - ) - validIds.insert(itemId) - - let itemLayer: EmojiPagerContentComponent.View.ItemLayer - if let current = self.visibleItemLayers[itemId] { - itemLayer = current - } else { - itemLayer = EmojiPagerContentComponent.View.ItemLayer( - item: item, - context: context, - attemptSynchronousLoad: attemptSynchronousLoad, - content: item.content, - cache: cache, - renderer: renderer, - placeholderColor: .clear, - blurredBadgeColor: .clear, - accentIconColor: theme.list.itemAccentColor, - pointSize: CGSize(width: 32.0, height: 32.0), - onUpdateDisplayPlaceholder: { _, _ in - } - ) - self.visibleItemLayers[itemId] = itemLayer - self.layer.addSublayer(itemLayer) - } - - switch item.tintMode { - case let .custom(color): - itemLayer.layerTintColor = color.cgColor - case .accent: - itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor - case .primary: - itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor - case .none: - itemLayer.layerTintColor = nil - } - - let itemFrame = itemLayout.frame(at: index) - itemLayer.frame = itemFrame - - itemLayer.isVisibleForAnimations = self.isStickers ? context.sharedContext.energyUsageSettings.loopStickers : context.sharedContext.energyUsageSettings.loopEmoji - } - } - - var removedIds: [EmojiPagerContentComponent.View.ItemLayer.Key] = [] - for (id, itemLayer) in self.visibleItemLayers { - if !validIds.contains(id) { - removedIds.append(id) - itemLayer.removeFromSuperlayer() - } - } - for id in removedIds { - self.visibleItemLayers.removeValue(forKey: id) - } - } - - func update( - context: AccountContext, - theme: PresentationTheme, - insets: UIEdgeInsets, - size: CGSize, - items: [EmojiPagerContentComponent.Item], - isStickers: Bool, - cache: AnimationCache, - renderer: MultiAnimationRenderer, - attemptSynchronousLoad: Bool - ) { - if self.theme === theme && self.currentInsets == insets && self.currentSize == size && self.items == items { - return - } - - self.context = context - self.theme = theme - self.currentInsets = insets - self.currentSize = size - self.items = items - self.isStickers = isStickers - self.cache = cache - self.renderer = renderer - - let itemLayout = ItemLayout(height: size.height, sideInset: insets.left, itemCount: items.count) - self.itemLayout = itemLayout - - self.ignoreScrolling = true - if itemLayout.contentSize != self.contentSize { - self.contentSize = itemLayout.contentSize - } - self.ignoreScrolling = false - - self.updateVisibleItems(transition: .immediate, attemptSynchronousLoad: attemptSynchronousLoad) - } -} - -private final class GroupExpandActionButton: UIButton { - override static var layerClass: AnyClass { - return PassthroughLayer.self - } - - let tintContainerLayer: SimpleLayer - - private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? - private let backgroundLayer: SimpleLayer - private let tintBackgroundLayer: SimpleLayer - private let textLayer: SimpleLayer - private let pressed: () -> Void - - init(pressed: @escaping () -> Void) { - self.pressed = pressed - - self.tintContainerLayer = SimpleLayer() - - self.backgroundLayer = SimpleLayer() - self.backgroundLayer.masksToBounds = true - - self.tintBackgroundLayer = SimpleLayer() - self.tintBackgroundLayer.masksToBounds = true - - self.textLayer = SimpleLayer() - - super.init(frame: CGRect()) - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerLayer - - self.layer.addSublayer(self.backgroundLayer) - - self.layer.addSublayer(self.textLayer) - - self.addTarget(self, action: #selector(self.onPressed), for: .touchUpInside) - } - - required init(coder: NSCoder) { - preconditionFailure() - } - - @objc private func onPressed() { - self.pressed() - } - - override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { - self.alpha = 0.6 - - return super.beginTracking(touch, with: event) - } - - override func endTracking(_ touch: UITouch?, with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.endTracking(touch, with: event) - } - - override func cancelTracking(with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.cancelTracking(with: event) - } - - override func touchesCancelled(_ touches: Set, with event: UIEvent?) { - let alpha = self.alpha - self.alpha = 1.0 - self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) - - super.touchesCancelled(touches, with: event) - } - - func update(theme: PresentationTheme, title: String, useOpaqueTheme: Bool) -> CGSize { - let textConstrainedWidth: CGFloat = 100.0 - let color = theme.list.itemCheckColors.foregroundColor - - if useOpaqueTheme { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor.cgColor - } else { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor - } - self.tintContainerLayer.backgroundColor = UIColor.white.cgColor - - let textSize: CGSize - if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == color, currentTextLayout.constrainedWidth == textConstrainedWidth { - textSize = currentTextLayout.size - } else { - let font: UIFont = Font.semibold(13.0) - let string = NSAttributedString(string: title.uppercased(), font: font, textColor: color) - let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) - textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) - self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - UIGraphicsPushContext(context) - - string.draw(in: stringBounds) - - UIGraphicsPopContext() - })?.cgImage - self.currentTextLayout = (title, color, textConstrainedWidth, textSize) - } - - var sideInset: CGFloat = 10.0 - if textSize.width > 24.0 { - sideInset = 6.0 - } - let size = CGSize(width: textSize.width + sideInset * 2.0, height: 28.0) - - let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize) - self.textLayer.frame = textFrame - - self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) - self.tintBackgroundLayer.frame = CGRect(origin: CGPoint(), size: size) - self.backgroundLayer.cornerRadius = min(size.width, size.height) / 2.0 - self.tintContainerLayer.cornerRadius = min(size.width, size.height) / 2.0 - - return size - } -} - -public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { - private final class EmojiSearchTextField: UITextField { - override func textRect(forBounds bounds: CGRect) -> CGRect { - return bounds.integral - } - } - - private struct Params: Equatable { - var context: AccountContext - var theme: PresentationTheme - var forceNeedsVibrancy: Bool - var strings: PresentationStrings - var text: String - var useOpaqueTheme: Bool - var isActive: Bool - var hasPresetSearch: Bool - var textInputState: EmojiSearchSearchBarComponent.TextInputState - var searchState: EmojiPagerContentComponent.SearchState - var size: CGSize - var canFocus: Bool - var searchCategories: EmojiSearchCategories? - - static func ==(lhs: Params, rhs: Params) -> Bool { - if lhs.context !== rhs.context { - return false - } - if lhs.theme !== rhs.theme { - return false - } - if lhs.forceNeedsVibrancy != rhs.forceNeedsVibrancy { - return false - } - if lhs.strings !== rhs.strings { - return false - } - if lhs.text != rhs.text { - return false - } - if lhs.useOpaqueTheme != rhs.useOpaqueTheme { - return false - } - if lhs.isActive != rhs.isActive { - return false - } - if lhs.hasPresetSearch != rhs.hasPresetSearch { - return false - } - if lhs.textInputState != rhs.textInputState { - return false - } - if lhs.searchState != rhs.searchState { - return false - } - if lhs.size != rhs.size { - return false - } - if lhs.canFocus != rhs.canFocus { - return false - } - if lhs.searchCategories != rhs.searchCategories { - return false - } - return true - } - } - - override public static var layerClass: AnyClass { - return PassthroughLayer.self - } - - private let activated: (Bool) -> Void - private let deactivated: (Bool) -> Void - private let updateQuery: (EmojiPagerContentComponent.SearchQuery?) -> Void - - let tintContainerView: UIView - - private let backgroundLayer: SimpleLayer - private let tintBackgroundLayer: SimpleLayer - - private let statusIcon = ComponentView() - - private let clearIconView: UIImageView - private let clearIconTintView: UIImageView - private let clearIconButton: HighlightTrackingButton - - private let cancelButtonTintTitle: ComponentView - private let cancelButtonTitle: ComponentView - private let cancelButton: HighlightTrackingButton - - private var placeholderContent = ComponentView() - - private var textFrame: CGRect? - private var textField: EmojiSearchTextField? - - private var tapRecognizer: UITapGestureRecognizer? - private(set) var currentPresetSearchTerm: EmojiSearchCategories.Group? - - private var params: Params? - - public var wantsDisplayBelowKeyboard: Bool { - return self.textField != nil - } - - init(activated: @escaping (Bool) -> Void, deactivated: @escaping (Bool) -> Void, updateQuery: @escaping (EmojiPagerContentComponent.SearchQuery?) -> Void) { - self.activated = activated - self.deactivated = deactivated - self.updateQuery = updateQuery - - self.tintContainerView = UIView() - - self.backgroundLayer = SimpleLayer() - self.tintBackgroundLayer = SimpleLayer() - - self.clearIconView = UIImageView() - self.clearIconTintView = UIImageView() - self.clearIconButton = HighlightableButton() - self.clearIconView.isHidden = true - self.clearIconTintView.isHidden = true - self.clearIconButton.isHidden = true - - self.cancelButtonTintTitle = ComponentView() - self.cancelButtonTitle = ComponentView() - self.cancelButton = HighlightTrackingButton() - - super.init(frame: CGRect()) - - self.layer.addSublayer(self.backgroundLayer) - self.tintContainerView.layer.addSublayer(self.tintBackgroundLayer) - - self.addSubview(self.clearIconView) - self.tintContainerView.addSubview(self.clearIconTintView) - self.addSubview(self.clearIconButton) - - self.addSubview(self.cancelButton) - self.clipsToBounds = true - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerView.layer - - let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) - self.tapRecognizer = tapRecognizer - self.addGestureRecognizer(tapRecognizer) - - self.cancelButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.layer.removeAnimation(forKey: "opacity") - cancelButtonTitleView.alpha = 0.4 - } - if let cancelButtonTintTitleView = strongSelf.cancelButtonTintTitle.view { - cancelButtonTintTitleView.layer.removeAnimation(forKey: "opacity") - cancelButtonTintTitleView.alpha = 0.4 - } - } else { - if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { - cancelButtonTitleView.alpha = 1.0 - cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - if let cancelButtonTintTitleView = strongSelf.cancelButtonTintTitle.view { - cancelButtonTintTitleView.alpha = 1.0 - cancelButtonTintTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - } - self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside) - - self.clearIconButton.highligthedChanged = { [weak self] highlighted in - if let strongSelf = self { - if highlighted { - strongSelf.clearIconView.layer.removeAnimation(forKey: "opacity") - strongSelf.clearIconView.alpha = 0.4 - strongSelf.clearIconTintView.layer.removeAnimation(forKey: "opacity") - strongSelf.clearIconTintView.alpha = 0.4 - } else { - strongSelf.clearIconView.alpha = 1.0 - strongSelf.clearIconView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - strongSelf.clearIconTintView.alpha = 1.0 - strongSelf.clearIconTintView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) - } - } - } - self.clearIconButton.addTarget(self, action: #selector(self.clearPressed), for: .touchUpInside) - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { - if case .ended = recognizer.state { - let location = recognizer.location(in: self) - if let view = self.statusIcon.view, view.frame.contains(location), self.currentPresetSearchTerm != nil { - self.clearCategorySearch() - } else { - self.activateTextInput() - } - } - } - - func clearCategorySearch() { - if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { - placeholderContentView.clearSelection(dispatchEvent : true) - } - } - - private func activateTextInput() { - guard let params = self.params else { - return - } - if self.textField == nil, let textFrame = self.textFrame, params.canFocus == true { - let backgroundFrame = self.backgroundLayer.frame - let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height)) - - let textField = EmojiSearchTextField(frame: textFieldFrame) - textField.keyboardAppearance = params.theme.rootController.keyboardColor.keyboardAppearance - textField.autocorrectionType = .no - textField.returnKeyType = .search - self.textField = textField - self.insertSubview(textField, belowSubview: self.clearIconView) - textField.delegate = self - textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) - } - - if params.canFocus { - self.currentPresetSearchTerm = nil - if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { - placeholderContentView.clearSelection(dispatchEvent: false) - } - } - - self.activated(true) - - self.textField?.becomeFirstResponder() - } - - @objc private func cancelPressed() { - self.currentPresetSearchTerm = nil - self.updateQuery(nil) - - self.clearIconView.isHidden = true - self.clearIconTintView.isHidden = true - self.clearIconButton.isHidden = true - - let textField = self.textField - self.textField = nil - - self.deactivated(textField?.isFirstResponder ?? false) - - if let textField { - textField.resignFirstResponder() - textField.removeFromSuperview() - } - - /*self.tintTextView.view?.isHidden = false - self.textView.view?.isHidden = false*/ - } - - @objc private func clearPressed() { - self.currentPresetSearchTerm = nil - self.updateQuery(nil) - self.textField?.text = "" - - self.clearIconView.isHidden = true - self.clearIconTintView.isHidden = true - self.clearIconButton.isHidden = true - - /*self.tintTextView.view?.isHidden = false - self.textView.view?.isHidden = false*/ - } - - var isActive: Bool { - return self.textField?.isFirstResponder ?? false - } - - func deactivate() { - if let text = self.textField?.text, !text.isEmpty { - self.textField?.endEditing(true) - } else { - self.cancelPressed() - } - } - - public func textFieldDidBeginEditing(_ textField: UITextField) { - } - - public func textFieldDidEndEditing(_ textField: UITextField) { - } - - public func textFieldShouldReturn(_ textField: UITextField) -> Bool { - textField.endEditing(true) - return false - } - - @objc private func textFieldChanged(_ textField: UITextField) { - self.update(transition: .immediate) - - let text = textField.text ?? "" - - var inputLanguage = textField.textInputMode?.primaryLanguage ?? "en" - if let range = inputLanguage.range(of: "-") { - inputLanguage = String(inputLanguage[inputLanguage.startIndex ..< range.lowerBound]) - } - if let range = inputLanguage.range(of: "_") { - inputLanguage = String(inputLanguage[inputLanguage.startIndex ..< range.lowerBound]) - } - - self.clearIconView.isHidden = text.isEmpty - self.clearIconTintView.isHidden = text.isEmpty - self.clearIconButton.isHidden = text.isEmpty - - self.currentPresetSearchTerm = nil - self.updateQuery(.text(value: text, language: inputLanguage)) - } - - private func update(transition: Transition) { - guard let params = self.params else { - return - } - self.params = nil - self.update(context: params.context, theme: params.theme, forceNeedsVibrancy: params.forceNeedsVibrancy, strings: params.strings, text: params.text, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, canFocus: params.canFocus, searchCategories: params.searchCategories, searchState: params.searchState, transition: transition) - } - - public func update(context: AccountContext, theme: PresentationTheme, forceNeedsVibrancy: Bool, strings: PresentationStrings, text: String, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, canFocus: Bool, searchCategories: EmojiSearchCategories?, searchState: EmojiPagerContentComponent.SearchState, transition: Transition) { - let textInputState: EmojiSearchSearchBarComponent.TextInputState - if let textField = self.textField { - textInputState = .active(hasText: !(textField.text ?? "").isEmpty) - } else { - textInputState = .inactive - } - - let params = Params( - context: context, - theme: theme, - forceNeedsVibrancy: forceNeedsVibrancy, - strings: strings, - text: text, - useOpaqueTheme: useOpaqueTheme, - isActive: isActive, - hasPresetSearch: self.currentPresetSearchTerm == nil, - textInputState: textInputState, - searchState: searchState, - size: size, - canFocus: canFocus, - searchCategories: searchCategories - ) - - if self.params == params { - return - } - - let isActiveWithText = isActive && self.currentPresetSearchTerm == nil - - if self.params?.theme !== theme { - /*self.searchIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.searchIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor - - self.searchIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white) - - self.backIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.backIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor - - self.backIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: .white)*/ - - self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate) - self.clearIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor - - self.clearIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white) - } - - self.params = params - - let sideInset: CGFloat = 12.0 - let topInset: CGFloat = 8.0 - let inputHeight: CGFloat = 36.0 - - let sideTextInset: CGFloat = sideInset + 4.0 + 24.0 - - if theme.overallDarkAppearance && forceNeedsVibrancy { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.withMultipliedAlpha(0.3).cgColor - self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor - } else if useOpaqueTheme { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor - self.tintBackgroundLayer.backgroundColor = UIColor.white.cgColor - } else { - self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor - self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor - } - - self.backgroundLayer.cornerRadius = inputHeight * 0.5 - self.tintBackgroundLayer.cornerRadius = inputHeight * 0.5 - - let cancelColor: UIColor - if theme.overallDarkAppearance && forceNeedsVibrancy { - cancelColor = theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor.withMultipliedAlpha(0.3) - } else { - cancelColor = useOpaqueTheme ? theme.list.itemAccentColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor - } - - let cancelTextSize = self.cancelButtonTitle.update( - transition: .immediate, - component: AnyComponent(Text( - text: strings.Common_Cancel, - font: Font.regular(17.0), - color: cancelColor - )), - environment: {}, - containerSize: CGSize(width: size.width - 32.0, height: 100.0) - ) - let _ = self.cancelButtonTintTitle.update( - transition: .immediate, - component: AnyComponent(Text( - text: strings.Common_Cancel, - font: Font.regular(17.0), - color: .white - )), - environment: {}, - containerSize: CGSize(width: size.width - 32.0, height: 100.0) - ) - - let cancelButtonSpacing: CGFloat = 8.0 - - var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight)) - if isActiveWithText { - backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing - } - transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame) - transition.setFrame(layer: self.tintBackgroundLayer, frame: backgroundFrame) - - transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height))) - - let textX: CGFloat = backgroundFrame.minX + sideTextInset - let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height)) - self.textFrame = textFrame - - let statusContent: EmojiSearchStatusComponent.Content - switch searchState { - case .empty: - statusContent = .search - case .searching: - statusContent = .progress - case .active: - statusContent = .results - } - - let statusSize = CGSize(width: 24.0, height: 24.0) - let _ = self.statusIcon.update( - transition: transition, - component: AnyComponent(EmojiSearchStatusComponent( - theme: theme, - forceNeedsVibrancy: forceNeedsVibrancy, - strings: strings, - useOpaqueTheme: useOpaqueTheme, - content: statusContent - )), - environment: {}, - containerSize: statusSize - ) - let iconFrame = CGRect(origin: CGPoint(x: textFrame.minX - statusSize.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - statusSize.height) / 2.0)), size: statusSize) - if let statusIconView = self.statusIcon.view as? EmojiSearchStatusComponent.View { - if statusIconView.superview == nil { - self.addSubview(statusIconView) - self.tintContainerView.addSubview(statusIconView.tintContainerView) - } - - transition.setFrame(view: statusIconView, frame: iconFrame) - transition.setFrame(view: statusIconView.tintContainerView, frame: iconFrame) - } - - /*if let image = self.searchIconView.image { - let iconFrame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - transition.setBounds(view: self.searchIconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - transition.setPosition(view: self.searchIconView, position: iconFrame.center) - transition.setBounds(view: self.searchIconTintView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - transition.setPosition(view: self.searchIconTintView, position: iconFrame.center) - transition.setScale(view: self.searchIconView, scale: self.currentPresetSearchTerm == nil ? 1.0 : 0.001) - transition.setAlpha(view: self.searchIconView, alpha: self.currentPresetSearchTerm == nil ? 1.0 : 0.0) - transition.setScale(view: self.searchIconTintView, scale: self.currentPresetSearchTerm == nil ? 1.0 : 0.001) - transition.setAlpha(view: self.searchIconTintView, alpha: self.currentPresetSearchTerm == nil ? 1.0 : 0.0) - } - - if let image = self.backIconView.image { - let iconFrame = CGRect(origin: CGPoint(x: textFrame.minX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - transition.setBounds(view: self.backIconView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - transition.setPosition(view: self.backIconView, position: iconFrame.center) - transition.setBounds(view: self.backIconTintView, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - transition.setPosition(view: self.backIconTintView, position: iconFrame.center) - transition.setScale(view: self.backIconView, scale: self.currentPresetSearchTerm != nil ? 1.0 : 0.001) - transition.setAlpha(view: self.backIconView, alpha: self.currentPresetSearchTerm != nil ? 1.0 : 0.0) - transition.setScale(view: self.backIconTintView, scale: self.currentPresetSearchTerm != nil ? 1.0 : 0.001) - transition.setAlpha(view: self.backIconTintView, alpha: self.currentPresetSearchTerm != nil ? 1.0 : 0.0) - }*/ - - let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX - 6.0, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - (textFrame.minX - 6.0), height: backgroundFrame.height)) - let _ = self.placeholderContent.update( - transition: transition, - component: AnyComponent(EmojiSearchSearchBarComponent( - context: context, - theme: theme, - forceNeedsVibrancy: forceNeedsVibrancy, - strings: strings, - useOpaqueTheme: useOpaqueTheme, - textInputState: textInputState, - categories: searchCategories, - searchTermUpdated: { [weak self] term in - guard let self else { - return - } - var shouldChangeActivation = false - if (self.currentPresetSearchTerm == nil) != (term == nil) { - shouldChangeActivation = true - } - self.currentPresetSearchTerm = term - - if shouldChangeActivation { - if let term { - self.update(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) - - self.updateQuery(.category(value: term)) - self.activated(false) - } else { - self.deactivated(self.textField?.isFirstResponder ?? false) - self.updateQuery(nil) - } - } else { - if let term { - self.updateQuery(.category(value: term)) - } else { - self.updateQuery(nil) - } - } - }, - activateTextInput: { [weak self] in - guard let self else { - return - } - self.activateTextInput() - } - )), - environment: {}, - containerSize: placeholderContentFrame.size - ) - if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { - if placeholderContentView.superview == nil { - self.addSubview(placeholderContentView) - self.tintContainerView.addSubview(placeholderContentView.tintContainerView) - } - transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame) - transition.setFrame(view: placeholderContentView.tintContainerView, frame: placeholderContentFrame) - } - - /*if let searchCategories { - let suggestedItemsView: ComponentView - var suggestedItemsTransition = transition - if let current = self.suggestedItemsView { - suggestedItemsView = current - } else { - suggestedItemsTransition = .immediate - suggestedItemsView = ComponentView() - self.suggestedItemsView = suggestedItemsView - } - - let itemsX: CGFloat = textFrame.maxX + 8.0 - let suggestedItemsFrame = CGRect(origin: CGPoint(x: itemsX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - itemsX, height: backgroundFrame.height)) - - if let suggestedItemsComponentView = suggestedItemsView.view { - if suggestedItemsComponentView.superview == nil { - self.addSubview(suggestedItemsComponentView) - } - suggestedItemsTransition.setFrame(view: suggestedItemsComponentView, frame: suggestedItemsFrame) - suggestedItemsTransition.setAlpha(view: suggestedItemsComponentView, alpha: isActiveWithText ? 0.0 : 1.0) - } - } else { - if let suggestedItemsView = self.suggestedItemsView { - self.suggestedItemsView = nil - if let suggestedItemsComponentView = suggestedItemsView.view { - transition.setAlpha(view: suggestedItemsComponentView, alpha: 0.0, completion: { [weak suggestedItemsComponentView] _ in - suggestedItemsComponentView?.removeFromSuperview() - }) - } - } - }*/ - - if let image = self.clearIconView.image { - let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) - transition.setFrame(view: self.clearIconView, frame: iconFrame) - transition.setFrame(view: self.clearIconTintView, frame: iconFrame) - transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0)) - } - - if let cancelButtonTitleComponentView = self.cancelButtonTitle.view { - if cancelButtonTitleComponentView.superview == nil { - self.addSubview(cancelButtonTitleComponentView) - cancelButtonTitleComponentView.isUserInteractionEnabled = false - } - transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) - transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0) - } - if let cancelButtonTintTitleComponentView = self.cancelButtonTintTitle.view { - if cancelButtonTintTitleComponentView.superview == nil { - self.tintContainerView.addSubview(cancelButtonTintTitleComponentView) - cancelButtonTintTitleComponentView.isUserInteractionEnabled = false - } - transition.setFrame(view: cancelButtonTintTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) - transition.setAlpha(view: cancelButtonTintTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0) - } - - var hasText = false - if let textField = self.textField { - textField.textColor = theme.contextMenu.primaryColor - transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height))) - - if let text = textField.text, !text.isEmpty { - hasText = true - } - } - let _ = hasText - - /*self.tintTextView.view?.isHidden = hasText - self.textView.view?.isHidden = hasText*/ - } -} - -private final class EmptySearchResultsView: UIView { - override public static var layerClass: AnyClass { - return PassthroughLayer.self - } - - let tintContainerView: UIView - let titleLabel: ComponentView - let titleTintLabel: ComponentView - let icon: ComponentView - - override init(frame: CGRect) { - self.tintContainerView = UIView() - - self.titleLabel = ComponentView() - self.titleTintLabel = ComponentView() - self.icon = ComponentView() - - super.init(frame: frame) - - (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerView.layer - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, searchInitiallyHidden: Bool, transition: Transition) { - let titleColor: UIColor - if useOpaqueTheme { - titleColor = theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor - } else { - titleColor = theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor - } - - let iconSize: CGSize - if let file = file { - iconSize = self.icon.update( - transition: .immediate, - component: AnyComponent(EmojiStatusComponent( - context: context, - animationCache: context.animationCache, - animationRenderer: context.animationRenderer, - content: .animation(content: .file(file: file), size: CGSize(width: 32.0, height: 32.0), placeholderColor: titleColor, themeColor: nil, loopMode: .forever), - isVisibleForAnimations: context.sharedContext.energyUsageSettings.loopEmoji, - action: nil - )), - environment: {}, - containerSize: CGSize(width: 32.0, height: 32.0) - ) - } else { - iconSize = CGSize() - } - - let titleSize = self.titleLabel.update( - transition: .immediate, - component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: titleColor)), - environment: {}, - containerSize: CGSize(width: size.width, height: 100.0) - ) - let _ = self.titleTintLabel.update( - transition: .immediate, - component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: .white)), - environment: {}, - containerSize: CGSize(width: size.width, height: 100.0) - ) - - let spacing: CGFloat = 4.0 - let contentHeight = iconSize.height + spacing + titleSize.height - let contentOriginY = searchInitiallyHidden ? floor((size.height - contentHeight) / 2.0) : 10.0 - let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize) - let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + spacing), size: titleSize) - - if let iconView = self.icon.view { - if iconView.superview == nil { - self.addSubview(iconView) - } - transition.setFrame(view: iconView, frame: iconFrame) - } - if let titleLabelView = self.titleLabel.view { - if titleLabelView.superview == nil { - self.addSubview(titleLabelView) - } - transition.setFrame(view: titleLabelView, frame: titleFrame) - } - if let titleTintLabelView = self.titleTintLabel.view { - if titleTintLabelView.superview == nil { - self.tintContainerView.addSubview(titleTintLabelView) - } - transition.setFrame(view: titleTintLabelView, frame: titleFrame) - } - } -} - public protocol EmojiContentPeekBehavior: AnyObject { func setGestureRecognizerEnabled(view: UIView, isEnabled: Bool, itemAtPoint: @escaping (CGPoint) -> (AnyHashable, CALayer, TelegramMediaFile)?) } @@ -3499,471 +1190,6 @@ public final class EmojiPagerContentComponent: Component { } } - final class CloneItemLayer: SimpleLayer { - } - - public final class ItemLayer: MultiAnimationRenderTarget { - public struct Key: Hashable { - var groupId: AnyHashable - var itemId: ItemContent.Id - - public init( - groupId: AnyHashable, - itemId: ItemContent.Id - ) { - self.groupId = groupId - self.itemId = itemId - } - } - - enum Badge: Equatable { - case premium - case locked - case featured - case text(String) - case customFile(TelegramMediaFile) - } - - public let item: Item - private let context: AccountContext - - private var content: ItemContent - private var theme: PresentationTheme? - - private let placeholderColor: UIColor - let pixelSize: CGSize - let pointSize: CGSize - private let size: CGSize - private var disposable: Disposable? - private var fetchDisposable: Disposable? - private var premiumBadgeView: PremiumBadgeView? - - private var iconLayer: SimpleLayer? - private var tintIconLayer: SimpleLayer? - - private(set) var tintContentLayer: SimpleLayer? - - private var badge: Badge? - private var validSize: CGSize? - - private var isInHierarchyValue: Bool = false - public var isVisibleForAnimations: Bool = false { - didSet { - if self.isVisibleForAnimations != oldValue { - self.updatePlayback() - } - } - } - public private(set) var displayPlaceholder: Bool = false - public let onUpdateDisplayPlaceholder: (Bool, Double) -> Void - - weak var cloneLayer: CloneItemLayer? { - didSet { - if let cloneLayer = self.cloneLayer { - cloneLayer.contents = self.contents - } - } - } - - override public var contents: Any? { - didSet { - self.onContentsUpdate() - if let cloneLayer = self.cloneLayer { - cloneLayer.contents = self.contents - } - } - } - - override public var position: CGPoint { - get { - return super.position - } set(value) { - if let mirrorLayer = self.tintContentLayer { - mirrorLayer.position = value - } - super.position = value - } - } - - override public var bounds: CGRect { - get { - return super.bounds - } set(value) { - if let mirrorLayer = self.tintContentLayer { - mirrorLayer.bounds = value - } - super.bounds = value - } - } - - override public func add(_ animation: CAAnimation, forKey key: String?) { - if let mirrorLayer = self.tintContentLayer { - mirrorLayer.add(animation, forKey: key) - } - - super.add(animation, forKey: key) - } - - override public func removeAllAnimations() { - if let mirrorLayer = self.tintContentLayer { - mirrorLayer.removeAllAnimations() - } - - super.removeAllAnimations() - } - - override public func removeAnimation(forKey: String) { - if let mirrorLayer = self.tintContentLayer { - mirrorLayer.removeAnimation(forKey: forKey) - } - - super.removeAnimation(forKey: forKey) - } - - public var onContentsUpdate: () -> Void = {} - public var onLoop: () -> Void = {} - - public init( - item: Item, - context: AccountContext, - attemptSynchronousLoad: Bool, - content: ItemContent, - cache: AnimationCache, - renderer: MultiAnimationRenderer, - placeholderColor: UIColor, - blurredBadgeColor: UIColor, - accentIconColor: UIColor, - pointSize: CGSize, - onUpdateDisplayPlaceholder: @escaping (Bool, Double) -> Void - ) { - self.item = item - self.context = context - self.content = content - self.placeholderColor = placeholderColor - self.onUpdateDisplayPlaceholder = onUpdateDisplayPlaceholder - - let scale = min(2.0, UIScreenScale) - let pixelSize = CGSize(width: pointSize.width * scale, height: pointSize.height * scale) - self.pixelSize = pixelSize - self.pointSize = pointSize - self.size = CGSize(width: pixelSize.width / scale, height: pixelSize.height / scale) - - super.init() - - switch content { - case let .animation(animationData): - let loadAnimation: () -> Void = { [weak self] in - guard let strongSelf = self else { - return - } - - strongSelf.disposable = renderer.add(target: strongSelf, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, unique: false, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: pixelSize.width >= 120.0, customColor: animationData.isTemplate ? .white : nil)) - } - - if attemptSynchronousLoad { - if !renderer.loadFirstFrameSynchronously(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize) { - self.updateDisplayPlaceholder(displayPlaceholder: true) - - self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in - if !isFinal { - if !success { - Queue.mainQueue().async { - guard let strongSelf = self else { - return - } - - strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) - } - } - return - } - - Queue.mainQueue().async { - loadAnimation() - - if !success { - guard let strongSelf = self else { - return - } - - strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) - } - } - }) - } else { - loadAnimation() - } - } else { - self.fetchDisposable = renderer.loadFirstFrame(target: self, cache: cache, itemId: animationData.resource.resource.id.stringRepresentation, size: pixelSize, fetch: animationCacheFetchFile(context: context, userLocation: .other, userContentType: .sticker, resource: animationData.resource, type: animationData.type.animationCacheAnimationType, keyframeOnly: true, customColor: animationData.isTemplate ? .white : nil), completion: { [weak self] success, isFinal in - if !isFinal { - if !success { - Queue.mainQueue().async { - guard let strongSelf = self else { - return - } - - strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) - } - } - return - } - - Queue.mainQueue().async { - loadAnimation() - - if !success { - guard let strongSelf = self else { - return - } - - strongSelf.updateDisplayPlaceholder(displayPlaceholder: true) - } - } - }) - } - case let .staticEmoji(staticEmoji): - let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - let preScaleFactor: CGFloat = 1.0 - let scaledSize = CGSize(width: floor(size.width * preScaleFactor), height: floor(size.height * preScaleFactor)) - let scaleFactor = scaledSize.width / size.width - - context.scaleBy(x: 1.0 / scaleFactor, y: 1.0 / scaleFactor) - - let string = NSAttributedString(string: staticEmoji, font: Font.regular(floor(32.0 * scaleFactor)), textColor: .black) - let boundingRect = string.boundingRect(with: scaledSize, options: .usesLineFragmentOrigin, context: nil) - UIGraphicsPushContext(context) - string.draw(at: CGPoint(x: floorToScreenPixels((scaledSize.width - boundingRect.width) / 2.0 + boundingRect.minX), y: floorToScreenPixels((scaledSize.height - boundingRect.height) / 2.0 + boundingRect.minY))) - UIGraphicsPopContext() - }) - self.contents = image?.cgImage - case let .icon(icon): - let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - UIGraphicsPushContext(context) - - switch icon { - case .premiumStar: - if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/EntityInputPremiumIcon"), color: accentIconColor) { - let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) - image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) - } - case let .topic(title, color): - let colors = topicIconColors(for: color) - if let image = generateTopicIcon(backgroundColors: colors.0.map { UIColor(rgb: $0) }, strokeColors: colors.1.map { UIColor(rgb: $0) }, title: title) { - let imageSize = image.size//.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) - image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) - } - case .stop: - if let image = generateTintedImage(image: UIImage(bundleImageName: "Premium/NoIcon"), color: .white) { - let imageSize = image.size.aspectFitted(CGSize(width: size.width - 6.0, height: size.height - 6.0)) - image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) - } - case .add: - break - } - - UIGraphicsPopContext() - })?.withRenderingMode(icon == .stop ? .alwaysTemplate : .alwaysOriginal) - self.contents = image?.cgImage - } - - if case .icon(.add) = content { - let tintContentLayer = SimpleLayer() - self.tintContentLayer = tintContentLayer - - let iconLayer = SimpleLayer() - self.iconLayer = iconLayer - self.addSublayer(iconLayer) - - let tintIconLayer = SimpleLayer() - self.tintIconLayer = tintIconLayer - tintContentLayer.addSublayer(tintIconLayer) - } - } - - override public init(layer: Any) { - guard let layer = layer as? ItemLayer else { - preconditionFailure() - } - - self.context = layer.context - self.item = layer.item - - self.content = layer.content - self.placeholderColor = layer.placeholderColor - self.size = layer.size - self.pixelSize = layer.pixelSize - self.pointSize = layer.pointSize - - self.onUpdateDisplayPlaceholder = { _, _ in } - - super.init(layer: layer) - } - - required public init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - deinit { - self.disposable?.dispose() - self.fetchDisposable?.dispose() - } - - public override func action(forKey event: String) -> CAAction? { - if event == kCAOnOrderIn { - self.isInHierarchyValue = true - } else if event == kCAOnOrderOut { - self.isInHierarchyValue = false - } - self.updatePlayback() - return nullAction - } - - func update( - content: ItemContent, - theme: PresentationTheme - ) { - var themeUpdated = false - if self.theme !== theme { - self.theme = theme - themeUpdated = true - } - var contentUpdated = false - if self.content != content { - self.content = content - contentUpdated = true - } - - if themeUpdated || contentUpdated { - if case let .icon(icon) = content, case let .topic(title, color) = icon { - let image = generateImage(self.size, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - UIGraphicsPushContext(context) - - let colors = topicIconColors(for: color) - if let image = generateTopicIcon(backgroundColors: colors.0.map { UIColor(rgb: $0) }, strokeColors: colors.1.map { UIColor(rgb: $0) }, title: title) { - let imageSize = image.size - image.draw(in: CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: floor((size.height - imageSize.height) / 2.0)), size: imageSize)) - } - - UIGraphicsPopContext() - }) - self.contents = image?.cgImage - } else if case .icon(.add) = content { - guard let iconLayer = self.iconLayer, let tintIconLayer = self.tintIconLayer else { - return - } - func generateIcon(color: UIColor) -> UIImage? { - return generateImage(self.pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in - context.clear(CGRect(origin: CGPoint(), size: size)) - - UIGraphicsPushContext(context) - - context.setFillColor(color.withMultipliedAlpha(0.2).cgColor) - context.fillEllipse(in: CGRect(origin: .zero, size: size).insetBy(dx: 8.0, dy: 8.0)) - context.setFillColor(color.cgColor) - - let plusSize = CGSize(width: 4.5, height: 31.5) - context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.width) / 2.0), y: floorToScreenPixels((size.height - plusSize.height) / 2.0), width: plusSize.width, height: plusSize.height), cornerRadius: plusSize.width / 2.0).cgPath) - context.addPath(UIBezierPath(roundedRect: CGRect(x: floorToScreenPixels((size.width - plusSize.height) / 2.0), y: floorToScreenPixels((size.height - plusSize.width) / 2.0), width: plusSize.height, height: plusSize.width), cornerRadius: plusSize.width / 2.0).cgPath) - context.fillPath() - - UIGraphicsPopContext() - }) - } - - let needsVibrancy = !theme.overallDarkAppearance - let color = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor - - iconLayer.contents = generateIcon(color: color)?.cgImage - tintIconLayer.contents = generateIcon(color: .white)?.cgImage - - tintIconLayer.isHidden = !needsVibrancy - } - } - } - - func update( - transition: Transition, - size: CGSize, - badge: Badge?, - blurredBadgeColor: UIColor, - blurredBadgeBackgroundColor: UIColor - ) { - if self.badge != badge || self.validSize != size { - self.badge = badge - self.validSize = size - - if let iconLayer = self.iconLayer, let tintIconLayer = self.tintIconLayer { - transition.setFrame(layer: iconLayer, frame: CGRect(origin: .zero, size: size)) - transition.setFrame(layer: tintIconLayer, frame: CGRect(origin: .zero, size: size)) - } - - if let badge = badge { - var badgeTransition = transition - let premiumBadgeView: PremiumBadgeView - if let current = self.premiumBadgeView { - premiumBadgeView = current - } else { - badgeTransition = .immediate - premiumBadgeView = PremiumBadgeView(context: self.context) - self.premiumBadgeView = premiumBadgeView - self.addSublayer(premiumBadgeView.layer) - } - - let badgeDiameter = min(16.0, floor(size.height * 0.5)) - let badgeSize = CGSize(width: badgeDiameter, height: badgeDiameter) - badgeTransition.setFrame(view: premiumBadgeView, frame: CGRect(origin: CGPoint(x: size.width - badgeSize.width, y: size.height - badgeSize.height), size: badgeSize)) - premiumBadgeView.update(transition: badgeTransition, badge: badge, backgroundColor: blurredBadgeColor, size: badgeSize) - - self.blurredRepresentationBackgroundColor = blurredBadgeBackgroundColor - self.blurredRepresentationTarget = premiumBadgeView.contentLayer - } else { - if let premiumBadgeView = self.premiumBadgeView { - self.premiumBadgeView = nil - premiumBadgeView.removeFromSuperview() - - self.blurredRepresentationBackgroundColor = nil - self.blurredRepresentationTarget = nil - } - } - } - } - - private func updatePlayback() { - let shouldBePlaying = self.isInHierarchyValue && self.isVisibleForAnimations - - self.shouldBeAnimating = shouldBePlaying - } - - public override func updateDisplayPlaceholder(displayPlaceholder: Bool) { - if self.displayPlaceholder == displayPlaceholder { - return - } - - self.displayPlaceholder = displayPlaceholder - self.onUpdateDisplayPlaceholder(displayPlaceholder, 0.0) - } - - public override func transitionToContents(_ contents: AnyObject, didLoop: Bool) { - self.contents = contents - - if self.displayPlaceholder { - self.displayPlaceholder = false - self.onUpdateDisplayPlaceholder(false, 0.2) - self.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) - } - - if didLoop { - self.onLoop() - } - } - } - private final class GroupBorderLayer: PassthroughShapeLayer { let tintContainerLayer: CAShapeLayer @@ -4104,7 +1330,7 @@ public final class EmojiPagerContentComponent: Component { } private enum VisualItemKey: Hashable { - case item(id: ItemLayer.Key) + case item(id: EmojiKeyboardItemLayer.Key) case header(groupId: AnyHashable) case groupExpandButton(groupId: AnyHashable) case groupActionButton(groupId: AnyHashable) @@ -4134,10 +1360,10 @@ public final class EmojiPagerContentComponent: Component { private var visibleSearchHeader: EmojiSearchHeaderView? private var visibleEmptySearchResultsView: EmptySearchResultsView? private var visibleCustomContentView: EmojiCustomContentView? - private var visibleItemPlaceholderViews: [ItemLayer.Key: ItemPlaceholderView] = [:] + private var visibleItemPlaceholderViews: [EmojiKeyboardItemLayer.Key: ItemPlaceholderView] = [:] private var visibleFillPlaceholdersViews: [Int: ItemPlaceholderView] = [:] - private var visibleItemSelectionLayers: [ItemLayer.Key: ItemSelectionLayer] = [:] - private var visibleItemLayers: [ItemLayer.Key: ItemLayer] = [:] + private var visibleItemSelectionLayers: [EmojiKeyboardItemLayer.Key: ItemSelectionLayer] = [:] + private var visibleItemLayers: [EmojiKeyboardItemLayer.Key: EmojiKeyboardItemLayer] = [:] private var visibleGroupHeaders: [AnyHashable: GroupHeaderLayer] = [:] private var visibleGroupBorders: [AnyHashable: GroupBorderLayer] = [:] private var visibleGroupPremiumButtons: [AnyHashable: ComponentView] = [:] @@ -4163,7 +1389,7 @@ public final class EmojiPagerContentComponent: Component { private var activeItemUpdated: ActionSlot<(AnyHashable, AnyHashable?, Transition)>? private var itemLayout: ItemLayout? - private var contextFocusItemKey: EmojiPagerContentComponent.View.ItemLayer.Key? + private var contextFocusItemKey: EmojiKeyboardItemLayer.Key? private var contextGesture: ContextGesture? private var tapRecognizer: UITapGestureRecognizer? @@ -4503,7 +1729,7 @@ public final class EmojiPagerContentComponent: Component { } public func layerForItem(groupId: AnyHashable, item: EmojiPagerContentComponent.Item) -> CALayer? { - let itemKey = EmojiPagerContentComponent.View.ItemLayer.Key(groupId: groupId, itemId: item.content.id) + let itemKey = EmojiKeyboardItemLayer.Key(groupId: groupId, itemId: item.content.id) if let itemLayer = self.visibleItemLayers[itemKey] { return itemLayer } else { @@ -4530,15 +1756,15 @@ public final class EmojiPagerContentComponent: Component { let offsetDirectionSign: Double = scrollPosition < self.scrollView.bounds.minY ? -1.0 : 1.0 - var previousVisibleLayers: [ItemLayer.Key: (CALayer, CGRect)] = [:] + var previousVisibleLayers: [EmojiKeyboardItemLayer.Key: (CALayer, CGRect)] = [:] for (id, layer) in self.visibleItemLayers { previousVisibleLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } - var previousVisibleItemSelectionLayers: [ItemLayer.Key: (CALayer, CGRect)] = [:] + var previousVisibleItemSelectionLayers: [EmojiKeyboardItemLayer.Key: (CALayer, CGRect)] = [:] for (id, layer) in self.visibleItemSelectionLayers { previousVisibleItemSelectionLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } - var previousVisiblePlaceholderViews: [ItemLayer.Key: (UIView, CGRect)] = [:] + var previousVisiblePlaceholderViews: [EmojiKeyboardItemLayer.Key: (UIView, CGRect)] = [:] for (id, view) in self.visibleItemPlaceholderViews { previousVisiblePlaceholderViews[id] = (view, view.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } @@ -5024,15 +2250,15 @@ public final class EmojiPagerContentComponent: Component { let offsetDirectionSign: Double = scrollPosition < self.scrollView.bounds.minY ? -1.0 : 1.0 - var previousVisibleLayers: [ItemLayer.Key: (CALayer, CGRect)] = [:] + var previousVisibleLayers: [EmojiKeyboardItemLayer.Key: (CALayer, CGRect)] = [:] for (id, layer) in self.visibleItemLayers { previousVisibleLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } - var previousVisibleItemSelectionLayers: [ItemLayer.Key: (ItemSelectionLayer, CGRect)] = [:] + var previousVisibleItemSelectionLayers: [EmojiKeyboardItemLayer.Key: (ItemSelectionLayer, CGRect)] = [:] for (id, layer) in self.visibleItemSelectionLayers { previousVisibleItemSelectionLayers[id] = (layer, layer.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } - var previousVisiblePlaceholderViews: [ItemLayer.Key: (UIView, CGRect)] = [:] + var previousVisiblePlaceholderViews: [EmojiKeyboardItemLayer.Key: (UIView, CGRect)] = [:] for (id, view) in self.visibleItemPlaceholderViews { previousVisiblePlaceholderViews[id] = (view, view.frame.offsetBy(dx: 0.0, dy: -self.scrollView.bounds.minY)) } @@ -5503,8 +2729,8 @@ public final class EmojiPagerContentComponent: Component { } private let longPressDuration: Double = 0.5 - private var longPressItem: EmojiPagerContentComponent.View.ItemLayer.Key? - private var currentLongPressLayer: CloneItemLayer? + private var longPressItem: EmojiKeyboardItemLayer.Key? + private var currentLongPressLayer: EmojiKeyboardCloneItemLayer? private var hapticFeedback: HapticFeedback? private var continuousHaptic: AnyObject? private var longPressTimer: SwiftSignalKit.Timer? @@ -5543,7 +2769,7 @@ public final class EmojiPagerContentComponent: Component { self.currentLongPressLayer = nil currentLongPressLayer.removeFromSuperlayer() } - let currentLongPressLayer = CloneItemLayer() + let currentLongPressLayer = EmojiKeyboardCloneItemLayer() currentLongPressLayer.position = self.scrollView.layer.convert(itemLayer.position, to: externalExpansionView.layer) currentLongPressLayer.bounds = itemLayer.convert(itemLayer.bounds, to: externalExpansionView.layer) currentLongPressLayer.transform = itemLayer.transform @@ -5641,10 +2867,10 @@ public final class EmojiPagerContentComponent: Component { } } - private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (Item, ItemLayer.Key)? { + private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (Item, EmojiKeyboardItemLayer.Key)? { let localPoint = self.convert(point, to: self.scrollView) - var closestItem: (key: ItemLayer.Key, distance: CGFloat)? + var closestItem: (key: EmojiKeyboardItemLayer.Key, distance: CGFloat)? for (key, itemLayer) in self.visibleItemLayers { if extendedHitRange { @@ -5806,7 +3032,7 @@ public final class EmojiPagerContentComponent: Component { var topVisibleGroupId: AnyHashable? var topVisibleSubgroupId: AnyHashable? - var validIds = Set() + var validIds = Set() var validGroupHeaderIds = Set() var validGroupBorderIds = Set() var validGroupPremiumButtonIds = Set() @@ -6136,7 +3362,7 @@ public final class EmojiPagerContentComponent: Component { } } - let itemId = ItemLayer.Key( + let itemId = EmojiKeyboardItemLayer.Key( groupId: itemGroup.groupId, itemId: item.content.id ) @@ -6151,7 +3377,7 @@ public final class EmojiPagerContentComponent: Component { var animateItemIn = false var updateItemLayerPlaceholder = false var itemTransition = transition - let itemLayer: ItemLayer + let itemLayer: EmojiKeyboardItemLayer if let current = self.visibleItemLayers[itemId] { itemLayer = current } else { @@ -6167,7 +3393,7 @@ public final class EmojiPagerContentComponent: Component { } let placeholderColor = keyboardChildEnvironment.theme.chat.inputPanel.primaryTextColor.withMultipliedAlpha(0.1) - itemLayer = ItemLayer( + itemLayer = EmojiKeyboardItemLayer( item: item, context: component.context, attemptSynchronousLoad: attemptSynchronousLoads, @@ -6264,7 +3490,7 @@ public final class EmojiPagerContentComponent: Component { let itemPosition = CGPoint(x: itemFrame.midX, y: itemFrame.midY) itemTransition.setPosition(layer: itemLayer, position: itemPosition) - var badge: ItemLayer.Badge? + var badge: EmojiKeyboardItemLayer.Badge? if itemGroup.displayPremiumBadges, let file = item.itemFile, file.isPremiumSticker { badge = .premium } else { @@ -6445,7 +3671,7 @@ public final class EmojiPagerContentComponent: Component { } var removedPlaceholerViews = false - var removedIds: [ItemLayer.Key] = [] + var removedIds: [EmojiKeyboardItemLayer.Key] = [] for (id, itemLayer) in self.visibleItemLayers { if !validIds.contains(id) { removedIds.append(id) @@ -6535,7 +3761,7 @@ public final class EmojiPagerContentComponent: Component { removedPlaceholerViews = true } } - var removedItemSelectionLayerIds: [ItemLayer.Key] = [] + var removedItemSelectionLayerIds: [EmojiKeyboardItemLayer.Key] = [] for (id, itemSelectionLayer) in self.visibleItemSelectionLayers { var fileId: MediaId? switch id.itemId { @@ -6924,7 +4150,7 @@ public final class EmojiPagerContentComponent: Component { var hintDisappearingGroupFrame: (groupId: AnyHashable, frame: CGRect)? var previousAbsoluteItemPositions: [VisualItemKey: CGPoint] = [:] - var anchorItems: [ItemLayer.Key: CGRect] = [:] + var anchorItems: [EmojiKeyboardItemLayer.Key: CGRect] = [:] if let previousComponent = previousComponent, let previousItemLayout = self.itemLayout, previousComponent.contentItemGroups != component.contentItemGroups, previousComponent.itemContentUniqueId == component.itemContentUniqueId { if !transition.animation.isImmediate { var previousItemPositionsValue: [VisualItemKey: CGPoint] = [:] @@ -6932,8 +4158,8 @@ public final class EmojiPagerContentComponent: Component { let itemGroup = previousComponent.contentItemGroups[groupIndex] for itemIndex in 0 ..< itemGroup.items.count { let item = itemGroup.items[itemIndex] - let itemKey: ItemLayer.Key - itemKey = ItemLayer.Key( + let itemKey: EmojiKeyboardItemLayer.Key + itemKey = EmojiKeyboardItemLayer.Key( groupId: itemGroup.groupId, itemId: item.content.id ) @@ -7200,7 +4426,7 @@ public final class EmojiPagerContentComponent: Component { var animatedScrollOffset: CGFloat = 0.0 if !anchorItems.isEmpty && !keepOffset { - let sortedAnchorItems: [(ItemLayer.Key, CGRect)] = anchorItems.sorted(by: { lhs, rhs in + let sortedAnchorItems: [(EmojiKeyboardItemLayer.Key, CGRect)] = anchorItems.sorted(by: { lhs, rhs in if lhs.value.minY != rhs.value.minY { return lhs.value.minY < rhs.value.minY } else { @@ -7214,8 +4440,8 @@ public final class EmojiPagerContentComponent: Component { continue } for j in 0 ..< component.contentItemGroups[i].items.count { - let itemKey: ItemLayer.Key - itemKey = ItemLayer.Key( + let itemKey: EmojiKeyboardItemLayer.Key + itemKey = EmojiKeyboardItemLayer.Key( groupId: component.contentItemGroups[i].groupId, itemId: component.contentItemGroups[i].items[j].content.id ) @@ -7261,8 +4487,8 @@ public final class EmojiPagerContentComponent: Component { let itemGroupLayout = itemLayout.itemGroupLayouts[groupIndex] for itemIndex in 0 ..< itemGroup.items.count { let item = itemGroup.items[itemIndex] - let itemKey: ItemLayer.Key - itemKey = ItemLayer.Key( + let itemKey: EmojiKeyboardItemLayer.Key + itemKey = EmojiKeyboardItemLayer.Key( groupId: itemGroup.groupId, itemId: item.content.id ) diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchHeaderView.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchHeaderView.swift new file mode 100644 index 0000000000..d8369244f7 --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchHeaderView.swift @@ -0,0 +1,607 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramCore +import TelegramPresentationData +import AccountContext +import SwiftSignalKit + +public final class EmojiSearchHeaderView: UIView, UITextFieldDelegate { + private final class EmojiSearchTextField: UITextField { + override func textRect(forBounds bounds: CGRect) -> CGRect { + return bounds.integral + } + } + + private struct Params: Equatable { + var context: AccountContext + var theme: PresentationTheme + var forceNeedsVibrancy: Bool + var strings: PresentationStrings + var text: String + var useOpaqueTheme: Bool + var isActive: Bool + var hasPresetSearch: Bool + var textInputState: EmojiSearchSearchBarComponent.TextInputState + var searchState: EmojiPagerContentComponent.SearchState + var size: CGSize + var canFocus: Bool + var searchCategories: EmojiSearchCategories? + + static func ==(lhs: Params, rhs: Params) -> Bool { + if lhs.context !== rhs.context { + return false + } + if lhs.theme !== rhs.theme { + return false + } + if lhs.forceNeedsVibrancy != rhs.forceNeedsVibrancy { + return false + } + if lhs.strings !== rhs.strings { + return false + } + if lhs.text != rhs.text { + return false + } + if lhs.useOpaqueTheme != rhs.useOpaqueTheme { + return false + } + if lhs.isActive != rhs.isActive { + return false + } + if lhs.hasPresetSearch != rhs.hasPresetSearch { + return false + } + if lhs.textInputState != rhs.textInputState { + return false + } + if lhs.searchState != rhs.searchState { + return false + } + if lhs.size != rhs.size { + return false + } + if lhs.canFocus != rhs.canFocus { + return false + } + if lhs.searchCategories != rhs.searchCategories { + return false + } + return true + } + } + + override public static var layerClass: AnyClass { + return PassthroughLayer.self + } + + private let activated: (Bool) -> Void + private let deactivated: (Bool) -> Void + private let updateQuery: (EmojiPagerContentComponent.SearchQuery?) -> Void + + let tintContainerView: UIView + + private let backgroundLayer: SimpleLayer + private let tintBackgroundLayer: SimpleLayer + + private let statusIcon = ComponentView() + + private let clearIconView: UIImageView + private let clearIconTintView: UIImageView + private let clearIconButton: HighlightTrackingButton + + private let cancelButtonTintTitle: ComponentView + private let cancelButtonTitle: ComponentView + private let cancelButton: HighlightTrackingButton + + private var placeholderContent = ComponentView() + + private var textFrame: CGRect? + private var textField: EmojiSearchTextField? + + private var tapRecognizer: UITapGestureRecognizer? + private(set) var currentPresetSearchTerm: EmojiSearchCategories.Group? + + private var params: Params? + + public var wantsDisplayBelowKeyboard: Bool { + return self.textField != nil + } + + init(activated: @escaping (Bool) -> Void, deactivated: @escaping (Bool) -> Void, updateQuery: @escaping (EmojiPagerContentComponent.SearchQuery?) -> Void) { + self.activated = activated + self.deactivated = deactivated + self.updateQuery = updateQuery + + self.tintContainerView = UIView() + + self.backgroundLayer = SimpleLayer() + self.tintBackgroundLayer = SimpleLayer() + + self.clearIconView = UIImageView() + self.clearIconTintView = UIImageView() + self.clearIconButton = HighlightableButton() + self.clearIconView.isHidden = true + self.clearIconTintView.isHidden = true + self.clearIconButton.isHidden = true + + self.cancelButtonTintTitle = ComponentView() + self.cancelButtonTitle = ComponentView() + self.cancelButton = HighlightTrackingButton() + + super.init(frame: CGRect()) + + self.layer.addSublayer(self.backgroundLayer) + self.tintContainerView.layer.addSublayer(self.tintBackgroundLayer) + + self.addSubview(self.clearIconView) + self.tintContainerView.addSubview(self.clearIconTintView) + self.addSubview(self.clearIconButton) + + self.addSubview(self.cancelButton) + self.clipsToBounds = true + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerView.layer + + let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))) + self.tapRecognizer = tapRecognizer + self.addGestureRecognizer(tapRecognizer) + + self.cancelButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { + cancelButtonTitleView.layer.removeAnimation(forKey: "opacity") + cancelButtonTitleView.alpha = 0.4 + } + if let cancelButtonTintTitleView = strongSelf.cancelButtonTintTitle.view { + cancelButtonTintTitleView.layer.removeAnimation(forKey: "opacity") + cancelButtonTintTitleView.alpha = 0.4 + } + } else { + if let cancelButtonTitleView = strongSelf.cancelButtonTitle.view { + cancelButtonTitleView.alpha = 1.0 + cancelButtonTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + if let cancelButtonTintTitleView = strongSelf.cancelButtonTintTitle.view { + cancelButtonTintTitleView.alpha = 1.0 + cancelButtonTintTitleView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + } + self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), for: .touchUpInside) + + self.clearIconButton.highligthedChanged = { [weak self] highlighted in + if let strongSelf = self { + if highlighted { + strongSelf.clearIconView.layer.removeAnimation(forKey: "opacity") + strongSelf.clearIconView.alpha = 0.4 + strongSelf.clearIconTintView.layer.removeAnimation(forKey: "opacity") + strongSelf.clearIconTintView.alpha = 0.4 + } else { + strongSelf.clearIconView.alpha = 1.0 + strongSelf.clearIconView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + strongSelf.clearIconTintView.alpha = 1.0 + strongSelf.clearIconTintView.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2) + } + } + } + self.clearIconButton.addTarget(self, action: #selector(self.clearPressed), for: .touchUpInside) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + @objc private func tapGesture(_ recognizer: UITapGestureRecognizer) { + if case .ended = recognizer.state { + let location = recognizer.location(in: self) + if let view = self.statusIcon.view, view.frame.contains(location), self.currentPresetSearchTerm != nil { + self.clearCategorySearch() + } else { + self.activateTextInput() + } + } + } + + func clearCategorySearch() { + if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { + placeholderContentView.clearSelection(dispatchEvent : true) + } + } + + private func activateTextInput() { + guard let params = self.params else { + return + } + if self.textField == nil, let textFrame = self.textFrame, params.canFocus == true { + let backgroundFrame = self.backgroundLayer.frame + let textFieldFrame = CGRect(origin: CGPoint(x: textFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textFrame.minX, height: backgroundFrame.height)) + + let textField = EmojiSearchTextField(frame: textFieldFrame) + textField.keyboardAppearance = params.theme.rootController.keyboardColor.keyboardAppearance + textField.autocorrectionType = .no + textField.returnKeyType = .search + self.textField = textField + self.insertSubview(textField, belowSubview: self.clearIconView) + textField.delegate = self + textField.addTarget(self, action: #selector(self.textFieldChanged(_:)), for: .editingChanged) + } + + if params.canFocus { + self.currentPresetSearchTerm = nil + if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { + placeholderContentView.clearSelection(dispatchEvent: false) + } + } + + self.activated(true) + + self.textField?.becomeFirstResponder() + } + + @objc private func cancelPressed() { + self.currentPresetSearchTerm = nil + self.updateQuery(nil) + + self.clearIconView.isHidden = true + self.clearIconTintView.isHidden = true + self.clearIconButton.isHidden = true + + let textField = self.textField + self.textField = nil + + self.deactivated(textField?.isFirstResponder ?? false) + + if let textField { + textField.resignFirstResponder() + textField.removeFromSuperview() + } + + /*self.tintTextView.view?.isHidden = false + self.textView.view?.isHidden = false*/ + } + + @objc private func clearPressed() { + self.currentPresetSearchTerm = nil + self.updateQuery(nil) + self.textField?.text = "" + + self.clearIconView.isHidden = true + self.clearIconTintView.isHidden = true + self.clearIconButton.isHidden = true + + /*self.tintTextView.view?.isHidden = false + self.textView.view?.isHidden = false*/ + } + + var isActive: Bool { + return self.textField?.isFirstResponder ?? false + } + + func deactivate() { + if let text = self.textField?.text, !text.isEmpty { + self.textField?.endEditing(true) + } else { + self.cancelPressed() + } + } + + public func textFieldDidBeginEditing(_ textField: UITextField) { + } + + public func textFieldDidEndEditing(_ textField: UITextField) { + } + + public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + textField.endEditing(true) + return false + } + + @objc private func textFieldChanged(_ textField: UITextField) { + self.update(transition: .immediate) + + let text = textField.text ?? "" + + var inputLanguage = textField.textInputMode?.primaryLanguage ?? "en" + if let range = inputLanguage.range(of: "-") { + inputLanguage = String(inputLanguage[inputLanguage.startIndex ..< range.lowerBound]) + } + if let range = inputLanguage.range(of: "_") { + inputLanguage = String(inputLanguage[inputLanguage.startIndex ..< range.lowerBound]) + } + + self.clearIconView.isHidden = text.isEmpty + self.clearIconTintView.isHidden = text.isEmpty + self.clearIconButton.isHidden = text.isEmpty + + self.currentPresetSearchTerm = nil + self.updateQuery(.text(value: text, language: inputLanguage)) + } + + private func update(transition: Transition) { + guard let params = self.params else { + return + } + self.params = nil + self.update(context: params.context, theme: params.theme, forceNeedsVibrancy: params.forceNeedsVibrancy, strings: params.strings, text: params.text, useOpaqueTheme: params.useOpaqueTheme, isActive: params.isActive, size: params.size, canFocus: params.canFocus, searchCategories: params.searchCategories, searchState: params.searchState, transition: transition) + } + + public func update(context: AccountContext, theme: PresentationTheme, forceNeedsVibrancy: Bool, strings: PresentationStrings, text: String, useOpaqueTheme: Bool, isActive: Bool, size: CGSize, canFocus: Bool, searchCategories: EmojiSearchCategories?, searchState: EmojiPagerContentComponent.SearchState, transition: Transition) { + let textInputState: EmojiSearchSearchBarComponent.TextInputState + if let textField = self.textField { + textInputState = .active(hasText: !(textField.text ?? "").isEmpty) + } else { + textInputState = .inactive + } + + let params = Params( + context: context, + theme: theme, + forceNeedsVibrancy: forceNeedsVibrancy, + strings: strings, + text: text, + useOpaqueTheme: useOpaqueTheme, + isActive: isActive, + hasPresetSearch: self.currentPresetSearchTerm == nil, + textInputState: textInputState, + searchState: searchState, + size: size, + canFocus: canFocus, + searchCategories: searchCategories + ) + + if self.params == params { + return + } + + let isActiveWithText = isActive && self.currentPresetSearchTerm == nil + + if self.params?.theme !== theme { + /*self.searchIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white)?.withRenderingMode(.alwaysTemplate) + self.searchIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor + + self.searchIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Loupe"), color: .white) + + self.backIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: .white)?.withRenderingMode(.alwaysTemplate) + self.backIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor + + self.backIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: .white)*/ + + self.clearIconView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white)?.withRenderingMode(.alwaysTemplate) + self.clearIconView.tintColor = useOpaqueTheme ? theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor + + self.clearIconTintView.image = generateTintedImage(image: UIImage(bundleImageName: "Components/Search Bar/Clear"), color: .white) + } + + self.params = params + + let sideInset: CGFloat = 12.0 + let topInset: CGFloat = 8.0 + let inputHeight: CGFloat = 36.0 + + let sideTextInset: CGFloat = sideInset + 4.0 + 24.0 + + if theme.overallDarkAppearance && forceNeedsVibrancy { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.withMultipliedAlpha(0.3).cgColor + self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor + } else if useOpaqueTheme { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueSelectionColor.cgColor + self.tintBackgroundLayer.backgroundColor = UIColor.white.cgColor + } else { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantSelectionColor.cgColor + self.tintBackgroundLayer.backgroundColor = UIColor(white: 1.0, alpha: 0.2).cgColor + } + + self.backgroundLayer.cornerRadius = inputHeight * 0.5 + self.tintBackgroundLayer.cornerRadius = inputHeight * 0.5 + + let cancelColor: UIColor + if theme.overallDarkAppearance && forceNeedsVibrancy { + cancelColor = theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor.withMultipliedAlpha(0.3) + } else { + cancelColor = useOpaqueTheme ? theme.list.itemAccentColor : theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor + } + + let cancelTextSize = self.cancelButtonTitle.update( + transition: .immediate, + component: AnyComponent(Text( + text: strings.Common_Cancel, + font: Font.regular(17.0), + color: cancelColor + )), + environment: {}, + containerSize: CGSize(width: size.width - 32.0, height: 100.0) + ) + let _ = self.cancelButtonTintTitle.update( + transition: .immediate, + component: AnyComponent(Text( + text: strings.Common_Cancel, + font: Font.regular(17.0), + color: .white + )), + environment: {}, + containerSize: CGSize(width: size.width - 32.0, height: 100.0) + ) + + let cancelButtonSpacing: CGFloat = 8.0 + + var backgroundFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset), size: CGSize(width: size.width - sideInset * 2.0, height: inputHeight)) + if isActiveWithText { + backgroundFrame.size.width -= cancelTextSize.width + cancelButtonSpacing + } + transition.setFrame(layer: self.backgroundLayer, frame: backgroundFrame) + transition.setFrame(layer: self.tintBackgroundLayer, frame: backgroundFrame) + + transition.setFrame(view: self.cancelButton, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX, y: 0.0), size: CGSize(width: cancelButtonSpacing + cancelTextSize.width, height: size.height))) + + let textX: CGFloat = backgroundFrame.minX + sideTextInset + let textFrame = CGRect(origin: CGPoint(x: textX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - textX, height: backgroundFrame.height)) + self.textFrame = textFrame + + let statusContent: EmojiSearchStatusComponent.Content + switch searchState { + case .empty: + statusContent = .search + case .searching: + statusContent = .progress + case .active: + statusContent = .results + } + + let statusSize = CGSize(width: 24.0, height: 24.0) + let _ = self.statusIcon.update( + transition: transition, + component: AnyComponent(EmojiSearchStatusComponent( + theme: theme, + forceNeedsVibrancy: forceNeedsVibrancy, + strings: strings, + useOpaqueTheme: useOpaqueTheme, + content: statusContent + )), + environment: {}, + containerSize: statusSize + ) + let iconFrame = CGRect(origin: CGPoint(x: textFrame.minX - statusSize.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - statusSize.height) / 2.0)), size: statusSize) + if let statusIconView = self.statusIcon.view as? EmojiSearchStatusComponent.View { + if statusIconView.superview == nil { + self.addSubview(statusIconView) + self.tintContainerView.addSubview(statusIconView.tintContainerView) + } + + transition.setFrame(view: statusIconView, frame: iconFrame) + transition.setFrame(view: statusIconView.tintContainerView, frame: iconFrame) + } + + let placeholderContentFrame = CGRect(origin: CGPoint(x: textFrame.minX - 6.0, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - (textFrame.minX - 6.0), height: backgroundFrame.height)) + let _ = self.placeholderContent.update( + transition: transition, + component: AnyComponent(EmojiSearchSearchBarComponent( + context: context, + theme: theme, + forceNeedsVibrancy: forceNeedsVibrancy, + strings: strings, + useOpaqueTheme: useOpaqueTheme, + textInputState: textInputState, + categories: searchCategories, + searchTermUpdated: { [weak self] term in + guard let self else { + return + } + var shouldChangeActivation = false + if (self.currentPresetSearchTerm == nil) != (term == nil) { + shouldChangeActivation = true + } + self.currentPresetSearchTerm = term + + if shouldChangeActivation { + if let term { + self.update(transition: Transition(animation: .curve(duration: 0.4, curve: .spring))) + + self.updateQuery(.category(value: term)) + self.activated(false) + } else { + self.deactivated(self.textField?.isFirstResponder ?? false) + self.updateQuery(nil) + } + } else { + if let term { + self.updateQuery(.category(value: term)) + } else { + self.updateQuery(nil) + } + } + }, + activateTextInput: { [weak self] in + guard let self else { + return + } + self.activateTextInput() + } + )), + environment: {}, + containerSize: placeholderContentFrame.size + ) + if let placeholderContentView = self.placeholderContent.view as? EmojiSearchSearchBarComponent.View { + if placeholderContentView.superview == nil { + self.addSubview(placeholderContentView) + self.tintContainerView.addSubview(placeholderContentView.tintContainerView) + } + transition.setFrame(view: placeholderContentView, frame: placeholderContentFrame) + transition.setFrame(view: placeholderContentView.tintContainerView, frame: placeholderContentFrame) + } + + /*if let searchCategories { + let suggestedItemsView: ComponentView + var suggestedItemsTransition = transition + if let current = self.suggestedItemsView { + suggestedItemsView = current + } else { + suggestedItemsTransition = .immediate + suggestedItemsView = ComponentView() + self.suggestedItemsView = suggestedItemsView + } + + let itemsX: CGFloat = textFrame.maxX + 8.0 + let suggestedItemsFrame = CGRect(origin: CGPoint(x: itemsX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.maxX - itemsX, height: backgroundFrame.height)) + + if let suggestedItemsComponentView = suggestedItemsView.view { + if suggestedItemsComponentView.superview == nil { + self.addSubview(suggestedItemsComponentView) + } + suggestedItemsTransition.setFrame(view: suggestedItemsComponentView, frame: suggestedItemsFrame) + suggestedItemsTransition.setAlpha(view: suggestedItemsComponentView, alpha: isActiveWithText ? 0.0 : 1.0) + } + } else { + if let suggestedItemsView = self.suggestedItemsView { + self.suggestedItemsView = nil + if let suggestedItemsComponentView = suggestedItemsView.view { + transition.setAlpha(view: suggestedItemsComponentView, alpha: 0.0, completion: { [weak suggestedItemsComponentView] _ in + suggestedItemsComponentView?.removeFromSuperview() + }) + } + } + }*/ + + if let image = self.clearIconView.image { + let iconFrame = CGRect(origin: CGPoint(x: backgroundFrame.maxX - image.size.width - 4.0, y: backgroundFrame.minY + floor((backgroundFrame.height - image.size.height) / 2.0)), size: image.size) + transition.setFrame(view: self.clearIconView, frame: iconFrame) + transition.setFrame(view: self.clearIconTintView, frame: iconFrame) + transition.setFrame(view: self.clearIconButton, frame: iconFrame.insetBy(dx: -8.0, dy: -10.0)) + } + + if let cancelButtonTitleComponentView = self.cancelButtonTitle.view { + if cancelButtonTitleComponentView.superview == nil { + self.addSubview(cancelButtonTitleComponentView) + cancelButtonTitleComponentView.isUserInteractionEnabled = false + } + transition.setFrame(view: cancelButtonTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) + transition.setAlpha(view: cancelButtonTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0) + } + if let cancelButtonTintTitleComponentView = self.cancelButtonTintTitle.view { + if cancelButtonTintTitleComponentView.superview == nil { + self.tintContainerView.addSubview(cancelButtonTintTitleComponentView) + cancelButtonTintTitleComponentView.isUserInteractionEnabled = false + } + transition.setFrame(view: cancelButtonTintTitleComponentView, frame: CGRect(origin: CGPoint(x: backgroundFrame.maxX + cancelButtonSpacing, y: floor((size.height - cancelTextSize.height) / 2.0)), size: cancelTextSize)) + transition.setAlpha(view: cancelButtonTintTitleComponentView, alpha: isActiveWithText ? 1.0 : 0.0) + } + + var hasText = false + if let textField = self.textField { + textField.textColor = theme.contextMenu.primaryColor + transition.setFrame(view: textField, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + sideTextInset, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width - sideTextInset - 32.0, height: backgroundFrame.height))) + + if let text = textField.text, !text.isEmpty { + hasText = true + } + } + let _ = hasText + + /*self.tintTextView.view?.isHidden = hasText + self.textView.view?.isHidden = hasText*/ + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchSearchBarComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchSearchBarComponent.swift index 93c8f1e164..b8e7be9d63 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchSearchBarComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmojiSearchSearchBarComponent.swift @@ -420,6 +420,15 @@ final class EmojiSearchSearchBarComponent: Component { } } + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + guard let component = self.component else { + return nil + } + let _ = component + + return super.hitTest(point, with: event) + } + private func updateScrolling(transition: Transition, fromScrolling: Bool) { guard let component = self.component, let itemLayout = self.itemLayout else { return @@ -427,8 +436,12 @@ final class EmojiSearchSearchBarComponent: Component { let itemAlpha: CGFloat switch component.textInputState { - case .active: - itemAlpha = 0.0 + case let .active(hasText): + if hasText { + itemAlpha = 0.0 + } else { + itemAlpha = 1.0 + } case .inactive: itemAlpha = 1.0 } @@ -674,7 +687,7 @@ final class EmojiSearchSearchBarComponent: Component { if self.scrollView.bounds.size != availableSize { transition.setFrame(view: self.scrollView, frame: CGRect(origin: CGPoint(), size: availableSize)) } - if case .active = component.textInputState { + if case .active(true) = component.textInputState { transition.setBoundsOrigin(view: self.scrollView, origin: CGPoint()) } if self.scrollView.contentSize != itemLayout.contentSize { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmptySearchResultsView.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmptySearchResultsView.swift new file mode 100644 index 0000000000..466c430e8d --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EmptySearchResultsView.swift @@ -0,0 +1,101 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AccountContext +import TelegramCore +import TelegramPresentationData +import EmojiStatusComponent + +final class EmptySearchResultsView: UIView { + override public static var layerClass: AnyClass { + return PassthroughLayer.self + } + + let tintContainerView: UIView + let titleLabel: ComponentView + let titleTintLabel: ComponentView + let icon: ComponentView + + override init(frame: CGRect) { + self.tintContainerView = UIView() + + self.titleLabel = ComponentView() + self.titleTintLabel = ComponentView() + self.icon = ComponentView() + + super.init(frame: frame) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerView.layer + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(context: AccountContext, theme: PresentationTheme, useOpaqueTheme: Bool, text: String, file: TelegramMediaFile?, size: CGSize, searchInitiallyHidden: Bool, transition: Transition) { + let titleColor: UIColor + if useOpaqueTheme { + titleColor = theme.chat.inputMediaPanel.panelContentOpaqueSearchOverlayColor + } else { + titleColor = theme.chat.inputMediaPanel.panelContentVibrantSearchOverlayColor + } + + let iconSize: CGSize + if let file = file { + iconSize = self.icon.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: context, + animationCache: context.animationCache, + animationRenderer: context.animationRenderer, + content: .animation(content: .file(file: file), size: CGSize(width: 32.0, height: 32.0), placeholderColor: titleColor, themeColor: nil, loopMode: .forever), + isVisibleForAnimations: context.sharedContext.energyUsageSettings.loopEmoji, + action: nil + )), + environment: {}, + containerSize: CGSize(width: 32.0, height: 32.0) + ) + } else { + iconSize = CGSize() + } + + let titleSize = self.titleLabel.update( + transition: .immediate, + component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: titleColor)), + environment: {}, + containerSize: CGSize(width: size.width, height: 100.0) + ) + let _ = self.titleTintLabel.update( + transition: .immediate, + component: AnyComponent(Text(text: text, font: Font.regular(15.0), color: .white)), + environment: {}, + containerSize: CGSize(width: size.width, height: 100.0) + ) + + let spacing: CGFloat = 4.0 + let contentHeight = iconSize.height + spacing + titleSize.height + let contentOriginY = searchInitiallyHidden ? floor((size.height - contentHeight) / 2.0) : 10.0 + let iconFrame = CGRect(origin: CGPoint(x: floor((size.width - iconSize.width) / 2.0), y: contentOriginY), size: iconSize) + let titleFrame = CGRect(origin: CGPoint(x: floor((size.width - titleSize.width) / 2.0), y: iconFrame.maxY + spacing), size: titleSize) + + if let iconView = self.icon.view { + if iconView.superview == nil { + self.addSubview(iconView) + } + transition.setFrame(view: iconView, frame: iconFrame) + } + if let titleLabelView = self.titleLabel.view { + if titleLabelView.superview == nil { + self.addSubview(titleLabelView) + } + transition.setFrame(view: titleLabelView, frame: titleFrame) + } + if let titleTintLabelView = self.titleTintLabel.view { + if titleTintLabelView.superview == nil { + self.tintContainerView.addSubview(titleTintLabelView) + } + transition.setFrame(view: titleTintLabelView, frame: titleFrame) + } + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift index eea256000d..236588fff2 100644 --- a/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/EntityKeyboardTopPanelComponent.swift @@ -85,7 +85,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { } final class View: UIView { - var itemLayer: EmojiPagerContentComponent.View.ItemLayer? + var itemLayer: EmojiKeyboardItemLayer? var placeholderView: EmojiPagerContentComponent.View.ItemPlaceholderView? var component: EntityKeyboardAnimationTopPanelComponent? var titleView: ComponentView? @@ -116,7 +116,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { if self.itemLayer == nil { let tintColor: EmojiPagerContentComponent.Item.TintMode = component.customTintColor.flatMap { .custom($0) } ?? .primary - let itemLayer = EmojiPagerContentComponent.View.ItemLayer( + let itemLayer = EmojiKeyboardItemLayer( item: EmojiPagerContentComponent.Item( animationData: component.item, content: .animation(component.item), @@ -157,7 +157,7 @@ final class EntityKeyboardAnimationTopPanelComponent: Component { transition.setPosition(layer: itemLayer, position: CGPoint(x: iconFrame.midX, y: iconFrame.midY)) transition.setBounds(layer: itemLayer, bounds: CGRect(origin: CGPoint(), size: iconFrame.size)) - var badge: EmojiPagerContentComponent.View.ItemLayer.Badge? + var badge: EmojiKeyboardItemLayer.Badge? if component.isPremiumLocked { badge = .locked } else if component.isFeatured { diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupEmbeddedView.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupEmbeddedView.swift new file mode 100644 index 0000000000..4721e8897f --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupEmbeddedView.swift @@ -0,0 +1,209 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AccountContext +import TelegramPresentationData +import AnimationCache +import MultiAnimationRenderer +import PagerComponent + +final class GroupEmbeddedView: UIScrollView, UIScrollViewDelegate, PagerExpandableScrollView { + private struct ItemLayout { + var itemSize: CGFloat + var itemSpacing: CGFloat + var sideInset: CGFloat + var itemCount: Int + var contentSize: CGSize + + init(height: CGFloat, sideInset: CGFloat, itemCount: Int) { + self.itemSize = 30.0 + self.itemSpacing = 20.0 + self.sideInset = sideInset + self.itemCount = itemCount + + self.contentSize = CGSize(width: self.sideInset * 2.0 + CGFloat(self.itemCount) * self.itemSize + CGFloat(self.itemCount - 1) * self.itemSpacing, height: height) + } + + func frame(at index: Int) -> CGRect { + return CGRect(origin: CGPoint(x: sideInset + CGFloat(index) * (self.itemSize + self.itemSpacing), y: floor((self.contentSize.height - self.itemSize) / 2.0)), size: CGSize(width: self.itemSize, height: self.itemSize)) + } + + func visibleItems(for rect: CGRect) -> Range? { + let offsetRect = rect.offsetBy(dx: -self.sideInset, dy: 0.0) + var minVisibleIndex = Int(floor((offsetRect.minX - self.itemSpacing) / (self.itemSize + self.itemSpacing))) + minVisibleIndex = max(0, minVisibleIndex) + var maxVisibleIndex = Int(ceil((offsetRect.maxX - self.itemSpacing) / (self.itemSize + self.itemSpacing))) + maxVisibleIndex = min(maxVisibleIndex, self.itemCount - 1) + + if minVisibleIndex <= maxVisibleIndex { + return minVisibleIndex ..< (maxVisibleIndex + 1) + } else { + return nil + } + } + } + + private let performItemAction: (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void + + private var visibleItemLayers: [EmojiKeyboardItemLayer.Key: EmojiKeyboardItemLayer] = [:] + private var ignoreScrolling: Bool = false + + private var context: AccountContext? + private var theme: PresentationTheme? + private var cache: AnimationCache? + private var renderer: MultiAnimationRenderer? + private var currentInsets: UIEdgeInsets? + private var currentSize: CGSize? + private var items: [EmojiPagerContentComponent.Item]? + private var isStickers: Bool = false + + private var itemLayout: ItemLayout? + + init(performItemAction: @escaping (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void) { + self.performItemAction = performItemAction + + super.init(frame: CGRect()) + + self.delaysContentTouches = false + if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { + self.contentInsetAdjustmentBehavior = .never + } + if #available(iOS 13.0, *) { + self.automaticallyAdjustsScrollIndicatorInsets = false + } + self.showsVerticalScrollIndicator = true + self.showsHorizontalScrollIndicator = false + self.delegate = self + self.clipsToBounds = true + self.scrollsToTop = false + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func tapGesture(point: CGPoint) -> Bool { + guard let itemLayout = self.itemLayout else { + return false + } + + for (_, itemLayer) in self.visibleItemLayers { + if itemLayer.frame.inset(by: UIEdgeInsets(top: -6.0, left: -itemLayout.itemSpacing, bottom: -6.0, right: -itemLayout.itemSpacing)).contains(point) { + self.performItemAction(itemLayer.item, self, itemLayer.frame, itemLayer) + return true + } + } + + return false + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + if !self.ignoreScrolling { + self.updateVisibleItems(transition: .immediate, attemptSynchronousLoad: false) + } + } + + private func updateVisibleItems(transition: Transition, attemptSynchronousLoad: Bool) { + guard let context = self.context, let theme = self.theme, let itemLayout = self.itemLayout, let items = self.items, let cache = self.cache, let renderer = self.renderer else { + return + } + + var validIds = Set() + if let itemRange = itemLayout.visibleItems(for: self.bounds) { + for index in itemRange.lowerBound ..< itemRange.upperBound { + let item = items[index] + let itemId = EmojiKeyboardItemLayer.Key( + groupId: AnyHashable(0), + itemId: item.content.id + ) + validIds.insert(itemId) + + let itemLayer: EmojiKeyboardItemLayer + if let current = self.visibleItemLayers[itemId] { + itemLayer = current + } else { + itemLayer = EmojiKeyboardItemLayer( + item: item, + context: context, + attemptSynchronousLoad: attemptSynchronousLoad, + content: item.content, + cache: cache, + renderer: renderer, + placeholderColor: .clear, + blurredBadgeColor: .clear, + accentIconColor: theme.list.itemAccentColor, + pointSize: CGSize(width: 32.0, height: 32.0), + onUpdateDisplayPlaceholder: { _, _ in + } + ) + self.visibleItemLayers[itemId] = itemLayer + self.layer.addSublayer(itemLayer) + } + + switch item.tintMode { + case let .custom(color): + itemLayer.layerTintColor = color.cgColor + case .accent: + itemLayer.layerTintColor = theme.list.itemAccentColor.cgColor + case .primary: + itemLayer.layerTintColor = theme.list.itemPrimaryTextColor.cgColor + case .none: + itemLayer.layerTintColor = nil + } + + let itemFrame = itemLayout.frame(at: index) + itemLayer.frame = itemFrame + + itemLayer.isVisibleForAnimations = self.isStickers ? context.sharedContext.energyUsageSettings.loopStickers : context.sharedContext.energyUsageSettings.loopEmoji + } + } + + var removedIds: [EmojiKeyboardItemLayer.Key] = [] + for (id, itemLayer) in self.visibleItemLayers { + if !validIds.contains(id) { + removedIds.append(id) + itemLayer.removeFromSuperlayer() + } + } + for id in removedIds { + self.visibleItemLayers.removeValue(forKey: id) + } + } + + func update( + context: AccountContext, + theme: PresentationTheme, + insets: UIEdgeInsets, + size: CGSize, + items: [EmojiPagerContentComponent.Item], + isStickers: Bool, + cache: AnimationCache, + renderer: MultiAnimationRenderer, + attemptSynchronousLoad: Bool + ) { + if self.theme === theme && self.currentInsets == insets && self.currentSize == size && self.items == items { + return + } + + self.context = context + self.theme = theme + self.currentInsets = insets + self.currentSize = size + self.items = items + self.isStickers = isStickers + self.cache = cache + self.renderer = renderer + + let itemLayout = ItemLayout(height: size.height, sideInset: insets.left, itemCount: items.count) + self.itemLayout = itemLayout + + self.ignoreScrolling = true + if itemLayout.contentSize != self.contentSize { + self.contentSize = itemLayout.contentSize + } + self.ignoreScrolling = false + + self.updateVisibleItems(transition: .immediate, attemptSynchronousLoad: attemptSynchronousLoad) + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupExpandActionButton.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupExpandActionButton.swift new file mode 100644 index 0000000000..0b3be8405d --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupExpandActionButton.swift @@ -0,0 +1,128 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData + +final class GroupExpandActionButton: UIButton { + override static var layerClass: AnyClass { + return PassthroughLayer.self + } + + let tintContainerLayer: SimpleLayer + + private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? + private let backgroundLayer: SimpleLayer + private let tintBackgroundLayer: SimpleLayer + private let textLayer: SimpleLayer + private let pressed: () -> Void + + init(pressed: @escaping () -> Void) { + self.pressed = pressed + + self.tintContainerLayer = SimpleLayer() + + self.backgroundLayer = SimpleLayer() + self.backgroundLayer.masksToBounds = true + + self.tintBackgroundLayer = SimpleLayer() + self.tintBackgroundLayer.masksToBounds = true + + self.textLayer = SimpleLayer() + + super.init(frame: CGRect()) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerLayer + + self.layer.addSublayer(self.backgroundLayer) + + self.layer.addSublayer(self.textLayer) + + self.addTarget(self, action: #selector(self.onPressed), for: .touchUpInside) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + @objc private func onPressed() { + self.pressed() + } + + override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { + self.alpha = 0.6 + + return super.beginTracking(touch, with: event) + } + + override func endTracking(_ touch: UITouch?, with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.endTracking(touch, with: event) + } + + override func cancelTracking(with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.cancelTracking(with: event) + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.touchesCancelled(touches, with: event) + } + + func update(theme: PresentationTheme, title: String, useOpaqueTheme: Bool) -> CGSize { + let textConstrainedWidth: CGFloat = 100.0 + let color = theme.list.itemCheckColors.foregroundColor + + if useOpaqueTheme { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlOpaqueOverlayColor.cgColor + } else { + self.backgroundLayer.backgroundColor = theme.chat.inputMediaPanel.panelContentControlVibrantOverlayColor.cgColor + } + self.tintContainerLayer.backgroundColor = UIColor.white.cgColor + + let textSize: CGSize + if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == color, currentTextLayout.constrainedWidth == textConstrainedWidth { + textSize = currentTextLayout.size + } else { + let font: UIFont = Font.semibold(13.0) + let string = NSAttributedString(string: title.uppercased(), font: font, textColor: color) + let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) + self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + string.draw(in: stringBounds) + + UIGraphicsPopContext() + })?.cgImage + self.currentTextLayout = (title, color, textConstrainedWidth, textSize) + } + + var sideInset: CGFloat = 10.0 + if textSize.width > 24.0 { + sideInset = 6.0 + } + let size = CGSize(width: textSize.width + sideInset * 2.0, height: 28.0) + + let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floor((size.height - textSize.height) / 2.0)), size: textSize) + self.textLayer.frame = textFrame + + self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) + self.tintBackgroundLayer.frame = CGRect(origin: CGPoint(), size: size) + self.backgroundLayer.cornerRadius = min(size.width, size.height) / 2.0 + self.tintContainerLayer.cornerRadius = min(size.width, size.height) / 2.0 + + return size + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderActionButton.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderActionButton.swift new file mode 100644 index 0000000000..cbc39084de --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderActionButton.swift @@ -0,0 +1,149 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData + +final class GroupHeaderActionButton: UIButton { + override static var layerClass: AnyClass { + return PassthroughLayer.self + } + + let tintContainerLayer: SimpleLayer + + private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? + private let backgroundLayer: SimpleLayer + private let tintBackgroundLayer: SimpleLayer + private let textLayer: SimpleLayer + private let tintTextLayer: SimpleLayer + private let pressed: () -> Void + + init(pressed: @escaping () -> Void) { + self.pressed = pressed + + self.tintContainerLayer = SimpleLayer() + + self.backgroundLayer = SimpleLayer() + self.backgroundLayer.masksToBounds = true + + self.tintBackgroundLayer = SimpleLayer() + self.tintBackgroundLayer.masksToBounds = true + + self.textLayer = SimpleLayer() + self.tintTextLayer = SimpleLayer() + + super.init(frame: CGRect()) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContainerLayer + + self.layer.addSublayer(self.backgroundLayer) + self.layer.addSublayer(self.textLayer) + + self.addTarget(self, action: #selector(self.onPressed), for: .touchUpInside) + + self.tintContainerLayer.addSublayer(self.tintBackgroundLayer) + self.tintContainerLayer.addSublayer(self.tintTextLayer) + } + + required init(coder: NSCoder) { + preconditionFailure() + } + + @objc private func onPressed() { + self.pressed() + } + + override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool { + self.alpha = 0.6 + + return super.beginTracking(touch, with: event) + } + + override func endTracking(_ touch: UITouch?, with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.endTracking(touch, with: event) + } + + override func cancelTracking(with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.cancelTracking(with: event) + } + + override func touchesCancelled(_ touches: Set, with event: UIEvent?) { + let alpha = self.alpha + self.alpha = 1.0 + self.layer.animateAlpha(from: alpha, to: 1.0, duration: 0.25) + + super.touchesCancelled(touches, with: event) + } + + func update(theme: PresentationTheme, title: String, compact: Bool) -> CGSize { + let textConstrainedWidth: CGFloat = 100.0 + + let needsVibrancy = !theme.overallDarkAppearance && compact + + let foregroundColor: UIColor + let backgroundColor: UIColor + + if compact { + foregroundColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor + backgroundColor = foregroundColor.withMultipliedAlpha(0.2) + } else { + foregroundColor = theme.list.itemCheckColors.foregroundColor + backgroundColor = theme.list.itemCheckColors.fillColor + } + + self.backgroundLayer.backgroundColor = backgroundColor.cgColor + self.tintBackgroundLayer.backgroundColor = UIColor.white.withAlphaComponent(0.2).cgColor + + self.tintContainerLayer.isHidden = !needsVibrancy + + let textSize: CGSize + if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == foregroundColor, currentTextLayout.constrainedWidth == textConstrainedWidth { + textSize = currentTextLayout.size + } else { + let font: UIFont = compact ? Font.medium(11.0) : Font.semibold(15.0) + let string = NSAttributedString(string: title.uppercased(), font: font, textColor: foregroundColor) + let tintString = NSAttributedString(string: title.uppercased(), font: font, textColor: .white) + let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) + self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + string.draw(in: stringBounds) + + UIGraphicsPopContext() + })?.cgImage + self.tintTextLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + tintString.draw(in: stringBounds) + + UIGraphicsPopContext() + })?.cgImage + self.currentTextLayout = (title, foregroundColor, textConstrainedWidth, textSize) + } + + let size = CGSize(width: textSize.width + (compact ? 6.0 : 16.0) * 2.0, height: compact ? 16.0 : 28.0) + + let textFrame = CGRect(origin: CGPoint(x: floor((size.width - textSize.width) / 2.0), y: floorToScreenPixels((size.height - textSize.height) / 2.0)), size: textSize) + self.textLayer.frame = textFrame + self.tintTextLayer.frame = textFrame + + self.backgroundLayer.frame = CGRect(origin: CGPoint(), size: size) + self.backgroundLayer.cornerRadius = min(size.width, size.height) / 2.0 + + self.tintBackgroundLayer.frame = self.backgroundLayer.frame + self.tintBackgroundLayer.cornerRadius = self.backgroundLayer.cornerRadius + + return size + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderLayer.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderLayer.swift new file mode 100644 index 0000000000..857c0ca5d1 --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/GroupHeaderLayer.swift @@ -0,0 +1,525 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AccountContext +import TelegramCore +import TelegramPresentationData +import AnimationCache +import MultiAnimationRenderer + +final class GroupHeaderLayer: UIView { + override static var layerClass: AnyClass { + return PassthroughLayer.self + } + + private let actionPressed: () -> Void + private let performItemAction: (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void + + private let textLayer: SimpleLayer + private let tintTextLayer: SimpleLayer + + private var subtitleLayer: SimpleLayer? + private var tintSubtitleLayer: SimpleLayer? + private var lockIconLayer: SimpleLayer? + private var tintLockIconLayer: SimpleLayer? + private var badgeLayer: SimpleLayer? + private var tintBadgeLayer: SimpleLayer? + private(set) var clearIconLayer: SimpleLayer? + private var tintClearIconLayer: SimpleLayer? + private var separatorLayer: SimpleLayer? + private var tintSeparatorLayer: SimpleLayer? + private var actionButton: GroupHeaderActionButton? + + private var groupEmbeddedView: GroupEmbeddedView? + + private var theme: PresentationTheme? + + private var currentTextLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? + private var currentSubtitleLayout: (string: String, color: UIColor, constrainedWidth: CGFloat, size: CGSize)? + + let tintContentLayer: SimpleLayer + + init(actionPressed: @escaping () -> Void, performItemAction: @escaping (EmojiPagerContentComponent.Item, UIView, CGRect, CALayer) -> Void) { + self.actionPressed = actionPressed + self.performItemAction = performItemAction + + self.textLayer = SimpleLayer() + self.tintTextLayer = SimpleLayer() + + self.tintContentLayer = SimpleLayer() + + super.init(frame: CGRect()) + + self.layer.addSublayer(self.textLayer) + self.tintContentLayer.addSublayer(self.tintTextLayer) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.tintContentLayer + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update( + context: AccountContext, + theme: PresentationTheme, + forceNeedsVibrancy: Bool, + layoutType: EmojiPagerContentComponent.ItemLayoutType, + hasTopSeparator: Bool, + actionButtonTitle: String?, + actionButtonIsCompact: Bool, + title: String, + subtitle: String?, + badge: String?, + isPremiumLocked: Bool, + hasClear: Bool, + embeddedItems: [EmojiPagerContentComponent.Item]?, + isStickers: Bool, + constrainedSize: CGSize, + insets: UIEdgeInsets, + cache: AnimationCache, + renderer: MultiAnimationRenderer, + attemptSynchronousLoad: Bool + ) -> (size: CGSize, centralContentWidth: CGFloat) { + var themeUpdated = false + if self.theme !== theme { + self.theme = theme + themeUpdated = true + } + + let needsVibrancy = !theme.overallDarkAppearance || forceNeedsVibrancy + + let textOffsetY: CGFloat + if hasTopSeparator { + textOffsetY = 9.0 + } else { + textOffsetY = 0.0 + } + + let subtitleColor: UIColor + if theme.overallDarkAppearance && forceNeedsVibrancy { + subtitleColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor.withMultipliedAlpha(0.2) + } else { + subtitleColor = theme.chat.inputMediaPanel.panelContentVibrantOverlayColor + } + + let color: UIColor + let needsTintText: Bool + if subtitle != nil { + color = theme.chat.inputPanel.primaryTextColor + needsTintText = false + } else { + color = subtitleColor + needsTintText = true + } + + let titleHorizontalOffset: CGFloat + if isPremiumLocked { + titleHorizontalOffset = 10.0 + 2.0 + } else { + titleHorizontalOffset = 0.0 + } + + var actionButtonSize: CGSize? + if let actionButtonTitle = actionButtonTitle { + let actionButton: GroupHeaderActionButton + if let current = self.actionButton { + actionButton = current + } else { + actionButton = GroupHeaderActionButton(pressed: self.actionPressed) + self.actionButton = actionButton + self.addSubview(actionButton) + self.tintContentLayer.addSublayer(actionButton.tintContainerLayer) + } + + actionButtonSize = actionButton.update(theme: theme, title: actionButtonTitle, compact: actionButtonIsCompact) + } else { + if let actionButton = self.actionButton { + self.actionButton = nil + actionButton.removeFromSuperview() + } + } + + var clearSize: CGSize = .zero + var clearWidth: CGFloat = 0.0 + if hasClear { + var updateImage = themeUpdated + + let clearIconLayer: SimpleLayer + if let current = self.clearIconLayer { + clearIconLayer = current + } else { + updateImage = true + clearIconLayer = SimpleLayer() + self.clearIconLayer = clearIconLayer + self.layer.addSublayer(clearIconLayer) + } + let tintClearIconLayer: SimpleLayer + if let current = self.tintClearIconLayer { + tintClearIconLayer = current + } else { + updateImage = true + tintClearIconLayer = SimpleLayer() + self.tintClearIconLayer = tintClearIconLayer + self.tintContentLayer.addSublayer(tintClearIconLayer) + } + + tintClearIconLayer.isHidden = !needsVibrancy + + clearSize = clearIconLayer.bounds.size + if updateImage, let image = PresentationResourcesChat.chatInputMediaPanelGridDismissImage(theme, color: subtitleColor) { + clearSize = image.size + clearIconLayer.contents = image.cgImage + } + if updateImage, let image = PresentationResourcesChat.chatInputMediaPanelGridDismissImage(theme, color: .white) { + tintClearIconLayer.contents = image.cgImage + } + + tintClearIconLayer.frame = clearIconLayer.frame + clearWidth = 4.0 + clearSize.width + } else { + if let clearIconLayer = self.clearIconLayer { + self.clearIconLayer = nil + clearIconLayer.removeFromSuperlayer() + } + if let tintClearIconLayer = self.tintClearIconLayer { + self.tintClearIconLayer = nil + tintClearIconLayer.removeFromSuperlayer() + } + } + + var textConstrainedWidth = constrainedSize.width - titleHorizontalOffset - 10.0 + if let actionButtonSize = actionButtonSize { + if actionButtonIsCompact { + textConstrainedWidth -= actionButtonSize.width * 2.0 + 10.0 + } else { + textConstrainedWidth -= actionButtonSize.width + 10.0 + } + } + if clearWidth > 0.0 { + textConstrainedWidth -= clearWidth + 8.0 + } + + let textSize: CGSize + if let currentTextLayout = self.currentTextLayout, currentTextLayout.string == title, currentTextLayout.color == color, currentTextLayout.constrainedWidth == textConstrainedWidth { + textSize = currentTextLayout.size + } else { + let font: UIFont + let stringValue: String + if subtitle == nil { + font = Font.medium(13.0) + stringValue = title.uppercased() + } else { + font = Font.semibold(16.0) + stringValue = title + } + let string = NSAttributedString(string: stringValue, font: font, textColor: color) + let whiteString = NSAttributedString(string: stringValue, font: font, textColor: .white) + let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 18.0), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) + textSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) + self.textLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + //string.draw(in: stringBounds) + string.draw(with: stringBounds, options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) + + UIGraphicsPopContext() + })?.cgImage + self.tintTextLayer.contents = generateImage(textSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + //whiteString.draw(in: stringBounds) + whiteString.draw(with: stringBounds, options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) + + UIGraphicsPopContext() + })?.cgImage + self.tintTextLayer.isHidden = !needsVibrancy + self.currentTextLayout = (title, color, textConstrainedWidth, textSize) + } + + var badgeSize: CGSize = .zero + if let badge { + func generateBadgeImage(color: UIColor) -> UIImage? { + let string = NSAttributedString(string: badge, font: Font.semibold(11.0), textColor: .white) + let stringBounds = string.boundingRect(with: CGSize(width: 120, height: 18.0), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) + + let badgeSize = CGSize(width: stringBounds.width + 8.0, height: 16.0) + return generateImage(badgeSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.setFillColor(color.cgColor) + context.addPath(UIBezierPath(roundedRect: CGRect(origin: .zero, size: badgeSize), cornerRadius: badgeSize.height / 2.0).cgPath) + context.fillPath() + + context.setBlendMode(.clear) + + UIGraphicsPushContext(context) + + string.draw(with: CGRect(origin: CGPoint(x: floorToScreenPixels((badgeSize.width - stringBounds.size.width) / 2.0), y: floorToScreenPixels((badgeSize.height - stringBounds.size.height) / 2.0)), size: stringBounds.size), options: [.usesLineFragmentOrigin, .truncatesLastVisibleLine], context: nil) + + UIGraphicsPopContext() + }) + } + + let badgeLayer: SimpleLayer + if let current = self.badgeLayer { + badgeLayer = current + } else { + badgeLayer = SimpleLayer() + self.badgeLayer = badgeLayer + self.layer.addSublayer(badgeLayer) + + if let image = generateBadgeImage(color: color.withMultipliedAlpha(0.66)) { + badgeLayer.contents = image.cgImage + badgeLayer.bounds = CGRect(origin: .zero, size: image.size) + } + } + badgeSize = badgeLayer.bounds.size + + let tintBadgeLayer: SimpleLayer + if let current = self.tintBadgeLayer { + tintBadgeLayer = current + } else { + tintBadgeLayer = SimpleLayer() + self.tintBadgeLayer = tintBadgeLayer + self.tintContentLayer.addSublayer(tintBadgeLayer) + + if let image = generateBadgeImage(color: .white) { + tintBadgeLayer.contents = image.cgImage + } + } + } else { + if let badgeLayer = self.badgeLayer { + self.badgeLayer = nil + badgeLayer.removeFromSuperlayer() + } + if let tintBadgeLayer = self.tintBadgeLayer { + self.tintBadgeLayer = nil + tintBadgeLayer.removeFromSuperlayer() + } + } + + let textFrame: CGRect + if subtitle == nil { + textFrame = CGRect(origin: CGPoint(x: titleHorizontalOffset + floor((constrainedSize.width - titleHorizontalOffset - (textSize.width + badgeSize.width)) / 2.0), y: textOffsetY), size: textSize) + } else { + textFrame = CGRect(origin: CGPoint(x: titleHorizontalOffset, y: textOffsetY), size: textSize) + } + self.textLayer.frame = textFrame + self.tintTextLayer.frame = textFrame + self.tintTextLayer.isHidden = !needsTintText + + if let badgeLayer = self.badgeLayer, let tintBadgeLayer = self.tintBadgeLayer { + badgeLayer.frame = CGRect(origin: CGPoint(x: textFrame.maxX + 4.0, y: 0.0), size: badgeLayer.frame.size) + tintBadgeLayer.frame = badgeLayer.frame + } + + if isPremiumLocked { + let lockIconLayer: SimpleLayer + if let current = self.lockIconLayer { + lockIconLayer = current + } else { + lockIconLayer = SimpleLayer() + self.lockIconLayer = lockIconLayer + self.layer.addSublayer(lockIconLayer) + } + if let image = PresentationResourcesChat.chatEntityKeyboardLock(theme, color: color) { + let imageSize = image.size + lockIconLayer.contents = image.cgImage + lockIconLayer.frame = CGRect(origin: CGPoint(x: textFrame.minX - imageSize.width - 3.0, y: 2.0 + UIScreenPixel), size: imageSize) + } else { + lockIconLayer.contents = nil + } + + let tintLockIconLayer: SimpleLayer + if let current = self.tintLockIconLayer { + tintLockIconLayer = current + } else { + tintLockIconLayer = SimpleLayer() + self.tintLockIconLayer = tintLockIconLayer + self.tintContentLayer.addSublayer(tintLockIconLayer) + } + if let image = PresentationResourcesChat.chatEntityKeyboardLock(theme, color: .white) { + tintLockIconLayer.contents = image.cgImage + tintLockIconLayer.frame = lockIconLayer.frame + tintLockIconLayer.isHidden = !needsVibrancy + } else { + tintLockIconLayer.contents = nil + } + } else { + if let lockIconLayer = self.lockIconLayer { + self.lockIconLayer = nil + lockIconLayer.removeFromSuperlayer() + } + if let tintLockIconLayer = self.tintLockIconLayer { + self.tintLockIconLayer = nil + tintLockIconLayer.removeFromSuperlayer() + } + } + + let subtitleSize: CGSize + if let subtitle = subtitle { + var updateSubtitleContents: UIImage? + var updateTintSubtitleContents: UIImage? + if let currentSubtitleLayout = self.currentSubtitleLayout, currentSubtitleLayout.string == subtitle, currentSubtitleLayout.color == subtitleColor, currentSubtitleLayout.constrainedWidth == textConstrainedWidth { + subtitleSize = currentSubtitleLayout.size + } else { + let string = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: subtitleColor) + let whiteString = NSAttributedString(string: subtitle, font: Font.regular(15.0), textColor: .white) + let stringBounds = string.boundingRect(with: CGSize(width: textConstrainedWidth, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + subtitleSize = CGSize(width: ceil(stringBounds.width), height: ceil(stringBounds.height)) + updateSubtitleContents = generateImage(subtitleSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + string.draw(in: stringBounds) + + UIGraphicsPopContext() + }) + updateTintSubtitleContents = generateImage(subtitleSize, opaque: false, scale: 0.0, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + + whiteString.draw(in: stringBounds) + + UIGraphicsPopContext() + }) + self.currentSubtitleLayout = (subtitle, subtitleColor, textConstrainedWidth, subtitleSize) + } + + let subtitleLayer: SimpleLayer + if let current = self.subtitleLayer { + subtitleLayer = current + } else { + subtitleLayer = SimpleLayer() + self.subtitleLayer = subtitleLayer + self.layer.addSublayer(subtitleLayer) + } + + if let updateSubtitleContents = updateSubtitleContents { + subtitleLayer.contents = updateSubtitleContents.cgImage + } + + let tintSubtitleLayer: SimpleLayer + if let current = self.tintSubtitleLayer { + tintSubtitleLayer = current + } else { + tintSubtitleLayer = SimpleLayer() + self.tintSubtitleLayer = tintSubtitleLayer + self.tintContentLayer.addSublayer(tintSubtitleLayer) + } + tintSubtitleLayer.isHidden = !needsVibrancy + + if let updateTintSubtitleContents = updateTintSubtitleContents { + tintSubtitleLayer.contents = updateTintSubtitleContents.cgImage + } + + let subtitleFrame = CGRect(origin: CGPoint(x: 0.0, y: textFrame.maxY + 1.0), size: subtitleSize) + subtitleLayer.frame = subtitleFrame + tintSubtitleLayer.frame = subtitleFrame + } else { + subtitleSize = CGSize() + if let subtitleLayer = self.subtitleLayer { + self.subtitleLayer = nil + subtitleLayer.removeFromSuperlayer() + } + if let tintSubtitleLayer = self.tintSubtitleLayer { + self.tintSubtitleLayer = nil + tintSubtitleLayer.removeFromSuperlayer() + } + } + + self.clearIconLayer?.frame = CGRect(origin: CGPoint(x: constrainedSize.width - clearSize.width, y: floorToScreenPixels((textSize.height - clearSize.height) / 2.0)), size: clearSize) + + var size: CGSize + size = CGSize(width: constrainedSize.width, height: constrainedSize.height) + + if let embeddedItems = embeddedItems { + let groupEmbeddedView: GroupEmbeddedView + if let current = self.groupEmbeddedView { + groupEmbeddedView = current + } else { + groupEmbeddedView = GroupEmbeddedView(performItemAction: self.performItemAction) + self.groupEmbeddedView = groupEmbeddedView + self.addSubview(groupEmbeddedView) + } + + let groupEmbeddedViewSize = CGSize(width: constrainedSize.width + insets.left + insets.right, height: 36.0) + groupEmbeddedView.frame = CGRect(origin: CGPoint(x: -insets.left, y: size.height - groupEmbeddedViewSize.height), size: groupEmbeddedViewSize) + groupEmbeddedView.update( + context: context, + theme: theme, + insets: insets, + size: groupEmbeddedViewSize, + items: embeddedItems, + isStickers: isStickers, + cache: cache, + renderer: renderer, + attemptSynchronousLoad: attemptSynchronousLoad + ) + } else { + if let groupEmbeddedView = self.groupEmbeddedView { + self.groupEmbeddedView = nil + groupEmbeddedView.removeFromSuperview() + } + } + + if let actionButtonSize = actionButtonSize, let actionButton = self.actionButton { + let actionButtonFrame = CGRect(origin: CGPoint(x: size.width - actionButtonSize.width, y: textFrame.minY + (actionButtonIsCompact ? 0.0 : 3.0)), size: actionButtonSize) + actionButton.bounds = CGRect(origin: CGPoint(), size: actionButtonFrame.size) + actionButton.center = actionButtonFrame.center + } + + if hasTopSeparator { + let separatorLayer: SimpleLayer + if let current = self.separatorLayer { + separatorLayer = current + } else { + separatorLayer = SimpleLayer() + self.separatorLayer = separatorLayer + self.layer.addSublayer(separatorLayer) + } + separatorLayer.backgroundColor = subtitleColor.cgColor + separatorLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)) + + let tintSeparatorLayer: SimpleLayer + if let current = self.tintSeparatorLayer { + tintSeparatorLayer = current + } else { + tintSeparatorLayer = SimpleLayer() + self.tintSeparatorLayer = tintSeparatorLayer + self.tintContentLayer.addSublayer(tintSeparatorLayer) + } + tintSeparatorLayer.backgroundColor = UIColor.white.cgColor + tintSeparatorLayer.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: UIScreenPixel)) + + tintSeparatorLayer.isHidden = !needsVibrancy + } else { + if let separatorLayer = self.separatorLayer { + self.separatorLayer = separatorLayer + separatorLayer.removeFromSuperlayer() + } + if let tintSeparatorLayer = self.tintSeparatorLayer { + self.tintSeparatorLayer = tintSeparatorLayer + tintSeparatorLayer.removeFromSuperlayer() + } + } + + return (size, titleHorizontalOffset + textSize.width + clearWidth) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return super.hitTest(point, with: event) + } + + func tapGesture(point: CGPoint) -> Bool { + if let groupEmbeddedView = self.groupEmbeddedView { + return groupEmbeddedView.tapGesture(point: self.convert(point, to: groupEmbeddedView)) + } else { + return false + } + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/PassthroughComponents.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/PassthroughComponents.swift new file mode 100644 index 0000000000..eb60512e4e --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/PassthroughComponents.swift @@ -0,0 +1,346 @@ +import Foundation +import UIKit +import Display +import ComponentFlow + +public class PassthroughLayer: CALayer { + public var mirrorLayer: CALayer? + + override init() { + super.init() + } + + override init(layer: Any) { + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override public var position: CGPoint { + get { + return super.position + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.position = value + } + super.position = value + } + } + + override public var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.bounds = value + } + super.bounds = value + } + } + + override public var opacity: Float { + get { + return super.opacity + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.opacity = value + } + super.opacity = value + } + } + + override public var sublayerTransform: CATransform3D { + get { + return super.sublayerTransform + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.sublayerTransform = value + } + super.sublayerTransform = value + } + } + + override public var transform: CATransform3D { + get { + return super.transform + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.transform = value + } + super.transform = value + } + } + + override public func add(_ animation: CAAnimation, forKey key: String?) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override public func removeAllAnimations() { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override public func removeAnimation(forKey: String) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } +} + +open class PassthroughView: UIView { + override public static var layerClass: AnyClass { + return PassthroughLayer.self + } + + public let passthroughView: UIView + + override public init(frame: CGRect) { + self.passthroughView = UIView() + + super.init(frame: frame) + + (self.layer as? PassthroughLayer)?.mirrorLayer = self.passthroughView.layer + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +class PassthroughShapeLayer: CAShapeLayer { + var mirrorLayer: CAShapeLayer? + + override init() { + super.init() + } + + override init(layer: Any) { + super.init(layer: layer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var position: CGPoint { + get { + return super.position + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.position = value + } + super.position = value + } + } + + override var bounds: CGRect { + get { + return super.bounds + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.bounds = value + } + super.bounds = value + } + } + + override var opacity: Float { + get { + return super.opacity + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.opacity = value + } + super.opacity = value + } + } + + override var sublayerTransform: CATransform3D { + get { + return super.sublayerTransform + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.sublayerTransform = value + } + super.sublayerTransform = value + } + } + + override var transform: CATransform3D { + get { + return super.transform + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.transform = value + } + super.transform = value + } + } + + override var path: CGPath? { + get { + return super.path + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.path = value + } + super.path = value + } + } + + override var fillColor: CGColor? { + get { + return super.fillColor + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.fillColor = value + } + super.fillColor = value + } + } + + override var fillRule: CAShapeLayerFillRule { + get { + return super.fillRule + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.fillRule = value + } + super.fillRule = value + } + } + + override var strokeColor: CGColor? { + get { + return super.strokeColor + } set(value) { + /*if let mirrorLayer = self.mirrorLayer { + mirrorLayer.strokeColor = value + }*/ + super.strokeColor = value + } + } + + override var strokeStart: CGFloat { + get { + return super.strokeStart + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.strokeStart = value + } + super.strokeStart = value + } + } + + override var strokeEnd: CGFloat { + get { + return super.strokeEnd + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.strokeEnd = value + } + super.strokeEnd = value + } + } + + override var lineWidth: CGFloat { + get { + return super.lineWidth + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.lineWidth = value + } + super.lineWidth = value + } + } + + override var miterLimit: CGFloat { + get { + return super.miterLimit + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.miterLimit = value + } + super.miterLimit = value + } + } + + override var lineCap: CAShapeLayerLineCap { + get { + return super.lineCap + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.lineCap = value + } + super.lineCap = value + } + } + + override var lineJoin: CAShapeLayerLineJoin { + get { + return super.lineJoin + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.lineJoin = value + } + super.lineJoin = value + } + } + + override var lineDashPhase: CGFloat { + get { + return super.lineDashPhase + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.lineDashPhase = value + } + super.lineDashPhase = value + } + } + + override var lineDashPattern: [NSNumber]? { + get { + return super.lineDashPattern + } set(value) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.lineDashPattern = value + } + super.lineDashPattern = value + } + } + + override func add(_ animation: CAAnimation, forKey key: String?) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.add(animation, forKey: key) + } + + super.add(animation, forKey: key) + } + + override func removeAllAnimations() { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAllAnimations() + } + + super.removeAllAnimations() + } + + override func removeAnimation(forKey: String) { + if let mirrorLayer = self.mirrorLayer { + mirrorLayer.removeAnimation(forKey: forKey) + } + + super.removeAnimation(forKey: forKey) + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/PremiumBadgeView.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/PremiumBadgeView.swift new file mode 100644 index 0000000000..6e0a0497d2 --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/PremiumBadgeView.swift @@ -0,0 +1,140 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import AccountContext +import TelegramCore + +private let premiumBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat List/PeerPremiumIcon"), color: .white) +private let featuredBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeAdd"), color: .white) +private let lockedBadgeIcon: UIImage? = generateTintedImage(image: UIImage(bundleImageName: "Chat/Input/Media/PanelBadgeLock"), color: .white) + +private let itemBadgeTextFont: UIFont = { + return Font.regular(10.0) +}() + +final class PremiumBadgeView: UIView { + private let context: AccountContext + + private var badge: EmojiKeyboardItemLayer.Badge? + + let contentLayer: SimpleLayer + private let overlayColorLayer: SimpleLayer + private let iconLayer: SimpleLayer + private var customFileLayer: InlineFileIconLayer? + + init(context: AccountContext) { + self.context = context + + self.contentLayer = SimpleLayer() + self.contentLayer.contentsGravity = .resize + self.contentLayer.masksToBounds = true + + self.overlayColorLayer = SimpleLayer() + self.overlayColorLayer.masksToBounds = true + + self.iconLayer = SimpleLayer() + + super.init(frame: CGRect()) + + self.layer.addSublayer(self.contentLayer) + self.layer.addSublayer(self.overlayColorLayer) + self.layer.addSublayer(self.iconLayer) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(transition: Transition, badge: EmojiKeyboardItemLayer.Badge, backgroundColor: UIColor, size: CGSize) { + if self.badge != badge { + self.badge = badge + + switch badge { + case .premium: + self.iconLayer.contents = premiumBadgeIcon?.cgImage + case .featured: + self.iconLayer.contents = featuredBadgeIcon?.cgImage + case .locked: + self.iconLayer.contents = lockedBadgeIcon?.cgImage + case let .text(text): + let string = NSAttributedString(string: text, font: itemBadgeTextFont) + let size = CGSize(width: 12.0, height: 12.0) + let stringBounds = string.boundingRect(with: CGSize(width: 100.0, height: 100.0), options: .usesLineFragmentOrigin, context: nil) + let image = generateImage(size, rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + UIGraphicsPushContext(context) + string.draw(at: CGPoint(x: floor((size.width - stringBounds.width) * 0.5), y: floor((size.height - stringBounds.height) * 0.5))) + UIGraphicsPopContext() + }) + self.iconLayer.contents = image?.cgImage + case .customFile: + self.iconLayer.contents = nil + } + + if case let .customFile(customFile) = badge { + let customFileLayer: InlineFileIconLayer + if let current = self.customFileLayer { + customFileLayer = current + } else { + customFileLayer = InlineFileIconLayer( + context: self.context, + userLocation: .other, + attemptSynchronousLoad: false, + file: customFile, + cache: self.context.animationCache, + renderer: self.context.animationRenderer, + unique: false, + placeholderColor: .clear, + pointSize: CGSize(width: 18.0, height: 18.0), + dynamicColor: nil + ) + self.customFileLayer = customFileLayer + self.layer.addSublayer(customFileLayer) + } + let _ = customFileLayer + } else { + if let customFileLayer = self.customFileLayer { + self.customFileLayer = nil + customFileLayer.removeFromSuperlayer() + } + } + } + + let iconInset: CGFloat + switch badge { + case .premium: + iconInset = 2.0 + case .featured: + iconInset = 0.0 + case .locked: + iconInset = 0.0 + case .text, .customFile: + iconInset = 0.0 + } + + switch badge { + case .text, .customFile: + self.contentLayer.isHidden = true + self.overlayColorLayer.isHidden = true + default: + self.contentLayer.isHidden = false + self.overlayColorLayer.isHidden = false + } + + self.overlayColorLayer.backgroundColor = backgroundColor.cgColor + + transition.setFrame(layer: self.contentLayer, frame: CGRect(origin: CGPoint(), size: size)) + transition.setCornerRadius(layer: self.contentLayer, cornerRadius: min(size.width / 2.0, size.height / 2.0)) + + transition.setFrame(layer: self.overlayColorLayer, frame: CGRect(origin: CGPoint(), size: size)) + transition.setCornerRadius(layer: self.overlayColorLayer, cornerRadius: min(size.width / 2.0, size.height / 2.0)) + + transition.setFrame(layer: self.iconLayer, frame: CGRect(origin: CGPoint(), size: size).insetBy(dx: iconInset, dy: iconInset)) + + if let customFileLayer = self.customFileLayer { + let iconSize = CGSize(width: 18.0, height: 18.0) + transition.setFrame(layer: customFileLayer, frame: CGRect(origin: CGPoint(), size: iconSize)) + } + } +} diff --git a/submodules/TelegramUI/Components/EntityKeyboard/Sources/WarpView.swift b/submodules/TelegramUI/Components/EntityKeyboard/Sources/WarpView.swift new file mode 100644 index 0000000000..7ccea1a793 --- /dev/null +++ b/submodules/TelegramUI/Components/EntityKeyboard/Sources/WarpView.swift @@ -0,0 +1,138 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import TelegramPresentationData + +final class WarpView: UIView { + private final class WarpPartView: UIView { + let cloneView: PortalView + + init?(contentView: PortalSourceView) { + guard let cloneView = PortalView(matchPosition: false) else { + return nil + } + self.cloneView = cloneView + + super.init(frame: CGRect()) + + self.layer.anchorPoint = CGPoint(x: 0.5, y: 0.0) + + self.clipsToBounds = true + self.addSubview(cloneView.view) + contentView.addPortal(view: cloneView) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(containerSize: CGSize, rect: CGRect, transition: Transition) { + transition.setFrame(view: self.cloneView.view, frame: CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: CGSize(width: containerSize.width, height: containerSize.height))) + } + } + + let contentView: PortalSourceView + + private let clippingView: UIView + + private var warpViews: [WarpPartView] = [] + private let warpMaskContainer: UIView + private let warpMaskGradientLayer: SimpleGradientLayer + + override init(frame: CGRect) { + self.contentView = PortalSourceView() + self.clippingView = UIView() + + self.warpMaskContainer = UIView() + self.warpMaskGradientLayer = SimpleGradientLayer() + self.warpMaskContainer.layer.mask = self.warpMaskGradientLayer + + super.init(frame: frame) + + self.clippingView.addSubview(self.contentView) + + self.clippingView.clipsToBounds = true + self.addSubview(self.clippingView) + self.addSubview(self.warpMaskContainer) + + for _ in 0 ..< 8 { + if let warpView = WarpPartView(contentView: self.contentView) { + self.warpViews.append(warpView) + self.warpMaskContainer.addSubview(warpView) + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func update(size: CGSize, topInset: CGFloat, warpHeight: CGFloat, theme: PresentationTheme, transition: Transition) { + transition.setFrame(view: self.contentView, frame: CGRect(origin: CGPoint(), size: size)) + + let allItemsHeight = warpHeight * 0.5 + for i in 0 ..< self.warpViews.count { + let itemHeight = warpHeight / CGFloat(self.warpViews.count) + let itemFraction = CGFloat(i + 1) / CGFloat(self.warpViews.count) + let _ = itemHeight + + let da = CGFloat.pi * 0.5 / CGFloat(self.warpViews.count) + let alpha = CGFloat.pi * 0.5 - itemFraction * CGFloat.pi * 0.5 + let endPoint = CGPoint(x: cos(alpha), y: sin(alpha)) + let prevAngle = alpha + da + let prevPt = CGPoint(x: cos(prevAngle), y: sin(prevAngle)) + var angle: CGFloat + angle = -atan2(endPoint.y - prevPt.y, endPoint.x - prevPt.x) + + let itemLengthVector = CGPoint(x: endPoint.x - prevPt.x, y: endPoint.y - prevPt.y) + let itemLength = sqrt(itemLengthVector.x * itemLengthVector.x + itemLengthVector.y * itemLengthVector.y) * warpHeight * 0.5 + let _ = itemLength + + var transform: CATransform3D + transform = CATransform3DIdentity + transform.m34 = 1.0 / 240.0 + + transform = CATransform3DTranslate(transform, 0.0, prevPt.x * allItemsHeight, (1.0 - prevPt.y) * allItemsHeight) + transform = CATransform3DRotate(transform, angle, 1.0, 0.0, 0.0) + + let positionY = size.height - allItemsHeight + 4.0 + CGFloat(i) * itemLength + let rect = CGRect(origin: CGPoint(x: 0.0, y: positionY), size: CGSize(width: size.width, height: itemLength)) + transition.setPosition(view: self.warpViews[i], position: CGPoint(x: rect.midX, y: 4.0)) + transition.setBounds(view: self.warpViews[i], bounds: CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: itemLength))) + transition.setTransform(view: self.warpViews[i], transform: transform) + self.warpViews[i].update(containerSize: size, rect: rect, transition: transition) + } + + let clippingTopInset: CGFloat = topInset + let frame = CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: CGSize(width: size.width, height: -clippingTopInset + size.height - 21.0)) + transition.setPosition(view: self.clippingView, position: frame.center) + transition.setBounds(view: self.clippingView, bounds: CGRect(origin: CGPoint(x: 0.0, y: clippingTopInset), size: frame.size)) + self.clippingView.clipsToBounds = true + + transition.setFrame(view: self.warpMaskContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: size.height - allItemsHeight), size: CGSize(width: size.width, height: allItemsHeight))) + + var locations: [NSNumber] = [] + var colors: [CGColor] = [] + let numStops = 6 + for i in 0 ..< numStops { + let step = CGFloat(i) / CGFloat(numStops - 1) + locations.append(step as NSNumber) + colors.append(UIColor.black.withAlphaComponent(1.0 - step * step).cgColor) + } + + let gradientHeight: CGFloat = 6.0 + self.warpMaskGradientLayer.startPoint = CGPoint(x: 0.0, y: (allItemsHeight - gradientHeight) / allItemsHeight) + self.warpMaskGradientLayer.endPoint = CGPoint(x: 0.0, y: 1.0) + + self.warpMaskGradientLayer.locations = locations + self.warpMaskGradientLayer.colors = colors + self.warpMaskGradientLayer.type = .axial + + transition.setFrame(layer: self.warpMaskGradientLayer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: allItemsHeight))) + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return self.contentView.hitTest(point, with: event) + } +} diff --git a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift index 1d7a582d77..df49f1a264 100644 --- a/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift +++ b/submodules/TelegramUI/Components/PeerAllowedReactionsScreen/Sources/EmojiListInputComponent.swift @@ -105,7 +105,7 @@ final class EmojiListInputComponent: Component { private var component: EmojiListInputComponent? private weak var state: EmptyComponentState? - private var itemLayers: [Int64: EmojiPagerContentComponent.View.ItemLayer] = [:] + private var itemLayers: [Int64: EmojiKeyboardItemLayer] = [:] private let trailingPlaceholder = ComponentView() private let caretIndicator: CaretIndicatorView @@ -239,7 +239,7 @@ final class EmojiListInputComponent: Component { var itemTransition = transition var animateIn = false - let itemLayer: EmojiPagerContentComponent.View.ItemLayer + let itemLayer: EmojiKeyboardItemLayer if let current = self.itemLayers[itemKey] { itemLayer = current } else { @@ -249,7 +249,7 @@ final class EmojiListInputComponent: Component { let animationData = EntityKeyboardAnimationData( file: item.file ) - itemLayer = EmojiPagerContentComponent.View.ItemLayer( + itemLayer = EmojiKeyboardItemLayer( item: EmojiPagerContentComponent.Item( animationData: animationData, content: .animation(animationData),