如何将 NSImage 保存为某个目录中的新文件(png、jpg、...)?
9 回答
你可以像这样向 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”的调用是必不可少的,否则您可能无法获得有效的图像。
做这样的事情:
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
不确定你们其他人的情况,但我更喜欢吃完整的墨西哥卷饼。上面描述的内容会起作用并且没有任何问题,但是我发现了一些遗漏的东西。在这里,我将强调我的观察:
- 首先提供了最好的表示,即使图像分辨率大于该分辨率,它似乎也是 72 DPI。所以你正在失去决心
- 其次是多页图像,例如动画 GIF 或 PDF 中的图像。来吧,尝试一个动画GIF,你会发现动画丢失了
- 最后,EXIF、GPS 等任何元数据都会丢失。
因此,如果您想转换该图像,您真的想失去所有这些吗?如果您想吃饱饭,那么让我们继续阅读...
有时,我的意思是有时没有什么比良好的老学校发展更好的了。是的,这意味着我们必须做一些工作!
让我们开始吧:
我在 NSData 中创建了一个类别。这些是类方法,因为您希望这些东西是线程安全的,没有什么比将您的东西放在堆栈上更安全的了。有两种方法,一种用于非多页图像的输出,一种用于多页图像的输出。
单张图片列表: JPG、PNG、BMP、JPEG-2000
多张图片列表: PDF、GIF、TIFF
首先在内存中创建一个可变数据空间。
NSMutableData * imageData = [NSMutableData data];
其次得到一个CGImageSourceRef。是的,听起来很丑是不是。还不错,让我们继续……你真的希望源图像不是表示或 NSImage 数据块。但是,我们确实有一个小问题。源可能不兼容,因此请确保对照 CGImageSourceCopyTypeIdentifiers() 中列出的那些检查您的 UTI
一些代码:
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。哇,所有这些裁判都需要跟踪。到目前为止,我们是 2!
我们将使用这个函数:CGImageDestinationCreateWithData()
- 第一个参数是你的 imageData (cast CFMutableDataRef)
- 第二个参数是你的输出 UTI,记住上面的列表。(例如 kUTTypePNG)
第三个参数是要保存的图像数。对于单个图像文件,这是 1 否则您可以简单地使用以下内容:
CGImageSourceGetCount(imageSource);
第 4 个参数为零。
检查您是否有这个 CGImageDestinationRef,现在让我们将源中的图像添加到其中......这还将包括任何/所有元数据并保留分辨率。
对于多个图像,我们循环:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
对于单个图像,它是索引 0 处的一行代码:
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
好的,完成它,将它写入磁盘或数据容器:
CGImageDestinationFinalize( imageDest );
所以 Mutable Data 从一开始就包含了我们所有的图像数据和元数据。
我们完成了吗?几乎,即使有垃圾收集,你也必须清理!记住两个 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;
调整大小或 ICO / ICNS 怎么样?这是另一天,但总而言之,您首先要解决调整大小的问题......
- 创建具有新大小的上下文:CGBitmapContextCreate()
- 从索引中获取图像参考:CGImageSourceCreateImageAtIndex()
- 获取元数据的副本:CGImageSourceCopyPropertiesAtIndex()
- 将图像绘制到上下文中:CGContextDrawImage()
- 从上下文中获取调整大小的图像:CGBitmapContextCreateImage()
- 现在将图像和元数据添加到 Dest Ref:CGImageDestinationAddImage()
冲洗并重复嵌入源中的多个图像。
ICO 和 ICNS 之间的唯一区别是一个是单个图像,而另一个是一个文件中的多个图像。打赌你能猜出哪个是哪个?!;-) 对于这些格式,您必须缩小到特定大小,否则会出现错误。该过程虽然与使用正确 UTI 的过程完全相同,但调整大小更加严格。
好的,希望这可以帮助其他人,你和我现在一样充实!
哎呀,忘了说。当你得到 NSData 对象时,你可以随心所欲地使用它,例如 writeToFile、writeToURL,或者如果你愿意,可以创建另一个 NSImage。
快乐编码!
使用 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
}
}
}
斯威夫特 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)")
}
}
}
迅捷风格:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}
为了帮助处理跨平台代码,我实现了一个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
用法:
if let data = UIImagePNGRepresentation(myImage) {
do { try data.write(to: url, options: [.atomic]) }
catch let error { print("Error writing image (\(error))") }
}
使用 SWIFT 的另一种有保证的工作方法:
我有一个“图像井”,用户可以在其中放置任何图像。而这个“Image Well”有一个通过插座访问的图像属性(NSImage 类型):
@IBOutlet weak var imageWell: NSImageView!
保存此图像的代码(您可以将其放在按钮操作中)是:
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 是否有图像。
在第二行中,我们对该图像进行位图表示。
在第 3 行中,我们将 BitmapRepresentation 转换为 JPG 类型,压缩因子设置为“1”(无压缩)。
在第 4 行中,我们使用给定路径保存 JPG 数据。“atomically: true”意味着文件被保存为一个片段,这向我们保证操作会成功。
注意:您可以在第 3 行使用另一个 NSBitmapImageFileType,将您的图像保存为另一种格式。它有很多:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
等等
带和不带压缩的 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];
}
}
}