mirror of
https://github.com/dscyrescotti/Memola.git
synced 2026-05-17 05:07:11 +02:00
Merge pull request #54 from dscyrescotti/feature/general-enhancement
Add general enhancements and fixes
This commit is contained in:
@@ -12,5 +12,5 @@ protocol RenderPass {
|
|||||||
var label: String { get }
|
var label: String { get }
|
||||||
var descriptor: MTLRenderPassDescriptor? { get set }
|
var descriptor: MTLRenderPassDescriptor? { get set }
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer)
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer)
|
||||||
func draw(into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer)
|
func draw(into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,9 +251,6 @@ extension GraphicContext {
|
|||||||
|
|
||||||
func appendStroke(with point: CGPoint) {
|
func appendStroke(with point: CGPoint) {
|
||||||
guard let currentStroke = currentElement?.stroke() else { return }
|
guard let currentStroke = currentElement?.stroke() else { return }
|
||||||
guard let currentPoint, point.distance(to: currentPoint) > currentStroke.thickness * currentStroke.penStyle.stepRate else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
currentStroke.append(to: point)
|
currentStroke.append(to: point)
|
||||||
self.currentPoint = point
|
self.currentPoint = point
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ extension Canvas {
|
|||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
self?.graphicContext.object = graphicContext
|
self?.graphicContext.object = graphicContext
|
||||||
self?.graphicContext.loadStrokes(bounds)
|
self?.graphicContext.loadStrokes(bounds)
|
||||||
context.refreshAllObjects()
|
context.refresh(canvas, mergeChanges: false)
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.state = .loaded
|
self?.state = .loaded
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,17 +60,23 @@ final class Renderer {
|
|||||||
self.viewPortRenderPass.view = canvasView
|
self.viewPortRenderPass.view = canvasView
|
||||||
}
|
}
|
||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize) {
|
func setUpdatesViewPort(_ value: Bool) {
|
||||||
if !updatesViewPort {
|
updatesViewPort = value
|
||||||
photoBackgroundRenderPass.resize(on: view, to: size, with: self)
|
}
|
||||||
strokeRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
graphicRenderPass.resize(on: view, to: size, with: self)
|
func setRedrawsGraphicRender() {
|
||||||
cacheRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
}
|
|
||||||
viewPortRenderPass.resize(on: view, to: size, with: self)
|
|
||||||
redrawsGraphicRender = true
|
redrawsGraphicRender = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resize(on view: MTKView, to size: CGSize) {
|
||||||
|
photoBackgroundRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
strokeRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
graphicRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
cacheRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
viewPortRenderPass.resize(on: view, to: size, with: self)
|
||||||
|
setRedrawsGraphicRender()
|
||||||
|
}
|
||||||
|
|
||||||
func draw(in view: MTKView, on canvas: Canvas) {
|
func draw(in view: MTKView, on canvas: Canvas) {
|
||||||
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
|
guard let commandBuffer = commandQueue.makeCommandBuffer() else {
|
||||||
NSLog("[Memola] - Unable to create command buffer")
|
NSLog("[Memola] - Unable to create command buffer")
|
||||||
@@ -94,6 +100,8 @@ final class Renderer {
|
|||||||
cacheRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
cacheRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
||||||
|
|
||||||
viewPortRenderPass.descriptor = view.currentRenderPassDescriptor
|
viewPortRenderPass.descriptor = view.currentRenderPassDescriptor
|
||||||
|
viewPortRenderPass.excludesPhotoBackground = photoBackgroundRenderPass.clearsTexture
|
||||||
|
viewPortRenderPass.excludesGraphic = cacheRenderPass.clearsTexture
|
||||||
viewPortRenderPass.photoBackgroundTexture = photoBackgroundRenderPass.photoBackgroundTexture
|
viewPortRenderPass.photoBackgroundTexture = photoBackgroundRenderPass.photoBackgroundTexture
|
||||||
viewPortRenderPass.cacheTexture = cacheRenderPass.cacheTexture
|
viewPortRenderPass.cacheTexture = cacheRenderPass.cacheTexture
|
||||||
viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self)
|
||||||
|
|||||||
@@ -61,12 +61,11 @@ final class PenStroke: Stroke, @unchecked Sendable {
|
|||||||
|
|
||||||
convenience init(object: StrokeObject) {
|
convenience init(object: StrokeObject) {
|
||||||
let style = StrokeStyle(rawValue: object.style) ?? .marker
|
let style = StrokeStyle(rawValue: object.style) ?? .marker
|
||||||
#warning("TODO: revisit here and check if there is any crash")
|
|
||||||
self.init(
|
self.init(
|
||||||
bounds: object.bounds,
|
bounds: object.bounds,
|
||||||
color: object.color,
|
color: object.color,
|
||||||
style: style,
|
style: style,
|
||||||
createdAt: object.createdAt ?? .now, // sometimes crash here
|
createdAt: object.createdAt ?? .now,
|
||||||
thickness: object.thickness
|
thickness: object.thickness
|
||||||
)
|
)
|
||||||
self.object = object
|
self.object = object
|
||||||
|
|||||||
@@ -34,14 +34,15 @@ class CacheRenderPass: RenderPass {
|
|||||||
cacheTexture = Textures.createCacheTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
cacheTexture = Textures.createCacheTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let descriptor else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Copying texture
|
// MARK: - Copying texture
|
||||||
guard let graphicTexture, let cacheTexture else { return }
|
guard let graphicTexture, let cacheTexture else { return false }
|
||||||
guard let cachePipelineState else { return }
|
guard let cachePipelineState else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else {
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
computeEncoder.label = "Cache Compute Encoder"
|
computeEncoder.label = "Cache Compute Encoder"
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ class CacheRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Drawing
|
// MARK: - Drawing
|
||||||
guard let graphicPipelineState else { return }
|
guard let graphicPipelineState else { return false }
|
||||||
descriptor.colorAttachments[0].texture = cacheTexture
|
descriptor.colorAttachments[0].texture = cacheTexture
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
@@ -67,23 +68,25 @@ class CacheRenderPass: RenderPass {
|
|||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
if let element = graphicContext.currentElement {
|
if let element = graphicContext.currentElement {
|
||||||
let elementGroup = ElementGroup(element)
|
let elementGroup = ElementGroup(element)
|
||||||
|
var status: Bool?
|
||||||
switch elementGroup.type {
|
switch elementGroup.type {
|
||||||
case .stroke:
|
case .stroke:
|
||||||
canvas.setGraphicRenderType(.inProgress)
|
canvas.setGraphicRenderType(.inProgress)
|
||||||
strokeRenderPass?.elementGroup = elementGroup
|
strokeRenderPass?.elementGroup = elementGroup
|
||||||
strokeRenderPass?.graphicDescriptor = descriptor
|
strokeRenderPass?.graphicDescriptor = descriptor
|
||||||
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
||||||
strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
case .eraser:
|
case .eraser:
|
||||||
eraserRenderPass?.elementGroup = elementGroup
|
eraserRenderPass?.elementGroup = elementGroup
|
||||||
eraserRenderPass?.descriptor = descriptor
|
eraserRenderPass?.descriptor = descriptor
|
||||||
eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
case .photo:
|
case .photo:
|
||||||
photoRenderPass?.elementGroup = elementGroup
|
photoRenderPass?.elementGroup = elementGroup
|
||||||
photoRenderPass?.descriptor = descriptor
|
photoRenderPass?.descriptor = descriptor
|
||||||
photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
status = photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
}
|
}
|
||||||
clearsTexture = false
|
clearsTexture = !(status ?? false)
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,19 +26,20 @@ class EraserRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Generating vertices
|
// MARK: - Generating vertices
|
||||||
guard !elementGroup.isEmpty, let quadPipelineState else { return }
|
guard !elementGroup.isEmpty, let quadPipelineState else { return false }
|
||||||
let eraserStrokes = elementGroup.elements.compactMap { element -> EraserStroke? in
|
let eraserStrokes = elementGroup.elements.compactMap { element -> EraserStroke? in
|
||||||
guard case .stroke(let anyStroke) = element else { return nil }
|
guard case .stroke(let anyStroke) = element else { return nil }
|
||||||
return anyStroke.value as? EraserStroke
|
return anyStroke.value as? EraserStroke
|
||||||
}
|
}
|
||||||
let quads = eraserStrokes.flatMap { $0.quads }
|
let quads = eraserStrokes.flatMap { $0.quads }
|
||||||
guard !quads.isEmpty else { return }
|
guard !quads.isEmpty else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Quad Compute Encoder"
|
computeEncoder.label = "Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -58,10 +59,10 @@ class EraserRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Rendering eraser
|
// MARK: - Rendering eraser
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Render Encoder"
|
renderEncoder.label = "Stroke Render Encoder"
|
||||||
|
|
||||||
guard let eraserPipelineState else { return }
|
guard let eraserPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -77,5 +78,6 @@ class EraserRenderPass: RenderPass {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,16 +30,18 @@ class GraphicRenderPass: RenderPass {
|
|||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
||||||
guard size != .zero else { return }
|
guard size != .zero else { return }
|
||||||
graphicTexture = Textures.createGraphicTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
graphicTexture = Textures.createGraphicTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
clearsTexture = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
descriptor?.colorAttachments[0].texture = graphicTexture
|
descriptor?.colorAttachments[0].texture = graphicTexture
|
||||||
descriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor?.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor?.colorAttachments[0].storeAction = .store
|
descriptor?.colorAttachments[0].storeAction = .store
|
||||||
|
|
||||||
let graphicContext = canvas.graphicContext
|
let graphicContext = canvas.graphicContext
|
||||||
if renderer.redrawsGraphicRender {
|
if renderer.redrawsGraphicRender {
|
||||||
|
clearsTexture = true
|
||||||
|
photoBackgroundRenderPass?.clearsTexture = true
|
||||||
canvas.setGraphicRenderType(.finished)
|
canvas.setGraphicRenderType(.finished)
|
||||||
var elementGroup: ElementGroup?
|
var elementGroup: ElementGroup?
|
||||||
let start = Date.now.timeIntervalSince1970 * 1000
|
let start = Date.now.timeIntervalSince1970 * 1000
|
||||||
@@ -51,7 +53,7 @@ class GraphicRenderPass: RenderPass {
|
|||||||
let _elementGroup = ElementGroup(_element)
|
let _elementGroup = ElementGroup(_element)
|
||||||
elementGroup = _elementGroup
|
elementGroup = _elementGroup
|
||||||
} else {
|
} else {
|
||||||
guard let _elementGroup = elementGroup else { return }
|
guard let _elementGroup = elementGroup else { continue }
|
||||||
if _elementGroup.isSameElement(_element) {
|
if _elementGroup.isSameElement(_element) {
|
||||||
_elementGroup.add(_element)
|
_elementGroup.add(_element)
|
||||||
} else {
|
} else {
|
||||||
@@ -75,6 +77,7 @@ class GraphicRenderPass: RenderPass {
|
|||||||
draw(for: elementGroup, into: commandBuffer, on: canvas, with: renderer)
|
draw(for: elementGroup, into: commandBuffer, on: canvas, with: renderer)
|
||||||
graphicContext.previousElement = nil
|
graphicContext.previousElement = nil
|
||||||
}
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private func draw(for elementGroup: ElementGroup, into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
private func draw(for elementGroup: ElementGroup, into commandBuffer: MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
||||||
@@ -84,24 +87,33 @@ class GraphicRenderPass: RenderPass {
|
|||||||
strokeRenderPass?.elementGroup = elementGroup
|
strokeRenderPass?.elementGroup = elementGroup
|
||||||
strokeRenderPass?.graphicDescriptor = descriptor
|
strokeRenderPass?.graphicDescriptor = descriptor
|
||||||
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
strokeRenderPass?.graphicPipelineState = graphicPipelineState
|
||||||
strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
clearsTexture = false
|
if clearsTexture, let status {
|
||||||
|
clearsTexture = !status
|
||||||
|
}
|
||||||
case .eraser:
|
case .eraser:
|
||||||
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
eraserRenderPass?.elementGroup = elementGroup
|
eraserRenderPass?.elementGroup = elementGroup
|
||||||
eraserRenderPass?.descriptor = descriptor
|
eraserRenderPass?.descriptor = descriptor
|
||||||
eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
clearsTexture = false
|
if clearsTexture, let status {
|
||||||
|
clearsTexture = !status
|
||||||
|
}
|
||||||
case .photo:
|
case .photo:
|
||||||
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
photoRenderPass?.elementGroup = elementGroup
|
photoRenderPass?.elementGroup = elementGroup
|
||||||
photoRenderPass?.descriptor = descriptor
|
photoRenderPass?.descriptor = descriptor
|
||||||
photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
let photoStatus = photoRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
|
|
||||||
photoBackgroundRenderPass?.elementGroup = elementGroup
|
photoBackgroundRenderPass?.elementGroup = elementGroup
|
||||||
photoBackgroundRenderPass?.clearsTexture = clearsTexture
|
let photoBackgroundStatus = photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
||||||
photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer)
|
|
||||||
clearsTexture = false
|
if clearsTexture, let photoStatus {
|
||||||
|
clearsTexture = !photoStatus
|
||||||
|
}
|
||||||
|
if photoBackgroundRenderPass?.clearsTexture == true, let photoBackgroundStatus {
|
||||||
|
photoBackgroundRenderPass?.clearsTexture = !photoBackgroundStatus
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,30 +27,31 @@ class PhotoBackgroundRenderPass: RenderPass {
|
|||||||
photoBackgroundPipelineState = PipelineStates.createPhotoPipelineState(from: renderer)
|
photoBackgroundPipelineState = PipelineStates.createPhotoPipelineState(from: renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) {
|
||||||
|
guard size != .zero else { return }
|
||||||
photoBackgroundTexture = Textures.createPhotoBackgroundTexture(from: renderer, size: size, pixelFormat: renderer.pixelFormat)
|
photoBackgroundTexture = Textures.createPhotoBackgroundTexture(from: renderer, size: size, pixelFormat: renderer.pixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let elementGroup else { return }
|
guard let elementGroup else { return false }
|
||||||
guard let descriptor else { return }
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
descriptor.colorAttachments[0].texture = photoBackgroundTexture
|
descriptor.colorAttachments[0].texture = photoBackgroundTexture
|
||||||
descriptor.colorAttachments[0].storeAction = .store
|
descriptor.colorAttachments[0].storeAction = .store
|
||||||
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
|
|
||||||
guard !elementGroup.isEmpty else { return }
|
guard !elementGroup.isEmpty else { return false }
|
||||||
|
|
||||||
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
||||||
guard case .photo(let photo) = element else { return nil }
|
guard case .photo(let photo) = element else { return nil }
|
||||||
return photo
|
return photo
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Photo Background Render Encoder"
|
renderEncoder.label = "Photo Background Render Encoder"
|
||||||
|
|
||||||
guard let photoBackgroundPipelineState else { return }
|
guard let photoBackgroundPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(photoBackgroundPipelineState)
|
renderEncoder.setRenderPipelineState(photoBackgroundPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -60,5 +61,6 @@ class PhotoBackgroundRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,21 +24,22 @@ class PhotoRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
guard !elementGroup.isEmpty else { return }
|
guard !elementGroup.isEmpty else { return false }
|
||||||
|
|
||||||
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
let photos = elementGroup.elements.compactMap { element -> Photo? in
|
||||||
guard case .photo(let photo) = element else { return nil }
|
guard case .photo(let photo) = element else { return nil }
|
||||||
return photo
|
return photo
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Photo Render Encoder"
|
renderEncoder.label = "Photo Render Encoder"
|
||||||
|
|
||||||
guard let photoPipelineState else { return }
|
guard let photoPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(photoPipelineState)
|
renderEncoder.setRenderPipelineState(photoPipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -48,5 +49,6 @@ class PhotoRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,12 +34,13 @@ class StrokeRenderPass: RenderPass {
|
|||||||
strokeTexture = Textures.createStrokeTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
strokeTexture = Textures.createStrokeTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
guard let elementGroup else { return }
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else { return }
|
guard let elementGroup else { return false }
|
||||||
|
guard let descriptor else { return false }
|
||||||
|
|
||||||
// MARK: - Generating vertices
|
// MARK: - Generating vertices
|
||||||
guard !elementGroup.isEmpty, let quadPipelineState else { return }
|
guard !elementGroup.isEmpty, let quadPipelineState else { return false }
|
||||||
let penStrokes = elementGroup.elements.compactMap { element -> PenStroke? in
|
let penStrokes = elementGroup.elements.compactMap { element -> PenStroke? in
|
||||||
guard case .stroke(let anyStroke) = element else { return nil }
|
guard case .stroke(let anyStroke) = element else { return nil }
|
||||||
return anyStroke.value as? PenStroke
|
return anyStroke.value as? PenStroke
|
||||||
@@ -47,8 +48,8 @@ class StrokeRenderPass: RenderPass {
|
|||||||
let penStroke = penStrokes.first
|
let penStroke = penStrokes.first
|
||||||
let quads = penStrokes.flatMap { $0.quads }
|
let quads = penStrokes.flatMap { $0.quads }
|
||||||
let erasedQuads = Set(penStrokes.flatMap { $0.eraserStrokes }).flatMap { $0.quads }
|
let erasedQuads = Set(penStrokes.flatMap { $0.eraserStrokes }).flatMap { $0.quads }
|
||||||
guard !quads.isEmpty else { return }
|
guard !quads.isEmpty else { return false }
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Quad Compute Encoder"
|
computeEncoder.label = "Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -68,16 +69,16 @@ class StrokeRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
// MARK: - Rendering stroke
|
// MARK: - Rendering stroke
|
||||||
guard let strokeTexture else { return }
|
guard let strokeTexture else { return false }
|
||||||
descriptor.colorAttachments[0].texture = strokeTexture
|
descriptor.colorAttachments[0].texture = strokeTexture
|
||||||
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0)
|
||||||
descriptor.colorAttachments[0].loadAction = .clear
|
descriptor.colorAttachments[0].loadAction = .clear
|
||||||
descriptor.colorAttachments[0].storeAction = .store
|
descriptor.colorAttachments[0].storeAction = .store
|
||||||
|
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Render Encoder"
|
renderEncoder.label = "Stroke Render Encoder"
|
||||||
|
|
||||||
guard let strokePipelineState else { return }
|
guard let strokePipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(strokePipelineState)
|
renderEncoder.setRenderPipelineState(strokePipelineState)
|
||||||
|
|
||||||
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
@@ -100,7 +101,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
|
|
||||||
// MARK: Erasing path
|
// MARK: Erasing path
|
||||||
if let eraserPipelineState = eraserRenderPass?.eraserPipelineState, !erasedQuads.isEmpty {
|
if let eraserPipelineState = eraserRenderPass?.eraserPipelineState, !erasedQuads.isEmpty {
|
||||||
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return }
|
guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false }
|
||||||
|
|
||||||
computeEncoder.label = "Erased Quad Compute Encoder"
|
computeEncoder.label = "Erased Quad Compute Encoder"
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
computeEncoder.endEncoding()
|
computeEncoder.endEncoding()
|
||||||
|
|
||||||
descriptor.colorAttachments[0].loadAction = .load
|
descriptor.colorAttachments[0].loadAction = .load
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Eraser Render Encoder"
|
renderEncoder.label = "Stroke Eraser Render Encoder"
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
renderEncoder.setRenderPipelineState(eraserPipelineState)
|
||||||
@@ -140,8 +141,8 @@ class StrokeRenderPass: RenderPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Drawing on graphic texture
|
// MARK: Drawing on graphic texture
|
||||||
guard let graphicDescriptor, let graphicPipelineState else { return }
|
guard let graphicDescriptor, let graphicPipelineState else { return false }
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return }
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return false }
|
||||||
renderEncoder.label = "Stroke Graphic Render Encoder"
|
renderEncoder.label = "Stroke Graphic Render Encoder"
|
||||||
renderEncoder.setRenderPipelineState(graphicPipelineState)
|
renderEncoder.setRenderPipelineState(graphicPipelineState)
|
||||||
|
|
||||||
@@ -151,5 +152,7 @@ class StrokeRenderPass: RenderPass {
|
|||||||
renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 11)
|
renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 11)
|
||||||
canvas.renderGraphic(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderGraphic(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
|
|
||||||
weak var view: MTKView?
|
weak var view: MTKView?
|
||||||
|
|
||||||
|
var excludesGraphic: Bool = false
|
||||||
|
var excludesPhotoBackground: Bool = false
|
||||||
|
|
||||||
init(renderer: Renderer) {
|
init(renderer: Renderer) {
|
||||||
pointGridPipelineState = PipelineStates.createPointGridPipelineState(from: renderer)
|
pointGridPipelineState = PipelineStates.createPointGridPipelineState(from: renderer)
|
||||||
lineGridPipelineState = PipelineStates.createLineGridPipelineState(from: renderer)
|
lineGridPipelineState = PipelineStates.createLineGridPipelineState(from: renderer)
|
||||||
@@ -31,12 +34,13 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
|
|
||||||
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { }
|
||||||
|
|
||||||
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) {
|
@discardableResult
|
||||||
|
func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool {
|
||||||
guard let descriptor else {
|
guard let descriptor else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
|
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
renderEncoder.label = "View Port Render Encoder"
|
renderEncoder.label = "View Port Render Encoder"
|
||||||
|
|
||||||
@@ -44,47 +48,56 @@ class ViewPortRenderPass: RenderPass {
|
|||||||
case .none:
|
case .none:
|
||||||
break
|
break
|
||||||
case .point:
|
case .point:
|
||||||
guard let pointGridPipelineState else { return }
|
guard let pointGridPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(pointGridPipelineState)
|
renderEncoder.setRenderPipelineState(pointGridPipelineState)
|
||||||
canvas.renderPointGrid(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderPointGrid(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
case .line:
|
case .line:
|
||||||
guard let lineGridPipelineState else { return }
|
guard let lineGridPipelineState else { return false }
|
||||||
renderEncoder.setRenderPipelineState(lineGridPipelineState)
|
renderEncoder.setRenderPipelineState(lineGridPipelineState)
|
||||||
canvas.renderLineGrid(device: renderer.device, renderEncoder: renderEncoder)
|
canvas.renderLineGrid(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
if renderer.updatesViewPort {
|
if renderer.updatesViewPort {
|
||||||
guard let viewPortUpdatePipelineState else {
|
guard let viewPortUpdatePipelineState else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(viewPortUpdatePipelineState)
|
renderEncoder.setRenderPipelineState(viewPortUpdatePipelineState)
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
if !excludesPhotoBackground {
|
||||||
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
||||||
|
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
if !excludesGraphic {
|
||||||
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
||||||
|
canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
guard let viewPortPipelineState else {
|
guard let viewPortPipelineState else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.setRenderPipelineState(viewPortPipelineState)
|
renderEncoder.setRenderPipelineState(viewPortPipelineState)
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
if !excludesPhotoBackground {
|
||||||
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0)
|
||||||
|
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
|
|
||||||
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
if !excludesGraphic {
|
||||||
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
renderEncoder.setFragmentTexture(cacheTexture, index: 0)
|
||||||
|
canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderEncoder.endEncoding()
|
renderEncoder.endEncoding()
|
||||||
|
|
||||||
guard let drawable = view?.currentDrawable else {
|
guard let drawable = view?.currentDrawable else {
|
||||||
return
|
return false
|
||||||
}
|
}
|
||||||
commandBuffer.present(drawable)
|
commandBuffer.present(drawable)
|
||||||
commandBuffer.commit()
|
commandBuffer.commit()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,10 +125,10 @@ public class Tool: NSObject, ObservableObject {
|
|||||||
|
|
||||||
func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) {
|
func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) {
|
||||||
guard let (resizedImage, dimension) = resizePhoto(of: image) else { return }
|
guard let (resizedImage, dimension) = resizePhoto(of: image) else { return }
|
||||||
let photoItem = bookmarkPhoto(of: resizedImage, in: dimension, with: canvasID)
|
let photoItem = bookmarkPhoto(of: resizedImage, and: image, in: dimension, with: canvasID)
|
||||||
isLoadingPhoto = false
|
|
||||||
withAnimation {
|
withAnimation {
|
||||||
selectedPhotoItem = photoItem
|
selectedPhotoItem = photoItem
|
||||||
|
isLoadingPhoto = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ public class Tool: NSObject, ObservableObject {
|
|||||||
return (newImage, dimension)
|
return (newImage, dimension)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bookmarkPhoto(of image: UIImage, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
|
private func bookmarkPhoto(of image: UIImage, and previewImage: UIImage, in dimension: CGSize, with canvasID: NSManagedObjectID) -> PhotoItem? {
|
||||||
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
|
guard let data = image.jpegData(compressionQuality: 1) else { return nil }
|
||||||
let fileManager = FileManager.default
|
let fileManager = FileManager.default
|
||||||
guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
|
||||||
@@ -180,7 +180,7 @@ public class Tool: NSObject, ObservableObject {
|
|||||||
var photoBookmark: PhotoItem?
|
var photoBookmark: PhotoItem?
|
||||||
do {
|
do {
|
||||||
let bookmark = try file.bookmarkData(options: .minimalBookmark)
|
let bookmark = try file.bookmarkData(options: .minimalBookmark)
|
||||||
photoBookmark = PhotoItem(id: file, image: image, dimension: dimension, bookmark: bookmark)
|
photoBookmark = PhotoItem(id: file, image: image, previewImage: previewImage, dimension: dimension, bookmark: bookmark)
|
||||||
} catch {
|
} catch {
|
||||||
NSLog("[Memola] - \(error.localizedDescription)")
|
NSLog("[Memola] - \(error.localizedDescription)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,7 +259,7 @@ extension CanvasViewController: UIScrollViewDelegate {
|
|||||||
|
|
||||||
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
func scrollViewDidZoom(_ scrollView: UIScrollView) {
|
||||||
canvas.setZoomScale(scrollView.zoomScale)
|
canvas.setZoomScale(scrollView.zoomScale)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
// renderer.resize(on: renderView, to: renderView.drawableSize)
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,7 +274,7 @@ extension CanvasViewController: UIScrollViewDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
// renderer.resize(on: renderView, to: renderView.drawableSize)
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +302,12 @@ extension CanvasViewController {
|
|||||||
drawingView.touchCancelled()
|
drawingView.touchCancelled()
|
||||||
canvas.updateClipBounds(scrollView, on: drawingView)
|
canvas.updateClipBounds(scrollView, on: drawingView)
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.updatesViewPort = true
|
renderer.setUpdatesViewPort(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func magnificationEnded() {
|
func magnificationEnded() {
|
||||||
renderer.updatesViewPort = false
|
renderer.setUpdatesViewPort(false)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -316,12 +316,12 @@ extension CanvasViewController {
|
|||||||
guard !renderer.updatesViewPort else { return }
|
guard !renderer.updatesViewPort else { return }
|
||||||
canvas.updateClipBounds(scrollView, on: drawingView)
|
canvas.updateClipBounds(scrollView, on: drawingView)
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.updatesViewPort = true
|
renderer.setUpdatesViewPort(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func draggingEnded() {
|
func draggingEnded() {
|
||||||
renderer.updatesViewPort = false
|
renderer.setUpdatesViewPort(false)
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -371,7 +371,7 @@ extension CanvasViewController {
|
|||||||
|
|
||||||
func gridModeChanged(_ mode: GridMode) {
|
func gridModeChanged(_ mode: GridMode) {
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
renderer.setRedrawsGraphicRender()
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -382,8 +382,7 @@ extension CanvasViewController {
|
|||||||
guard let event = history.undo() else { return }
|
guard let event = history.undo() else { return }
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
canvas.graphicContext.undoGraphic(for: event)
|
canvas.graphicContext.undoGraphic(for: event)
|
||||||
renderer.redrawsGraphicRender = true
|
renderer.setRedrawsGraphicRender()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
@@ -392,8 +391,7 @@ extension CanvasViewController {
|
|||||||
guard let event = history.redo() else { return }
|
guard let event = history.redo() else { return }
|
||||||
drawingView.disableUserInteraction()
|
drawingView.disableUserInteraction()
|
||||||
canvas.graphicContext.redoGraphic(for: event)
|
canvas.graphicContext.redoGraphic(for: event)
|
||||||
renderer.redrawsGraphicRender = true
|
renderer.setRedrawsGraphicRender()
|
||||||
renderer.resize(on: renderView, to: renderView.drawableSize)
|
|
||||||
renderView.draw()
|
renderView.draw()
|
||||||
drawingView.enableUserInteraction()
|
drawingView.enableUserInteraction()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import Foundation
|
|||||||
struct PhotoItem: Identifiable, Equatable {
|
struct PhotoItem: Identifiable, Equatable {
|
||||||
var id: URL
|
var id: URL
|
||||||
let image: UIImage
|
let image: UIImage
|
||||||
|
let previewImage: UIImage
|
||||||
let dimension: CGSize
|
let dimension: CGSize
|
||||||
let bookmark: Data
|
let bookmark: Data
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ struct PhotoPreview: View {
|
|||||||
@ObservedObject var tool: Tool
|
@ObservedObject var tool: Tool
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Image(uiImage: photoItem.image)
|
Image(uiImage: photoItem.previewImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFill()
|
.scaledToFit()
|
||||||
.frame(width: 100, height: 100)
|
.frame(height: 100)
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
.overlay {
|
.overlay {
|
||||||
RoundedRectangle(cornerRadius: 5)
|
RoundedRectangle(cornerRadius: 5)
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ struct MemosView: View {
|
|||||||
penObject.color = color.components
|
penObject.color = color.components
|
||||||
return penObject
|
return penObject
|
||||||
}
|
}
|
||||||
|
markerPenObjects.first?.isSelected = true
|
||||||
|
|
||||||
let graphicContextObject = GraphicContextObject(\.viewContext)
|
let graphicContextObject = GraphicContextObject(\.viewContext)
|
||||||
graphicContextObject.elements = []
|
graphicContextObject.elements = []
|
||||||
|
|||||||
Reference in New Issue
Block a user