Drawing improvements

This commit is contained in:
Ilya Laktyushin
2022-12-22 20:58:01 +04:00
parent d19a12977a
commit 729b4d952c
10 changed files with 244 additions and 362 deletions

View File

@@ -2,10 +2,6 @@ import Foundation
import UIKit
import Display
protocol DrawingRenderLayer: CALayer {
}
final class MarkerTool: DrawingElement, Codable {
let uuid: UUID
@@ -62,6 +58,10 @@ final class MarkerTool: DrawingElement, Codable {
try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
func setupRenderLayer() -> DrawingRenderLayer? {
return nil
}
@@ -219,6 +219,10 @@ final class NeonTool: DrawingElement, Codable {
// try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
func setupRenderLayer() -> DrawingRenderLayer? {
let layer = RenderLayer()
layer.setup(size: self.drawingSize, color: self.color, lineWidth: self.renderLineWidth, strokeWidth: self.renderStrokeWidth, shadowRadius: self.renderShadowRadius)
@@ -275,7 +279,6 @@ final class NeonTool: DrawingElement, Codable {
}
final class FillTool: DrawingElement, Codable {
let uuid: UUID
let drawingSize: CGSize
@@ -320,6 +323,10 @@ final class FillTool: DrawingElement, Codable {
// try container.encode(self.points, forKey: .points)
}
func setupRenderView(screenSize: CGSize) -> DrawingRenderView? {
return nil
}
func setupRenderLayer() -> DrawingRenderLayer? {
return nil
}
@@ -334,6 +341,9 @@ final class FillTool: DrawingElement, Codable {
if self.isBlur {
if let blurredImage = self.blurredImage?.cgImage {
context.translateBy(x: size.width / 2.0, y: size.height / 2.0)
context.scaleBy(x: 1.0, y: -1.0)
context.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
context.draw(blurredImage, in: CGRect(origin: .zero, size: size))
}
} else {
@@ -352,220 +362,3 @@ final class FillTool: DrawingElement, Codable {
return false
}
}
final class BlurTool: DrawingElement, Codable {
class RenderLayer: SimpleLayer, DrawingRenderLayer {
var lineWidth: CGFloat = 0.0
let blurLayer = SimpleLayer()
let fillLayer = SimpleShapeLayer()
func setup(size: CGSize, lineWidth: CGFloat, image: UIImage?) {
self.contentsScale = 1.0
self.lineWidth = lineWidth
let bounds = CGRect(origin: .zero, size: size)
self.frame = bounds
self.blurLayer.frame = bounds
self.fillLayer.frame = bounds
if self.blurLayer.contents == nil, let image = image {
self.blurLayer.contents = image.cgImage
}
self.blurLayer.mask = self.fillLayer
self.fillLayer.frame = bounds
self.fillLayer.contentsScale = 1.0
self.fillLayer.strokeColor = UIColor.white.cgColor
self.fillLayer.fillColor = UIColor.clear.cgColor
self.fillLayer.lineCap = .round
self.fillLayer.lineWidth = lineWidth
self.addSublayer(self.blurLayer)
}
func updatePath(_ path: CGPath) {
self.fillLayer.path = path
}
}
var getFullImage: () -> UIImage? = { return nil }
let uuid: UUID
let drawingSize: CGSize
var path: BezierPath?
var renderPath: CGPath?
let renderLineWidth: CGFloat
var translation = CGPoint()
private var currentRenderLayer: DrawingRenderLayer?
var isValid: Bool {
return self.renderPath != nil
}
required init(drawingSize: CGSize, lineWidth: CGFloat) {
self.uuid = UUID()
self.drawingSize = drawingSize
let minLineWidth = max(1.0, max(drawingSize.width, drawingSize.height) * 0.003)
let maxLineWidth = max(10.0, max(drawingSize.width, drawingSize.height) * 0.09)
let lineWidth = minLineWidth + (maxLineWidth - minLineWidth) * lineWidth
self.renderLineWidth = lineWidth
}
private enum CodingKeys: String, CodingKey {
case uuid
case drawingSize
case renderLineWidth
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.uuid = try container.decode(UUID.self, forKey: .uuid)
self.drawingSize = try container.decode(CGSize.self, forKey: .drawingSize)
self.renderLineWidth = try container.decode(CGFloat.self, forKey: .renderLineWidth)
// self.points = try container.decode([CGPoint].self, forKey: .points)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.uuid, forKey: .uuid)
try container.encode(self.drawingSize, forKey: .drawingSize)
try container.encode(self.renderLineWidth, forKey: .renderLineWidth)
// try container.encode(self.points, forKey: .points)
}
func setupRenderLayer() -> DrawingRenderLayer? {
let layer = RenderLayer()
layer.setup(size: self.drawingSize, lineWidth: self.renderLineWidth, image: self.getFullImage())
self.currentRenderLayer = layer
return layer
}
func updatePath(_ path: DrawingGesturePipeline.DrawingResult, state: DrawingGesturePipeline.DrawingGestureState) {
guard case let .smoothCurve(bezierPath) = path else {
return
}
self.path = bezierPath
let renderPath = bezierPath.path.cgPath
self.renderPath = renderPath
if let currentRenderLayer = self.currentRenderLayer as? RenderLayer {
currentRenderLayer.updatePath(renderPath)
}
}
func draw(in context: CGContext, size: CGSize) {
context.translateBy(x: self.translation.x, y: self.translation.y)
let renderLayer: DrawingRenderLayer?
if let currentRenderLayer = self.currentRenderLayer {
renderLayer = currentRenderLayer
} else {
renderLayer = self.setupRenderLayer()
}
renderLayer?.render(in: context)
}
}
enum CodableDrawingElement {
case pen(PenTool)
case marker(MarkerTool)
case neon(NeonTool)
case blur(BlurTool)
case fill(FillTool)
init?(element: DrawingElement) {
if let element = element as? PenTool {
self = .pen(element)
} else if let element = element as? MarkerTool {
self = .marker(element)
} else if let element = element as? NeonTool {
self = .neon(element)
} else if let element = element as? BlurTool {
self = .blur(element)
} else if let element = element as? FillTool {
self = .fill(element)
} else {
return nil
}
}
var element: DrawingElement {
switch self {
case let .pen(element):
return element
case let .marker(element):
return element
case let .neon(element):
return element
case let .blur(element):
return element
case let .fill(element):
return element
}
}
}
extension CodableDrawingElement: Codable {
private enum CodingKeys: String, CodingKey {
case type
case element
}
private enum ElementType: Int, Codable {
case pen
case marker
case neon
case blur
case fill
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(ElementType.self, forKey: .type)
switch type {
case .pen:
self = .pen(try container.decode(PenTool.self, forKey: .element))
case .marker:
self = .marker(try container.decode(MarkerTool.self, forKey: .element))
case .neon:
self = .neon(try container.decode(NeonTool.self, forKey: .element))
case .blur:
self = .blur(try container.decode(BlurTool.self, forKey: .element))
case .fill:
self = .fill(try container.decode(FillTool.self, forKey: .element))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case let .pen(payload):
try container.encode(ElementType.pen, forKey: .type)
try container.encode(payload, forKey: .element)
case let .marker(payload):
try container.encode(ElementType.marker, forKey: .type)
try container.encode(payload, forKey: .element)
case let .neon(payload):
try container.encode(ElementType.neon, forKey: .type)
try container.encode(payload, forKey: .element)
case let .blur(payload):
try container.encode(ElementType.blur, forKey: .type)
try container.encode(payload, forKey: .element)
case let .fill(payload):
try container.encode(ElementType.fill, forKey: .type)
try container.encode(payload, forKey: .element)
}
}
}