diff --git a/SilicaViewer/Document.swift b/SilicaViewer/Document.swift index 74bcbdb..80a55f4 100644 --- a/SilicaViewer/Document.swift +++ b/SilicaViewer/Document.swift @@ -494,7 +494,7 @@ class Document: NSDocument { var masterImage = ccgContext?.makeImage() let context = CIContext() - var previousImage: CGImage? + var previousImage: CGImage? = masterImage for layer in info.layers.reversed() { // start by creating a new layer composite image, needed for image masking @@ -529,25 +529,22 @@ class Document: NSDocument { } } - let layerImage = (layerContext?.makeImage())! + let layerImage = layerContext?.makeImage() - if layer.clipped { - guard let result = previousImage?.toGrayscale() else { - return nil - } - - let newImage = layerImage.masking(result)! + 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)! + 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) @@ -660,76 +657,32 @@ public extension NSImage { public extension CGImage { func toGrayscale() -> CGImage? { - guard let format = vImage_CGImageFormat(cgImage: self) else { + let ciImage = CIImage(cgImage: self) + + let filter = CIFilter(name: "CIColorControls") + filter?.setValue(ciImage, forKey: kCIInputImageKey) + filter?.setValue(5.0, forKey: kCIInputBrightnessKey) + filter?.setValue(0.0, forKey: kCIInputSaturationKey) + filter?.setValue(1.1, forKey: kCIInputContrastKey) + + guard let intermediateImage = filter?.outputImage 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.") + guard let image = CIContext().createCGImage(intermediateImage, from: CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) else { + return nil } - let redCoefficient: Float = 0.2126 - let greenCoefficient: Float = 0.7152 - let blueCoefficient: Float = 0.0722 + let grayColorSpace = CGColorSpaceCreateDeviceGray() + let maskBitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue).union(.byteOrder16Big) - let divisor: Int32 = 0x1000 - let fDivisor = Float(divisor) - - var coefficientsMatrix = [ - Int16(redCoefficient * fDivisor), - Int16(greenCoefficient * fDivisor), - Int16(blueCoefficient * fDivisor) - ] + let maskContext = CGContext(data: nil, width: self.width, height: self.height, bitsPerComponent: 16, bytesPerRow: 0, space: grayColorSpace, bitmapInfo: maskBitmapInfo.rawValue) - // 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 + maskContext?.setFillColor(.black) + maskContext?.fill(CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) - vImageMatrixMultiply_ARGB8888ToPlanar8(&sourceBuffer, - &destinationBuffer, - &coefficientsMatrix, - divisor, - preBias, - postBias, - vImage_Flags(kvImageNoFlags)) + maskContext?.draw(image, in: CGRect(origin: .zero, size: CGSize(width: self.width, height: self.height))) - // 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) + return maskContext?.makeImage() } }