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 {
var x: Int = 0
var y: Int = 0
var image: NSImage = NSImage()
var image: CGImage?
}
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.
*/
func getTileSize(x: Int, y: Int) -> (Int, Int) {
func getTileSize(_ x: Int, _ y: Int) -> (Int, Int) {
var width: Int = info.tileSize
var height: Int = info.tileSize
@ -146,7 +146,7 @@ class Document: NSDocument {
let x = chunk.x
var y = chunk.y
let (width, height) = getTileSize(x: x, y: y)
let (width, height) = getTileSize(x, y)
if y == rows {
y = 0
@ -240,7 +240,7 @@ class Document: NSDocument {
return .luminosity
}
return .sourceOver;
return .sourceAtop
}
func parseSilicaLayer(archive: Archive, dict: NSDictionary, isMask: Bool) -> SilicaLayer? {
@ -288,7 +288,7 @@ class Document: NSDocument {
let queue = DispatchQueue(label: "imageWork")
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
}
@ -298,7 +298,7 @@ class Document: NSDocument {
return
}
let (width, height) = getTileSize(x: x, y: y)
let (width, height) = getTileSize(x, y)
let numChannels = isMask ? 1 : 4
let byteSize = width * height * numChannels
@ -327,10 +327,8 @@ class Document: NSDocument {
return
}
let image = NSImage(cgImage: cgimage, size: NSZeroSize)
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].y = y
}
@ -491,68 +489,70 @@ class Document: NSDocument {
ccgContext?.setFillColor(info.backgroundColor)
ccgContext?.fill(info.cgRect)
var masterImage = ccgContext?.makeImage()
let context = CIContext()
var masterImage = CIImage(cgImage: (ccgContext?.makeImage())!)
var previousImage: CGImage? = masterImage
var previousImage: CGImage? = nil
for layer in info.layers.reversed() {
// 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)
if !layer.data.hidden {
// 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)
maskContext?.fill(info.cgRect)
Swift.print(layer.name + " is " + kernel!.name)
let kernel = getBlendKernel(layer)
if layer.mask != nil {
let grayColorSpace = CGColorSpaceCreateDeviceGray()
let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big)
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))
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)
for chunk in layer.mask!.chunks {
maskContext?.draw(chunk.image!, in: getChunkRect(chunk))
}
}
}
for chunk in layer.data.chunks {
layerContext?.setAlpha(CGFloat(layer.data.opacity))
layerContext?.setBlendMode(.copy)
for chunk in layer.data.chunks {
layerContext?.setAlpha(CGFloat(layer.data.opacity))
layerContext?.setBlendMode(.normal)
if !layer.data.hidden {
layerContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: graphicsContext, hints: nil)!, in: getChunkRect(chunk))
if !layer.data.hidden {
layerContext?.draw(chunk.image!, in: getChunkRect(chunk))
}
}
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)!
}
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
let ciImage = CIImage(cgImage: (masterImage)!)
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)!
}
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 {
image = image.imageRotatedByDegreess(degrees: 90)