How can I save a NSImage as a new file (png, jpg, ...) in a certain directory?
-
4I added a bounty since someone called the first option an ugly hack and I can't seem to easily find a a seemingly correct and definite answer on google , more voting/answers please. – Roman A. Taycher Oct 05 '10 at 10:52
9 Answers
You could add a category to NSImage like this
@interface NSImage(saveAsJpegWithName)
- (void) saveAsJpegWithName:(NSString*) fileName;
@end
@implementation NSImage(saveAsJpegWithName)
- (void) saveAsJpegWithName:(NSString*) fileName
{
// Cache the reduced image
NSData *imageData = [self TIFFRepresentation];
NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
NSDictionary *imageProps = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:1.0] forKey:NSImageCompressionFactor];
imageData = [imageRep representationUsingType:NSJPEGFileType properties:imageProps];
[imageData writeToFile:fileName atomically:NO];
}
@end
The call to "TIFFRepresentation" is essential otherwise you may not get a valid image.

- 3,468
- 3
- 21
- 13
-
3
-
5No, TIFF does Alpha and multi-page images just fine. What gave you the idea it didn't? It's what Apple recommends as a destination format for retina images (multi-representation TIFFs). – uliwitness Feb 08 '14 at 09:39
-
TIFF should support alpha and Transparency https://en.wikipedia.org/wiki/Transparency_(graphic) – Coldsteel48 Jun 28 '15 at 13:35
-
-
-
I'm getting `TIFFRepresentation` data much larger (5x times) than the original file. is it normal? do I still want to transfer it over the network? – peetonn Mar 08 '17 at 23:19
Do something like this:
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
-
6You should get the bitmap image rep in a more reliable way than assuming that it is the first representation. You also cannot assume that there is one; if the image was loaded from a PDF, for example, then there will be an NSPDFImageRep, not an NSBitmapImageRep. – Peter Hosey Jun 15 '10 at 02:09
-
14
-
6If one gets a `[* representationUsingType:properties:]: unrecognized selector sent to instance` the workaround is here: [NSCGImageSnapshotRep, how get bitmapData](http://stackoverflow.com/questions/4438802/nscgimagesnapshotrep-how-get-bitmapdata) – Joshcodes Apr 02 '13 at 14:18
Not sure about the rest of you, but I prefer eating my full enchilada. What is described above will work and nothing is wrong with it, but I found a few things left out. Here I will highlight my observations:
- First the best representation is provided which seems to be 72 DPI even if the image resolution is greater than that. So you are losing resolution
- Second what about multi-page images such as those in animated GIFs or PDFs. Go ahead, try an animated GIF, you will find the animation lost
- Lastly any metadata such as EXIF, GPS, etc...data will be lost.
So if you want to convert that image, do you really want to lose out on all of this? If you wish to eat your full meal, then lets read on...
Sometimes and I do mean sometimes there is nothing better than good ole old school development. Yeah that means we have to do a bit of work!
Let's get started:
I create a category in NSData. These are class methods because you want these things to be thread safe and there is nothing safer than putting your stuff on the stack. There are two types of methods, one for the output of non-multi-page images and one for the output of multi-page images.
List of single images: JPG, PNG, BMP, JPEG-2000
List of multiple images: PDF, GIF, TIFF
First create a mutable data space in memory.
NSMutableData * imageData = [NSMutableData data];
Second get a CGImageSourceRef. Yeah sounding ugly already isn't it. It's not that bad, let's keep going... You really want the source image not a representation or NSImage chunk of data. However, we do have a small issue. The source might not be compatible, so make sure you check your UTI against those listed from CGImageSourceCopyTypeIdentifiers()
Some code:
CGImageSourceRef imageSource = nil;
if ( /* CHECK YOUR UTI HERE */ )
return CGImageSourceCreateWithURL( (CFURLRef)aURL, nil );
NSImage * anImage = [[NSImage alloc] initWithContentsOfURL:aURL];
if ( anImage )
return CGImageSourceCreateWithData( (CFDataRef)[anImage TIFFRepresentation], nil );
Wait a sec, why is NSImage there? Well there are some formats that don't have metadata and CGImageSource does not support, but these are valid images. An example is old style PICT images.
Now we have a CGImageSourceRef, make sure it's not nil and then let's now get a CGImageDestinationRef. Wow all these ref's to keep track of. So far we are at 2!
We will use this function: CGImageDestinationCreateWithData()
- 1st Param is your imageData (cast CFMutableDataRef)
- 2nd Param is your output UTI, remember the list above. (e.g. kUTTypePNG)
3rd Param is the count of images to save. For single image files this is 1 otherwise you can simply use the following:
CGImageSourceGetCount( imageSource );
4th Param is nil.
Check to see if you have this CGImageDestinationRef and now let's add our images from the source into it...this will also include any/all metadata and retain the resolution.
For multiple images we loop:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
For single image it's one line of code at index 0:
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
Ok, finalize it which writes it to disk or data container:
CGImageDestinationFinalize( imageDest );
So that Mutable Data from the beginning has all of our image data and metadata in it now.
Are we done yet? Almost, even with garbage collection you have to clean-up! Remember two Ref's one for the source and one for the destination so do a CFRelease()
Now we are done and what you end up with is a converted image retaining all of its metadata, resolution, etc...
My NSData category methods look like this:
+ (NSData *) JPGDataFromURL:(NSURL *)aURL;
+ (NSData *) PNGDataFromURL:(NSURL *)aURL;
+ (NSData *) BMPDataFromURL:(NSURL *)aURL;
+ (NSData *) JPG2DataFromURL:(NSURL *)aURL;
+ (NSData *) PDFDataFromURL:(NSURL *)aURL;
+ (NSData *) GIFDataFromURL:(NSURL *)aURL;
+ (NSData *) TIFFDataFromURL:(NSURL *)aURL;
What about resizing or ICO / ICNS? This is for another day, but in summary you first tackle resizing...
- Create a context with new size: CGBitmapContextCreate()
- Get the an image ref from index: CGImageSourceCreateImageAtIndex()
- Get a copy of the metadata: CGImageSourceCopyPropertiesAtIndex()
- Draw the image into the context: CGContextDrawImage()
- Get the resized image from the context: CGBitmapContextCreateImage()
- Now add the image and metadata to the Dest Ref: CGImageDestinationAddImage()
Rinse and repeat for multiple-images embedded in the source.
The only difference between an ICO and ICNS is that one is a single image while the other one is multiple-images in one file. Bet you can guess which is which?! ;-) For these formats you have to resize down to a particular size otherwise ERROR will ensue. The process though is exactly the same where you use the proper UTI, but the resizing is a bit more strict.
Ok hope this helps others out there and you are as full as I am now!
Opps, forgot to mention. When you get the NSData object do as you want with it such as writeToFile, writeToURL, or heck create another NSImage if you want.
Happy coding!

- 2,516
- 27
- 30
-
2This is a good start, certainly better than the other answers (even though those are rated higher) but it's not complete. First the logic as presented here won't handle PDF files by itself (CGImageDestinationAddImageFromSource won't work since the image source will by nil). PDF and other non-copyable image sources need special treatment (to either get images via thumbnails or otherwise copy the data). Second, it won't copy any meta data by itself, you need to do it explicitly by retrieving the properties dictionary from the image source and adding it to the image destination. – danielv Oct 22 '13 at 16:10
-
Two questions please. 1) how can you be sure that [anImage TIFFRepresentation] always returns something? aren't there NSImage's that don't have TIFFRepresentation?. 2) Could you please provide a working-order code-snippet for at least one (say JPEPDataFromURL) of these functions in full? I tried to follow your instructions, and at least for me - it won't work (I'm trying to save incoming video data frame as JPEG to disk, and my input goes from AVSession's CMSampleBuffer through an NSImage - and I try to use your code to save this to a file. – Motti Shneor Apr 28 '19 at 19:33
save as PNG using swift3
import AppKit
extension NSImage {
@discardableResult
func saveAsPNG(url: URL) -> Bool {
guard let tiffData = self.tiffRepresentation else {
print("failed to get tiffRepresentation. url: \(url)")
return false
}
let imageRep = NSBitmapImageRep(data: tiffData)
guard let imageData = imageRep?.representation(using: .PNG, properties: [:]) else {
print("failed to get PNG representation. url: \(url)")
return false
}
do {
try imageData.write(to: url)
return true
} catch {
print("failed to write to disk. url: \(url)")
return false
}
}
}

- 50,398
- 25
- 166
- 151
-
let tiffData = image.tiffRepresentation! let imageRep = NSBitmapImageRep(data: tiffData) let data = imageRep?.representation(using: .PNG, properties: [:]) do { try data?.write(to: URL(fileURLWithPath: path3), options: NSData.WritingOptions() ) } catch { print("\(error)") } – Hope May 03 '17 at 06:48
Swift 4.2 Solution
public extension NSImage {
public func writePNG(toURL url: URL) {
guard let data = tiffRepresentation,
let rep = NSBitmapImageRep(data: data),
let imgData = rep.representation(using: .png, properties: [.compressionFactor : NSNumber(floatLiteral: 1.0)]) else {
Swift.print("\(self) Error Function '\(#function)' Line: \(#line) No tiff rep found for image writing to \(url)")
return
}
do {
try imgData.write(to: url)
}catch let error {
Swift.print("\(self) Error Function '\(#function)' Line: \(#line) \(error.localizedDescription)")
}
}
}

- 2,184
- 25
- 18
-
Can you explain why you use `self.self` instead of `self`? Is there any difference? – MartinW Dec 24 '17 at 12:49
-
Turns out there is no difference. Perhaps this was in an earlier version of swift, but I thought it used print the explicit class name but it looks like either just return `<
>`
Swift Style:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}

- 7,979
- 1
- 64
- 49
To help with cross-platform code, I implemented a version ofUIImagePNGRepresentation()
that runs on Mac (and uses NSImage
).
#if os(macOS)
public func UIImagePNGRepresentation(_ image: NSImage) -> Data? {
guard let cgImage = image.cgImage(forProposedRect: nil, context: nil, hints: nil)
else { return nil }
let imageRep = NSBitmapImageRep(cgImage: cgImage)
imageRep.size = image.size // display size in points
return imageRep.representation(using: .png, properties: [:])
}
#endif
Usage:
if let data = UIImagePNGRepresentation(myImage) {
do { try data.write(to: url, options: [.atomic]) }
catch let error { print("Error writing image (\(error))") }
}

- 3,147
- 20
- 29
-
This worked well for me, but I had to remove the imageRep.size = image.size code to keep the same image size as the original. – EricS May 03 '21 at 20:44
Just one more guaranteed to work approach using SWIFT:
I have an "Image Well" where to user can drop any image. And this "Image Well" has an image property (of type NSImage) accessed via outlet:
@IBOutlet weak var imageWell: NSImageView!
And the code that saves this image (you can put it inside the button action) is:
if imageWell.image != nil {
let bMImg = NSBitmapImageRep(data: (imageWell?.image?.TIFFRepresentation)!)
let dataToSave = bMImg?.representationUsingType(NSBitmapImageFileType.NSJPEGFileType, properties: [NSImageCompressionFactor : 1])
dataToSave?.writeToFile("/users/user/desktop/image.jpg", atomically: true)
}
In the 1st line of the given code we check if our Image Well has an image.
In the 2nd line we make a bitmap representation of that image.
In the 3rd line we convert our BitmapRepresentation to a JPG type with a compression factor set to "1" (no compression).
In the 4th line we save the JPG data with a given path. "atomically: true" means that the file is saved as one piece and that assures us that the operation will be successfull.
N.B.: You can use another NSBitmapImageFileType in the 3rd line, to save your image to another format. It has plenty:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
etc.

- 11
- 1
Objc solution with and without compression
NSImage* image;
// No compression
NSData* data = [image TIFFRepresentation];
// With compression
NSData* data = [image
TIFFRepresentationUsingCompression:NSTIFFCompressionLZW
factor:0.5f];
if (data != nil) {
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithData:data];
if (bitmap != nil) {
NSData* bitmapData = [bitmap
representationUsingType:NSBitmapImageFileTypePNG
properties:@{}];
if (bitmapData != nil) {
[bitmapData
writeToFile:<file_path>
atomically:true];
}
}
}

- 79
- 3