From 6ad06a1360f17e2a12eb981f1b65bc6dac38ef4a Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 21 Sep 2021 04:41:05 -0400 Subject: [PATCH] Create toGrayscale function --- SilicaViewer/Document.swift | 177 +++++++++++++++++------------------- 1 file changed, 83 insertions(+), 94 deletions(-) diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index cd343be..a54e73c 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -143,9 +143,7 @@ class Document: NSDocument { layer.data.opacity = (dict["opacity"] as? NSNumber)!.doubleValue layer.data.hidden = (dict["hidden"] as? Bool)! layer.clipped = (dict["clipped"] as? Bool)! - - dump(dict, indent: 2) - + if maskClassID != 0 { layer.mask = parseSilicaLayer(archive: archive, dict: maskClass as! NSDictionary, isMask: true)?.data } @@ -343,7 +341,7 @@ class Document: NSDocument { parseDocument(archive: archive, dict: propertyList as! NSDictionary) } - func makeComposite() -> NSImage { + func makeComposite() -> NSImage? { // create the final composite output image let rgbColorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue).union(.byteOrder32Big) @@ -355,9 +353,7 @@ class Document: NSDocument { var previousImage: CGImage? - for (index, layer) in info.layers.reversed().enumerated() { - dump(layer, indent: 5) - + 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: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue) @@ -419,95 +415,12 @@ class Document: NSDocument { ccgContext?.setBlendMode(.sourceAtop) if layer.clipped { - let previousLayer = info.layers.reversed()[index - 1] - - var format: vImage_CGImageFormat = { - guard - let format = vImage_CGImageFormat(cgImage: previousImage!) else { - fatalError("Unable to create format.") - } - - return format - }() - - var sourceBuffer: vImage_Buffer = { - guard - var sourceImageBuffer = try? vImage_Buffer(cgImage: previousImage!, - format: format), - - var scaledBuffer = try? vImage_Buffer(width: Int(sourceImageBuffer.height / 3), - height: Int(sourceImageBuffer.width / 3), - bitsPerPixel: format.bitsPerPixel) else { - fatalError("Unable to create source buffers.") - } - - defer { - sourceImageBuffer.free() - } - - vImageScale_ARGB8888(&sourceImageBuffer, - &scaledBuffer, - nil, - vImage_Flags(kvImageNoFlags)) - - return scaledBuffer - }() - - /* - The 1-channel, 8-bit vImage buffer used as the operation destination. - */ - var destinationBuffer: vImage_Buffer = { - guard var destinationBuffer = try? vImage_Buffer(width: Int(sourceBuffer.width), - height: Int(sourceBuffer.height), - bitsPerPixel: 8) else { - fatalError("Unable to create destination buffers.") - } - - return destinationBuffer - }() - - - let redCoefficient: Float = 0.2126 - let greenCoefficient: Float = 0.7152 - let blueCoefficient: Float = 0.0722 - - let divisor: Int32 = 0x1000 - let fDivisor = Float(divisor) - - var coefficientsMatrix = [ - Int16(redCoefficient * fDivisor), - Int16(greenCoefficient * fDivisor), - Int16(blueCoefficient * fDivisor) - ] - - // Use the matrix of coefficients to compute the scalar luminance by - // returning the dot product of each RGB pixel and the coefficients - // matrix. - let preBias: [Int16] = [0, 0, 0, 0] - let postBias: Int32 = 0 - - vImageMatrixMultiply_ARGB8888ToPlanar8(&sourceBuffer, - &destinationBuffer, - &coefficientsMatrix, - divisor, - preBias, - postBias, - vImage_Flags(kvImageNoFlags)) - - // Create a 1-channel, 8-bit grayscale format that's used to - // generate a displayable image. - let monoFormat = vImage_CGImageFormat( - bitsPerComponent: 8, - bitsPerPixel: 8, - colorSpace: CGColorSpaceCreateDeviceGray(), - bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), - renderingIntent: .defaultIntent)! - - // Create a Core Graphics image from the grayscale destination buffer. - let result = (try? destinationBuffer.createCGImage(format: monoFormat))! + guard let result = previousImage?.toGrayscale() else { + return nil + } let newImage = layerImage.masking(result)! - + previousImage = newImage ccgContext?.draw(newImage, in: CGRect(x: 0, y: 0, width: info.width, height: info.height)) @@ -628,3 +541,79 @@ public extension NSImage { return flipedImage } } + +public extension CGImage { + func toGrayscale() -> CGImage? { + guard let format = vImage_CGImageFormat(cgImage: self) else { + return nil + } + + var sourceBuffer: vImage_Buffer = { + guard + var sourceImageBuffer = try? vImage_Buffer(cgImage: self, + format: format), + + var scaledBuffer = try? vImage_Buffer(width: Int(sourceImageBuffer.height / 3), + height: Int(sourceImageBuffer.width / 3), + bitsPerPixel: format.bitsPerPixel) else { + fatalError("Unable to create source buffers.") + } + + defer { + sourceImageBuffer.free() + } + + vImageScale_ARGB8888(&sourceImageBuffer, + &scaledBuffer, + nil, + vImage_Flags(kvImageNoFlags)) + + return scaledBuffer + }() + + guard var destinationBuffer = try? vImage_Buffer(width: Int(sourceBuffer.width), + height: Int(sourceBuffer.height), + bitsPerPixel: 8) else { + fatalError("Unable to create destination buffers.") + } + + let redCoefficient: Float = 0.2126 + let greenCoefficient: Float = 0.7152 + let blueCoefficient: Float = 0.0722 + + let divisor: Int32 = 0x1000 + let fDivisor = Float(divisor) + + var coefficientsMatrix = [ + Int16(redCoefficient * fDivisor), + Int16(greenCoefficient * fDivisor), + Int16(blueCoefficient * fDivisor) + ] + + // Use the matrix of coefficients to compute the scalar luminance by + // returning the dot product of each RGB pixel and the coefficients + // matrix. + let preBias: [Int16] = [0, 0, 0, 0] + let postBias: Int32 = 0 + + vImageMatrixMultiply_ARGB8888ToPlanar8(&sourceBuffer, + &destinationBuffer, + &coefficientsMatrix, + divisor, + preBias, + postBias, + vImage_Flags(kvImageNoFlags)) + + // Create a 1-channel, 8-bit grayscale format that's used to + // generate a displayable image. + let monoFormat = vImage_CGImageFormat( + bitsPerComponent: 8, + bitsPerPixel: 8, + colorSpace: CGColorSpaceCreateDeviceGray(), + bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue), + renderingIntent: .defaultIntent)! + + // Create a Core Graphics image from the grayscale destination buffer. + return try? destinationBuffer.createCGImage(format: monoFormat) + } +}