如何将NSImage保存为新文件

时间:2010-06-14 16:12:07

标签: cocoa image macos file nsimage

如何将NSImage保存为某个目录中的新文件(png,jpg,...)?

8 个答案:

答案 0 :(得分:161)

您可以像这样添加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”的调用至关重要,否则您可能无法获得有效图像。

答案 1 :(得分:50)

做这样的事情:

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

答案 2 :(得分:33)

不确定你们其他人,但我更喜欢吃我完整的辣酱玉米饼馅。上面描述的内容将起作用并且没有任何问题,但我发现遗漏了一些东西。在这里,我将重点介绍我的观察结果:

  • 首先提供最佳表示,似乎是72 DPI 即使图像分辨率大于该值。所以你输了 分辨率
  • 第二个关于多页图像的内容,例如动画GIF中的图像 PDF文件。继续,尝试动画GIF,你会发现动画丢失
  • 最后,任何元数据(如EXIF,GPS等)数据都将丢失。

因此,如果您想要转换该图片,您真的想要失去所有这些吗? 如果您想吃饱餐,那么请继续阅读......

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

让我们开始吧:

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

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

多张图片列表: PDF,GIF,TIFF

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

NSMutableData * imageData    = [NSMutableData data];

第二次获得CGImageSourceRef。听起来丑陋已经不是了。它并没有那么糟糕,让我们继续......你真的希望源图像不是表示或NSImage数据块。但是,我们确实有一个小问题。源可能不兼容,因此请确保检查UTI与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,确保它不是nil然后让我们现在得到一个CGImageDestinationRef。哇所有这些参考资料都要记录下来。到目前为止,我们已经2岁了!

我们将使用此函数:CGImageDestinationCreateWithData()

  • 1st Param是你的imageData(施放CFMutableDataRef)
  • 2nd Param是你的输出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 );

因此,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怎么样? 这是另一天,但总的来说,你首先要处理调整大小......

  1. 使用新大小创建上下文:CGBitmapContextCreate()
  2. 从索引获取图像引用:CGImageSourceCreateImageAtIndex()
  3. 获取元数据的副本:CGImageSourceCopyPropertiesAtIndex()
  4. 将图像绘制到上下文中:CGContextDrawImage()
  5. 从上下文中获取已调整大小的图像:CGBitmapContextCreateImage()
  6. 现在将图像和元数据添加到Dest Ref:CGImageDestinationAddImage()
  7. 冲洗并重复插入源中的多个图像。

    ICO和ICNS之间的唯一区别是,一个是单个图像,而另一个是一个文件中的多个图像。打赌你猜猜哪个是哪个?! ;-) 对于这些格式,您必须调整大小到特定大小,否则将发生ERROR。 但是,使用正确的UTI的过程完全相同,但调整大小更严格。

    好的希望这可以帮助其他人,你就像我现在一样充实!

    奥普斯,忘了提。当您获得NSData对象时,可以根据需要执行此操作,例如writeToFile,writeToURL或heck,如果需要,可以创建另一个NSImage。

    快乐的编码!

答案 3 :(得分:14)

使用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 :(得分:12)

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

答案 5 :(得分:1)

使用SWIFT再一次保证工作方法:

我有一个“图像井”,用户可以在哪里删除任何图像。这个“Image Well”有一个通过outlet访问的图像属性(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”表示文件保存为单件,确保操作成功。

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

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

答案 6 :(得分:1)

为帮助跨平台代码,我实现了var v = []; a.push("hello world"); console.log(b.length); // print 1 版本,该版本在Mac上运行(并使用UIImagePNGRepresentation())。

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

答案 7 :(得分:0)

带压缩和不带压缩的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];
        }
    }
}