mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Optimize bezier tesselation
This commit is contained in:
parent
770afde804
commit
571fb8f033
@ -2,11 +2,14 @@ import Foundation
|
||||
import UIKit
|
||||
|
||||
import LottieMeshSwift
|
||||
import Postbox
|
||||
|
||||
public final class ViewController: UIViewController {
|
||||
override public func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
TempBox.initializeShared(basePath: NSTemporaryDirectory(), processType: "test", launchSpecificId: Int64.random(in: Int64.min ..< Int64.max))
|
||||
|
||||
self.view.backgroundColor = .black
|
||||
|
||||
let path = Bundle.main.path(forResource: "Fireworks", ofType: "json")!
|
||||
@ -16,7 +19,12 @@ public final class ViewController: UIViewController {
|
||||
}*/
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
let animation = generateMeshAnimation(data: try! Data(contentsOf: URL(fileURLWithPath: path)))!
|
||||
let startTime = CFAbsoluteTimeGetCurrent()
|
||||
let animationFile = generateMeshAnimation(data: try! Data(contentsOf: URL(fileURLWithPath: path)))!
|
||||
print("Time: \((CFAbsoluteTimeGetCurrent() - startTime) * 1000.0)")
|
||||
let buffer = MeshReadBuffer(data: try! Data(contentsOf: URL(fileURLWithPath: animationFile.path)))
|
||||
let animation = MeshAnimation.read(buffer: buffer)
|
||||
|
||||
let renderer = MeshRenderer(wireframe: true)!
|
||||
|
||||
renderer.frame = CGRect(origin: CGPoint(x: 0.0, y: 50.0), size: CGSize(width: 300.0, height: 300.0))
|
||||
|
@ -34,7 +34,7 @@ typedef NS_CLOSED_ENUM(NSInteger, LottieMeshFillRule) {
|
||||
- (void)getVertexAt:(NSInteger)index x:(float * _Nullable)x y:(float * _Nullable)y;
|
||||
|
||||
- (NSInteger)triangleCount;
|
||||
- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2;
|
||||
- (void * _Nonnull)getTriangles;
|
||||
|
||||
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill:(LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke;
|
||||
|
||||
|
@ -10,12 +10,6 @@ MeshGenerator::Point bezierQuadraticPointAt(MeshGenerator::Point const &p0, Mesh
|
||||
return MeshGenerator::Point(x, y);
|
||||
}
|
||||
|
||||
MeshGenerator::Point bezierQubicPointAt(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3, float t) {
|
||||
float x = powf((1.0 - t), 3.0) * p0.x + 3.0 * powf((1.0 - t), 2.0) * t * p1.x + 3.0 * (1.0 - t) * powf(t, 2.0) * p2.x + powf(t, 3.0) * p3.x;
|
||||
float y = powf((1.0 - t), 3.0) * p0.y + 3.0 * powf((1.0 - t), 2.0) * t * p1.y + 3.0 * (1.0 - t) * powf(t, 2.0) * p2.y + powf(t, 3.0) * p3.y;
|
||||
return MeshGenerator::Point(x, y);
|
||||
}
|
||||
|
||||
float approximateBezierQuadraticLength(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2) {
|
||||
float length = 0.0f;
|
||||
float t = 0.1;
|
||||
@ -29,17 +23,51 @@ float approximateBezierQuadraticLength(MeshGenerator::Point const &p0, MeshGener
|
||||
return length;
|
||||
}
|
||||
|
||||
float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3) {
|
||||
float length = 0.0f;
|
||||
float t = 0.1;
|
||||
MeshGenerator::Point last = p0;
|
||||
while (t < 1.01) {
|
||||
auto point = bezierQubicPointAt(p0, p1, p2, p3, t);
|
||||
length += last.distance(point);
|
||||
last = point;
|
||||
t += 0.1;
|
||||
void tesselateBezier(MeshGenerator::Path &path, MeshGenerator::Point const &p1, MeshGenerator::Point const &p2, MeshGenerator::Point const &p3, MeshGenerator::Point const &p4, int level) {
|
||||
const float tessTol = 0.25f / 0.5f;
|
||||
|
||||
float x1 = p1.x;
|
||||
float y1 = p1.y;
|
||||
float x2 = p2.x;
|
||||
float y2 = p2.y;
|
||||
float x3 = p3.x;
|
||||
float y3 = p3.y;
|
||||
float x4 = p4.x;
|
||||
float y4 = p4.y;
|
||||
|
||||
float x12, y12, x23, y23, x34, y34, x123, y123, x234, y234, x1234, y1234;
|
||||
float dx, dy, d2, d3;
|
||||
|
||||
if (level > 10) {
|
||||
return;
|
||||
}
|
||||
return length;
|
||||
|
||||
x12 = (x1 + x2) * 0.5f;
|
||||
y12 = (y1 + y2) * 0.5f;
|
||||
x23 = (x2 + x3) * 0.5f;
|
||||
y23 = (y2 + y3) * 0.5f;
|
||||
x34 = (x3 + x4) * 0.5f;
|
||||
y34 = (y3 + y4) * 0.5f;
|
||||
x123 = (x12 + x23) * 0.5f;
|
||||
y123 = (y12 + y23) * 0.5f;
|
||||
|
||||
dx = x4 - x1;
|
||||
dy = y4 - y1;
|
||||
d2 = std::abs(((x2 - x4) * dy - (y2 - y4) * dx));
|
||||
d3 = std::abs(((x3 - x4) * dy - (y3 - y4) * dx));
|
||||
|
||||
if ((d2 + d3) * (d2 + d3) < tessTol * (dx * dx + dy * dy)) {
|
||||
path.points.emplace_back(x4, y4);
|
||||
return;
|
||||
}
|
||||
|
||||
x234 = (x23+x34) * 0.5f;
|
||||
y234 = (y23+y34) * 0.5f;
|
||||
x1234 = (x123 + x234) * 0.5f;
|
||||
y1234 = (y123 + y234) * 0.5f;
|
||||
|
||||
tesselateBezier(path, MeshGenerator::Point(x1, y1), MeshGenerator::Point(x12, y12), MeshGenerator::Point(x123, y123), MeshGenerator::Point(x1234, y1234), level + 1);
|
||||
tesselateBezier(path, MeshGenerator::Point(x1234, y1234), MeshGenerator::Point(x234, y234), MeshGenerator::Point(x34, y34), MeshGenerator::Point(x4, y4), level + 1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -80,7 +108,11 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
||||
return (NSInteger)(_mesh->triangles.size() / 3);
|
||||
}
|
||||
|
||||
- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2 {
|
||||
- (void * _Nonnull)getTriangles {
|
||||
return _mesh->triangles.data();
|
||||
}
|
||||
|
||||
/*- (void)getTriangleAt:(NSInteger)index v0:(NSInteger * _Nullable)v0 v1:(NSInteger * _Nullable)v1 v2:(NSInteger * _Nullable)v2 {
|
||||
if (v0) {
|
||||
*v0 = (NSInteger)_mesh->triangles[index * 3 + 0];
|
||||
}
|
||||
@ -90,7 +122,7 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
||||
if (v2) {
|
||||
*v2 = (NSInteger)_mesh->triangles[index * 3 + 2];
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
+ (LottieMeshData * _Nullable)generateWithPath:(UIBezierPath * _Nonnull)path fill: (LottieMeshFill * _Nullable)fill stroke:(LottieMeshStroke * _Nullable)stroke {
|
||||
float scale = 1.0f;
|
||||
@ -173,14 +205,8 @@ float approximateBezierQubicLength(MeshGenerator::Point const &p0, MeshGenerator
|
||||
MeshGenerator::Point p1(element->points[0].x * scale, element->points[0].y * scale);
|
||||
MeshGenerator::Point p2(element->points[1].x * scale, element->points[1].y * scale);
|
||||
MeshGenerator::Point p3(element->points[2].x * scale, element->points[2].y * scale);
|
||||
|
||||
float step = 10.0f * flatness / approximateBezierQubicLength(p0, p1, p2, p3);
|
||||
while (t < 1.0f) {
|
||||
auto point = bezierQubicPointAt(p0, p1, p2, p3, t);
|
||||
paths[paths.size() - 1].points.push_back(point);
|
||||
t += step;
|
||||
}
|
||||
paths[paths.size() - 1].points.push_back(p3);
|
||||
|
||||
tesselateBezier(paths[paths.size() - 1], p0, p1, p2, p3, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -573,13 +573,13 @@ public final class MeshRenderer: MTKView {
|
||||
}
|
||||
}
|
||||
|
||||
private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloat, superTransform: CGAffineTransform, writeSegment: (MeshAnimation.Frame.Segment) -> Void) {
|
||||
private func generateSegments(writeBuffer: MeshWriteBuffer, segmentCount: inout Int, geometry: CapturedGeometryNode, superAlpha: CGFloat, superTransform: CGAffineTransform) {
|
||||
if geometry.isHidden || geometry.alpha.isZero {
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0 ..< geometry.subnodes.count {
|
||||
generateSegments(geometry: geometry.subnodes[i], superAlpha: superAlpha * geometry.alpha, superTransform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform), writeSegment: writeSegment)
|
||||
generateSegments(writeBuffer: writeBuffer, segmentCount: &segmentCount, geometry: geometry.subnodes[i], superAlpha: superAlpha * geometry.alpha, superTransform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform))
|
||||
}
|
||||
|
||||
if let displayItem = geometry.displayItem {
|
||||
@ -617,17 +617,6 @@ private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloa
|
||||
meshData = LottieMeshData.generate(with: UIBezierPath(cgPath: displayItem.path), fill: nil, stroke: LottieMeshStroke(lineWidth: stroke.lineWidth, lineJoin: stroke.lineJoin, lineCap: stroke.lineCap, miterLimit: stroke.miterLimit))
|
||||
}
|
||||
if let meshData = meshData, meshData.triangleCount() != 0 {
|
||||
let mappedTriangles = WriteBuffer()
|
||||
for i in 0 ..< meshData.triangleCount() {
|
||||
var v0: Int = 0
|
||||
var v1: Int = 0
|
||||
var v2: Int = 0
|
||||
meshData.getTriangleAt(i, v0: &v0, v1: &v1, v2: &v2)
|
||||
mappedTriangles.writeInt32(Int32(v0))
|
||||
mappedTriangles.writeInt32(Int32(v1))
|
||||
mappedTriangles.writeInt32(Int32(v2))
|
||||
}
|
||||
|
||||
let mappedVertices = WriteBuffer()
|
||||
for i in 0 ..< meshData.vertexCount() {
|
||||
var x: Float = 0.0
|
||||
@ -636,13 +625,15 @@ private func generateSegments(geometry: CapturedGeometryNode, superAlpha: CGFloa
|
||||
mappedVertices.writeFloat(x)
|
||||
mappedVertices.writeFloat(y)
|
||||
}
|
||||
|
||||
let trianglesData = Data(bytes: meshData.getTriangles(), count: meshData.triangleCount() * 3 * 4)
|
||||
|
||||
let verticesData = mappedVertices.makeData()
|
||||
let trianglesData = mappedTriangles.makeData()
|
||||
|
||||
let segment = MeshAnimation.Frame.Segment(vertices: DataRange(data: verticesData, range: 0 ..< verticesData.count), triangles: DataRange(data: trianglesData, range: 0 ..< trianglesData.count), fill: triangleFill, transform: CATransform3DGetAffineTransform(geometry.transform).concatenating(superTransform))
|
||||
|
||||
writeSegment(segment)
|
||||
segment.write(buffer: writeBuffer)
|
||||
segmentCount += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -666,9 +657,9 @@ public func generateMeshAnimation(data: Data) -> TempBoxFile? {
|
||||
|
||||
for i in 0 ..< Int(animation.endFrame) {
|
||||
container.setFrame(frame: CGFloat(i))
|
||||
#if DEBUG
|
||||
//#if DEBUG
|
||||
print("Frame \(i) / \(Int(animation.endFrame))")
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
let segmentCountOffset = writeBuffer.offset
|
||||
writeBuffer.writeInt32(0)
|
||||
@ -677,11 +668,7 @@ public func generateMeshAnimation(data: Data) -> TempBoxFile? {
|
||||
let geometry = container.captureGeometry()
|
||||
geometry.transform = CATransform3DMakeTranslation(256.0, 256.0, 0.0)
|
||||
|
||||
generateSegments(geometry: geometry, superAlpha: 1.0, superTransform: .identity, writeSegment: { segment in
|
||||
segment.write(buffer: writeBuffer)
|
||||
|
||||
segmentCount += 1
|
||||
})
|
||||
generateSegments(writeBuffer: writeBuffer, segmentCount: &segmentCount, geometry: geometry, superAlpha: 1.0, superTransform: .identity)
|
||||
|
||||
let currentOffset = writeBuffer.offset
|
||||
writeBuffer.seek(offset: segmentCountOffset)
|
||||
|
Loading…
x
Reference in New Issue
Block a user