#ifndef MainThreadAnimationLayer_hpp #define MainThreadAnimationLayer_hpp #include "Lottie/Public/Primitives/CALayer.hpp" #include "Lottie/Public/ImageProvider/AnimationImageProvider.hpp" #include "Lottie/Private/Model/Animation.hpp" #include "Lottie/Public/TextProvider/AnimationTextProvider.hpp" #include "Lottie/Public/FontProvider/AnimationFontProvider.hpp" #include "Lottie/Private/MainThread/LayerContainers/Utility/LayerImageProvider.hpp" #include "Lottie/Private/MainThread/LayerContainers/Utility/LayerTextProvider.hpp" #include "Lottie/Private/MainThread/LayerContainers/Utility/CompositionLayersInitializer.hpp" #include "Lottie/Private/MainThread/LayerContainers/Utility/LayerFontProvider.hpp" #include "Lottie/Public/DynamicProperties/AnyValueProvider.hpp" #include "Lottie/Public/DynamicProperties/AnimationKeypath.hpp" namespace lottie { class BlankImageProvider: public AnimationImageProvider { public: std::shared_ptr imageForAsset(ImageAsset const &asset) { return nullptr; } }; class MainThreadAnimationLayer: public CALayer { public: MainThreadAnimationLayer( Animation const &animation, std::shared_ptr const &imageProvider, std::shared_ptr const &textProvider, std::shared_ptr const &fontProvider ) { if (animation.assetLibrary) { _layerImageProvider = std::make_shared(imageProvider, animation.assetLibrary->imageAssets); } else { std::map> imageAssets; _layerImageProvider = std::make_shared(imageProvider, imageAssets); } _layerTextProvider = std::make_shared(textProvider); _layerFontProvider = std::make_shared(fontProvider); setBounds(CGRect(0.0, 0.0, animation.width, animation.height)); auto layers = initializeCompositionLayers( animation.layers, animation.assetLibrary, _layerImageProvider, textProvider, fontProvider, animation.framerate ); std::vector> imageLayers; std::vector> textLayers; std::shared_ptr mattedLayer; for (auto layerIt = layers.rbegin(); layerIt != layers.rend(); layerIt++) { std::shared_ptr const &layer = *layerIt; layer->setBounds(bounds()); _animationLayers.push_back(layer); if (layer->isImageCompositionLayer()) { imageLayers.push_back(std::static_pointer_cast(layer)); } if (layer->isTextCompositionLayer()) { textLayers.push_back(std::static_pointer_cast(layer)); } if (mattedLayer) { /// The previous layer requires this layer to be its matte mattedLayer->setMatteLayer(layer); mattedLayer = nullptr; continue; } if (layer->matteType().has_value() && (layer->matteType() == MatteType::Add || layer->matteType() == MatteType::Invert)) { /// We have a layer that requires a matte. mattedLayer = layer; } addSublayer(layer); } _layerImageProvider->addImageLayers(imageLayers); _layerImageProvider->reloadImages(); _layerTextProvider->addTextLayers(textLayers); _layerTextProvider->reloadTexts(); _layerFontProvider->addTextLayers(textLayers); _layerFontProvider->reloadTexts(); setNeedsDisplay(true); } void setRespectAnimationFrameRate(bool respectAnimationFrameRate) { _respectAnimationFrameRate = respectAnimationFrameRate; } void display() { double newFrame = currentFrame(); if (_respectAnimationFrameRate) { newFrame = floor(newFrame); } for (const auto &layer : _animationLayers) { layer->displayWithFrame(newFrame, false); } } std::vector> const &animationLayers() const { return _animationLayers; } void reloadImages() { _layerImageProvider->reloadImages(); } /// Forces the view to update its drawing. void forceDisplayUpdate() { for (const auto &layer : _animationLayers) { layer->displayWithFrame(currentFrame(), true); } } void logHierarchyKeypaths() { printf("Lottie: Logging Animation Keypaths\n"); assert(false); //animationLayers.forEach({ $0.logKeypaths(for: nil) }) } void setValueProvider(std::shared_ptr const &valueProvider, AnimationKeypath const &keypath) { for (const auto &layer : _animationLayers) { assert(false); /*if let foundProperties = layer.nodeProperties(for: keypath) { for property in foundProperties { property.setProvider(provider: valueProvider) } layer.displayWithFrame(frame: presentation()?.currentFrame ?? currentFrame, forceUpdates: true) }*/ } } std::optional getValue(AnimationKeypath const &keypath, std::optional atFrame) { for (const auto &layer : _animationLayers) { assert(false); /*if let foundProperties = layer.nodeProperties(for: keypath), let first = foundProperties.first { return first.valueProvider.value(frame: atFrame ?? currentFrame) }*/ } return std::nullopt; } std::optional getOriginalValue(AnimationKeypath const &keypath, std::optional atFrame) { for (const auto &layer : _animationLayers) { assert(false); /*if let foundProperties = layer.nodeProperties(for: keypath), let first = foundProperties.first { return first.originalValueProvider.value(frame: atFrame ?? currentFrame) }*/ } return std::nullopt; } std::shared_ptr layerForKeypath(AnimationKeypath const &keyPath) { assert(false); /*for layer in animationLayers { if let foundLayer = layer.layer(for: keypath) { return foundLayer } }*/ return nullptr; } std::vector> animatorNodesForKeypath(AnimationKeypath const &keypath) { std::vector> results; /*for (const auto &layer : _animationLayers) { if let nodes = layer.animatorNodes(for: keypath) { results.append(contentsOf: nodes) } }*/ return results; } double currentFrame() const { return _currentFrame; } void setCurrentFrame(double currentFrame) { _currentFrame = currentFrame; for (size_t i = 0; i < _animationLayers.size(); i++) { _animationLayers[i]->displayWithFrame(_currentFrame, false); } } std::shared_ptr imageProvider() const { return _layerImageProvider->imageProvider(); } void setImageProvider(std::shared_ptr const &imageProvider) { _layerImageProvider->setImageProvider(imageProvider); } std::shared_ptr textProvider() const { return _layerTextProvider->textProvider(); } void setTextProvider(std::shared_ptr const &textProvider) { _layerTextProvider->setTextProvider(textProvider); } std::shared_ptr fontProvider() const { return _layerFontProvider->fontProvider(); } void setFontProvider(std::shared_ptr const &fontProvider) { _layerFontProvider->setFontProvider(fontProvider); } virtual std::shared_ptr renderTreeNode() { std::vector> subnodes; for (const auto &animationLayer : _animationLayers) { bool found = false; for (const auto &sublayer : sublayers()) { if (animationLayer == sublayer) { found = true; break; } } if (found) { auto node = animationLayer->renderTreeNode(); if (node) { subnodes.push_back(node); } } } return std::make_shared( bounds(), position(), CATransform3D::identity(), 1.0, false, false, nullptr, subnodes, nullptr, false ); } private: // MARK: Internal /// The animatable Current Frame Property double _currentFrame = 0.0; std::shared_ptr _imageProvider; std::shared_ptr _textProvider; std::shared_ptr _fontProvider; bool _respectAnimationFrameRate = true; std::vector> _animationLayers; std::shared_ptr _layerImageProvider; std::shared_ptr _layerTextProvider; std::shared_ptr _layerFontProvider; }; } #endif /* MainThreadAnimationLayer_hpp */