cocoa image macos file nsimage

cocoa - Cómo guardar un NSImage como un nuevo archivo



macos file (7)

¿Cómo puedo guardar un NSImage como un nuevo archivo (png, jpg, ...) en un directorio determinado?


guardar como PNG usando 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 } } }


Haz algo como esto:

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


No estoy seguro del resto de ustedes, pero prefiero comer mi enchilada completa. Lo que se describe arriba funcionará y no pasa nada, pero encontré algunas cosas omitidas. Aquí destacaré mis observaciones:

  • Primero se proporciona la mejor representación que parece ser de 72 DPI, incluso si la resolución de la imagen es mayor que eso. Entonces estás perdiendo resolución
  • En segundo lugar, ¿qué pasa con las imágenes de varias páginas, como las de los archivos GIF o PDF animados? Adelante, prueba un GIF animado, encontrarás la animación perdida
  • Por último, se perderán todos los metadatos, como EXIF, GPS, etc.

Entonces, si quieres convertir esa imagen, ¿realmente quieres perder todo esto? Si deseas comer tu comida completa, entonces sigamos leyendo ...

A veces y quiero decir que a veces no hay nada mejor que el buen desarrollo de la vieja escuela. Sí, eso significa que tenemos que hacer un poco de trabajo!

Empecemos:

Creo una categoría en NSData. Estos son métodos de clase porque quieres que estas cosas sean seguras para hilos y no hay nada más seguro que poner tus cosas en la pila. Hay dos tipos de métodos, uno para la salida de imágenes que no son de varias páginas y otro para la salida de imágenes de varias páginas.

Lista de imágenes individuales: JPG, PNG, BMP, JPEG-2000

Lista de imágenes múltiples: PDF, GIF, TIFF

Primero crea un espacio de datos mutable en la memoria.

NSMutableData * imageData = [NSMutableData data];

En segundo lugar, obtenga un CGImageSourceRef. Sí, suena feo, ¿verdad? No es tan malo, sigamos adelante ... Realmente quieres que la imagen de origen no sea una representación ni un fragmento de datos NSImage. Sin embargo, tenemos un pequeño problema. Es posible que la fuente no sea compatible, por lo tanto, asegúrese de verificar su UTI contra los listados en CGImageSourceCopyTypeIdentifiers ()

Cierto código:

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 );

Espera un segundo, ¿por qué hay NSImage allí? Bueno, hay algunos formatos que no tienen metadatos y CGImageSource no es compatible, pero estas son imágenes válidas. Un ejemplo son las imágenes PICT de estilo antiguo.

Ahora tenemos un CGImageSourceRef, asegúrese de que no sea nulo y luego obtengamos un CGImageDestinationRef. Wow todos estos refs para hacer un seguimiento de. Hasta ahora estamos en 2!

Utilizaremos esta función: CGImageDestinationCreateWithData ()

  • 1st Param es tu imageData (elenco CFMutableDataRef)
  • 2nd Param es su UTI de salida, recuerde la lista anterior. (por ejemplo, kUTTypePNG)
  • 3er parámetro es el recuento de imágenes para guardar. Para archivos de imagen única, esto es 1; de lo contrario, puede simplemente usar lo siguiente:

    CGImageSourceGetCount (imageSource);

  • 4º Param es nulo.

Compruebe si tiene este CGImageDestinationRef y ahora agreguemos nuestras imágenes de la fuente en él ... esto también incluirá todos los metadatos y conservará la resolución.

Para múltiples imágenes, bucleamos:

for ( NSUInteger i = 0; i < count; ++i ) CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );

Para una sola imagen, es una línea de código en el índice 0:

CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);

Ok, finalícela, que la escribe en el disco o contenedor de datos:

CGImageDestinationFinalize( imageDest );

Para que Mutable Data desde el principio tenga todos nuestros datos de imágenes y metadatos ahora.

¿Ya terminamos? ¡Casi, incluso con la recolección de basura, tienes que limpiar! Recuerda dos Ref''s para la fuente y otra para el destino, así que haz una CFRelease ()

Ahora hemos terminado y con lo que terminas es una imagen convertida que conserva todos sus metadatos, resolución, etc.

Los métodos de mi categoría NSData tienen este aspecto:

+ (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;

¿Qué hay del redimensionamiento o ICO / ICNS? Esto es para otro día, pero en resumen, primero abordas el cambio de tamaño ...

  1. Crear un contexto con un nuevo tamaño: CGBitmapContextCreate ()
  2. Obtenga una imagen ref del índice: CGImageSourceCreateImageAtIndex ()
  3. Obtenga una copia de los metadatos: CGImageSourceCopyPropertiesAtIndex ()
  4. Dibuje la imagen en el contexto: CGContextDrawImage ()
  5. Obtenga la imagen cambiada de tamaño del contexto: CGBitmapContextCreateImage ()
  6. Ahora agregue la imagen y los metadatos a la Dest Ref: CGImageDestinationAddImage ()

Enjuague y repita para múltiples imágenes incrustadas en la fuente.

La única diferencia entre un ICO e ICNS es que una es una sola imagen mientras que la otra es múltiples imágenes en un archivo. Apuesto a que puedes adivinar cual es cual? ;-) Para estos formatos, debes cambiar el tamaño a un tamaño particular, de lo contrario se producirá ERROR. El proceso, sin embargo, es exactamente el mismo en el que se usa la UTI adecuada, pero el cambio de tamaño es un poco más estricto.

¡De acuerdo, espero que esto ayude a otros y estés tan lleno como estoy ahora!

Opps, se olvidó de mencionar. Cuando obtiene el objeto NSData haga lo que quiera con él, como writeToFile, writeToURL o heck cree otro NSImage si lo desea.

Feliz codificación!


Podría agregar una categoría a NSImage como este

@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

La llamada a "TIFFRepresentation" es esencial; de lo contrario, es posible que no obtenga una imagen válida.


Solo un enfoque más garantizado para trabajar con SWIFT:

Tengo un "pozo de imagen" donde el usuario puede soltar cualquier imagen. Y este "Image Well" tiene una propiedad de imagen (de tipo NSImage) a la que se accede a través de un outlet:

@IBOutlet weak var imageWell: NSImageView!

Y el código que guarda esta imagen (puede ponerlo dentro de la acción del botón) es:

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

En la primera línea del código dado verificamos si nuestra Imagen Bien tiene una imagen.

En la 2da línea hacemos una representación de mapa de bits de esa imagen.

En la tercera línea, convertimos nuestra presentación de mapa de bits en un tipo de JPG con un factor de compresión establecido en "1" (sin compresión).

En la 4ª línea, guardamos los datos JPG con una ruta determinada. "atómicamente: verdadero" significa que el archivo se guarda como una sola pieza y eso nos asegura que la operación será exitosa.

NB: Puede usar otro NSBitmapImageFileType en la 3ra línea, para guardar su imagen a otro formato. Tiene mucho:

NSBitmapImageFileType.NSBMPFileType NSBitmapImageFileType.NSGIFFileType NSBitmapImageFileType.NSPNGFileType

etc.


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


Solución 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)") } } }