diff --git a/Memola/Canvas/Abstracts/RenderPass.swift b/Memola/Canvas/Abstracts/RenderPass.swift index 9830e20..22989c0 100644 --- a/Memola/Canvas/Abstracts/RenderPass.swift +++ b/Memola/Canvas/Abstracts/RenderPass.swift @@ -12,5 +12,5 @@ protocol RenderPass { var label: String { get } var descriptor: MTLRenderPassDescriptor? { get set } 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 } diff --git a/Memola/Canvas/Contexts/GraphicContext.swift b/Memola/Canvas/Contexts/GraphicContext.swift index 101cd77..e07f3aa 100644 --- a/Memola/Canvas/Contexts/GraphicContext.swift +++ b/Memola/Canvas/Contexts/GraphicContext.swift @@ -251,9 +251,6 @@ extension GraphicContext { func appendStroke(with point: CGPoint) { 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) self.currentPoint = point } diff --git a/Memola/Canvas/Core/Canvas.swift b/Memola/Canvas/Core/Canvas.swift index 03b52aa..6e823dd 100644 --- a/Memola/Canvas/Core/Canvas.swift +++ b/Memola/Canvas/Core/Canvas.swift @@ -65,7 +65,7 @@ extension Canvas { let graphicContext = canvas.graphicContext self?.graphicContext.object = graphicContext self?.graphicContext.loadStrokes(bounds) - context.refreshAllObjects() + context.refresh(canvas, mergeChanges: false) DispatchQueue.main.async { [weak self] in self?.state = .loaded } diff --git a/Memola/Canvas/Core/Renderer.swift b/Memola/Canvas/Core/Renderer.swift index 6e36d98..0f143f3 100644 --- a/Memola/Canvas/Core/Renderer.swift +++ b/Memola/Canvas/Core/Renderer.swift @@ -60,17 +60,23 @@ final class Renderer { self.viewPortRenderPass.view = canvasView } - func resize(on view: MTKView, to size: CGSize) { - if !updatesViewPort { - 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) + func setUpdatesViewPort(_ value: Bool) { + updatesViewPort = value + } + + func setRedrawsGraphicRender() { 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) { guard let commandBuffer = commandQueue.makeCommandBuffer() else { NSLog("[Memola] - Unable to create command buffer") @@ -94,6 +100,8 @@ final class Renderer { cacheRenderPass.draw(into: commandBuffer, on: canvas, with: self) viewPortRenderPass.descriptor = view.currentRenderPassDescriptor + viewPortRenderPass.excludesPhotoBackground = photoBackgroundRenderPass.clearsTexture + viewPortRenderPass.excludesGraphic = cacheRenderPass.clearsTexture viewPortRenderPass.photoBackgroundTexture = photoBackgroundRenderPass.photoBackgroundTexture viewPortRenderPass.cacheTexture = cacheRenderPass.cacheTexture viewPortRenderPass.draw(into: commandBuffer, on: canvas, with: self) diff --git a/Memola/Canvas/Elements/Geometries/Stroke/Strokes/PenStroke.swift b/Memola/Canvas/Elements/Geometries/Stroke/Strokes/PenStroke.swift index d0cce4a..3f1aa47 100644 --- a/Memola/Canvas/Elements/Geometries/Stroke/Strokes/PenStroke.swift +++ b/Memola/Canvas/Elements/Geometries/Stroke/Strokes/PenStroke.swift @@ -61,12 +61,11 @@ final class PenStroke: Stroke, @unchecked Sendable { convenience init(object: StrokeObject) { let style = StrokeStyle(rawValue: object.style) ?? .marker - #warning("TODO: revisit here and check if there is any crash") self.init( bounds: object.bounds, color: object.color, style: style, - createdAt: object.createdAt ?? .now, // sometimes crash here + createdAt: object.createdAt ?? .now, thickness: object.thickness ) self.object = object diff --git a/Memola/Canvas/RenderPasses/CacheRenderPass.swift b/Memola/Canvas/RenderPasses/CacheRenderPass.swift index 9086361..75d4e5b 100644 --- a/Memola/Canvas/RenderPasses/CacheRenderPass.swift +++ b/Memola/Canvas/RenderPasses/CacheRenderPass.swift @@ -34,14 +34,15 @@ class CacheRenderPass: RenderPass { cacheTexture = Textures.createCacheTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat) } - func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) { - guard let descriptor else { return } + @discardableResult + func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool { + guard let descriptor else { return false } // MARK: - Copying texture - guard let graphicTexture, let cacheTexture else { return } - guard let cachePipelineState else { return } + guard let graphicTexture, let cacheTexture else { return false } + guard let cachePipelineState else { return false } guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { - return + return false } computeEncoder.label = "Cache Compute Encoder" @@ -58,7 +59,7 @@ class CacheRenderPass: RenderPass { computeEncoder.endEncoding() // MARK: - Drawing - guard let graphicPipelineState else { return } + guard let graphicPipelineState else { return false } descriptor.colorAttachments[0].texture = cacheTexture descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0) descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load @@ -67,23 +68,25 @@ class CacheRenderPass: RenderPass { let graphicContext = canvas.graphicContext if let element = graphicContext.currentElement { let elementGroup = ElementGroup(element) + var status: Bool? switch elementGroup.type { case .stroke: canvas.setGraphicRenderType(.inProgress) strokeRenderPass?.elementGroup = elementGroup strokeRenderPass?.graphicDescriptor = descriptor strokeRenderPass?.graphicPipelineState = graphicPipelineState - strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) + status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) case .eraser: eraserRenderPass?.elementGroup = elementGroup eraserRenderPass?.descriptor = descriptor - eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) + status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) case .photo: photoRenderPass?.elementGroup = elementGroup 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 } } diff --git a/Memola/Canvas/RenderPasses/EraserRenderPass.swift b/Memola/Canvas/RenderPasses/EraserRenderPass.swift index 226b541..30b8c90 100644 --- a/Memola/Canvas/RenderPasses/EraserRenderPass.swift +++ b/Memola/Canvas/RenderPasses/EraserRenderPass.swift @@ -26,19 +26,20 @@ class EraserRenderPass: RenderPass { func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { } - func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) { - guard let elementGroup else { return } - guard let descriptor else { return } + @discardableResult + func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool { + guard let elementGroup else { return false } + guard let descriptor else { return false } // 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 guard case .stroke(let anyStroke) = element else { return nil } return anyStroke.value as? EraserStroke } let quads = eraserStrokes.flatMap { $0.quads } - guard !quads.isEmpty else { return } - guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return } + guard !quads.isEmpty else { return false } + guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false } computeEncoder.label = "Quad Compute Encoder" @@ -58,10 +59,10 @@ class EraserRenderPass: RenderPass { computeEncoder.endEncoding() // 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" - guard let eraserPipelineState else { return } + guard let eraserPipelineState else { return false } renderEncoder.setRenderPipelineState(eraserPipelineState) canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder) @@ -77,5 +78,6 @@ class EraserRenderPass: RenderPass { ) } renderEncoder.endEncoding() + return true } } diff --git a/Memola/Canvas/RenderPasses/GraphicRenderPass.swift b/Memola/Canvas/RenderPasses/GraphicRenderPass.swift index c392315..81a4da6 100644 --- a/Memola/Canvas/RenderPasses/GraphicRenderPass.swift +++ b/Memola/Canvas/RenderPasses/GraphicRenderPass.swift @@ -30,16 +30,18 @@ class GraphicRenderPass: RenderPass { func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { guard size != .zero else { return } 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].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0) descriptor?.colorAttachments[0].storeAction = .store let graphicContext = canvas.graphicContext if renderer.redrawsGraphicRender { + clearsTexture = true + photoBackgroundRenderPass?.clearsTexture = true canvas.setGraphicRenderType(.finished) var elementGroup: ElementGroup? let start = Date.now.timeIntervalSince1970 * 1000 @@ -51,7 +53,7 @@ class GraphicRenderPass: RenderPass { let _elementGroup = ElementGroup(_element) elementGroup = _elementGroup } else { - guard let _elementGroup = elementGroup else { return } + guard let _elementGroup = elementGroup else { continue } if _elementGroup.isSameElement(_element) { _elementGroup.add(_element) } else { @@ -75,6 +77,7 @@ class GraphicRenderPass: RenderPass { draw(for: elementGroup, into: commandBuffer, on: canvas, with: renderer) graphicContext.previousElement = nil } + return true } 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?.graphicDescriptor = descriptor strokeRenderPass?.graphicPipelineState = graphicPipelineState - strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) - clearsTexture = false + let status = strokeRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) + if clearsTexture, let status { + clearsTexture = !status + } case .eraser: descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load eraserRenderPass?.elementGroup = elementGroup eraserRenderPass?.descriptor = descriptor - eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) - clearsTexture = false + let status = eraserRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) + if clearsTexture, let status { + clearsTexture = !status + } case .photo: descriptor?.colorAttachments[0].loadAction = clearsTexture ? .clear : .load photoRenderPass?.elementGroup = elementGroup 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?.clearsTexture = clearsTexture - photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) - clearsTexture = false + let photoBackgroundStatus = photoBackgroundRenderPass?.draw(into: commandBuffer, on: canvas, with: renderer) + + if clearsTexture, let photoStatus { + clearsTexture = !photoStatus + } + if photoBackgroundRenderPass?.clearsTexture == true, let photoBackgroundStatus { + photoBackgroundRenderPass?.clearsTexture = !photoBackgroundStatus + } } } } diff --git a/Memola/Canvas/RenderPasses/PhotoBackgroundRenderPass.swift b/Memola/Canvas/RenderPasses/PhotoBackgroundRenderPass.swift index fbb5d11..86f1eb5 100644 --- a/Memola/Canvas/RenderPasses/PhotoBackgroundRenderPass.swift +++ b/Memola/Canvas/RenderPasses/PhotoBackgroundRenderPass.swift @@ -27,30 +27,31 @@ class PhotoBackgroundRenderPass: RenderPass { 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) } - func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) { - guard let elementGroup else { return } - guard let descriptor else { return } + func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool { + guard let elementGroup else { return false } + guard let descriptor else { return false } descriptor.colorAttachments[0].texture = photoBackgroundTexture descriptor.colorAttachments[0].storeAction = .store descriptor.colorAttachments[0].loadAction = clearsTexture ? .clear : .load 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 guard case .photo(let photo) = element else { return nil } 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" - guard let photoBackgroundPipelineState else { return } + guard let photoBackgroundPipelineState else { return false } renderEncoder.setRenderPipelineState(photoBackgroundPipelineState) canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder) @@ -60,5 +61,6 @@ class PhotoBackgroundRenderPass: RenderPass { } renderEncoder.endEncoding() + return true } } diff --git a/Memola/Canvas/RenderPasses/PhotoRenderPass.swift b/Memola/Canvas/RenderPasses/PhotoRenderPass.swift index f4c291c..843aa18 100644 --- a/Memola/Canvas/RenderPasses/PhotoRenderPass.swift +++ b/Memola/Canvas/RenderPasses/PhotoRenderPass.swift @@ -24,21 +24,22 @@ class PhotoRenderPass: RenderPass { func resize(on view: MTKView, to size: CGSize, with renderer: Renderer) { } - func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) { - guard let elementGroup else { return } - guard let descriptor else { return } + @discardableResult + func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool { + 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 guard case .photo(let photo) = element else { return nil } 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" - guard let photoPipelineState else { return } + guard let photoPipelineState else { return false } renderEncoder.setRenderPipelineState(photoPipelineState) canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder) @@ -48,5 +49,6 @@ class PhotoRenderPass: RenderPass { } renderEncoder.endEncoding() + return true } } diff --git a/Memola/Canvas/RenderPasses/StrokeRenderPass.swift b/Memola/Canvas/RenderPasses/StrokeRenderPass.swift index 90061aa..de89aa5 100644 --- a/Memola/Canvas/RenderPasses/StrokeRenderPass.swift +++ b/Memola/Canvas/RenderPasses/StrokeRenderPass.swift @@ -34,12 +34,13 @@ class StrokeRenderPass: RenderPass { strokeTexture = Textures.createStrokeTexture(from: renderer, size: size, pixelFormat: view.colorPixelFormat) } - func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) { - guard let elementGroup else { return } - guard let descriptor else { return } + @discardableResult + func draw(into commandBuffer: any MTLCommandBuffer, on canvas: Canvas, with renderer: Renderer) -> Bool { + guard let elementGroup else { return false } + guard let descriptor else { return false } // 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 guard case .stroke(let anyStroke) = element else { return nil } return anyStroke.value as? PenStroke @@ -47,8 +48,8 @@ class StrokeRenderPass: RenderPass { let penStroke = penStrokes.first let quads = penStrokes.flatMap { $0.quads } let erasedQuads = Set(penStrokes.flatMap { $0.eraserStrokes }).flatMap { $0.quads } - guard !quads.isEmpty else { return } - guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return } + guard !quads.isEmpty else { return false } + guard let computeEncoder = commandBuffer.makeComputeCommandEncoder() else { return false } computeEncoder.label = "Quad Compute Encoder" @@ -68,16 +69,16 @@ class StrokeRenderPass: RenderPass { computeEncoder.endEncoding() // MARK: - Rendering stroke - guard let strokeTexture else { return } + guard let strokeTexture else { return false } descriptor.colorAttachments[0].texture = strokeTexture descriptor.colorAttachments[0].clearColor = MTLClearColor(red: 1, green: 1, blue: 1, alpha: 0) descriptor.colorAttachments[0].loadAction = .clear 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" - guard let strokePipelineState else { return } + guard let strokePipelineState else { return false } renderEncoder.setRenderPipelineState(strokePipelineState) canvas.setUniformsBuffer(device: renderer.device, renderEncoder: renderEncoder) @@ -100,7 +101,7 @@ class StrokeRenderPass: RenderPass { // MARK: Erasing path 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" @@ -120,7 +121,7 @@ class StrokeRenderPass: RenderPass { computeEncoder.endEncoding() 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.setRenderPipelineState(eraserPipelineState) @@ -140,8 +141,8 @@ class StrokeRenderPass: RenderPass { } // MARK: Drawing on graphic texture - guard let graphicDescriptor, let graphicPipelineState else { return } - guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return } + guard let graphicDescriptor, let graphicPipelineState else { return false } + guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: graphicDescriptor) else { return false } renderEncoder.label = "Stroke Graphic Render Encoder" renderEncoder.setRenderPipelineState(graphicPipelineState) @@ -151,5 +152,7 @@ class StrokeRenderPass: RenderPass { renderEncoder.setVertexBuffer(uniformsBuffer, offset: 0, index: 11) canvas.renderGraphic(device: renderer.device, renderEncoder: renderEncoder) renderEncoder.endEncoding() + + return true } } diff --git a/Memola/Canvas/RenderPasses/ViewPortRenderPass.swift b/Memola/Canvas/RenderPasses/ViewPortRenderPass.swift index 6a25ea3..d939bb1 100644 --- a/Memola/Canvas/RenderPasses/ViewPortRenderPass.swift +++ b/Memola/Canvas/RenderPasses/ViewPortRenderPass.swift @@ -22,6 +22,9 @@ class ViewPortRenderPass: RenderPass { weak var view: MTKView? + var excludesGraphic: Bool = false + var excludesPhotoBackground: Bool = false + init(renderer: Renderer) { pointGridPipelineState = PipelineStates.createPointGridPipelineState(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 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 { - return + return false } guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: descriptor) else { - return + return false } renderEncoder.label = "View Port Render Encoder" @@ -44,47 +48,56 @@ class ViewPortRenderPass: RenderPass { case .none: break case .point: - guard let pointGridPipelineState else { return } + guard let pointGridPipelineState else { return false } renderEncoder.setRenderPipelineState(pointGridPipelineState) canvas.renderPointGrid(device: renderer.device, renderEncoder: renderEncoder) case .line: - guard let lineGridPipelineState else { return } + guard let lineGridPipelineState else { return false } renderEncoder.setRenderPipelineState(lineGridPipelineState) canvas.renderLineGrid(device: renderer.device, renderEncoder: renderEncoder) } if renderer.updatesViewPort { guard let viewPortUpdatePipelineState else { - return + return false } renderEncoder.setRenderPipelineState(viewPortUpdatePipelineState) - renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0) - canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder) + if !excludesPhotoBackground { + renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0) + canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder) + } - renderEncoder.setFragmentTexture(cacheTexture, index: 0) - canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder) + if !excludesGraphic { + renderEncoder.setFragmentTexture(cacheTexture, index: 0) + canvas.renderViewPortUpdate(device: renderer.device, renderEncoder: renderEncoder) + } } else { guard let viewPortPipelineState else { - return + return false } renderEncoder.setRenderPipelineState(viewPortPipelineState) - renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0) - canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder) + if !excludesPhotoBackground { + renderEncoder.setFragmentTexture(photoBackgroundTexture, index: 0) + canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder) + } - renderEncoder.setFragmentTexture(cacheTexture, index: 0) - canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder) + if !excludesGraphic { + renderEncoder.setFragmentTexture(cacheTexture, index: 0) + canvas.renderViewPort(device: renderer.device, renderEncoder: renderEncoder) + } } renderEncoder.endEncoding() guard let drawable = view?.currentDrawable else { - return + return false } commandBuffer.present(drawable) commandBuffer.commit() + return true } } diff --git a/Memola/Canvas/Tool/Core/Tool.swift b/Memola/Canvas/Tool/Core/Tool.swift index c68c7c3..18502f7 100644 --- a/Memola/Canvas/Tool/Core/Tool.swift +++ b/Memola/Canvas/Tool/Core/Tool.swift @@ -125,10 +125,10 @@ public class Tool: NSObject, ObservableObject { func selectPhoto(_ image: UIImage, for canvasID: NSManagedObjectID) { guard let (resizedImage, dimension) = resizePhoto(of: image) else { return } - let photoItem = bookmarkPhoto(of: resizedImage, in: dimension, with: canvasID) - isLoadingPhoto = false + let photoItem = bookmarkPhoto(of: resizedImage, and: image, in: dimension, with: canvasID) withAnimation { selectedPhotoItem = photoItem + isLoadingPhoto = false } } @@ -153,7 +153,7 @@ public class Tool: NSObject, ObservableObject { 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 } let fileManager = FileManager.default guard let directory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { @@ -180,7 +180,7 @@ public class Tool: NSObject, ObservableObject { var photoBookmark: PhotoItem? do { 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 { NSLog("[Memola] - \(error.localizedDescription)") } diff --git a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift index 167f97f..395b096 100644 --- a/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift +++ b/Memola/Canvas/View/Bridge/ViewController/CanvasViewController.swift @@ -259,7 +259,7 @@ extension CanvasViewController: UIScrollViewDelegate { func scrollViewDidZoom(_ scrollView: UIScrollView) { canvas.setZoomScale(scrollView.zoomScale) - renderer.resize(on: renderView, to: renderView.drawableSize) +// renderer.resize(on: renderView, to: renderView.drawableSize) renderView.draw() } @@ -274,7 +274,7 @@ extension CanvasViewController: UIScrollViewDelegate { } func scrollViewDidScroll(_ scrollView: UIScrollView) { - renderer.resize(on: renderView, to: renderView.drawableSize) +// renderer.resize(on: renderView, to: renderView.drawableSize) renderView.draw() } @@ -302,12 +302,12 @@ extension CanvasViewController { drawingView.touchCancelled() canvas.updateClipBounds(scrollView, on: drawingView) drawingView.disableUserInteraction() - renderer.updatesViewPort = true + renderer.setUpdatesViewPort(true) } func magnificationEnded() { - renderer.updatesViewPort = false - renderer.resize(on: renderView, to: renderView.drawableSize) + renderer.setUpdatesViewPort(false) + renderer.setRedrawsGraphicRender() renderView.draw() drawingView.enableUserInteraction() } @@ -316,12 +316,12 @@ extension CanvasViewController { guard !renderer.updatesViewPort else { return } canvas.updateClipBounds(scrollView, on: drawingView) drawingView.disableUserInteraction() - renderer.updatesViewPort = true + renderer.setUpdatesViewPort(true) } func draggingEnded() { - renderer.updatesViewPort = false - renderer.resize(on: renderView, to: renderView.drawableSize) + renderer.setUpdatesViewPort(false) + renderer.setRedrawsGraphicRender() renderView.draw() drawingView.enableUserInteraction() } @@ -371,7 +371,7 @@ extension CanvasViewController { func gridModeChanged(_ mode: GridMode) { drawingView.disableUserInteraction() - renderer.resize(on: renderView, to: renderView.drawableSize) + renderer.setRedrawsGraphicRender() renderView.draw() drawingView.enableUserInteraction() } @@ -382,8 +382,7 @@ extension CanvasViewController { guard let event = history.undo() else { return } drawingView.disableUserInteraction() canvas.graphicContext.undoGraphic(for: event) - renderer.redrawsGraphicRender = true - renderer.resize(on: renderView, to: renderView.drawableSize) + renderer.setRedrawsGraphicRender() renderView.draw() drawingView.enableUserInteraction() } @@ -392,8 +391,7 @@ extension CanvasViewController { guard let event = history.redo() else { return } drawingView.disableUserInteraction() canvas.graphicContext.redoGraphic(for: event) - renderer.redrawsGraphicRender = true - renderer.resize(on: renderView, to: renderView.drawableSize) + renderer.setRedrawsGraphicRender() renderView.draw() drawingView.enableUserInteraction() } diff --git a/Memola/Features/Memo/PhotoPreview/PhotoItem.swift b/Memola/Features/Memo/PhotoPreview/PhotoItem.swift index d38272e..79e2539 100644 --- a/Memola/Features/Memo/PhotoPreview/PhotoItem.swift +++ b/Memola/Features/Memo/PhotoPreview/PhotoItem.swift @@ -11,6 +11,7 @@ import Foundation struct PhotoItem: Identifiable, Equatable { var id: URL let image: UIImage + let previewImage: UIImage let dimension: CGSize let bookmark: Data diff --git a/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift b/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift index 1fcf43e..16e7399 100644 --- a/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift +++ b/Memola/Features/Memo/PhotoPreview/PhotoPreview.swift @@ -12,10 +12,10 @@ struct PhotoPreview: View { @ObservedObject var tool: Tool var body: some View { - Image(uiImage: photoItem.image) + Image(uiImage: photoItem.previewImage) .resizable() - .scaledToFill() - .frame(width: 100, height: 100) + .scaledToFit() + .frame(height: 100) .cornerRadius(5) .overlay { RoundedRectangle(cornerRadius: 5) diff --git a/Memola/Features/Memos/MemosView.swift b/Memola/Features/Memos/MemosView.swift index 3eedd63..680f278 100644 --- a/Memola/Features/Memos/MemosView.swift +++ b/Memola/Features/Memos/MemosView.swift @@ -85,6 +85,7 @@ struct MemosView: View { penObject.color = color.components return penObject } + markerPenObjects.first?.isSelected = true let graphicContextObject = GraphicContextObject(\.viewContext) graphicContextObject.elements = []