From b839bb5b797ddf5aac6e2356aa916693c8be276f Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 15 Jun 2022 10:53:34 -0400 Subject: [PATCH] Properly rotate and flip canvas on PSD export --- SilicaViewer/AppDelegate.swift | 74 +++++++++++++++++++++++-- SilicaViewer/Base.lproj/Main.storyboard | 4 +- SilicaViewer/Document.swift | 6 +- 3 files changed, 73 insertions(+), 11 deletions(-) diff --git a/SilicaViewer/AppDelegate.swift b/SilicaViewer/AppDelegate.swift index c0e3c58..9eaceb5 100644 --- a/SilicaViewer/AppDelegate.swift +++ b/SilicaViewer/AppDelegate.swift @@ -123,23 +123,50 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { try? canvasPng?.write(to: savePanel.url!) } else { - let writer = PSDWriter(documentSize: (document?.info.cgSize)!) + var degreesToRotate = 0.0 + + if document!.info.orientation == 3 { + degreesToRotate = 90 + } else if document!.info.orientation == 4 { + degreesToRotate = -90 + } + + var flipHoriz = false + var flipVert = false + + if document!.info.flippedHorizontally && (document!.info.orientation == 1 || document!.info.orientation == 2) { + flipHoriz = true + } else if document!.info.flippedHorizontally && (document!.info.orientation == 3 || document!.info.orientation == 4) { + flipVert = true + } else if document!.info.flippedVertically && (document!.info.orientation == 1 || document!.info.orientation == 2) { + flipVert = true + } else if !document!.info.flippedVertically && (document!.info.orientation == 3 || document!.info.orientation == 4) { + flipHoriz = true + } + + var rect = document!.info.cgRect + rect.size.width = document!.info.cgSize.height + rect.size.height = document!.info.cgSize.width + + let writer = PSDWriter(documentSize: rect.size) writer?.shouldUnpremultiplyLayerData = true let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) - let ccgContext = CGContext(data: nil, width: document!.info.width, height: document!.info.height, bitsPerComponent: 8, bytesPerRow: document!.info.width * 4, space: document!.info.colorSpace, bitmapInfo: bitmapInfo.rawValue) + let ccgContext = CGContext(data: nil, width: Int(rect.size.width), height: Int(rect.size.height), bitsPerComponent: 8, bytesPerRow: Int(rect.size.width) * 4, space: document!.info.colorSpace, bitmapInfo: bitmapInfo.rawValue) ccgContext?.setFillColor(document!.info.backgroundColor) - ccgContext?.fill(document!.info.cgRect) + ccgContext?.fill(rect) writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero) for layer in document!.info.layers.reversed() { - let finalCgImage = document!.simpleDrawLayer(layer)! + let finalCgImage = document!.simpleDrawLayer(layer.data)!.rotate(angle: self.degreesToRadians(degreesToRotate), flipHorizontally: flipHoriz, flipVertically: flipVert) if(layer.mask != nil) { - writer?.addLayer(with: document!.makeBlendImage(layer), andName: layer.name + " (Mask)", andOpacity: 1.0, andOffset: .zero) + let mask = document!.simpleDrawLayer(layer.mask!)!.rotate(angle: self.degreesToRadians(degreesToRotate), flipHorizontally: flipHoriz, flipVertically: flipVert) + + writer?.addLayer(with: mask, andName: layer.name + " (Mask)", andOpacity: 1.0, andOffset: .zero) } writer?.addLayer(with: finalCgImage, andName: layer.name, andOpacity: Float(layer.data.opacity), andOffset: .zero, andBlendMode: Int(self.getPSDBlendMode(layer).rawValue), andIsNonBaseLayer: layer.clipped) @@ -152,6 +179,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { } } + func degreesToRadians(_ value: Double) -> Double { + return value * .pi / 180 + } + @IBAction func exportThumbnailAction(_ sender: Any) { let document = NSApplication.shared.keyWindow?.windowController?.document as? Document; @@ -273,6 +304,37 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { } } } - +} + +extension CGImage { + // we really only handle 90 degree turns, which is totally fine + func rotate(angle : CGFloat, flipHorizontally : Bool, flipVertically : Bool) -> CGImage? { + let newWidth: CGFloat = CGFloat(height) + let newHeight: CGFloat = CGFloat(width) + + let ctx = CGContext(data: nil, + width: Int(newWidth), + height: Int(newHeight), + bitsPerComponent: bitsPerComponent, + bytesPerRow: bytesPerRow, + space: colorSpace!, + bitmapInfo: bitmapInfo.rawValue)! + + ctx.translateBy(x: newWidth / 2, y: newHeight / 2) + ctx.rotate(by: -angle) + + if flipVertically { + ctx.scaleBy(x: 1.0, y: -1.0) + } + + if flipHorizontally { + ctx.scaleBy(x: -1.0, y: 1.0) + } + + let dstRect = CGRect(x: -width / 2, y: -height / 2, width: width, height: height) + ctx.draw(self, in: dstRect) + + return ctx.makeImage() + } } diff --git a/SilicaViewer/Base.lproj/Main.storyboard b/SilicaViewer/Base.lproj/Main.storyboard index 88ce3af..4b8360d 100644 --- a/SilicaViewer/Base.lproj/Main.storyboard +++ b/SilicaViewer/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index 73f5d06..774e2d4 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -578,7 +578,7 @@ class Document: NSDocument { return (maskContext?.makeImage())! } - func simpleDrawLayer(_ layer : SilicaLayer) -> CGImage? { + func simpleDrawLayer(_ layer : SilicaLayerData) -> CGImage? { let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) // start by creating a new layer composite image, needed for image masking @@ -586,11 +586,11 @@ class Document: NSDocument { layerContext?.clear(info.cgRect) - for chunk in layer.data.chunks { + for chunk in layer.chunks { layerContext?.setAlpha(1.0) layerContext?.setBlendMode(.normal) - if !layer.data.hidden { + if !layer.hidden { layerContext?.draw(chunk.image!, in: getChunkRect(chunk)) } }