简体   繁体   中英

How can I write an NSImage to a JPEG file in Swift?

A Swift question. Not an Obj-C question. Please don't mark it as duplicate

I am trying to write a class that generates a JPEG thumbnail on OS X. I hope I have something sensible that forms the scaled down NSImage but I'm struggling to write a JPEG file to disk.

class ImageThumbnail {
    let size = CGSize(width: 128, height: 128)
    var originalPath = ""

    init(originalPath: String){
        self.originalPath = originalPath
    }

    func generateThumbnail() {
        let url = NSURL(fileURLWithPath: originalPath)

        if let imageSource = CGImageSourceCreateWithURL(url, nil) {
            let options: [NSString: NSObject] = [
                kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height) / 2.0,
                kCGImageSourceCreateThumbnailFromImageAlways: true
            ]

            let cgImage = CGImageSourceCreateThumbnailAtIndex(imageSource, 0, options)
            let scaledImage = cgImage.flatMap { NSImage(CGImage: $0, size: size) }

            // How do I write scaledImage to a JPEG file?
        }
    }
}

Example usage: ImageThumbail(originalPath: "/path/to/original").generateThumbnail()

First you need to get a bitmap representation of the image, get a JPEG representation of that image (or whatever format you want), and then write the binary data to a file:

if let bits = scaledImage?.representations.first as? NSBitmapImageRep {
    let data = bits.representationUsingType(.NSJPEGFileType, properties: [:])
    data?.writeToFile("/path/myImage.jpg", atomically: false)
}

NSBitmapImageFileType gives you a choice of representations:

public enum NSBitmapImageFileType : UInt {

    case NSTIFFFileType
    case NSBMPFileType
    case NSGIFFileType
    case NSJPEGFileType
    case NSPNGFileType
    case NSJPEG2000FileType
}

It appears that either in Swift 3 NSCGImageSnapshotRep no longer casts as NSBitmapImageRep , or this works for only some images (I can't tell which is true as I only encountered images that do not cast to NSBitmapImageRep in my case when loading some JPEG data into an NSImage, then trying to encode a JPEG representation again).

This slight alteration to another answer works for me in Swift 3 + macOS Sierra SDK in Xcode 8:

let cgImage = img.cgImage(forProposedRect: nil, context: nil, hints: nil)!
let bitmapRep = NSBitmapImageRep(cgImage: cgImage)
let jpegData = bitmapRep.representation(using: NSBitmapImageFileType.JPEG, properties: [:])!

(Please excuse the forced unwraps, just there for brevity – definitely optionals to check in production code given current SDK has introduced optionality here.)

extension NSImage {
    func imagePNGRepresentation() -> NSData? {
        if let imageTiffData = self.tiffRepresentation, let imageRep = NSBitmapImageRep(data: imageTiffData) {
            // let imageProps = [NSImageCompressionFactor: 0.9] // Tiff/Jpeg
            // let imageProps = [NSImageInterlaced: NSNumber(value: true)] // PNG
            let imageProps: [String: Any] = [:]
            let imageData = imageRep.representation(using: NSBitmapImageFileType.PNG, properties: imageProps) as NSData?
            return imageData
        }
        return nil
    }
}

if let thumbImageData = thumbimage.imagePNGRepresentation() {
    thumbImageData.write(toFile: "/path/to/thumb.png", atomically: false)
}

This extension:

public extension NSImage {
    func write(to url: URL, format: NSBitmapImageRep.FileType, props: [NSBitmapImageRep.PropertyKey : Any] ) -> Result<URL, Error> {
        return self.representations.first
            .asNonOptional
            .map { $0 as? NSBitmapImageRep }
            .flatMap { data -> R<NSBitmapImageRep> in data.asNonOptional }
            .map { $0.representation(using: .jpeg, properties: props) }
            .flatMap{ $0.asNonOptional }
            .map { try? $0.write(to: url) }
            .map { $0.asNonOptional }
            .map { _ in url}
    }
}

works with most needed file formats and supports of any needed properties.

usage:

_ = someImage.write(to: url, format: .png, props: [.compressionFactor : NSNumber(floatLiteral: 1.0)])

_ = someImage.write(to: url, format: .jpeg, props: [.compressionFactor : NSNumber(floatLiteral: 1.0)])

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM