1
Fork 0

Prevent a bunch of unnecessary CG->NS<-CI image conversions

This commit is contained in:
Joshua Goins 2021-09-29 13:02:32 -04:00
parent 3c7c1f865a
commit 8a83dbc25b

View file

@ -6,7 +6,7 @@ import Accelerate
struct SilicaChunk { struct SilicaChunk {
var x: Int = 0 var x: Int = 0
var y: Int = 0 var y: Int = 0
var image: NSImage = NSImage() var image: CGImage?
} }
struct SilicaLayerData { struct SilicaLayerData {
@ -103,7 +103,7 @@ class Document: NSDocument {
/* /*
Returns the correct tile size, taking into account the remainder between tile size and image size. Returns the correct tile size, taking into account the remainder between tile size and image size.
*/ */
func getTileSize(x: Int, y: Int) -> (Int, Int) { func getTileSize(_ x: Int, _ y: Int) -> (Int, Int) {
var width: Int = info.tileSize var width: Int = info.tileSize
var height: Int = info.tileSize var height: Int = info.tileSize
@ -146,7 +146,7 @@ class Document: NSDocument {
let x = chunk.x let x = chunk.x
var y = chunk.y var y = chunk.y
let (width, height) = getTileSize(x: x, y: y) let (width, height) = getTileSize(x, y)
if y == rows { if y == rows {
y = 0 y = 0
@ -240,7 +240,7 @@ class Document: NSDocument {
return .luminosity return .luminosity
} }
return .sourceOver; return .sourceAtop
} }
func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? { func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? {
@ -288,7 +288,7 @@ class Document: NSDocument {
let queue = DispatchQueue(label: "imageWork") let queue = DispatchQueue(label: "imageWork")
DispatchQueue.concurrentPerform(iterations: chunkPaths.count) { (i: Int) in DispatchQueue.concurrentPerform(iterations: chunkPaths.count) { (i: Int) in
guard let threadArchive = Archive(data: self.data!, accessMode: Archive.AccessMode.read) else { guard let threadArchive = Archive(data: self.data!, accessMode: .read) else {
return return
} }
@ -298,7 +298,7 @@ class Document: NSDocument {
return return
} }
let (width, height) = getTileSize(x: x, y: y) let (width, height) = getTileSize(x, y)
let numChannels = isMask ? 1 : 4 let numChannels = isMask ? 1 : 4
let byteSize = width * height * numChannels let byteSize = width * height * numChannels
@ -326,11 +326,9 @@ class Document: NSDocument {
guard let cgimage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 8 * numChannels, bytesPerRow: width * numChannels, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: render) else { guard let cgimage = CGImage(width: width, height: height, bitsPerComponent: 8, bitsPerPixel: 8 * numChannels, bytesPerRow: width * numChannels, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: render) else {
return return
} }
let image = NSImage(cgImage: cgimage, size: NSZeroSize)
queue.async(group: dispatchGroup) { queue.async(group: dispatchGroup) {
layer.data.chunks[i].image = image layer.data.chunks[i].image = cgimage
layer.data.chunks[i].x = x layer.data.chunks[i].x = x
layer.data.chunks[i].y = y layer.data.chunks[i].y = y
} }
@ -491,68 +489,70 @@ class Document: NSDocument {
ccgContext?.setFillColor(info.backgroundColor) ccgContext?.setFillColor(info.backgroundColor)
ccgContext?.fill(info.cgRect) ccgContext?.fill(info.cgRect)
var masterImage = ccgContext?.makeImage()
let context = CIContext() let context = CIContext()
var masterImage = CIImage(cgImage: (ccgContext?.makeImage())!)
var previousImage: CGImage? = masterImage
var previousImage: CGImage? = nil
for layer in info.layers.reversed() { for layer in info.layers.reversed() {
// start by creating a new layer composite image, needed for image masking if !layer.data.hidden {
let layerContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 8, bytesPerRow: info.width * 4, space: info.colorSpace, bitmapInfo: bitmapInfo.rawValue) // 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)
let graphicsContext = NSGraphicsContext(cgContext: layerContext!, flipped: false)
layerContext?.clear(info.cgRect)
let grayColorSpace = CGColorSpaceCreateDeviceGray()
let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) var maskContext: CGContext?
let maskContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 16, bytesPerRow: 0, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue) let kernel = getBlendKernel(layer)
maskContext?.setFillColor(.white) Swift.print(layer.name + " is " + kernel!.name)
maskContext?.fill(info.cgRect)
if layer.mask != nil {
let grayColorSpace = CGColorSpaceCreateDeviceGray()
let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big)
maskContext = CGContext(data: nil, width: info.width, height: info.height, bitsPerComponent: 16, bytesPerRow: 0, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue)
maskContext?.setFillColor(.white)
maskContext?.fill(info.cgRect)
let kernel = getBlendKernel(layer) for chunk in layer.mask!.chunks {
maskContext?.draw(chunk.image!, in: getChunkRect(chunk))
if layer.mask != nil {
for chunk in layer.mask!.chunks {
if !layer.data.hidden {
maskContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: graphicsContext, hints: nil)!, in: getChunkRect(chunk))
} }
} }
}
for chunk in layer.data.chunks {
layerContext?.setAlpha(CGFloat(layer.data.opacity))
layerContext?.setBlendMode(.copy)
if !layer.data.hidden { for chunk in layer.data.chunks {
layerContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: graphicsContext, hints: nil)!, in: getChunkRect(chunk)) layerContext?.setAlpha(CGFloat(layer.data.opacity))
} layerContext?.setBlendMode(.normal)
}
let layerImage = layerContext?.makeImage()
if layer.clipped && previousImage != nil {
let result = previousImage!.toGrayscale()
let newImage = layerImage!.masking(result!)
previousImage = newImage
} else if layer.mask != nil {
let maskImage = (maskContext?.makeImage())!
let newImage = layerImage!.masking(maskImage)!
previousImage = newImage
} else {
previousImage = layerImage
}
// apply image if !layer.data.hidden {
let ciImage = CIImage(cgImage: (masterImage)!) layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
let newCiImage = kernel!.apply(foreground: CIImage(cgImage: previousImage!), background: ciImage, colorSpace: info.colorSpace) }
}
masterImage = context.createCGImage(newCiImage!, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace)!
let layerImage = layerContext?.makeImage()
if layer.clipped && previousImage != nil {
let result = previousImage!.toGrayscale()
let newImage = layerImage!.masking(result!)
previousImage = newImage
} else if layer.mask != nil && maskContext != nil {
let maskImage = (maskContext?.makeImage())!
let newImage = layerImage!.masking(maskImage)!
previousImage = newImage
} else {
previousImage = layerImage
}
// apply image
masterImage = kernel!.apply(foreground: CIImage(cgImage: previousImage!), background: masterImage, colorSpace: info.colorSpace)!
}
} }
var image = NSImage(cgImage: masterImage!, size: info.nsSize) let cgImage = context.createCGImage(masterImage, from: info.cgRect, format: .RGBA8, colorSpace: info.colorSpace)!
var image = NSImage(cgImage: cgImage, size: info.nsSize)
if info.orientation == 3 { if info.orientation == 3 {
image = image.imageRotatedByDegreess(degrees: 90) image = image.imageRotatedByDegreess(degrees: 90)