Lottie tests

This commit is contained in:
Isaac 2024-06-12 23:04:22 +04:00
parent 97996ee812
commit d74e29d790
8 changed files with 187 additions and 236 deletions

View File

@ -1,5 +1,5 @@
#ifndef CoreGraphicsCanvasImpl_h #ifndef CoreGraphicsCoreGraphicsCanvasImpl_h
#define CoreGraphicsCanvasImpl_h #define CoreGraphicsCoreGraphicsCanvasImpl_h
#include <LottieCpp/LottieCpp.h> #include <LottieCpp/LottieCpp.h>
@ -7,7 +7,9 @@
namespace lottie { namespace lottie {
class CanvasImpl: public Canvas { class CoreGraphicsCanvasImpl: public Canvas {
class Layer;
public: public:
class Image { class Image {
public: public:
@ -20,12 +22,9 @@ public:
}; };
public: public:
CanvasImpl(int width, int height); CoreGraphicsCanvasImpl(int width, int height);
CanvasImpl(CGContextRef context, int width, int height); CoreGraphicsCanvasImpl(CGContextRef context, int width, int height);
virtual ~CanvasImpl(); virtual ~CoreGraphicsCanvasImpl();
virtual int width() const override;
virtual int height() const override;
std::shared_ptr<Canvas> makeLayer(int width, int height) override; std::shared_ptr<Canvas> makeLayer(int width, int height) override;
@ -42,32 +41,23 @@ public:
virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override; virtual void fill(lottie::CGRect const &rect, lottie::Color const &fillColor) override;
virtual void setBlendMode(BlendMode blendMode) override; virtual void setBlendMode(BlendMode blendMode) override;
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform2D const &transform) override; virtual void concatenate(lottie::Transform2D const &transform) override;
virtual std::shared_ptr<Image> makeImage() const; virtual std::shared_ptr<Image> makeImage();
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override; virtual void draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) override;
CGContextRef nativeContext() const { virtual void pushLayer(CGRect const &rect) override;
return _context; virtual void popLayer() override;
}
std::vector<uint8_t> &backingData() { std::vector<uint8_t> &backingData();
return _backingData; int bytesPerRow();
}
private:
int bytesPerRow() { std::shared_ptr<Layer> &currentLayer();
return _bytesPerRow;
}
private: private:
int _width = 0;
int _height = 0;
int _bytesPerRow = 0;
std::vector<uint8_t> _backingData;
CGContextRef _context = nil;
CGContextRef _topContext = nil; CGContextRef _topContext = nil;
CGLayerRef _layer = nil; std::vector<std::shared_ptr<Layer>> _layerStack;
}; };
} }

View File

