Как сохранить NSImage как новый файл - PullRequest
72 голосов
/ 14 июня 2010

Как сохранить NSImage в виде нового файла (png, jpg, ...) в определенном каталоге?

Ответы [ 8 ]

161 голосов
/ 09 июля 2010

Вы можете добавить категорию к 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» необходим, иначе вы не сможете получить действительное изображение.

50 голосов
/ 14 июня 2010

Сделайте что-то вроде этого:

NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
33 голосов
/ 01 марта 2012

Не уверен насчет остальных из вас, но я предпочитаю есть свою полную энчиладу.То, что описано выше, будет работать, и в этом нет ничего плохого, но я обнаружил, что некоторые вещи не учтены.Здесь я остановлюсь на моих наблюдениях:

  • Сначала предоставляется лучшее представление, которое кажется 72 DPI, даже если разрешение изображения выше этого.Таким образом, вы теряете разрешение
  • Во-вторых, что касается многостраничных изображений, таких как изображения в анимированных GIF-файлах или PDF-файлах.Попробуйте анимированный GIF, вы найдете потерянную анимацию
  • Наконец, любые метаданные, такие как EXIF, GPS и т. Д., Данные будут потеряны.

Так что если выХотите конвертировать это изображение, вы действительно хотите потерять все это?Если вы хотите съесть свою полную еду, давайте продолжим читать ...

Иногда и я имею в виду, что иногда нет ничего лучше, чем хорошее старое школьное развитие.Да, это означает, что мы должны немного поработать!

Давайте начнем:

Я создаю категорию в NSData.Это классовые методы, потому что вы хотите, чтобы эти вещи были потокобезопасными, и нет ничего более безопасного, чем положить ваши вещи в стек.Существует два типа методов: один для вывода не многостраничных изображений, а другой для вывода многостраничных изображений.

Список отдельных изображений: JPG, PNG,BMP, JPEG-2000

Список нескольких изображений: PDF, GIF, TIFF

Сначала создайте изменяемое пространство данных в памяти.

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.Вау все эти ссылки, чтобы отслеживать.Пока мы находимся на уровне 2!

Мы будем использовать эту функцию: CGImageDestinationCreateWithData ()

  • 1-й параметр - это ваш imageData (приведение CFMutableDataRef)
  • 2-й параметр - вашвыходной UTI, запомни список выше.(например, kUTTypePNG)
  • 3-й параметр - это количество изображений для сохранения.Для отдельных файлов изображений это 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 );

Так что в изменяемых данных с самого начала есть все наши данные изображения и метаданные в нем сейчас.

Мы уже закончили?Почти даже с уборкой мусора приходится убирать!Запомните две ссылки: одну для источника и одну для назначения, поэтому сделайте 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 ()

Промойте и повторите для нескольких изображенийвстроено в источник.

Единственное различие между ICO и ICNS состоит в том, что одно является одним изображением, а другое - несколькими изображениями в одном файле.Спорим, ты можешь угадать, что есть что ?!;-) Для этих форматов вы должны изменить размер до определенного размера, иначе возникнет ОШИБКА.Хотя процесс точно такой же, когда вы используете правильный UTI, но изменение размера немного более строгое.

Хорошо, надеюсь, это поможет другим, и вы полны, как я сейчас!

Оппс, забыл упомянуть.Когда вы получаете объект NSData, делайте с ним как хотите, например writeToFile, writeToURL или heck, создайте еще один NSImage, если хотите.

Удачного кодирования!

14 голосов
/ 24 сентября 2016

сохранить как 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
        }
    }
}
12 голосов
/ 11 июля 2017

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)")
        }
    }
}
5 голосов
/ 13 сентября 2015

Свифт Стиль:

if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
      if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
      {
           data.writeToFile("/path/to/file.png", atomically: false)
      }
}
1 голос
/ 13 марта 2019

Чтобы помочь с кроссплатформенным кодом, я реализовал версию 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))") }
}
1 голос
/ 26 июля 2016

Еще один гарантированный подход к работе с использованием SWIFT:

У меня есть «Image Well», куда пользователь может добавить любое изображение.И у этого «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)
}

В 1-й строке данного кода мы проверяем, имеет ли наш Image Image изображение.

Во 2-й строке мы создаем растровое представление этого изображения.

Inв 3-й строке мы преобразуем наше BitmapRepresentation в тип JPG с коэффициентом сжатия, установленным в «1» (без сжатия).

В 4-й строке мы сохраняем данные JPG с заданным путем.«atomically: true» означает, что файл сохранен как один фрагмент, и это гарантирует нам, что операция будет успешной.

NB: Вы можете использовать другой NSBitmapImageFileType в 3-й строке, чтобысохраните ваше изображение в другом формате.В ней много всего:

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

и т. Д.

...