2010-06-14 13 views
65

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

+4

añadí una recompensa ya que alguien llamó a la primera opción de un truco feo y me parece que no puede encontrar fácilmente aa respuesta aparentemente correcta y definitiva en Google, más votación/respuestas, por favor. –

Respuesta

46

hacer algo como esto:

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

Debe obtener la representación de imagen de mapa de bits de una manera más confiable que asumir que es la primera representación. Tampoco puedes asumir que hay uno; si la imagen se cargó desde un PDF, por ejemplo, habrá un NSPDFImageRep, no un NSBitmapImageRep. –

+13

Esto es buggy. Vea abajo para una respuesta regular. – Eonil

+6

Si se obtiene un '[* representationUsingType: properties:]: selector no reconocido enviado a instancia' la solución está aquí: [NSCGImageSnapshotRep, cómo obtener bitmapData] (http://stackoverflow.com/questions/4438802/nscgimagesnapshotrep-how-get -bitmapdata) – Joshcodes

154

Se podría añadir una categoría para NSImage como esto

@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 no puede obtener una imagen válida.

+3

Pero TIFF es RGB y descarta cualquier información de transparencia. –

+4

No, TIFF hace bien las imágenes alfa y de varias páginas. ¿Qué te dio la idea de que no fue así? Es lo que Apple recomienda como formato de destino para imágenes de retina (TIFF de representación múltiple). – uliwitness

+0

TIFF debería ser compatible con alfa y transparencia https://en.wikipedia.org/wiki/Transparency_(graphic) – ColdSteel

30

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í voy a destacar mis observaciones: se proporciona

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

Entonces, si quiere convertir esa imagen, ¿realmente quiere perder todo esto? Si desea comer su comida completa, entonces leamos en ...

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!

Vamos a empezar:

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 varias imágenes: PDF, GIF, TIFF

En primer lugar crear un espacio de datos mutables 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. La fuente puede no ser compatible, así que asegúrese de comprobar su infección del tracto urinario en contra de los que se enumeran CGImageSourceCopyTypeIdentifiers()

Algunos 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é es 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!

vamos a utilizar esta función: CGImageDestinationCreateWithData()

  • primera Param es su propiedad imageData (fundido CFMutableDataRef)
  • segundo Param es su salida de IU, recordar la lista anterior. (por ejemplo, kUTTypePNG)
  • 3er parámetro es el recuento de imágenes para guardar. Para archivos de imagen única, este es 1; de lo contrario, puede simplemente usar lo siguiente:

    CGImageSourceGetCount (imageSource);

  • 4th Param is nil.

Comprobar para ver si tiene este CGImageDestinationRef y ahora vamos a añadir nuestras imágenes de la fuente en ella ... esto también va a incluir cualquier/todos los metadatos y retener la resolución.

Para múltiples imágenes que bucle:

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

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

CGImageDestinationAddImageFromSource(imageDest, imageSource, 0, nil); 

Ok, lo completara el que lo escribe en contenedor de disco o de datos:

CGImageDestinationFinalize(imageDest); 

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

¿Ya hemos terminado? ¡Casi, incluso con la recolección de basura, tienes que limpiar! Recuerde una dos de Referencia para la fuente y otro para el destino, para hacer un CFRelease()

Ahora hemos terminado y lo que usted termina con una conserva todos sus metadatos, resolución, etc ...

imagen convertida

métodos categoría Mi NSData 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é pasa con el cambio de tamaño o de ICO/ICNS? Esto es para otro día, pero en resumen, primero abordas el cambio de tamaño ...

  1. Crear un contexto con el nuevo tamaño: CGBitmapContextCreate()
  2. Obtener el árbitro con una imagen de índice: CGImageSourceCreateImageAtIndex()
  3. obtener una copia de los metadatos: CGImageSourceCopyPropertiesAtIndex()
  4. dibujar la imagen en el contexto: CGContextDrawImage()
  5. obtener la imagen cambia de tamaño a partir del contexto: CGBitmapContextCreateImage()
  6. Ahora agregue la imagen y metadatos a los 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 varias imágenes en un solo archivo. Apuesto a que puedes adivinar cual es cual? ;-) Para estos formatos tiene que cambiar el tamaño a un tamaño particular, de lo contrario ERROR se producirá. El proceso, sin embargo, es exactamente el mismo en el que utiliza la UTI adecuada, pero el cambio de tamaño es un poco más estricto.

Ok espero que esto ayude a los demás y que estés tan lleno como lo 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.

Happy coding!

+2

Este es un buen comienzo, sin duda mejor que las otras respuestas (aunque estas tienen una calificación más alta) pero no está completo. En primer lugar, la lógica tal como se presenta aquí no manejará archivos PDF por sí mismo (CGImageDestinationAddImageFromSource no funcionará dado que la fuente de la imagen será nula). PDF y otras fuentes de imágenes no copiables necesitan un tratamiento especial (ya sea para obtener imágenes a través de imágenes en miniatura o copiar los datos de otra manera). En segundo lugar, no copiará ningún metadato por sí mismo, debe hacerlo explícitamente recuperando el diccionario de propiedades del origen de la imagen y agregándolo al destino de la imagen. – danielv

5

Swift Estilo:

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

Sólo una más garantizado para trabajar enfoque utilizando SWIFT:

que tiene una "imagen bien" dónde el usuario puede soltar cualquier imagen. Y esta "imagen bien" tiene una propiedad de imagen (de tipo NSImage) se accede a través de salida:

@IBOutlet weak var imageWell: NSImageView! 

y el código que guarda esta imagen (se puede poner 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("https://stackoverflow.com/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 segunda línea hacemos una representación de mapa de bits de esa imagen.

En la 3ª línea, convertimos nuestro BitmapRepresentation en un tipo de JPG con un factor de compresión ajustado a "1" (sin compresión).

En la cuarta 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.

N.B .: Puede utilizar otro NSBitmapImageFileType en la 3ra línea, para guardar su imagen a otro formato. Tiene un montón:

NSBitmapImageFileType.NSBMPFileType 
NSBitmapImageFileType.NSGIFFileType 
NSBitmapImageFileType.NSPNGFileType 

etc.

11

guardar como PNG utilizando 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 
     } 
    } 
} 
+0

let tiffData = image.tiffRepresentation! let imageRep = NSBitmapImageRep (data: tiffData) let data = imageRep? .representation (using: .PNG, properties: [:]) do { try data? .write (to: URL (fileURLWithPath: path3), opciones NSData.WritingOptions:()) } catch { de impresión ("\ (error)") } – Hope

4

Swift 4 Solución

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

Puede explicar por qué se utiliza 'self.self' en lugar de' self'? Hay alguna diferencia? – MartinW

+0

Resulta que no hay diferencia. Tal vez esto fue en una versión anterior de swift, pero pensé que se usaba para imprimir el nombre explícito de clase, pero parece que simplemente devuelve '<>' –

Cuestiones relacionadas