@ -62,19 +62,9 @@ bool addEnumeratedPath(CGContextRef context, CanvasPathEnumerator const &enumera
} }
CanvasImpl::Image::Image(::CGImageRef image) { class CoreGraphicsCanvasImpl::Layer {
_image = CGImageRetain(image); public:
} explicit Layer(int width, int height) {
CanvasImpl::Image::~Image() {
CFRelease(_image);
}
::CGImageRef CanvasImpl::Image::nativeImage() const {
return _image;
}
CanvasImpl::CanvasImpl(int width, int height) {
_width = width; _width = width;
_height = height; _height = height;
_bytesPerRow = alignUp(width * 4, 16); _bytesPerRow = alignUp(width * 4, 16);
@ -82,96 +72,107 @@ CanvasImpl::CanvasImpl(int width, int height) {
memset(_backingData.data(), 0, _backingData.size()); memset(_backingData.data(), 0, _backingData.size());
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst; CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst;
_context = CGBitmapContextCreate(_backingData.data(), _width, _height, 8, _bytesPerRow, colorSpace, bitmapInfo); _context = CGBitmapContextCreate(_backingData.data(), _width, _height, 8, _bytesPerRow, colorSpace, bitmapInfo);
CFRelease(colorSpace);
CGContextClearRect(_context, CGRectMake(0.0, 0.0, _width, _height)); CGContextClearRect(_context, CGRectMake(0.0, 0.0, _width, _height));
//CGContextSetInterpolationQuality(_context, kCGInterpolationLow);
//CGContextSetAllowsAntialiasing(_context, true);
//CGContextSetShouldAntialias(_context, true);
CFRelease(colorSpace);
_topContext = CGContextRetain(_context);
} }
CanvasImpl::CanvasImpl(CGContextRef context, int width, int height) { ~Layer() {
CGContextRelease(_context);
}
CGContextRef context() const {
return _context;
}
public:
CGContextRef _context = nil;
int _width = 0;
int _height = 0;
int _bytesPerRow = 0;
std::vector<uint8_t> _backingData;
};
CoreGraphicsCanvasImpl::Image::Image(::CGImageRef image) {
_image = CGImageRetain(image);
}
CoreGraphicsCanvasImpl::Image::~Image() {
CFRelease(_image);
}
::CGImageRef CoreGraphicsCanvasImpl::Image::nativeImage() const {
return _image;
}
CoreGraphicsCanvasImpl::CoreGraphicsCanvasImpl(int width, int height) {
_layerStack.push_back(std::make_shared<Layer>(width, height));
_topContext = CGContextRetain(currentLayer()->context());
}
CoreGraphicsCanvasImpl::CoreGraphicsCanvasImpl(CGContextRef context, int width, int height) {
_layerStack.push_back(std::make_shared<Layer>(width, height));
_topContext = CGContextRetain(context); _topContext = CGContextRetain(context);
_layer = CGLayerCreateWithContext(context, CGSizeMake(width, height), nil);
_context = CGContextRetain(CGLayerGetContext(_layer));
_width = width;
_height = height;
} }
CanvasImpl::~CanvasImpl() { CoreGraphicsCanvasImpl::~CoreGraphicsCanvasImpl() {
CFRelease(_context);
if (_topContext) { if (_topContext) {
CFRelease(_topContext); CFRelease(_topContext);
} }
if (_layer) {
CFRelease(_layer);
}
} }
int CanvasImpl::width() const { std::shared_ptr<Canvas> CoreGraphicsCanvasImpl::makeLayer(int width, int height) {
return _width; return std::make_shared<CoreGraphicsCanvasImpl>(_topContext, width, height);
} }
int CanvasImpl::height() const { void CoreGraphicsCanvasImpl::saveState() {
return _height; CGContextSaveGState(currentLayer()->context());
} }
std::shared_ptr<Canvas> CanvasImpl::makeLayer(int width, int height) { void CoreGraphicsCanvasImpl::restoreState() {
return std::make_shared<CanvasImpl>(_topContext, width, height); CGContextRestoreGState(currentLayer()->context());
} }
void CanvasImpl::saveState() { void CoreGraphicsCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) {
CGContextSaveGState(_context); if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
}
void CanvasImpl::restoreState() {
CGContextRestoreGState(_context);
}
void CanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) {
if (!addEnumeratedPath(_context, enumeratePath)) {
return; return;
} }
CGFloat components[4] = { color.r, color.g, color.b, color.a }; CGFloat components[4] = { color.r, color.g, color.b, color.a };
CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components);
CGContextSetFillColorWithColor(_context, nativeColor); CGContextSetFillColorWithColor(currentLayer()->context(), nativeColor);
CFRelease(nativeColor); CFRelease(nativeColor);
switch (fillRule) { switch (fillRule) {
case lottie::FillRule::EvenOdd: { case lottie::FillRule::EvenOdd: {
CGContextEOFillPath(_context); CGContextEOFillPath(currentLayer()->context());
break; break;
} }
default: { default: {
CGContextFillPath(_context); CGContextFillPath(currentLayer()->context());
break; break;
} }
} }
} }
void CanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { void CoreGraphicsCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) {
CGContextSaveGState(_context); CGContextSaveGState(currentLayer()->context());
if (!addEnumeratedPath(_context, enumeratePath)) { if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
return; return;
} }
switch (fillRule) { switch (fillRule) {
case lottie::FillRule::EvenOdd: { case lottie::FillRule::EvenOdd: {
CGContextEOClip(_context); CGContextEOClip(currentLayer()->context());
break; break;
} }
default: { default: {
CGContextClip(_context); CGContextClip(currentLayer()->context());
break; break;
} }
} }
@ -195,29 +196,29 @@ void CanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumeratePat
CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size());
if (nativeGradient) { if (nativeGradient) {
CGContextDrawLinearGradient(_context, nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextDrawLinearGradient(currentLayer()->context(), nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CFRelease(nativeGradient); CFRelease(nativeGradient);
} }
CGContextResetClip(_context); CGContextResetClip(currentLayer()->context());
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
} }
void CanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { void CoreGraphicsCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) {
CGContextSaveGState(_context); CGContextSaveGState(currentLayer()->context());
if (!addEnumeratedPath(_context, enumeratePath)) { if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
return; return;
} }
switch (fillRule) { switch (fillRule) {
case lottie::FillRule::EvenOdd: { case lottie::FillRule::EvenOdd: {
CGContextEOClip(_context); CGContextEOClip(currentLayer()->context());
break; break;
} }
default: { default: {
CGContextClip(_context); CGContextClip(currentLayer()->context());
break; break;
} }
} }
@ -241,60 +242,60 @@ void CanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePat
CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size());
if (nativeGradient) { if (nativeGradient) {
CGContextDrawRadialGradient(_context, nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextDrawRadialGradient(currentLayer()->context(), nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CFRelease(nativeGradient); CFRelease(nativeGradient);
} }
CGContextResetClip(_context); CGContextResetClip(currentLayer()->context());
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
} }
void CanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, lottie::Color const &color) { void CoreGraphicsCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, lottie::Color const &color) {
if (!addEnumeratedPath(_context, enumeratePath)) { if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
return; return;
} }
CGFloat components[4] = { color.r, color.g, color.b, color.a }; CGFloat components[4] = { color.r, color.g, color.b, color.a };
CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components);
CGContextSetStrokeColorWithColor(_context, nativeColor); CGContextSetStrokeColorWithColor(currentLayer()->context(), nativeColor);
CFRelease(nativeColor); CFRelease(nativeColor);
CGContextSetLineWidth(_context, lineWidth); CGContextSetLineWidth(currentLayer()->context(), lineWidth);
switch (lineJoin) { switch (lineJoin) {
case lottie::LineJoin::Miter: { case lottie::LineJoin::Miter: {
CGContextSetLineJoin(_context, kCGLineJoinMiter); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinMiter);
break; break;
} }
case lottie::LineJoin::Round: { case lottie::LineJoin::Round: {
CGContextSetLineJoin(_context, kCGLineJoinRound); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinRound);
break; break;
} }
case lottie::LineJoin::Bevel: { case lottie::LineJoin::Bevel: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
default: { default: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
} }
switch (lineCap) { switch (lineCap) {
case lottie::LineCap::Butt: { case lottie::LineCap::Butt: {
CGContextSetLineCap(_context, kCGLineCapButt); CGContextSetLineCap(currentLayer()->context(), kCGLineCapButt);
break; break;
} }
case lottie::LineCap::Round: { case lottie::LineCap::Round: {
CGContextSetLineCap(_context, kCGLineCapRound); CGContextSetLineCap(currentLayer()->context(), kCGLineCapRound);
break; break;
} }
case lottie::LineCap::Square: { case lottie::LineCap::Square: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
default: { default: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
} }
@ -304,54 +305,54 @@ void CanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lin
for (const auto value : dashPattern) { for (const auto value : dashPattern) {
mappedDashPattern.push_back(value); mappedDashPattern.push_back(value);
} }
CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); CGContextSetLineDash(currentLayer()->context(), dashPhase, mappedDashPattern.data(), mappedDashPattern.size());
} }
CGContextStrokePath(_context); CGContextStrokePath(currentLayer()->context());
} }
void CanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) { void CoreGraphicsCanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) {
CGContextSaveGState(_context); CGContextSaveGState(currentLayer()->context());
if (!addEnumeratedPath(_context, enumeratePath)) { if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
return; return;
} }
CGContextSetLineWidth(_context, lineWidth); CGContextSetLineWidth(currentLayer()->context(), lineWidth);
switch (lineJoin) { switch (lineJoin) {
case lottie::LineJoin::Miter: { case lottie::LineJoin::Miter: {
CGContextSetLineJoin(_context, kCGLineJoinMiter); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinMiter);
break; break;
} }
case lottie::LineJoin::Round: { case lottie::LineJoin::Round: {
CGContextSetLineJoin(_context, kCGLineJoinRound); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinRound);
break; break;
} }
case lottie::LineJoin::Bevel: { case lottie::LineJoin::Bevel: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
default: { default: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
} }
switch (lineCap) { switch (lineCap) {
case lottie::LineCap::Butt: { case lottie::LineCap::Butt: {
CGContextSetLineCap(_context, kCGLineCapButt); CGContextSetLineCap(currentLayer()->context(), kCGLineCapButt);
break; break;
} }
case lottie::LineCap::Round: { case lottie::LineCap::Round: {
CGContextSetLineCap(_context, kCGLineCapRound); CGContextSetLineCap(currentLayer()->context(), kCGLineCapRound);
break; break;
} }
case lottie::LineCap::Square: { case lottie::LineCap::Square: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
default: { default: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
} }
@ -361,11 +362,11 @@ void CanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumerateP
for (const auto value : dashPattern) { for (const auto value : dashPattern) {
mappedDashPattern.push_back(value); mappedDashPattern.push_back(value);
} }
CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); CGContextSetLineDash(currentLayer()->context(), dashPhase, mappedDashPattern.data(), mappedDashPattern.size());
} }
CGContextReplacePathWithStrokedPath(_context); CGContextReplacePathWithStrokedPath(currentLayer()->context());
CGContextClip(_context); CGContextClip(currentLayer()->context());
std::vector<double> components; std::vector<double> components;
components.reserve(gradient.colors().size() + 4); components.reserve(gradient.colors().size() + 4);
@ -386,57 +387,57 @@ void CanvasImpl::linearGradientStrokePath(CanvasPathEnumerator const &enumerateP
CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size());
if (nativeGradient) { if (nativeGradient) {
CGContextDrawLinearGradient(_context, nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextDrawLinearGradient(currentLayer()->context(), nativeGradient, CGPointMake(start.x, start.y), CGPointMake(end.x, end.y), kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CFRelease(nativeGradient); CFRelease(nativeGradient);
} }
CGContextResetClip(_context); CGContextResetClip(currentLayer()->context());
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
} }
void CanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) { void CoreGraphicsCanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) {
CGContextSaveGState(_context); CGContextSaveGState(currentLayer()->context());
if (!addEnumeratedPath(_context, enumeratePath)) { if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
return; return;
} }
CGContextSetLineWidth(_context, lineWidth); CGContextSetLineWidth(currentLayer()->context(), lineWidth);
switch (lineJoin) { switch (lineJoin) {
case lottie::LineJoin::Miter: { case lottie::LineJoin::Miter: {
CGContextSetLineJoin(_context, kCGLineJoinMiter); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinMiter);
break; break;
} }
case lottie::LineJoin::Round: { case lottie::LineJoin::Round: {
CGContextSetLineJoin(_context, kCGLineJoinRound); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinRound);
break; break;
} }
case lottie::LineJoin::Bevel: { case lottie::LineJoin::Bevel: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
default: { default: {
CGContextSetLineJoin(_context, kCGLineJoinBevel); CGContextSetLineJoin(currentLayer()->context(), kCGLineJoinBevel);
break; break;
} }
} }
switch (lineCap) { switch (lineCap) {
case lottie::LineCap::Butt: { case lottie::LineCap::Butt: {
CGContextSetLineCap(_context, kCGLineCapButt); CGContextSetLineCap(currentLayer()->context(), kCGLineCapButt);
break; break;
} }
case lottie::LineCap::Round: { case lottie::LineCap::Round: {
CGContextSetLineCap(_context, kCGLineCapRound); CGContextSetLineCap(currentLayer()->context(), kCGLineCapRound);
break; break;
} }
case lottie::LineCap::Square: { case lottie::LineCap::Square: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
default: { default: {
CGContextSetLineCap(_context, kCGLineCapSquare); CGContextSetLineCap(currentLayer()->context(), kCGLineCapSquare);
break; break;
} }
} }
@ -446,11 +447,11 @@ void CanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumerateP
for (const auto value : dashPattern) { for (const auto value : dashPattern) {
mappedDashPattern.push_back(value); mappedDashPattern.push_back(value);
} }
CGContextSetLineDash(_context, dashPhase, mappedDashPattern.data(), mappedDashPattern.size()); CGContextSetLineDash(currentLayer()->context(), dashPhase, mappedDashPattern.data(), mappedDashPattern.size());
} }
CGContextReplacePathWithStrokedPath(_context); CGContextReplacePathWithStrokedPath(currentLayer()->context());
CGContextClip(_context); CGContextClip(currentLayer()->context());
std::vector<double> components; std::vector<double> components;
components.reserve(gradient.colors().size() + 4); components.reserve(gradient.colors().size() + 4);
@ -471,24 +472,24 @@ void CanvasImpl::radialGradientStrokePath(CanvasPathEnumerator const &enumerateP
CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size()); CGGradientRef nativeGradient = CGGradientCreateWithColorComponents(CGBitmapContextGetColorSpace(_topContext), components.data(), locations.data(), locations.size());
if (nativeGradient) { if (nativeGradient) {
CGContextDrawRadialGradient(_context, nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); CGContextDrawRadialGradient(currentLayer()->context(), nativeGradient, CGPointMake(startCenter.x, startCenter.y), startRadius, CGPointMake(endCenter.x, endCenter.y), endRadius, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
CFRelease(nativeGradient); CFRelease(nativeGradient);
} }
CGContextResetClip(_context); CGContextResetClip(currentLayer()->context());
CGContextRestoreGState(_context); CGContextRestoreGState(currentLayer()->context());
} }
void CanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) { void CoreGraphicsCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillColor) {
CGFloat components[4] = { fillColor.r, fillColor.g, fillColor.b, fillColor.a }; CGFloat components[4] = { fillColor.r, fillColor.g, fillColor.b, fillColor.a };
CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components); CGColorRef nativeColor = CGColorCreate(CGBitmapContextGetColorSpace(_topContext), components);
CGContextSetFillColorWithColor(_context, nativeColor); CGContextSetFillColorWithColor(currentLayer()->context(), nativeColor);
CFRelease(nativeColor); CFRelease(nativeColor);
CGContextFillRect(_context, CGRectMake(rect.x, rect.y, rect.width, rect.height)); CGContextFillRect(currentLayer()->context(), CGRectMake(rect.x, rect.y, rect.width, rect.height));
} }
void CanvasImpl::setBlendMode(BlendMode blendMode) { void CoreGraphicsCanvasImpl::setBlendMode(BlendMode blendMode) {
::CGBlendMode nativeMode = kCGBlendModeNormal; ::CGBlendMode nativeMode = kCGBlendModeNormal;
switch (blendMode) { switch (blendMode) {
case BlendMode::Normal: { case BlendMode::Normal: {
@ -504,21 +505,17 @@ void CanvasImpl::setBlendMode(BlendMode blendMode) {
break; break;
} }
} }
CGContextSetBlendMode(_context, nativeMode); CGContextSetBlendMode(currentLayer()->context(), nativeMode);
} }
void CanvasImpl::setAlpha(float alpha) { void CoreGraphicsCanvasImpl::concatenate(lottie::Transform2D const &transform) {
CGContextSetAlpha(_context, alpha); CGContextConcatCTM(currentLayer()->context(), CATransform3DGetAffineTransform(nativeTransform(transform)));
} }
void CanvasImpl::concatenate(lottie::Transform2D const &transform) { std::shared_ptr<CoreGraphicsCanvasImpl::Image> CoreGraphicsCanvasImpl::makeImage() {
CGContextConcatCTM(_context, CATransform3DGetAffineTransform(nativeTransform(transform))); ::CGImageRef nativeImage = CGBitmapContextCreateImage(currentLayer()->context());
}
std::shared_ptr<CanvasImpl::Image> CanvasImpl::makeImage() const {
::CGImageRef nativeImage = CGBitmapContextCreateImage(_context);
if (nativeImage) { if (nativeImage) {
auto image = std::make_shared<CanvasImpl::Image>(nativeImage); auto image = std::make_shared<CoreGraphicsCanvasImpl::Image>(nativeImage);
CFRelease(nativeImage); CFRelease(nativeImage);
return image; return image;
} else { } else {
@ -526,15 +523,23 @@ std::shared_ptr<CanvasImpl::Image> CanvasImpl::makeImage() const {
} }
} }
void CanvasImpl::draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) { void CoreGraphicsCanvasImpl::draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) {
CanvasImpl *impl = (CanvasImpl *)other.get(); CGContextSetAlpha(currentLayer()->context(), alpha);
if (impl->_layer) { CoreGraphicsCanvasImpl *impl = (CoreGraphicsCanvasImpl *)other.get();
CGContextDrawLayerInRect(_context, CGRectMake(rect.x, rect.y, rect.width, rect.height), impl->_layer);
} else {
auto image = impl->makeImage(); auto image = impl->makeImage();
CGContextDrawImage(_context, CGRectMake(rect.x, rect.y, rect.width, rect.height), ((CanvasImpl::Image *)image.get())->nativeImage()); CGContextDrawImage(currentLayer()->context(), CGRectMake(rect.x, rect.y, rect.width, rect.height), ((CoreGraphicsCanvasImpl::Image *)image.get())->nativeImage());
CGContextSetAlpha(currentLayer()->context(), 1.0);
} }
void CoreGraphicsCanvasImpl::pushLayer(CGRect const &rect) {
} }
void CoreGraphicsCanvasImpl::popLayer() {
} }
std::shared_ptr<CoreGraphicsCanvasImpl::Layer> &CoreGraphicsCanvasImpl::currentLayer() {
return _layerStack[_layerStack.size() - 1];
}
}

