如何将NSImage另存为新文件

我怎样才能保存NSImage作为一个新的文件(PNG,JPG,…)在一个特定的目录?

做这样的事情:

NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0]; NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil]; [data writeToFile: @"/path/to/file.png" atomically: NO]; 

你可以像这样向NSImage添加一个类别

 @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 

调用“TIFFRepresentation”是必不可less的,否则您可能无法获得有效的图像。

不知道你的其他人,但我更喜欢吃我完整的辣酱玉米饼馅。 上面所描述的将会起作用,没有什么问题,但是我发现了一些遗漏的东西。 在这里,我将强调我的观察:

  • 首先提供最好的表示,即使图像分辨率大于72 DPI也是如此。 所以你正在失去决心
  • 其次,多页面图像如animationGIF或PDF中的图像。 继续,尝试一个animationGIF,你会发现animation丢失
  • 最后任何元数据,如EXIF,GPS等…数据将丢失。

所以如果你想转换这个图像,你真的想在这一切上失去? 如果你想吃完整餐,那么让我们继续阅读…

有时候,我的意思是,有时没有什么比善良的老学校发展更好。 是的,这意味着我们必须做一些工作!

让我们开始吧:

我在NSData中创build一个类别。 这些是类方法,因为你希望这些东西是线程安全的,没有什么比把东西放在堆栈上更安全的了。 有两种方法,一种用于输出非多页图像,另一种用于多页图像的输出。

单张图片列表: JPG,PNG,BMP,JPEG-2000

多个图像列表: PDF,GIF,TIFF

首先在内存中创build一个可变的数据空间。

 NSMutableData * imageData = [NSMutableData data]; 

第二个得到一个CGImageSourceRef。 听起来丑陋已经不是了。 这并不坏,让我们继续…你真的想要源图像不是一个表示或NSImage数据块。 但是,我们确实有一个小问题。 源代码可能不兼容,所以确保你检查你的UTI与列在CGImageSourceCopyTypeIdentifiers()

一些代码:

 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 ); 

等一下,为什么NSImage在那里? 那么有一些格式没有元数据和CGImageSource不支持,但这些是有效的图像。 一个例子是老式PICT图像。

现在我们有一个CGImageSourceRef,确保它不是零,然后让我们现在得到一个CGImageDestinationRef。 哇所有这些裁判logging。 到目前为止,我们在2!

我们将使用这个函数:CGImageDestinationCreateWithData()

  • 第一个参数是你的imageData(cast CFMutableDataRef)
  • 第二个参数是你的输出UTI,请记住上面的列表。 (如kUTTypePNG)
  • 第三参数是要保存的图像的数量。 对于单个图像文件,这是1,否则您可以简单地使用以下内容:

    CGImageSourceGetCount(imageSource);

  • 第四参数是零。

检查一下你是否有这个CGImageDestinationRef,现在让我们从源代码中添加我们的图像到它里面……这也将包括任何/所有的元数据并保留这个分辨率。

对于我们循环的多个图像:

 for ( NSUInteger i = 0; i < count; ++i ) CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil ); 

对于单个图像,它是索引0处的一行代码:

 CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil); 

好的,确定它写入磁盘或数据容器:

 CGImageDestinationFinalize( imageDest ); 

因此,从一开始,可变数据就包含了我们所有的图像数据和元数据。

我们完成了吗? 几乎,即使垃圾收集,你必须清理! 记住两个Ref的来源和一个目的地,所以做一个CFRelease()

现在我们完成了,你最终得到的是一个保存所有元数据,分辨率等的转换后的图像。

我的NSData类别的方法是这样的:

 + (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; 

如何resize或ICO / ICNS? 这是另一天,但总的来说,你首先解决调整…

  1. 用新大小创build一个上下文:CGBitmapContextCreate()
  2. 从索引获取图像ref:CGImageSourceCreateImageAtIndex()
  3. 获取元数据的副本:CGImageSourceCopyPropertiesAtIndex()
  4. 将图像绘制到上下文中:CGContextDrawImage()
  5. 从上下文获取resize的图像:CGBitmapContextCreateImage()
  6. 现在将图像和元数据添加到目标参考:CGImageDestinationAddImage()

冲洗并重复embedded源中的多个图像。

ICO和ICNS之间的唯一区别是一个是单个图像,另一个是多个图像在一个文件中。 打赌你猜猜哪个是哪个? ;-)对于这些格式,你必须调整到一个特定的大小,否则错误将随之而来。 虽然使用适当的UTI的过程是完全一样的,但是resize是更严格的。

好的,希望这可以帮助其他人,而且你已经像现在一样充实了!

奥普斯,忘了提及。 当你得到的NSData对象做你想要的,如writeToFile,writeToURL,或者如果你想要创build另一个NSImage。

快乐的编码!

另存为PNG使用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 } } } 

Swift风格:

 if let imgRep = image?.representations[0] as? NSBitmapImageRep { if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:]) { data.writeToFile("/path/to/file.png", atomically: false) } } 

Swift 4解决scheme

 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.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.self) Error Function '\(#function)' Line: \(#line) \(error.localizedDescription)") } } } 

只有一个保证使用SWIFT的工作方法:

我有一个“图像很好”,用户可以放置任何图像。 而这个“Image Well”有一个通过sockets访问的图像属性(typesNSImage):

 @IBOutlet weak var imageWell: NSImageView! 

保存这个图像的代码(你可以把它放在button动作中)是:

 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) } 

在给定代码的第一行中,我们检查我们的Image Well是否有图像。

在第二行中,我们对该图像进行位图表示。

在第三行中,我们将BitmapRepresentation转换为压缩因子设置为“1”(无压缩)的JPGtypes。

在第四行中,我们用给定的path保存JPG数据。 “primefaces:真”意味着文件被保存为一个整体,并保证我们的操作是成功的。

注意:您可以在第三行使用另一个NSBitmapImageFileType,将图像保存为其他格式。 它有很多:

 NSBitmapImageFileType.NSBMPFileType NSBitmapImageFileType.NSGIFFileType NSBitmapImageFileType.NSPNGFileType 

等等