Properly rotate and flip canvas on PSD export
This commit is contained in:
parent
9f3d694469
commit
b839bb5b79
3 changed files with 73 additions and 11 deletions
|
@ -123,23 +123,50 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
|
||||||
|
|
||||||
try? canvasPng?.write(to: savePanel.url!)
|
try? canvasPng?.write(to: savePanel.url!)
|
||||||
} else {
|
} 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
|
writer?.shouldUnpremultiplyLayerData = true
|
||||||
|
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
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?.setFillColor(document!.info.backgroundColor)
|
||||||
ccgContext?.fill(document!.info.cgRect)
|
ccgContext?.fill(rect)
|
||||||
|
|
||||||
writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero)
|
writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero)
|
||||||
|
|
||||||
for layer in document!.info.layers.reversed() {
|
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) {
|
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)
|
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) {
|
@IBAction func exportThumbnailAction(_ sender: Any) {
|
||||||
let document = NSApplication.shared.keyWindow?.windowController?.document as? Document;
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="19529"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="20037"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
|
|
|
@ -578,7 +578,7 @@ class Document: NSDocument {
|
||||||
return (maskContext?.makeImage())!
|
return (maskContext?.makeImage())!
|
||||||
}
|
}
|
||||||
|
|
||||||
func simpleDrawLayer(_ layer : SilicaLayer) -> CGImage? {
|
func simpleDrawLayer(_ layer : SilicaLayerData) -> CGImage? {
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
// start by creating a new layer composite image, needed for image masking
|
// start by creating a new layer composite image, needed for image masking
|
||||||
|
@ -586,11 +586,11 @@ class Document: NSDocument {
|
||||||
|
|
||||||
layerContext?.clear(info.cgRect)
|
layerContext?.clear(info.cgRect)
|
||||||
|
|
||||||
for chunk in layer.data.chunks {
|
for chunk in layer.chunks {
|
||||||
layerContext?.setAlpha(1.0)
|
layerContext?.setAlpha(1.0)
|
||||||
layerContext?.setBlendMode(.normal)
|
layerContext?.setBlendMode(.normal)
|
||||||
|
|
||||||
if !layer.data.hidden {
|
if !layer.hidden {
|
||||||
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
|
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue