Search code examples
swiftmacosnsimage

Swift on MacOS - How to save NSImage to disk


I have the following code working:

let myImage = NSImage(named: "my-image.png")

filter.setValue(myImage, forKey: kCIInputImageKey)
filter.setValue(0.5, forKey: kCIInputIntensityKey)
            
let resultImage = filter.outputImage

How can I save the filtered image (as a PNG) to disk? Please note that this is a MacOS version where UIImage is not available (Xcode throws: No such module 'UIImage' when trying to import)


Solution

  • You can create a Core Image Context and createCGImage from your ciimage filter result. You can do it as follow:

    import Cocoa
    
    class ViewController: NSViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let context = CIContext()
            let desktopURL = FileManager.default.urls(for: .desktopDirectory, in: .userDomainMask).first!
            guard
                let filter = CIFilter(name: "CISepiaTone"),
                let imageURL = Bundle.main.url(forResource: "my-image", withExtension: "png"),
                let ciImage = CIImage(contentsOf: imageURL)
            else { return }
    
            filter.setValue(ciImage, forKey: kCIInputImageKey)
            filter.setValue(0.5, forKey: kCIInputIntensityKey)
    
            guard let result = filter.outputImage, let cgImage = context.createCGImage(result, from: result.extent)
            else { return }
    
            let destinationURL = desktopURL.appendingPathComponent("my-image.png")
            let nsImage = NSImage(cgImage: cgImage, size: ciImage.extent.size)
            if nsImage.pngWrite(to: destinationURL, options: .withoutOverwriting) {
                print("File saved")
            }
        }
    }
    

    You will need those extensions to get the png representation data to write the resulting image to disk:

    extension NSImage {
        var pngData: Data? {
            guard let tiffRepresentation = tiffRepresentation, let bitmapImage = NSBitmapImageRep(data: tiffRepresentation) else { return nil }
            return bitmapImage.representation(using: .png, properties: [:])
        }
        func pngWrite(to url: URL, options: Data.WritingOptions = .atomic) -> Bool {
            do {
                try pngData?.write(to: url, options: options)
                return true
            } catch {
                print(error)
                return false
            }
        }
    }