44

in my Cocoa application, I load a .jpg file from disk, manipulate it. Now it needs to be written to disk as a .png file. How can you do that?

Thanks for your help!

zmerr
  • 534
  • 3
  • 18

5 Answers5

114

Using CGImageDestination and passing kUTTypePNG is the correct approach. Here's a quick snippet:

@import MobileCoreServices; // or `@import CoreServices;` on Mac
@import ImageIO;

BOOL CGImageWriteToFile(CGImageRef image, NSString *path) {
    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:path];
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, NULL);
    if (!destination) {
        NSLog(@"Failed to create CGImageDestination for %@", path);
        return NO;
    }

    CGImageDestinationAddImage(destination, image, nil);

    if (!CGImageDestinationFinalize(destination)) {
        NSLog(@"Failed to write image to %@", path);
        CFRelease(destination);
        return NO;
    }

    CFRelease(destination);
    return YES;
}

You'll need to add ImageIO and CoreServices (or MobileCoreServices on iOS) to your project and include the headers.


If you're on iOS and don't need a solution that works on Mac too, you can use a simpler approach:

// `image` is a CGImageRef
// `path` is a NSString with the path to where you want to save it
[UIImagePNGRepresentation([UIImage imageWithCGImage:image]) writeToFile:path atomically:YES];

In my tests, the ImageIO approach was about 10% faster than the UIImage approach on my iPhone 5s. In the simulator, the UIImage approach was faster. It's probably worth testing each for your particular situation on the device if you're really concerned with performance.

Sam Soffes
  • 14,831
  • 9
  • 76
  • 80
35

Here is a macOS-friendly, Swift 3 & 4 example:

@discardableResult func writeCGImage(_ image: CGImage, to destinationURL: URL) -> Bool {
    guard let destination = CGImageDestinationCreateWithURL(destinationURL as CFURL, kUTTypePNG, 1, nil) else { return false }
    CGImageDestinationAddImage(destination, image, nil)
    return CGImageDestinationFinalize(destination)
}
Dan Messing
  • 818
  • 7
  • 16
23

Create a CGImageDestination, passing kUTTypePNG as the type of file to create. Add the image, then finalize the destination.

eonil
  • 83,476
  • 81
  • 317
  • 516
Peter Hosey
  • 95,783
  • 15
  • 211
  • 370
  • ivanceras: http://developer.apple.com/mac/library/documentation/GraphicsImaging/Reference/CGImageDestination/ If you want a more specific answer, you should ask a more specific question (and you should do so as a separate question). – Peter Hosey Jul 30 '10 at 08:24
  • Just a note. We have to add `ImageIO.framework` to reference the functions even documentation is saying it's at `ApplicationServices/ImageIO`. – eonil Jun 02 '11 at 13:55
  • 1
    @Eonil: It depends on which platform you're building for. Cocoa Touch doesn't have umbrella frameworks, so when building for iOS, you do need to link against ImageIO directly. When building for Mac OS X, you link against ApplicationServices and get everything within it. – Peter Hosey Jun 02 '11 at 15:32
  • 3
    See my answer for an example of doing this. – Sam Soffes Oct 05 '11 at 22:09
6

Swift 5+ adopted version

import Foundation
import CoreGraphics
import CoreImage
import ImageIO
import MobileCoreServices

extension CIImage {
  
  public func convertToCGImage() -> CGImage? {
    let context = CIContext(options: nil)
    if let cgImage = context.createCGImage(self, from: self.extent) {
      return cgImage
    }
    return nil
  }
  
  public func data() -> Data? {
    convertToCGImage()?.pngData()
  }
}

extension CGImage {
  
  public func pngData() -> Data? {
    let cfdata: CFMutableData = CFDataCreateMutable(nil, 0)
    if let destination = CGImageDestinationCreateWithData(cfdata, kUTTypePNG as CFString, 1, nil) {
      CGImageDestinationAddImage(destination, self, nil)
      if CGImageDestinationFinalize(destination) {
        return cfdata as Data
      }
    }
    
    return nil
  }
}
hbk
  • 10,908
  • 11
  • 91
  • 124
2

The provided solutions probably still work fine but there is some newer API for this in CoreImage which does the same and is a bit more "Swifty" to use:

import CoreImage

func write(cgimage: CGImage, to url: URL) throws {
    let cicontext = CIContext()
    let ciimage = CIImage(cgImage: cgimage)
    try cicontext.writePNGRepresentation(of: ciimage, to: url, format: .RGBA8, colorSpace: ciimage.colorSpace!)
}
YourMJK
  • 1,429
  • 1
  • 11
  • 22