mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-15 13:35:19 +00:00
Lottie experiments
This commit is contained in:
parent
2f177e9a48
commit
b6781e1905
@ -37,6 +37,7 @@ public:
|
||||
virtual void 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) override;
|
||||
|
||||
virtual void clip(CGRect const &rect) override;
|
||||
virtual bool clipPath(CanvasPathEnumerator const &enumeratePath, FillRule fillRule, Transform2D const &transform) override;
|
||||
virtual void concatenate(lottie::Transform2D const &transform) override;
|
||||
|
||||
virtual std::shared_ptr<Image> makeImage();
|
||||
|
@ -503,6 +503,29 @@ void CoreGraphicsCanvasImpl::clip(CGRect const &rect) {
|
||||
CGContextClipToRect(currentLayer()->context(), CGRectMake(rect.x, rect.y, rect.width, rect.height));
|
||||
}
|
||||
|
||||
bool CoreGraphicsCanvasImpl::clipPath(CanvasPathEnumerator const &enumeratePath, FillRule fillRule, Transform2D const &transform) {
|
||||
CGContextSaveGState(currentLayer()->context());
|
||||
concatenate(transform);
|
||||
|
||||
if (!addEnumeratedPath(currentLayer()->context(), enumeratePath)) {
|
||||
CGContextRestoreGState(currentLayer()->context());
|
||||
return false;
|
||||
}
|
||||
CGContextRestoreGState(currentLayer()->context());
|
||||
switch (fillRule) {
|
||||
case lottie::FillRule::EvenOdd: {
|
||||
CGContextEOClip(currentLayer()->context());
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
CGContextClip(currentLayer()->context());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CoreGraphicsCanvasImpl::concatenate(lottie::Transform2D const &transform) {
|
||||
CGContextConcatCTM(currentLayer()->context(), CATransform3DGetAffineTransform(nativeTransform(transform)));
|
||||
}
|
||||
|
@ -50,6 +50,17 @@ void skPath(CanvasPathEnumerator const &enumeratePath, SkPath &nativePath) {
|
||||
});
|
||||
}
|
||||
|
||||
SkMatrix skMatrix(Transform2D const &transform) {
|
||||
SkScalar m9[9] = {
|
||||
transform.rows().columns[0][0], transform.rows().columns[1][0], transform.rows().columns[2][0],
|
||||
transform.rows().columns[0][1], transform.rows().columns[1][1], transform.rows().columns[2][1],
|
||||
transform.rows().columns[0][2], transform.rows().columns[1][2], transform.rows().columns[2][2]
|
||||
};
|
||||
SkMatrix matrix;
|
||||
matrix.set9(m9);
|
||||
return matrix;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SkiaCanvasImpl::SkiaCanvasImpl(int width, int height) {
|
||||
@ -243,15 +254,20 @@ void SkiaCanvasImpl::clip(CGRect const &rect) {
|
||||
_canvas->clipRect(SkRect::MakeXYWH(rect.x, rect.y, rect.width, rect.height), true);
|
||||
}
|
||||
|
||||
bool SkiaCanvasImpl::clipPath(CanvasPathEnumerator const &enumeratePath, FillRule fillRule, Transform2D const &transform) {
|
||||
SkPath nativePath;
|
||||
skPath(enumeratePath, nativePath);
|
||||
nativePath.setFillType(fillRule == FillRule::EvenOdd ? SkPathFillType::kEvenOdd : SkPathFillType::kWinding);
|
||||
if (!transform.isIdentity()) {
|
||||
nativePath.transform(skMatrix(transform));
|
||||
}
|
||||
_canvas->clipPath(nativePath, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SkiaCanvasImpl::concatenate(lottie::Transform2D const &transform) {
|
||||
SkScalar m9[9] = {
|
||||
transform.rows().columns[0][0], transform.rows().columns[1][0], transform.rows().columns[2][0],
|
||||
transform.rows().columns[0][1], transform.rows().columns[1][1], transform.rows().columns[2][1],
|
||||
transform.rows().columns[0][2], transform.rows().columns[1][2], transform.rows().columns[2][2]
|
||||
};
|
||||
SkMatrix matrix;
|
||||
matrix.set9(m9);
|
||||
_canvas->concat(matrix);
|
||||
_canvas->concat(skMatrix(transform));
|
||||
}
|
||||
|
||||
bool SkiaCanvasImpl::pushLayer(CGRect const &rect, float alpha, std::optional<MaskMode> maskMode) {
|
||||
|
@ -25,6 +25,7 @@ public:
|
||||
virtual void 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) override;
|
||||
|
||||
virtual void clip(CGRect const &rect) override;
|
||||
virtual bool clipPath(CanvasPathEnumerator const &enumeratePath, FillRule fillRule, Transform2D const &transform) override;
|
||||
virtual void concatenate(lottie::Transform2D const &transform) override;
|
||||
|
||||
virtual bool pushLayer(CGRect const &rect, float alpha, std::optional<MaskMode> maskMode) override;
|
||||
|
@ -4,7 +4,6 @@
|
||||
#import <LottieCpp/NullCanvasImpl.h>
|
||||
|
||||
#import "CoreGraphicsCanvasImpl.h"
|
||||
#import "ThorVGCanvasImpl.h"
|
||||
#import "SkiaCanvasImpl.h"
|
||||
|
||||
#include <LottieCpp/RenderTreeNode.h>
|
||||
@ -113,32 +112,6 @@ CGRect getPathNativeBoundingBox(CGPathRef _Nonnull path) {
|
||||
|
||||
return image;
|
||||
}
|
||||
} else if ((int64_t)"" < 0) {
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
lottie::ThorVGCanvasImpl::initializeOnce();
|
||||
});
|
||||
|
||||
int bytesPerRow = ((int)size.width) * 4;
|
||||
auto context = std::make_shared<lottie::ThorVGCanvasImpl>((int)size.width, (int)size.height, bytesPerRow);
|
||||
|
||||
_canvasRenderer->render(_renderer, context, lottie::Vector2D(size.width, size.height), configuration);
|
||||
|
||||
context->flush();
|
||||
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
|
||||
|
||||
CGContextRef targetContext = CGBitmapContextCreate((void *)context->backingData(), (int)size.width, (int)size.height, 8, bytesPerRow, colorSpace, bitmapInfo);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
|
||||
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:1.0f orientation:UIImageOrientationDownMirrored];
|
||||
CGImageRelease(bitmapImage);
|
||||
|
||||
CGContextRelease(targetContext);
|
||||
|
||||
return image;
|
||||
} else {
|
||||
auto context = std::make_shared<lottie::NullCanvasImpl>((int)size.width, (int)size.height);
|
||||
_canvasRenderer->render(_renderer, context, lottie::Vector2D(size.width, size.height), configuration);
|
||||
|
@ -1,224 +0,0 @@
|
||||
#include "ThorVGCanvasImpl.h"
|
||||
|
||||
namespace lottie {
|
||||
|
||||
namespace {
|
||||
|
||||
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 PathCommandType::LineTo: {
|
||||
shape->lineTo(command.points[0].x, command.points[0].y);
|
||||
break;
|
||||
}
|
||||
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 PathCommandType::Close: {
|
||||
shape->close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tvg::Matrix tvgTransform(lottie::Transform2D const &transform) {
|
||||
tvg::Matrix result;
|
||||
result.e11 = transform.rows().columns[0][0];
|
||||
result.e21 = transform.rows().columns[0][1];
|
||||
result.e31 = transform.rows().columns[0][2];
|
||||
result.e12 = transform.rows().columns[1][0];
|
||||
result.e22 = transform.rows().columns[1][1];
|
||||
result.e32 = transform.rows().columns[1][2];
|
||||
result.e13 = transform.rows().columns[2][0];
|
||||
result.e23 = transform.rows().columns[2][1];
|
||||
result.e33 = transform.rows().columns[2][2];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::initializeOnce() {
|
||||
tvg::Initializer::init(0);
|
||||
}
|
||||
|
||||
ThorVGCanvasImpl::ThorVGCanvasImpl(int width, int height, int bytesPerRow) :
|
||||
_transform(lottie::Transform2D::identity()) {
|
||||
_canvas = tvg::SwCanvas::gen();
|
||||
|
||||
_bytesPerRow = bytesPerRow;
|
||||
|
||||
_backingData = (uint32_t *)malloc(_bytesPerRow * height);
|
||||
memset(_backingData, 0, _bytesPerRow * height);
|
||||
|
||||
_canvas->target(_backingData, _bytesPerRow / 4, width, height, tvg::SwCanvas::ARGB8888);
|
||||
}
|
||||
|
||||
ThorVGCanvasImpl::~ThorVGCanvasImpl() {
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::saveState() {
|
||||
_stateStack.push_back(_transform);
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::restoreState() {
|
||||
if (_stateStack.empty()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
_transform = _stateStack[_stateStack.size() - 1];
|
||||
_stateStack.pop_back();
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::fillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Color const &color) {
|
||||
auto shape = tvg::Shape::gen();
|
||||
tvgPath(enumeratePath, 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 * 255.0));
|
||||
shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding);
|
||||
|
||||
_canvas->push(std::move(shape));
|
||||
}
|
||||
|
||||
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(enumeratePath, shape.get());
|
||||
|
||||
shape->transform(tvgTransform(_transform));
|
||||
|
||||
auto fill = tvg::LinearGradient::gen();
|
||||
fill->linear(start.x, start.y, end.x, end.y);
|
||||
|
||||
std::vector<tvg::Fill::ColorStop> colors;
|
||||
for (size_t i = 0; i < gradient.colors().size(); i++) {
|
||||
const auto &color = gradient.colors()[i];
|
||||
tvg::Fill::ColorStop colorStop;
|
||||
colorStop.offset = gradient.locations()[i];
|
||||
colorStop.r = (int)(color.r * 255.0);
|
||||
colorStop.g = (int)(color.g * 255.0);
|
||||
colorStop.b = (int)(color.b * 255.0);
|
||||
colorStop.a = (int)(color.a * 255.0);
|
||||
colors.push_back(colorStop);
|
||||
}
|
||||
fill->colorStops(colors.data(), (uint32_t)colors.size());
|
||||
shape->fill(std::move(fill));
|
||||
|
||||
shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding);
|
||||
|
||||
_canvas->push(std::move(shape));
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, Gradient const &gradient, Vector2D const ¢er, float radius) {
|
||||
auto shape = tvg::Shape::gen();
|
||||
tvgPath(enumeratePath, shape.get());
|
||||
|
||||
shape->transform(tvgTransform(_transform));
|
||||
|
||||
auto fill = tvg::RadialGradient::gen();
|
||||
fill->radial(center.x, center.y, radius);
|
||||
|
||||
std::vector<tvg::Fill::ColorStop> colors;
|
||||
for (size_t i = 0; i < gradient.colors().size(); i++) {
|
||||
const auto &color = gradient.colors()[i];
|
||||
tvg::Fill::ColorStop colorStop;
|
||||
colorStop.offset = gradient.locations()[i];
|
||||
colorStop.r = (int)(color.r * 255.0);
|
||||
colorStop.g = (int)(color.g * 255.0);
|
||||
colorStop.b = (int)(color.b * 255.0);
|
||||
colorStop.a = (int)(color.a * 255.0);
|
||||
colors.push_back(colorStop);
|
||||
}
|
||||
fill->colorStops(colors.data(), (uint32_t)colors.size());
|
||||
shape->fill(std::move(fill));
|
||||
|
||||
shape->fill(fillRule == lottie::FillRule::EvenOdd ? tvg::FillRule::EvenOdd : tvg::FillRule::Winding);
|
||||
|
||||
_canvas->push(std::move(shape));
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> const &dashPattern, lottie::Color const &color) {
|
||||
auto shape = tvg::Shape::gen();
|
||||
tvgPath(enumeratePath, shape.get());
|
||||
|
||||
shape->transform(tvgTransform(_transform));
|
||||
|
||||
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);
|
||||
|
||||
switch (lineJoin) {
|
||||
case lottie::LineJoin::Miter: {
|
||||
shape->strokeJoin(tvg::StrokeJoin::Miter);
|
||||
break;
|
||||
}
|
||||
case lottie::LineJoin::Round: {
|
||||
shape->strokeJoin(tvg::StrokeJoin::Round);
|
||||
break;
|
||||
}
|
||||
case lottie::LineJoin::Bevel: {
|
||||
shape->strokeJoin(tvg::StrokeJoin::Bevel);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
shape->strokeJoin(tvg::StrokeJoin::Bevel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (lineCap) {
|
||||
case lottie::LineCap::Butt: {
|
||||
shape->strokeCap(tvg::StrokeCap::Butt);
|
||||
break;
|
||||
}
|
||||
case lottie::LineCap::Round: {
|
||||
shape->strokeCap(tvg::StrokeCap::Round);
|
||||
break;
|
||||
}
|
||||
case lottie::LineCap::Square: {
|
||||
shape->strokeCap(tvg::StrokeCap::Square);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
shape->strokeCap(tvg::StrokeCap::Square);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dashPattern.empty()) {
|
||||
std::vector<float> intervals;
|
||||
intervals.reserve(dashPattern.size());
|
||||
for (auto value : dashPattern) {
|
||||
intervals.push_back(value);
|
||||
}
|
||||
shape->strokeDash(intervals.data(), (uint32_t)intervals.size());
|
||||
//TODO:phase
|
||||
}
|
||||
|
||||
_canvas->push(std::move(shape));
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::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 ThorVGCanvasImpl::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 ThorVGCanvasImpl::concatenate(lottie::Transform2D const &transform) {
|
||||
_transform = transform * _transform;
|
||||
}
|
||||
|
||||
void ThorVGCanvasImpl::flush() {
|
||||
_canvas->draw();
|
||||
_canvas->sync();
|
||||
|
||||
_statsNumStrokes = 0;
|
||||
}
|
||||
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
#ifndef ThorVGCanvasImpl_h
|
||||
#define ThorVGCanvasImpl_h
|
||||
|
||||
#include <LottieCpp/LottieCpp.h>
|
||||
|
||||
#include <thorvg/thorvg.h>
|
||||
|
||||
namespace lottie {
|
||||
|
||||
class ThorVGCanvasImpl: public Canvas {
|
||||
public:
|
||||
static void initializeOnce();
|
||||
|
||||
ThorVGCanvasImpl(int width, int height, int bytesPerRow);
|
||||
virtual ~ThorVGCanvasImpl();
|
||||
|
||||
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, lottie::Gradient const &gradient, lottie::Vector2D const &start, lottie::Vector2D const &end) override;
|
||||
virtual void radialGradientFillPath(CanvasPathEnumerator const &enumeratePath, lottie::FillRule fillRule, lottie::Gradient const &gradient, Vector2D const ¢er, float radius) override;
|
||||
virtual void strokePath(CanvasPathEnumerator const &enumeratePath, float lineWidth, lottie::LineJoin lineJoin, lottie::LineCap lineCap, float dashPhase, std::vector<float> 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<float> 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<float> const &dashPattern, Gradient const &gradient, lottie::Vector2D const &startCenter, float startRadius, lottie::Vector2D const &endCenter, float endRadius) override;
|
||||
|
||||
virtual void concatenate(lottie::Transform2D const &transform) override;
|
||||
|
||||
uint32_t *backingData() {
|
||||
return _backingData;
|
||||
}
|
||||
|
||||
int bytesPerRow() const {
|
||||
return _bytesPerRow;
|
||||
}
|
||||
|
||||
void flush();
|
||||
|
||||
private:
|
||||
std::unique_ptr<tvg::SwCanvas> _canvas;
|
||||
|
||||
lottie::Transform2D _transform;
|
||||
std::vector<lottie::Transform2D> _stateStack;
|
||||
int _bytesPerRow = 0;
|
||||
uint32_t *_backingData = nullptr;
|
||||
int _statsNumStrokes = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -88,7 +88,7 @@ private final class ReferenceCompareTest {
|
||||
}
|
||||
|
||||
var continueFromName: String?
|
||||
//continueFromName = "1258816259754165.json"
|
||||
//continueFromName = "562563904580878375.json"
|
||||
|
||||
let _ = await processAnimationFolderAsync(basePath: bundlePath, path: "", stopOnFailure: !testNonReference, process: { path, name, alwaysDraw in
|
||||
if let continueFromNameValue = continueFromName {
|
||||
@ -152,6 +152,10 @@ private final class ManualReferenceCompareTest {
|
||||
let bundlePath = Bundle.main.path(forResource: "TestDataBundle", ofType: "bundle")!
|
||||
self.fileList = buildAnimationFolderItems(basePath: bundlePath, path: "")
|
||||
|
||||
if let index = self.fileList.firstIndex(where: { $0.fileName == "shit.json" }) {
|
||||
self.currentFileIndex = index
|
||||
}
|
||||
|
||||
self.renderSize = CGSize(width: 256.0, height: 256.0)
|
||||
|
||||
self.view = view
|
||||
|
BIN
Tests/LottieMetalTest/skia/device/libskia.framework/Info.plist
Normal file
BIN
Tests/LottieMetalTest/skia/device/libskia.framework/Info.plist
Normal file
Binary file not shown.
BIN
Tests/LottieMetalTest/skia/device/libskia.framework/libskia
Executable file
BIN
Tests/LottieMetalTest/skia/device/libskia.framework/libskia
Executable file
Binary file not shown.
@ -1 +1 @@
|
||||
Subproject commit 2b10b84f626c66288366f0ad2d6af2a396ec454e
|
||||
Subproject commit b885e63e766890d1cbf36b66cfe27cca55a6ec90
|
@ -4141,7 +4141,7 @@ public final class ChatHistoryListNodeImpl: ListView, ChatHistoryNode, ChatHisto
|
||||
}
|
||||
} else if self.interactiveReadActionDisposable == nil {
|
||||
if case let .peer(peerId) = self.chatLocation {
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory {
|
||||
if !self.context.sharedContext.immediateExperimentalUISettings.skipReadHistory && !self.context.account.isSupportUser {
|
||||
self.interactiveReadActionDisposable = self.context.engine.messages.installInteractiveReadMessagesAction(peerId: peerId)
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user