mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
686 lines
17 KiB
C++
686 lines
17 KiB
C++
#ifndef CALayer_hpp
|
|
#define CALayer_hpp
|
|
|
|
#include "Lottie/Public/Primitives/Color.hpp"
|
|
#include <LottieCpp/Vectors.h>
|
|
#include <LottieCpp/CGPath.h>
|
|
#include "Lottie/Private/Model/ShapeItems/Fill.hpp"
|
|
#include "Lottie/Private/Model/Layers/LayerModel.hpp"
|
|
#include "Lottie/Public/Primitives/DrawingAttributes.hpp"
|
|
#include "Lottie/Private/Model/ShapeItems/GradientFill.hpp"
|
|
|
|
#include <memory>
|
|
#include <vector>
|
|
#include <functional>
|
|
|
|
namespace lottie {
|
|
|
|
class RenderableItem {
|
|
public:
|
|
enum class Type {
|
|
Shape,
|
|
GradientFill
|
|
};
|
|
|
|
public:
|
|
RenderableItem() {
|
|
}
|
|
|
|
virtual ~RenderableItem() = default;
|
|
|
|
virtual Type type() const = 0;
|
|
virtual CGRect boundingRect() const = 0;
|
|
|
|
virtual bool isEqual(std::shared_ptr<RenderableItem> rhs) const = 0;
|
|
};
|
|
|
|
class ShapeRenderableItem: public RenderableItem {
|
|
public:
|
|
struct Fill {
|
|
Color color;
|
|
FillRule rule;
|
|
|
|
Fill(Color color_, FillRule rule_) :
|
|
color(color_), rule(rule_) {
|
|
}
|
|
|
|
bool operator==(Fill const &rhs) const {
|
|
if (color != rhs.color) {
|
|
return false;
|
|
}
|
|
if (rule != rhs.rule) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(Fill const &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
};
|
|
|
|
struct Stroke {
|
|
Color color;
|
|
double lineWidth = 0.0;
|
|
LineJoin lineJoin = LineJoin::Round;
|
|
LineCap lineCap = LineCap::Square;
|
|
double dashPhase = 0.0;
|
|
std::vector<double> dashPattern;
|
|
|
|
Stroke(
|
|
Color color_,
|
|
double lineWidth_,
|
|
LineJoin lineJoin_,
|
|
LineCap lineCap_,
|
|
double dashPhase_,
|
|
std::vector<double> dashPattern_
|
|
) :
|
|
color(color_),
|
|
lineWidth(lineWidth_),
|
|
lineJoin(lineJoin_),
|
|
lineCap(lineCap_),
|
|
dashPhase(dashPhase_),
|
|
dashPattern(dashPattern_) {
|
|
}
|
|
|
|
bool operator==(Stroke const &rhs) const {
|
|
if (color != rhs.color) {
|
|
return false;
|
|
}
|
|
if (lineWidth != rhs.lineWidth) {
|
|
return false;
|
|
}
|
|
if (lineJoin != rhs.lineJoin) {
|
|
return false;
|
|
}
|
|
if (lineCap != rhs.lineCap) {
|
|
return false;
|
|
}
|
|
if (dashPhase != rhs.dashPhase) {
|
|
return false;
|
|
}
|
|
if (dashPattern != rhs.dashPattern) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool operator!=(Stroke const &rhs) const {
|
|
return !(*this == rhs);
|
|
}
|
|
};
|
|
|
|
public:
|
|
ShapeRenderableItem(
|
|
std::shared_ptr<CGPath> path_,
|
|
std::optional<Fill> const &fill_,
|
|
std::optional<Stroke> const &stroke_
|
|
) :
|
|
path(path_),
|
|
fill(fill_),
|
|
stroke(stroke_) {
|
|
}
|
|
|
|
virtual Type type() const override {
|
|
return Type::Shape;
|
|
}
|
|
|
|
virtual CGRect boundingRect() const override {
|
|
if (path) {
|
|
CGRect shapeBounds = path->boundingBox();
|
|
if (stroke) {
|
|
shapeBounds = shapeBounds.insetBy(-stroke->lineWidth / 2.0, -stroke->lineWidth / 2.0);
|
|
}
|
|
return shapeBounds;
|
|
} else {
|
|
return CGRect(0.0, 0.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
|
|
virtual bool isEqual(std::shared_ptr<RenderableItem> rhs) const override {
|
|
if (rhs->type() != type()) {
|
|
return false;
|
|
}
|
|
ShapeRenderableItem *other = (ShapeRenderableItem *)rhs.get();
|
|
if ((path == nullptr) != (other->path == nullptr)) {
|
|
return false;
|
|
} else if (path) {
|
|
if (!path->isEqual(other->path.get())) {
|
|
return false;
|
|
}
|
|
}
|
|
if (fill != other->fill) {
|
|
return false;
|
|
}
|
|
if (stroke != other->stroke) {
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public:
|
|
std::shared_ptr<CGPath> path;
|
|
std::optional<Fill> fill;
|
|
std::optional<Stroke> stroke;
|
|
};
|
|
|
|
class GradientFillRenderableItem: public RenderableItem {
|
|
public:
|
|
GradientFillRenderableItem(
|
|
std::shared_ptr<CGPath> path_,
|
|
FillRule pathFillRule_,
|
|
GradientType gradientType_,
|
|
std::vector<Color> const &colors_,
|
|
std::vector<double> const &locations_,
|
|
Vector2D const &start_,
|
|
Vector2D const &end_,
|
|
CGRect bounds_
|
|
) :
|
|
path(path_),
|
|
pathFillRule(pathFillRule_),
|
|
gradientType(gradientType_),
|
|
colors(colors_),
|
|
locations(locations_),
|
|
start(start_),
|
|
end(end_),
|
|
bounds(bounds_) {
|
|
}
|
|
|
|
virtual Type type() const override {
|
|
return Type::GradientFill;
|
|
}
|
|
|
|
virtual CGRect boundingRect() const override {
|
|
return bounds;
|
|
}
|
|
|
|
virtual bool isEqual(std::shared_ptr<RenderableItem> rhs) const override {
|
|
if (rhs->type() != type()) {
|
|
return false;
|
|
}
|
|
GradientFillRenderableItem *other = (GradientFillRenderableItem *)rhs.get();
|
|
|
|
if (gradientType != other->gradientType) {
|
|
return false;
|
|
}
|
|
if (colors != other->colors) {
|
|
return false;
|
|
}
|
|
if (locations != other->locations) {
|
|
return false;
|
|
}
|
|
if (start != other->start) {
|
|
return false;
|
|
}
|
|
if (end != other->end) {
|
|
return false;
|
|
}
|
|
if (bounds != other->bounds) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public:
|
|
std::shared_ptr<CGPath> path;
|
|
FillRule pathFillRule;
|
|
GradientType gradientType;
|
|
std::vector<Color> colors;
|
|
std::vector<double> locations;
|
|
Vector2D start;
|
|
Vector2D end;
|
|
CGRect bounds;
|
|
};
|
|
|
|
class RenderTreeNodeContent {
|
|
public:
|
|
enum class ShadingType {
|
|
Solid,
|
|
Gradient
|
|
};
|
|
|
|
class Shading {
|
|
public:
|
|
Shading() {
|
|
}
|
|
|
|
virtual ~Shading() = default;
|
|
|
|
virtual ShadingType type() const = 0;
|
|
};
|
|
|
|
class SolidShading: public Shading {
|
|
public:
|
|
SolidShading(Color const &color_, double opacity_) :
|
|
color(color_),
|
|
opacity(opacity_) {
|
|
}
|
|
|
|
virtual ShadingType type() const override {
|
|
return ShadingType::Solid;
|
|
}
|
|
|
|
public:
|
|
Color color;
|
|
double opacity = 0.0;
|
|
};
|
|
|
|
class GradientShading: public Shading {
|
|
public:
|
|
GradientShading(
|
|
double opacity_,
|
|
GradientType gradientType_,
|
|
std::vector<Color> const &colors_,
|
|
std::vector<double> const &locations_,
|
|
Vector2D const &start_,
|
|
Vector2D const &end_
|
|
) :
|
|
opacity(opacity_),
|
|
gradientType(gradientType_),
|
|
colors(colors_),
|
|
locations(locations_),
|
|
start(start_),
|
|
end(end_) {
|
|
}
|
|
|
|
virtual ShadingType type() const override {
|
|
return ShadingType::Gradient;
|
|
}
|
|
|
|
public:
|
|
double opacity = 0.0;
|
|
GradientType gradientType;
|
|
std::vector<Color> colors;
|
|
std::vector<double> locations;
|
|
Vector2D start;
|
|
Vector2D end;
|
|
};
|
|
|
|
struct Stroke {
|
|
std::shared_ptr<Shading> shading;
|
|
double lineWidth = 0.0;
|
|
LineJoin lineJoin = LineJoin::Round;
|
|
LineCap lineCap = LineCap::Square;
|
|
double miterLimit = 4.0;
|
|
double dashPhase = 0.0;
|
|
std::vector<double> dashPattern;
|
|
|
|
Stroke(
|
|
std::shared_ptr<Shading> shading_,
|
|
double lineWidth_,
|
|
LineJoin lineJoin_,
|
|
LineCap lineCap_,
|
|
double miterLimit_,
|
|
double dashPhase_,
|
|
std::vector<double> dashPattern_
|
|
) :
|
|
shading(shading_),
|
|
lineWidth(lineWidth_),
|
|
lineJoin(lineJoin_),
|
|
lineCap(lineCap_),
|
|
miterLimit(miterLimit_),
|
|
dashPhase(dashPhase_),
|
|
dashPattern(dashPattern_) {
|
|
}
|
|
};
|
|
|
|
struct Fill {
|
|
std::shared_ptr<Shading> shading;
|
|
FillRule rule;
|
|
|
|
Fill(
|
|
std::shared_ptr<Shading> shading_,
|
|
FillRule rule_
|
|
) :
|
|
shading(shading_),
|
|
rule(rule_) {
|
|
}
|
|
};
|
|
|
|
public:
|
|
RenderTreeNodeContent(
|
|
std::vector<BezierPath> paths_,
|
|
std::shared_ptr<Stroke> stroke_,
|
|
std::shared_ptr<Fill> fill_
|
|
) :
|
|
paths(paths_),
|
|
stroke(stroke_),
|
|
fill(fill_) {
|
|
}
|
|
|
|
public:
|
|
std::vector<BezierPath> paths;
|
|
std::shared_ptr<Stroke> stroke;
|
|
std::shared_ptr<Fill> fill;
|
|
};
|
|
|
|
class RenderTreeNode {
|
|
public:
|
|
RenderTreeNode(
|
|
CGRect bounds_,
|
|
Vector2D position_,
|
|
CATransform3D transform_,
|
|
double alpha_,
|
|
bool masksToBounds_,
|
|
bool isHidden_,
|
|
std::shared_ptr<RenderTreeNodeContent> content_,
|
|
std::vector<std::shared_ptr<RenderTreeNode>> subnodes_,
|
|
std::shared_ptr<RenderTreeNode> mask_,
|
|
bool invertMask_
|
|
) :
|
|
_bounds(bounds_),
|
|
_position(position_),
|
|
_transform(transform_),
|
|
_alpha(alpha_),
|
|
_masksToBounds(masksToBounds_),
|
|
_isHidden(isHidden_),
|
|
_content(content_),
|
|
_subnodes(subnodes_),
|
|
_mask(mask_),
|
|
_invertMask(invertMask_) {
|
|
}
|
|
|
|
~RenderTreeNode() {
|
|
}
|
|
|
|
public:
|
|
CGRect const &bounds() const {
|
|
return _bounds;
|
|
}
|
|
|
|
Vector2D const &position() const {
|
|
return _position;
|
|
}
|
|
|
|
CATransform3D const &transform() const {
|
|
return _transform;
|
|
}
|
|
|
|
double alpha() const {
|
|
return _alpha;
|
|
}
|
|
|
|
bool masksToBounds() const {
|
|
return _masksToBounds;
|
|
}
|
|
|
|
bool isHidden() const {
|
|
return _isHidden;
|
|
}
|
|
|
|
std::shared_ptr<RenderTreeNodeContent> const &content() const {
|
|
return _content;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<RenderTreeNode>> const &subnodes() const {
|
|
return _subnodes;
|
|
}
|
|
|
|
std::shared_ptr<RenderTreeNode> const &mask() const {
|
|
return _mask;
|
|
}
|
|
|
|
bool invertMask() const {
|
|
return _invertMask;
|
|
}
|
|
|
|
public:
|
|
CGRect _bounds;
|
|
Vector2D _position;
|
|
CATransform3D _transform = CATransform3D::identity();
|
|
double _alpha = 1.0;
|
|
bool _masksToBounds = false;
|
|
bool _isHidden = false;
|
|
std::shared_ptr<RenderTreeNodeContent> _content;
|
|
std::vector<std::shared_ptr<RenderTreeNode>> _subnodes;
|
|
std::shared_ptr<RenderTreeNode> _mask;
|
|
bool _invertMask = false;
|
|
};
|
|
|
|
class CALayer: public std::enable_shared_from_this<CALayer> {
|
|
public:
|
|
CALayer() {
|
|
}
|
|
|
|
virtual ~CALayer() = default;
|
|
|
|
void addSublayer(std::shared_ptr<CALayer> layer) {
|
|
if (layer->_superlayer) {
|
|
layer->_superlayer->removeSublayer(layer.get());
|
|
}
|
|
layer->_superlayer = this;
|
|
_sublayers.push_back(layer);
|
|
}
|
|
|
|
void insertSublayer(std::shared_ptr<CALayer> 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);
|
|
}
|
|
}
|
|
|
|
bool needsDisplay() const {
|
|
return _needsDisplay;
|
|
}
|
|
void setNeedsDisplay(bool needsDisplay) {
|
|
_needsDisplay = true;
|
|
}
|
|
|
|
virtual bool implementsDraw() const {
|
|
return false;
|
|
}
|
|
|
|
virtual bool isInvertedMatte() const {
|
|
return false;
|
|
}
|
|
|
|
virtual std::shared_ptr<RenderableItem> renderableItem() {
|
|
return nullptr;
|
|
}
|
|
|
|
bool isHidden() const {
|
|
return _isHidden;
|
|
}
|
|
void setIsHidden(bool isHidden) {
|
|
_isHidden = isHidden;
|
|
}
|
|
|
|
float opacity() const {
|
|
return _opacity;
|
|
}
|
|
void setOpacity(float opacity) {
|
|
_opacity = opacity;
|
|
}
|
|
|
|
Vector2D const &position() const {
|
|
return _position;
|
|
}
|
|
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();
|
|
}
|
|
|
|
CATransform3D const &transform() const {
|
|
return _transform;
|
|
}
|
|
void setTransform(CATransform3D const &transform) {
|
|
_transform = transform;
|
|
}
|
|
|
|
std::shared_ptr<CALayer> const &mask() const {
|
|
return _mask;
|
|
}
|
|
void setMask(std::shared_ptr<CALayer> mask) {
|
|
_mask = mask;
|
|
}
|
|
|
|
bool masksToBounds() const {
|
|
return _masksToBounds;
|
|
}
|
|
void setMasksToBounds(bool masksToBounds) {
|
|
_masksToBounds = masksToBounds;
|
|
}
|
|
|
|
std::vector<std::shared_ptr<CALayer>> const &sublayers() const {
|
|
return _sublayers;
|
|
}
|
|
|
|
std::optional<BlendMode> const &compositingFilter() const {
|
|
return _compositingFilter;
|
|
}
|
|
void setCompositingFilter(std::optional<BlendMode> const &compositingFilter) {
|
|
_compositingFilter = compositingFilter;
|
|
}
|
|
|
|
protected:
|
|
template <typename Derived>
|
|
std::shared_ptr<Derived> shared_from_base() {
|
|
return std::static_pointer_cast<Derived>(shared_from_this());
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
CALayer *_superlayer = nullptr;
|
|
std::vector<std::shared_ptr<CALayer>> _sublayers;
|
|
bool _needsDisplay = false;
|
|
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);
|
|
CATransform3D _transform = CATransform3D::identity();
|
|
std::shared_ptr<CALayer> _mask;
|
|
bool _masksToBounds = false;
|
|
std::optional<BlendMode> _compositingFilter;
|
|
};
|
|
|
|
class CAShapeLayer: public CALayer {
|
|
public:
|
|
CAShapeLayer() {
|
|
}
|
|
|
|
virtual ~CAShapeLayer() = default;
|
|
|
|
std::optional<Color> const &strokeColor() {
|
|
return _strokeColor;
|
|
}
|
|
void setStrokeColor(std::optional<Color> const &strokeColor) {
|
|
_strokeColor = strokeColor;
|
|
}
|
|
|
|
std::optional<Color> const &fillColor() {
|
|
return _fillColor;
|
|
}
|
|
void setFillColor(std::optional<Color> const &fillColor) {
|
|
_fillColor = fillColor;
|
|
}
|
|
|
|
FillRule fillRule() {
|
|
return _fillRule;
|
|
}
|
|
void setFillRule(FillRule fillRule) {
|
|
_fillRule = fillRule;
|
|
}
|
|
|
|
std::shared_ptr<CGPath> const &path() const {
|
|
return _path;
|
|
}
|
|
void setPath(std::shared_ptr<CGPath> const &path) {
|
|
_path = path;
|
|
}
|
|
|
|
double lineWidth() const {
|
|
return _lineWidth;
|
|
}
|
|
void setLineWidth(double lineWidth) {
|
|
_lineWidth = lineWidth;
|
|
}
|
|
|
|
LineJoin lineJoin() const {
|
|
return _lineJoin;
|
|
}
|
|
void setLineJoin(LineJoin lineJoin) {
|
|
_lineJoin = lineJoin;
|
|
}
|
|
|
|
LineCap lineCap() const {
|
|
return _lineCap;
|
|
}
|
|
void setLineCap(LineCap lineCap) {
|
|
_lineCap = lineCap;
|
|
}
|
|
|
|
double lineDashPhase() const {
|
|
return _lineDashPhase;
|
|
}
|
|
void setLineDashPhase(double lineDashPhase) {
|
|
_lineDashPhase = lineDashPhase;
|
|
}
|
|
|
|
std::vector<double> const &dashPattern() const {
|
|
return _dashPattern;
|
|
}
|
|
void setDashPattern(std::vector<double> const &dashPattern) {
|
|
_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> renderableItem() override;
|
|
|
|
private:
|
|
std::optional<Color> _strokeColor;
|
|
std::optional<Color> _fillColor = Color(0.0, 0.0, 0.0, 1.0);
|
|
FillRule _fillRule = FillRule::NonZeroWinding;
|
|
std::shared_ptr<CGPath> _path;
|
|
double _lineWidth = 1.0;
|
|
LineJoin _lineJoin = LineJoin::Miter;
|
|
LineCap _lineCap = LineCap::Butt;
|
|
double _lineDashPhase = 0.0;
|
|
std::vector<double> _dashPattern;
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* CALayer_hpp */
|