View File

@ -50,8 +50,7 @@ void skPath(CanvasPathEnumerator const &enumeratePath, SkPath &nativePath) {
} }
SkiaCanvasImpl::SkiaCanvasImpl(int width, int height) : SkiaCanvasImpl::SkiaCanvasImpl(int width, int height) {
_width(width), _height(height) {
int bytesPerRow = width * 4; int bytesPerRow = width * 4;
_pixelData = malloc(bytesPerRow * height); _pixelData = malloc(bytesPerRow * height);
_ownsPixelData = true; _ownsPixelData = true;
@ -90,14 +89,6 @@ SkiaCanvasImpl::~SkiaCanvasImpl() {
} }
} }
int SkiaCanvasImpl::width() const {
return _width;
}
int SkiaCanvasImpl::height() const {
return _height;
}
std::shared_ptr<Canvas> SkiaCanvasImpl::makeLayer(int width, int height) { std::shared_ptr<Canvas> SkiaCanvasImpl::makeLayer(int width, int height) {
return std::make_shared<SkiaCanvasImpl>(width, height); return std::make_shared<SkiaCanvasImpl>(width, height);
} }
@ -113,7 +104,6 @@ void SkiaCanvasImpl::restoreState() {
void SkiaCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) { void SkiaCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) {
SkPaint paint; SkPaint paint;
paint.setColor(skColor(color)); paint.setColor(skColor(color));
paint.setAlphaf(_alpha);
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setBlendMode(_blendMode); paint.setBlendMode(_blendMode);
@ -138,7 +128,7 @@ void SkiaCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumerat
std::vector<SkColor> colors; std::vector<SkColor> colors;
for (const auto &color : gradient.colors()) { for (const auto &color : gradient.colors()) {
colors.push_back(skColor(Color(color.r, color.g, color.b, color.a * _alpha))); colors.push_back(skColor(Color(color.r, color.g, color.b, color.a)));
} }
std::vector<SkScalar> locations; std::vector<SkScalar> locations;
@ -164,7 +154,7 @@ void SkiaCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumerat
std::vector<SkColor> colors; std::vector<SkColor> colors;
for (const auto &color : gradient.colors()) { for (const auto &color : gradient.colors()) {
colors.push_back(skColor(Color(color.r, color.g, color.b, color.a * _alpha))); colors.push_back(skColor(Color(color.r, color.g, color.b, color.a)));
} }
std::vector<SkScalar> locations; std::vector<SkScalar> locations;
@ -186,7 +176,6 @@ void SkiaCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setBlendMode(_blendMode); paint.setBlendMode(_blendMode);
paint.setColor(skColor(color)); paint.setColor(skColor(color));
paint.setAlphaf(_alpha);
paint.setStyle(SkPaint::Style::kStroke_Style); paint.setStyle(SkPaint::Style::kStroke_Style);
paint.setStrokeWidth(lineWidth); paint.setStrokeWidth(lineWidth);
@ -255,7 +244,6 @@ void SkiaCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fillC
SkPaint paint; SkPaint paint;
paint.setAntiAlias(true); paint.setAntiAlias(true);
paint.setColor(skColor(fillColor)); paint.setColor(skColor(fillColor));
paint.setAlphaf(_alpha);
paint.setBlendMode(_blendMode); paint.setBlendMode(_blendMode);
_canvas->drawRect(SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), paint); _canvas->drawRect(SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), paint);
@ -282,10 +270,6 @@ void SkiaCanvasImpl::setBlendMode(BlendMode blendMode) {
} }
} }
void SkiaCanvasImpl::setAlpha(float alpha) {
_alpha = alpha;
}
void SkiaCanvasImpl::concatenate(lottie::Transform2D const &transform) { void SkiaCanvasImpl::concatenate(lottie::Transform2D const &transform) {
SkScalar m9[9] = { SkScalar m9[9] = {
transform.rows().columns[0][0], transform.rows().columns[1][0], transform.rows().columns[2][0], transform.rows().columns[0][0], transform.rows().columns[1][0], transform.rows().columns[2][0],
@ -297,12 +281,12 @@ void SkiaCanvasImpl::concatenate(lottie::Transform2D const &transform) {
_canvas->concat(matrix); _canvas->concat(matrix);
} }
void SkiaCanvasImpl::draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) { void SkiaCanvasImpl::draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) {
SkiaCanvasImpl *impl = (SkiaCanvasImpl *)other.get(); SkiaCanvasImpl *impl = (SkiaCanvasImpl *)other.get();
auto image = impl->surface()->makeImageSnapshot(); auto image = impl->surface()->makeImageSnapshot();
SkPaint paint; SkPaint paint;
paint.setBlendMode(_blendMode); paint.setBlendMode(_blendMode);
paint.setAlphaf(_alpha); paint.setAlphaf(alpha);
_canvas->drawImageRect(image.get(), SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), SkSamplingOptions(SkFilterMode::kLinear), &paint); _canvas->drawImageRect(image.get(), SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), SkSamplingOptions(SkFilterMode::kLinear), &paint);
} }

