macos 如何将 NSImage 保存为新文件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3038820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to save a NSImage as a new file
提问by burki
How can I save a NSImage as a new file (png, jpg, ...) in a certain directory?
如何将 NSImage 保存为某个目录中的新文件(png、jpg、...)?
采纳答案by garph0
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];
回答by Pegolon
You could add a category to NSImage like this
你可以像这样向 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
The call to "TIFFRepresentation" is essential otherwise you may not get a valid image.
对“TIFFRepresentation”的调用是必不可少的,否则您可能无法获得有效的图像。
回答by Arvin
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.
- 首先提供的最佳表示似乎是 72 DPI,即使图像分辨率大于 72 DPI。所以你正在失去分辨率
- 其次是多页图像,例如动画 GIF 或 PDF 中的图像。来吧,尝试一个动画GIF,你会发现动画丢失了
- 最后,任何元数据,如 EXIF、GPS 等……数据都将丢失。
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.
我在 NSData 中创建了一个类别。这些是类方法,因为你希望这些东西是线程安全的,没有什么比把你的东西放在堆栈上更安全的了。有两种方法,一种用于输出非多页图像,一种用于输出多页图像。
List of single images:JPG, PNG, BMP, JPEG-2000
单张图片列表:JPG、PNG、BMP、JPEG-2000
List of multiple images:PDF, GIF, TIFF
多张图片列表: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()
其次得到一个 CGImageSourceRef。是的,听起来已经很丑了,不是吗。还不错,让我们继续……您确实希望源图像不是表示或 NSImage 数据块。但是,我们确实有一个小问题。来源可能不兼容,因此请确保根据 CGImageSourceCopyTypeIdentifiers() 中列出的内容检查您的 UTI
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.
等一下,为什么 NSImage 在那里?好吧,有些格式没有元数据并且 CGImageSource 不支持,但这些都是有效的图像。一个例子是旧式 PICT 图像。
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!
现在我们有一个 CGImageSourceRef,确保它不是 nil,然后让我们现在得到一个 CGImageDestinationRef。哇所有这些参考文献都要跟踪。到目前为止,我们是 2!
We will use this function: CGImageDestinationCreateWithData()
我们将使用这个函数: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.
- 第一个参数是你的 imageData (cast CFMutableDataRef)
- 第二个参数是你的输出 UTI,记住上面的列表。(例如 kUTTypePNG)
第三个参数是要保存的图像数。对于单个图像文件,这是 1,否则您可以简单地使用以下内容:
CGImageSourceGetCount( imageSource );
第 4 个参数为零。
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.
检查你是否有这个 CGImageDestinationRef,现在让我们将我们的图像从源添加到它......这也将包括任何/所有元数据并保留分辨率。
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:
对于单个图像,它是索引 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()
我们完成了吗?几乎,即使有垃圾收集,你也必须清理!记住两个 Ref,一个用于源,一个用于目标,所以 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 类别方法如下所示:
+ (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...
调整大小或 ICO / ICNS 怎么样?这是另一天,但总而言之,您首先要解决调整大小...
- 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()
- 创建具有新大小的上下文:CGBitmapContextCreate()
- 从索引获取图像引用:CGImageSourceCreateImageAtIndex()
- 获取元数据的副本:CGImageSourceCopyPropertiesAtIndex()
- 将图像绘制到上下文中:CGContextDrawImage()
- 从上下文中获取调整大小的图像:CGBitmapContextCreateImage()
- 现在将图像和元数据添加到 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.
ICO 和 ICNS 之间的唯一区别是一个是单个图像,而另一个是一个文件中的多个图像。打赌你能猜出哪个是哪个?!;-) 对于这些格式,您必须将大小调整为特定大小,否则会出现错误。该过程虽然与您使用正确 UTI 的过程完全相同,但调整大小要严格一些。
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.
呵呵,忘记说了。当您获得 NSData 对象时,可以随心所欲地使用它,例如 writeToFile、writeToURL,或者如果您愿意,可以创建另一个 NSImage。
Happy coding!
快乐编码!
回答by neoneye
save as PNG using swift3
使用 swift3 保存为 PNG
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
}
}
}
回答by Charlton Provatas
Swift 4.2 Solution
Swift 4.2 解决方案
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)")
}
}
}
回答by Peter Kreinz
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)
}
}
回答by Robin Stewart
To help with cross-platform code, I implemented a version ofUIImagePNGRepresentation()
that runs on Mac (and uses NSImage
).
为了帮助处理跨平台代码,我实现了一个UIImagePNGRepresentation()
在 Mac 上运行的版本(并使用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))") }
}
回答by German G.
Just one more guaranteed to work approach using SWIFT:
还有一种使用 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:
我有一个“图像井”,用户可以在其中放置任何图像。这个“Image Well”有一个通过插座访问的图像属性(NSImage类型):
@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.
在给定代码的第一行中,我们检查 Image Well 是否有图像。
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).
在第三行中,我们将 BitmapRepresentation 转换为 JPG 类型,压缩因子设置为“1”(无压缩)。
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.
在第 4 行中,我们使用给定的路径保存 JPG 数据。"atomically: true" 意味着文件被保存为一个文件,这向我们保证操作将成功。
N.B.:You can use another NSBitmapImageFileType in the 3rd line, to save your image to another format. It has plenty:
注意:您可以在第 3 行中使用另一个 NSBitmapImageFileType,将您的图像保存为另一种格式。它有很多:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
etc.
等等。
回答by Kritua
Objc solution with and without compression
有和没有压缩的objc解决方案
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];
}
}
}