diff --git a/SilicaViewer/AppDelegate.swift b/SilicaViewer/AppDelegate.swift index 95511d0..c0e3c58 100644 --- a/SilicaViewer/AppDelegate.swift +++ b/SilicaViewer/AppDelegate.swift @@ -124,36 +124,25 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { try? canvasPng?.write(to: savePanel.url!) } else { let writer = PSDWriter(documentSize: (document?.info.cgSize)!) - - let cgImage = document?.makeComposite()?.cgImage(forProposedRect: nil, context: nil, hints: nil) - + 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) ccgContext?.setFillColor(document!.info.backgroundColor) ccgContext?.fill(document!.info.cgRect) - - let context = CIContext() - + writer?.addLayer(with: ccgContext?.makeImage(), andName: "Background", andOpacity: 1.0, andOffset: .zero) - - var masterImage = CIImage(cgImage: cgImage!) - - for layer in document!.info.layers.reversed() { - var test : CGImage? = nil - - masterImage = document!.blendLayer(layer, previousImage: &test) - - let finalCgImage = context.createCGImage(masterImage, from: document!.info.cgRect, format: .RGBA8, colorSpace: document!.info.colorSpace) + + for layer in document!.info.layers.reversed() { + let finalCgImage = document!.simpleDrawLayer(layer)! if(layer.mask != nil) { 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() diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index a6cb7d1..73f5d06 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -578,6 +578,26 @@ class Document: NSDocument { 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 { let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) @@ -628,6 +648,35 @@ class Document: NSDocument { 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? { // create the final composite output image let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) @@ -656,10 +705,7 @@ class Document: NSDocument { var maskContext: CGContext? - var kernel = getBlendKernel(layer) - //kernel = .sourceOver - - Swift.print(layer.name + " - " + kernel!.name) + let kernel = getBlendKernel(layer) if layer.mask != nil { let grayColorSpace = CGColorSpaceCreateDeviceGray() @@ -690,19 +736,13 @@ class Document: NSDocument { var clippedMaster: CGImage? = layerImage 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... - - Swift.print("processing clipped layer " + layer.name) - + let temporaryClippedMaster = blendLayer(layer, previousImage: &clippedMaster) clippedMaster = context.createCGImage(temporaryClippedMaster, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace) } - Swift.print("clipping " + layer.name + "...") - layerContext?.setAlpha(1.0) layerContext?.setBlendMode(.sourceAtop) @@ -711,9 +751,7 @@ class Document: NSDocument { let layerImage = layerContext?.makeImage() - if layer.mask != nil && maskContext != nil { - Swift.print("masking " + layer.name + "...") - + if layer.mask != nil && maskContext != nil { let maskImage = (maskContext?.makeImage())! let newImage = layerImage!.masking(maskImage)!