View File

@ -14,9 +14,6 @@ public:
SkiaCanvasImpl(int width, int height, int bytesPerRow, void *pixelData); SkiaCanvasImpl(int width, int height, int bytesPerRow, void *pixelData);
virtual ~SkiaCanvasImpl(); virtual ~SkiaCanvasImpl();
virtual int width() const override;
virtual int height() const override;
virtual std::shared_ptr<Canvas> makeLayer(int width, int height) override; virtual std::shared_ptr<Canvas> makeLayer(int width, int height) override;
virtual void saveState() override; virtual void saveState() override;
@ -32,11 +29,9 @@ public:
virtual void setBlendMode(BlendMode blendMode) override; virtual void setBlendMode(BlendMode blendMode) override;
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform2D const &transform) override; virtual void concatenate(lottie::Transform2D const &transform) override;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override; virtual void draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) override;
void flush(); void flush();
sk_sp<SkSurface> surface() const; sk_sp<SkSurface> surface() const;
@ -44,12 +39,9 @@ public:
private: private:
void *_pixelData = nullptr; void *_pixelData = nullptr;
bool _ownsPixelData = false; bool _ownsPixelData = false;
int _width = 0;
int _height = 0;
sk_sp<SkSurface> _surface; sk_sp<SkSurface> _surface;
SkCanvas *_canvas = nullptr; SkCanvas *_canvas = nullptr;
SkBlendMode _blendMode = SkBlendMode::kSrcOver; SkBlendMode _blendMode = SkBlendMode::kSrcOver;
double _alpha = 1.0;
}; };
} }

