Automatically unpremultiply, prevent duplicate opacity and blending for PSD export
This fixes the last few bugs related to PSD export!
This commit is contained in:
parent
981604d68d
commit
ded53bb5ae
2 changed files with 60 additions and 33 deletions
|
@ -124,8 +124,7 @@ 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)!)
|
let writer = PSDWriter(documentSize: (document?.info.cgSize)!)
|
||||||
|
writer?.shouldUnpremultiplyLayerData = true
|
||||||
let cgImage = document?.makeComposite()?.cgImage(forProposedRect: nil, context: nil, hints: nil)
|
|
||||||
|
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
|
@ -134,26 +133,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations {
|
||||||
ccgContext?.setFillColor(document!.info.backgroundColor)
|
ccgContext?.setFillColor(document!.info.backgroundColor)
|
||||||
ccgContext?.fill(document!.info.cgRect)
|
ccgContext?.fill(document!.info.cgRect)
|
||||||
|
|
||||||
let context = CIContext()
|
|
||||||
|
|
||||||
writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero)
|
writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero)
|
||||||
|
|
||||||
var masterImage = CIImage(cgImage: cgImage!)
|
|
||||||
|
|
||||||
for layer in document!.info.layers.reversed() {
|
for layer in document!.info.layers.reversed() {
|
||||||
var test : CGImage? = nil
|
let finalCgImage = document!.simpleDrawLayer(layer)!
|
||||||
|
|
||||||
masterImage = document!.blendLayer(layer, previousImage: &test)
|
|
||||||
|
|
||||||
let finalCgImage = context.createCGImage(masterImage, from: document!.info.cgRect, format: .RGBA8, colorSpace: document!.info.colorSpace)
|
|
||||||
|
|
||||||
if(layer.mask != nil) {
|
if(layer.mask != nil) {
|
||||||
writer?.addLayer(with: document!.makeBlendImage(layer), andName: layer.name + " (Mask)", andOpacity: 1.0, andOffset: .zero)
|
writer?.addLayer(with: document!.makeBlendImage(layer), 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, andMaskLayerName: layer.name + " (Mask)")
|
|
||||||
} else {
|
|
||||||
writer?.addLayer(with: finalCgImage, andName: layer.name, andOpacity: Float(layer.data.opacity), andOffset: .zero, andBlendMode: Int(self.getPSDBlendMode(layer).rawValue), andIsNonBaseLayer: layer.clipped, andMaskLayerName: nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writer?.addLayer(with: finalCgImage, andName: layer.name, andOpacity: Float(layer.data.opacity), andOffset: .zero, andBlendMode: Int(self.getPSDBlendMode(layer).rawValue), andIsNonBaseLayer: layer.clipped)
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = writer?.createPSDData()
|
let data = writer?.createPSDData()
|
||||||
|
|
|
@ -578,6 +578,26 @@ class Document: NSDocument {
|
||||||
return (maskContext?.makeImage())!
|
return (maskContext?.makeImage())!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func simpleDrawLayer(_ layer : SilicaLayer) -> CGImage? {
|
||||||
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
|
// start by creating a new layer composite image, needed for image masking
|
||||||
|
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue)
|
||||||
|
|
||||||
|
layerContext?.clear(info.cgRect)
|
||||||
|
|
||||||
|
for chunk in layer.data.chunks {
|
||||||
|
layerContext?.setAlpha(1.0)
|
||||||
|
layerContext?.setBlendMode(.normal)
|
||||||
|
|
||||||
|
if !layer.data.hidden {
|
||||||
|
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return layerContext?.makeImage()
|
||||||
|
}
|
||||||
|
|
||||||
func blendLayer(_ layer : SilicaLayer, previousImage : inout CGImage?) -> CIImage {
|
func blendLayer(_ layer : SilicaLayer, previousImage : inout CGImage?) -> CIImage {
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
|
@ -628,6 +648,35 @@ class Document: NSDocument {
|
||||||
return clippingLayers
|
return clippingLayers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func blendLayer(_ layer : SilicaLayer, previousImage : inout CGImage?, applyOpacity : Bool) -> CIImage {
|
||||||
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
|
||||||
|
// start by creating a new layer composite image, needed for image masking
|
||||||
|
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue)
|
||||||
|
|
||||||
|
layerContext?.clear(info.cgRect)
|
||||||
|
|
||||||
|
let kernel = getBlendKernel(layer)
|
||||||
|
|
||||||
|
for chunk in layer.data.chunks {
|
||||||
|
if applyOpacity {
|
||||||
|
layerContext?.setAlpha(CGFloat(layer.data.opacity))
|
||||||
|
} else {
|
||||||
|
layerContext?.setAlpha(1.0)
|
||||||
|
}
|
||||||
|
layerContext?.setBlendMode(.normal)
|
||||||
|
|
||||||
|
if !layer.data.hidden {
|
||||||
|
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layerImage = layerContext?.makeImage()
|
||||||
|
|
||||||
|
// apply image
|
||||||
|
return kernel!.apply(foreground: CIImage(cgImage: layerImage!), background: previousImage == nil ? CIImage(color: .clear) : CIImage(cgImage: previousImage!), colorSpace: info.colorSpace)!
|
||||||
|
}
|
||||||
|
|
||||||
func makeComposite() -> NSImage? {
|
func makeComposite() -> NSImage? {
|
||||||
// create the final composite output image
|
// create the final composite output image
|
||||||
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big)
|
||||||
|
@ -656,10 +705,7 @@ class Document: NSDocument {
|
||||||
|
|
||||||
var maskContext: CGContext?
|
var maskContext: CGContext?
|
||||||
|
|
||||||
var kernel = getBlendKernel(layer)
|
let kernel = getBlendKernel(layer)
|
||||||
//kernel = .sourceOver
|
|
||||||
|
|
||||||
Swift.print(layer.name + " - " + kernel!.name)
|
|
||||||
|
|
||||||
if layer.mask != nil {
|
if layer.mask != nil {
|
||||||
let grayColorSpace = CGColorSpaceCreateDeviceGray()
|
let grayColorSpace = CGColorSpaceCreateDeviceGray()
|
||||||
|
@ -690,19 +736,13 @@ class Document: NSDocument {
|
||||||
|
|
||||||
var clippedMaster: CGImage? = layerImage
|
var clippedMaster: CGImage? = layerImage
|
||||||
for layer in clippingLayers {
|
for layer in clippingLayers {
|
||||||
Swift.print("- " + layer.name + " is clipping with us...")
|
|
||||||
|
|
||||||
// so we if we want to clip, we want to gather all of the clipping layers in order first...
|
// so we if we want to clip, we want to gather all of the clipping layers in order first...
|
||||||
|
|
||||||
Swift.print("processing clipped layer " + layer.name)
|
|
||||||
|
|
||||||
let temporaryClippedMaster = blendLayer(layer, previousImage: &clippedMaster)
|
let temporaryClippedMaster = blendLayer(layer, previousImage: &clippedMaster)
|
||||||
|
|
||||||
clippedMaster = context.createCGImage(temporaryClippedMaster, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace)
|
clippedMaster = context.createCGImage(temporaryClippedMaster, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
Swift.print("clipping " + layer.name + "...")
|
|
||||||
|
|
||||||
layerContext?.setAlpha(1.0)
|
layerContext?.setAlpha(1.0)
|
||||||
layerContext?.setBlendMode(.sourceAtop)
|
layerContext?.setBlendMode(.sourceAtop)
|
||||||
|
|
||||||
|
@ -712,8 +752,6 @@ class Document: NSDocument {
|
||||||
let layerImage = layerContext?.makeImage()
|
let layerImage = layerContext?.makeImage()
|
||||||
|
|
||||||
if layer.mask != nil && maskContext != nil {
|
if layer.mask != nil && maskContext != nil {
|
||||||
Swift.print("masking " + layer.name + "...")
|
|
||||||
|
|
||||||
let maskImage = (maskContext?.makeImage())!
|
let maskImage = (maskContext?.makeImage())!
|
||||||
let newImage = layerImage!.masking(maskImage)!
|
let newImage = layerImage!.masking(maskImage)!
|
||||||
|
|
||||||
|
|
Reference in a new issue