Как сохранить NSImage в качестве нового файла

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

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

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

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

  • 1-й параметр - ваш образ (cast CFMutableDataRef)
  • Второй параметр - ваш выход UTI, запомните список выше. (например. kUTTypePNG)
  • 3rd Param - это количество изображений для сохранения. Для отдельных файлов изображений это 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()
  • Получить ссылку ref из индекса: CGImageSourceCreateImageAtIndex()
  • Получить копию метаданных: CGImageSourceCopyPropertiesAtIndex()
  • Нарисуйте изображение в контексте: CGContextDrawImage()
  • Получить измененное изображение из контекста: CGBitmapContextCreateImage()
  • Теперь добавьте изображение и метаданные в Dest Ref: CGImageDestinationAddImage()

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

Единственное различие между ICO и ICNS заключается в том, что один является одним изображением, а другой - несколькими изображениями в одном файле. Ставка, вы можете догадаться, что это?!;-) Для этих форматов вам необходимо изменить размер до определенного размера, иначе появится ERROR. Процесс, хотя и является тем же самым, когда вы используете правильный UTI, но изменение размера немного более строгое.

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

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

Счастливое кодирование!

Ответ 4

сохранить как 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
        }
    }
}

Ответ 5

Стиль 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)
      }
}

Ответ 6

Решение Swift 4

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

Ответ 7

Еще один гарантированный подход к работе с использованием 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)
}

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

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

В третьей строке мы преобразуем наше представление BitmapRepresentation в тип JPG с коэффициентом сжатия, установленным в "1" (без сжатия).

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

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

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

и др.