From 8a83dbc25b1f117577bf5cf1022c881d47a02ac5 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 29 Sep 2021 13:02:32 -0400 Subject: [PATCH] Prevent a bunch of unnecessary CG->NS<-CI image conversions --- SilicaViewer/Document.swift | 124 ++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index 80a55f4..f6c405c 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -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 @@ -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 { 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 previousImage: CGImage? = masterImage + var masterImage = CIImage(cgImage: (ccgContext?.makeImage())!) + + 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) - - let graphicsContext = NSGraphicsContext(cgContext: layerContext!, flipped: false) - - let grayColorSpace = CGColorSpaceCreateDeviceGray() - let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) - - let 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) + 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) + + layerContext?.clear(info.cgRect) + + var maskContext: CGContext? + + let kernel = getBlendKernel(layer) + + Swift.print(layer.name + " is " + kernel!.name) + + 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) - - 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.mask!.chunks { + maskContext?.draw(chunk.image!, in: getChunkRect(chunk)) } } - } - - for chunk in layer.data.chunks { - layerContext?.setAlpha(CGFloat(layer.data.opacity)) - layerContext?.setBlendMode(.copy) - if !layer.data.hidden { - layerContext?.draw(chunk.image.cgImage(forProposedRect: nil, context: graphicsContext, hints: nil)!, 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 { - let maskImage = (maskContext?.makeImage())! - let newImage = layerImage!.masking(maskImage)! - - previousImage = newImage - } else { - previousImage = layerImage - } + for chunk in layer.data.chunks { + layerContext?.setAlpha(CGFloat(layer.data.opacity)) + layerContext?.setBlendMode(.normal) - // 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)! + 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)! + } } - 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)