#ifndef MaskContainerLayer_hpp #define MaskContainerLayer_hpp #include "Lottie/Private/Model/Objects/Mask.hpp" #include "Lottie/Public/Primitives/CALayer.hpp" #include "Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/Protocols/NodePropertyMap.hpp" #include "Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/NodeProperty.hpp" #include "Lottie/Private/MainThread/NodeRenderSystem/NodeProperties/ValueProviders/KeyframeInterpolator.hpp" namespace lottie { inline MaskMode usableMaskMode(MaskMode mode) { switch (mode) { case MaskMode::Add: return MaskMode::Add; case MaskMode::Subtract: return MaskMode::Subtract; case MaskMode::Intersect: return MaskMode::Intersect; case MaskMode::Lighten: return MaskMode::Add; case MaskMode::Darken: return MaskMode::Darken; case MaskMode::Difference: return MaskMode::Intersect; case MaskMode::None: return MaskMode::None; } } class MaskNodeProperties: public NodePropertyMap { public: MaskNodeProperties(std::shared_ptr const &mask) : _mode(mask->mode()), _inverted(mask->inverted) { _opacity = std::make_shared>(std::make_shared>(mask->opacity->keyframes)); _shape = std::make_shared>(std::make_shared>(mask->shape.keyframes)); _expansion = std::make_shared>(std::make_shared>(mask->expansion->keyframes)); _propertyMap.insert(std::make_pair("Opacity", _opacity)); _propertyMap.insert(std::make_pair("Shape", _shape)); _propertyMap.insert(std::make_pair("Expansion", _expansion)); for (const auto &it : _propertyMap) { _properties.push_back(it.second); } } virtual std::vector> &properties() override { return _properties; } virtual std::vector> const &childKeypaths() const override { return _childKeypaths; } std::shared_ptr> const &opacity() const { return _opacity; } std::shared_ptr> const &shape() const { return _shape; } std::shared_ptr> const &expansion() const { return _expansion; } MaskMode mode() const { return _mode; } bool inverted() const { return _inverted; } private: std::map> _propertyMap; std::vector> _childKeypaths; std::vector> _properties; MaskMode _mode = MaskMode::Add; bool _inverted = false; std::shared_ptr> _opacity; std::shared_ptr> _shape; std::shared_ptr> _expansion; }; class MaskLayer: public CALayer { public: MaskLayer(std::shared_ptr const &mask) : _properties(mask) { _maskLayer = std::make_shared(); addSublayer(_maskLayer); if (mask->mode() == MaskMode::Add) { _maskLayer->setFillColor(Color(1.0, 0.0, 0.0, 1.0)); } else { _maskLayer->setFillColor(Color(0.0, 1.0, 0.0, 1.0)); } _maskLayer->setFillRule(FillRule::EvenOdd); } void updateWithFrame(double frame, bool forceUpdates) { if (_properties.opacity()->needsUpdate(frame) || forceUpdates) { _properties.opacity()->update(frame); setOpacity(_properties.opacity()->value().value); } if (_properties.shape()->needsUpdate(frame) || forceUpdates) { _properties.shape()->update(frame); _properties.expansion()->update(frame); auto path = _properties.shape()->value().cgPath(); auto usableMode = usableMaskMode(_properties.mode()); if ((usableMode == MaskMode::Subtract && !_properties.inverted()) || (usableMode == MaskMode::Add && _properties.inverted())) { /// Add a bounds rect to invert the mask auto newPath = CGPath::makePath(); newPath->addRect(CGRect::veryLarge()); newPath->addPath(path); path = std::static_pointer_cast(newPath); } _maskLayer->setPath(path); } } private: MaskNodeProperties _properties; std::shared_ptr _maskLayer; }; class MaskContainerLayer: public CALayer { public: MaskContainerLayer(std::vector> const &masks) { auto containerLayer = std::make_shared(); bool firstObject = true; for (const auto &mask : masks) { auto maskLayer = std::make_shared(mask); _maskLayers.push_back(maskLayer); auto usableMode = usableMaskMode(mask->mode()); if (usableMode == MaskMode::None) { continue; } else if (usableMode == MaskMode::Add || firstObject) { firstObject = false; containerLayer->addSublayer(maskLayer); } else { containerLayer->setMask(maskLayer); auto newContainer = std::make_shared(); newContainer->addSublayer(containerLayer); containerLayer = newContainer; } } addSublayer(containerLayer); } // MARK: Internal void updateWithFrame(double frame, bool forceUpdates) { for (const auto &maskLayer : _maskLayers) { maskLayer->updateWithFrame(frame, forceUpdates); } } private: std::vector> _maskLayers; }; } #endif /* MaskContainerLayer_hpp */