Como salvar uma NSImage como um novo arquivo

Como posso salvar uma NSImage como um novo arquivo (png, jpg, …) em um determinado diretório?

Faça algo assim:

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

Você pode adicionar uma categoria à NSImage como esta

 @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 

A chamada para “TIFFRepresentation” é essencial caso contrário você não pode obter uma imagem válida.

Não tenho certeza sobre o resto de vocês, mas eu prefiro comer meu enchilada completo. O que está descrito acima irá funcionar e nada está errado com isso, mas eu encontrei algumas coisas deixadas de fora. Aqui vou destacar minhas observações:

  • Primeiro, a melhor representação é fornecida, que parece ser de 72 DPI, mesmo que a resolução da imagem seja maior que essa. Então você está perdendo resolução
  • Em segundo lugar, o que dizer de imagens de várias páginas, como aquelas em GIFs animados ou PDFs. Vá em frente, tente um GIF animado, você vai encontrar a animação perdida
  • Por fim, quaisquer metadados, como EXIF, GPS, etc …, serão perdidos.

Então, se você quiser converter essa imagem, você realmente quer perder tudo isso? Se você quiser comer sua refeição completa, então vamos ler …

Às vezes e eu quero dizer às vezes não há nada melhor do que o bom desenvolvimento da velha escola. Sim, isso significa que temos que fazer um pouco de trabalho!

Vamos começar:

Eu crio uma categoria no NSData. Estes são methods de class porque você quer que estas coisas sejam seguras e não há nada mais seguro do que colocar suas coisas na pilha. Existem dois tipos de methods, um para a saída de imagens não multi-páginas e outro para a saída de imagens de várias páginas.

Lista de imagens individuais: JPG, PNG, BMP, JPEG-2000

Lista de várias imagens: PDF, GIF, TIFF

Primeiro crie um espaço de dados mutável na memory.

 NSMutableData * imageData = [NSMutableData data]; 

Segundo, obtenha um CGImageSourceRef. Sim, soando feio, não é? Não é tão ruim assim, vamos continuar … Você realmente quer que a imagem de origem não seja uma representação ou um pedaço de dados da NSImage. No entanto, temos um pequeno problema. A origem pode não ser compatível, portanto, verifique sua UTI em relação às listadas em CGImageSourceCopyTypeIdentifiers ()

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

Espere um pouco, por que a NSImage está lá? Bem, existem alguns formatos que não têm metadados e CGImageSource não suporta, mas estas são imagens válidas. Um exemplo são imagens PICT de estilo antigo.

Agora temos um CGImageSourceRef, certifique-se de que ele não seja nulo e, em seguida, vamos agora obter um CGImageDestinationRef. Uau todos esses refs para acompanhar. Até agora estamos em 2!

Usaremos essa function: CGImageDestinationCreateWithData ()

  • 1st Param é seu imageData (casting CFMutableDataRef)
  • 2º Param é sua saída UTI, lembre-se da lista acima. (por exemplo, kUTTypePNG)
  • 3rd Param é a contagem de imagens para salvar. Para arquivos de imagem única, isso é 1, caso contrário, você pode simplesmente usar o seguinte:

    CGImageSourceGetCount (imageSource);

  • 4º Param é nulo.

Verifique se você tem este CGImageDestinationRef e agora vamos adicionar nossas imagens da fonte nele … isso também includeá qualquer / todos os metadados e manterá a resolução.

Para várias imagens, fazemos um loop:

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

Para imagem única, é uma linha de código no índice 0:

 CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil); 

Ok, finalize o que grava no disco ou no container de dados:

 CGImageDestinationFinalize( imageDest ); 

De modo que os dados mutáveis ​​do começo tenham todos os dados e metadados da imagem nele agora.

Já terminamos? Quase, mesmo com a garbage collection você tem que limpar! Lembre-se de dois Refs para a fonte e um para o destino, então faça um CFRelease ()

Agora terminamos e o que você tem é uma imagem convertida, mantendo todos os seus metadados, resolução, etc ...

Meus methods de categoria NSData são assim:

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

E quanto ao redimensionamento ou ICO / ICNS? Isto é para outro dia, mas em resumo você primeiro aborda o redimensionamento ...

  1. Crie um contexto com novo tamanho: CGBitmapContextCreate ()
  2. Obtém a imagem ref do índice: CGImageSourceCreateImageAtIndex ()
  3. Obtenha uma cópia dos metadados: CGImageSourceCopyPropertiesAtIndex ()
  4. Desenhe a imagem no contexto: CGContextDrawImage ()
  5. Obter a imagem redimensionada do contexto: CGBitmapContextCreateImage ()
  6. Agora adicione a imagem e os metadados ao Dest Ref: CGImageDestinationAddImage ()

Enxagúe e repita para várias imagens incorporadas na fonte.

A única diferença entre um ICO e um ICNS é que um é uma única imagem enquanto o outro é várias imagens em um arquivo. Aposto que você pode adivinhar qual é qual ?! 😉 Para esses formatos, você precisa resize para um tamanho específico, caso contrário ocorrerá um erro. O processo é exatamente o mesmo onde você usa a UTI adequada, mas o redimensionamento é um pouco mais rigoroso.

Ok espero que isso ajude os outros lá fora e você está tão cheio como estou agora!

Opps, esqueci de mencionar. Quando você obtiver o object NSData, faça como deseja, como writeToFile, writeToURL ou heck, crie outra NSImage, se desejar.

Codificação feliz!

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

Solução 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)") } } } 

Estilo rápido:

 if let imgRep = image?.representations[0] as? NSBitmapImageRep { if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:]) { data.writeToFile("/path/to/file.png", atomically: false) } } 

Apenas mais uma abordagem garantida para trabalhar usando o SWIFT:

Eu tenho um “Image Well”, onde o usuário pode soltar qualquer imagem. E este “Image Well” tem uma propriedade de imagem (do tipo NSImage) acessada via outlet:

 @IBOutlet weak var imageWell: NSImageView! 

E o código que salva essa imagem (você pode colocá-la dentro da ação do botão) é:

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

Na primeira linha do código, verificamos se o nosso Image Well possui uma imagem.

Na segunda linha, fazemos uma representação em bitmap dessa imagem.

Na terceira linha, convertemos nosso BitmapRepresentation em um tipo JPG com um fator de compactação definido como “1” (sem compactação).

Na quarta linha, salvamos os dados JPG com um determinado caminho. “atomicamente: true” significa que o arquivo é salvo como uma única parte e isso nos garante que a operação será bem-sucedida.

NB: Você pode usar outro NSBitmapImageFileType na terceira linha para salvar sua imagem em outro formato. Tem muito:

 NSBitmapImageFileType.NSBMPFileType NSBitmapImageFileType.NSGIFFileType NSBitmapImageFileType.NSPNGFileType 

etc.