View File

@ -64,13 +64,13 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
} }
if (useReferenceRendering) { if (useReferenceRendering) {
auto context = std::make_shared<lottie::CanvasImpl>((int)size.width, (int)size.height); auto context = std::make_shared<lottie::CoreGraphicsCanvasImpl>((int)size.width, (int)size.height);
_canvasRenderer->render(_renderer, context, lottie::Vector2D(size.width, size.height)); _canvasRenderer->render(_renderer, context, lottie::Vector2D(size.width, size.height));
auto image = context->makeImage(); auto image = context->makeImage();
return [[UIImage alloc] initWithCGImage:std::static_pointer_cast<lottie::CanvasImpl::Image>(image)->nativeImage()]; return [[UIImage alloc] initWithCGImage:std::static_pointer_cast<lottie::CoreGraphicsCanvasImpl::Image>(image)->nativeImage()];
} else { } else {
if ((int64_t)"" > 0) { if ((int64_t)"" > 0) {
/*auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul((int)size.width, (int)size.height)); /*auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul((int)size.width, (int)size.height));

View File

@ -49,7 +49,7 @@ void ThorVGCanvasImpl::initializeOnce() {
} }
ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height, int bytesPerRow) : ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height, int bytesPerRow) :
_width(width), _height(height), _transform(lottie::Transform2D::identity()) { _transform(lottie::Transform2D::identity()) {
_canvas = tvg::SwCanvas::gen(); _canvas = tvg::SwCanvas::gen();
_bytesPerRow = bytesPerRow; _bytesPerRow = bytesPerRow;
@ -63,14 +63,6 @@ _width(width), _height(height), _transform(lottie::Transform2D::identity()) {
ThorVGCanvasImpl::~ThorVGCanvasImpl() { ThorVGCanvasImpl::~ThorVGCanvasImpl() {
} }
int ThorVGCanvasImpl::width() const {
return _width;
}
int ThorVGCanvasImpl::height() const {
return _height;
}
std::shared_ptr<Canvas> ThorVGCanvasImpl::makeLayer(int width, int height) { std::shared_ptr<Canvas> ThorVGCanvasImpl::makeLayer(int width, int height) {
return std::make_shared<ThorVGCanvasImpl>(width, height, width * 4); return std::make_shared<ThorVGCanvasImpl>(width, height, width * 4);
} }
@ -94,7 +86,7 @@ void ThorVGCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lotti
shape->transform(tvgTransform(_transform)); 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((int)(color.r * 255.0), (int)(color.g * 255.0), (int)(color.b * 255.0), (int)(color.a * 255.0));
shape->fill(fillRule == lottie::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)); _canvas->push(std::move(shape));
@ -117,7 +109,7 @@ void ThorVGCanvasImpl::linearGradientFillPath(CanvasPathEnumerator const &enumer
colorStop.r = (int)(color.r * 255.0); colorStop.r = (int)(color.r * 255.0);
colorStop.g = (int)(color.g * 255.0); colorStop.g = (int)(color.g * 255.0);
colorStop.b = (int)(color.b * 255.0); colorStop.b = (int)(color.b * 255.0);
colorStop.a = (int)(color.a * _alpha * 255.0); colorStop.a = (int)(color.a * 255.0);
colors.push_back(colorStop); colors.push_back(colorStop);
} }
fill->colorStops(colors.data(), (uint32_t)colors.size()); fill->colorStops(colors.data(), (uint32_t)colors.size());
@ -145,7 +137,7 @@ void ThorVGCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumer
colorStop.r = (int)(color.r * 255.0); colorStop.r = (int)(color.r * 255.0);
colorStop.g = (int)(color.g * 255.0); colorStop.g = (int)(color.g * 255.0);
colorStop.b = (int)(color.b * 255.0); colorStop.b = (int)(color.b * 255.0);
colorStop.a = (int)(color.a * _alpha * 255.0); colorStop.a = (int)(color.a * 255.0);
colors.push_back(colorStop); colors.push_back(colorStop);
} }
fill->colorStops(colors.data(), (uint32_t)colors.size()); fill->colorStops(colors.data(), (uint32_t)colors.size());
@ -162,7 +154,7 @@ void ThorVGCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, flo
shape->transform(tvgTransform(_transform)); shape->transform(tvgTransform(_transform));
shape->strokeFill((int)(color.r * 255.0), (int)(color.g * 255.0), (int)(color.b * 255.0), (int)(color.a * _alpha * 255.0)); shape->strokeFill((int)(color.r * 255.0), (int)(color.g * 255.0), (int)(color.b * 255.0), (int)(color.a * 255.0));
shape->strokeWidth(lineWidth); shape->strokeWidth(lineWidth);
switch (lineJoin) { switch (lineJoin) {
@ -228,7 +220,7 @@ void ThorVGCanvasImpl::fill(lottie::CGRect const &rect, lottie::Color const &fil
shape->transform(tvgTransform(_transform)); shape->transform(tvgTransform(_transform));
shape->fill((int)(fillColor.r * 255.0), (int)(fillColor.g * 255.0), (int)(fillColor.b * 255.0), (int)(fillColor.a * _alpha * 255.0)); shape->fill((int)(fillColor.r * 255.0), (int)(fillColor.g * 255.0), (int)(fillColor.b * 255.0), (int)(fillColor.a * 255.0));
_canvas->push(std::move(shape)); _canvas->push(std::move(shape));
} }
@ -254,10 +246,6 @@ void ThorVGCanvasImpl::setBlendMode(BlendMode blendMode) {
}*/ }*/
} }
void ThorVGCanvasImpl::setAlpha(float alpha) {
_alpha = alpha;
}
void ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) { void ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) {
_transform = transform * _transform; _transform = transform * _transform;
/*_canvas->concat(SkM44( /*_canvas->concat(SkM44(
@ -268,7 +256,7 @@ void ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) {
));*/ ));*/
} }
void ThorVGCanvasImpl::draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) { void ThorVGCanvasImpl::draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) {
/*ThorVGCanvasImpl *impl = (ThorVGCanvasImpl *)other.get(); /*ThorVGCanvasImpl *impl = (ThorVGCanvasImpl *)other.get();
auto image = impl->surface()->makeImageSnapshot(); auto image = impl->surface()->makeImageSnapshot();
SkPaint paint; SkPaint paint;

View File

@ -14,9 +14,6 @@ public:
ThorVGCanvasImpl(int width, int height, int bytesPerRow); ThorVGCanvasImpl(int width, int height, int bytesPerRow);
virtual ~ThorVGCanvasImpl(); virtual ~ThorVGCanvasImpl();
virtual int width() const override;
virtual int height() const override;
virtual std::shared_ptr<Canvas> makeLayer(int width, int height) override; virtual std::shared_ptr<Canvas> makeLayer(int width, int height) override;
virtual void saveState() override; virtual void saveState() override;
@ -32,11 +29,9 @@ public:
virtual void setBlendMode(BlendMode blendMode) override; virtual void setBlendMode(BlendMode blendMode) override;
virtual void setAlpha(float alpha) override;
virtual void concatenate(lottie::Transform2D const &transform) override; virtual void concatenate(lottie::Transform2D const &transform) override;
virtual void draw(std::shared_ptr<Canvas> const &other, lottie::CGRect const &rect) override; virtual void draw(std::shared_ptr<Canvas> const &other, float alpha, lottie::CGRect const &rect) override;
uint32_t *backingData() { uint32_t *backingData() {
return _backingData; return _backingData;
@ -49,11 +44,8 @@ public:
void flush(); void flush();
private: private:
int _width = 0;
int _height = 0;
std::unique_ptr<tvg::SwCanvas> _canvas; std::unique_ptr<tvg::SwCanvas> _canvas;
float _alpha = 1.0;
lottie::Transform2D _transform; lottie::Transform2D _transform;
std::vector<lottie::Transform2D> _stateStack; std::vector<lottie::Transform2D> _stateStack;
int _bytesPerRow = 0; int _bytesPerRow = 0;

View File

@ -119,9 +119,9 @@ public final class ViewController: UIViewController {
self.view.layer.addSublayer(MetalEngine.shared.rootLayer) self.view.layer.addSublayer(MetalEngine.shared.rootLayer)
if !"".isEmpty { if "".isEmpty {
if #available(iOS 13.0, *) { if #available(iOS 13.0, *) {
self.test = ReferenceCompareTest(view: self.view, testNonReference: true) self.test = ReferenceCompareTest(view: self.view, testNonReference: false)
} }
} else if !"".isEmpty { } else if !"".isEmpty {
/*let cachedAnimation = cacheLottieMetalAnimation(path: filePath)! /*let cachedAnimation = cacheLottieMetalAnimation(path: filePath)!