Lottie experiments

This commit is contained in:
Isaac 2024-06-17 14:52:13 +04:00
parent 2f177e9a48
commit b6781e1905
12 changed files with 56 additions and 313 deletions

View File

@ -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();

View File

@ -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)));
}

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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 &center, 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;
}
}

View File

@ -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 &center, 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

View File

@ -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

Binary file not shown.

@ -1 +1 @@
Subproject commit 2b10b84f626c66288366f0ad2d6af2a396ec454e
Subproject commit b885e63e766890d1cbf36b66cfe27cca55a6ec90

View File

@ -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)
}
}