Pergunta

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

Foi útil?

Solução

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

Outras dicas

Você pode adicionar uma categoria ao 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

A chamada para "tiffrepresentation" é essencial, caso contrário você pode não obter uma imagem válida.

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

  • Primeiro é fornecida a melhor representação que parece ser 72 DPI mesmo que a resolução da imagem seja maior do que isso.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, experimente um GIF animado, você encontrará a animação perdida
  • Por último, quaisquer metadados como EXIF, GPS, etc...dados serão perdidos.

Então, se você deseja converter essa imagem, você realmente quer perder tudo isso?Se você deseja comer sua refeição completa, então vamos continuar lendo...

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

Vamos começar:

Eu crio uma categoria no NSData.Esses são métodos de classe porque você deseja que essas coisas sejam seguras para threads e não há nada mais seguro do que colocar suas coisas na pilha.Existem dois tipos de métodos, um para a saída de imagens sem várias páginas e outro para a saída de imagens com várias páginas.

Lista de imagens únicas: JPG, PNG, BMP, JPEG-2000

Lista de múltiplas imagens: PDF, GIF, TIFF

Primeiro crie um espaço de dados mutável na memória.

NSMutableData * imageData    = [NSMutableData data];

Segundo, obtenha um CGImageSourceRef.Sim, já parece feio, não é?Não é tão ruim assim, vamos continuar...Você realmente deseja que a imagem de origem não seja uma representação ou um pedaço de dados NSImage.No entanto, temos um pequeno problema.A fonte pode não ser compatível, portanto, certifique-se de verificar seu UTI em relação aos listados 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 o NSImage está aí?Bem, existem alguns formatos que não possuem metadados e CGImageSource não suporta, mas essas são imagens válidas.Um exemplo são as imagens PICT de estilo antigo.

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

Usaremos esta função:CGImageDestinationCreateWithData()

  • O primeiro parâmetro é o seu imageData (cast CFMutableDataRef)
  • O segundo parâmetro é a sua UTI de saída, lembre-se da lista acima.(por exemplo.kUTTypePNG)
  • O terceiro parâmetro é a contagem de imagens a serem salvas.Para arquivos de imagem únicos, isso é 1, caso contrário, você pode simplesmente usar o seguinte:

    CGImageSourceGetCount(imageSource);

  • 4º Parâmetro é nulo.

Verifique se você tem este CGImageDestinationRef e agora vamos adicionar nossas imagens da fonte nele... isso também incluirá todos/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 e grave no disco ou contêiner de dados:

CGImageDestinationFinalize( imageDest );

Para que os Dados Mutáveis ​​​​desde o início tenham todos os nossos dados de imagem e metadados agora.

Já terminamos?Quase, mesmo com coleta de lixo é preciso limpar!Lembre-se de duas Ref's, uma para a origem e outra para o destino, então faça um CFRelease()

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

Meus métodos 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?Isso fica para outro dia, mas, em resumo, primeiro você aborda o redimensionamento...

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

Enxágue e repita para múltiplas imagens incorporadas na fonte.

A única diferença entre um ICO e um ICNS é que um é uma imagem única, enquanto o outro contém várias imagens em um arquivo.Aposto que você consegue adivinhar qual é qual?!;-) Para esses formatos, você tem que redimensionar para um tamanho específico, caso contrário, ERRO ocorrerá.O processo, porém, é exatamente o mesmo quando você usa o UTI adequado, mas o redimensionamento é um pouco mais rigoroso.

Ok, espero que isso ajude outras pessoas e você esteja tão satisfeito quanto eu agora!

Ops, esqueci de mencionar.Quando você obtiver o objeto NSData, faça o que quiser com ele, como writeToFile, writeToURL ou crie outro NSImage, se desejar.

Boa codificação!

salve 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.2

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

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 garantia de trabalhar na abordagem usando o SWIFT:

Eu tenho uma "imagem bem" onde o usuário pode soltar qualquer imagem. E esta "imagem bem" possui uma propriedade de imagem (do tipo nsimage) acessada via saída:

@IBOutlet weak var imageWell: NSImageView!

E o código que salva esta 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 1ª linha do código fornecido, verificamos se nossa imagem tem uma imagem.

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

Na 3ª linha, convertemos nossa representação de bitmapreprestação em um tipo JPG com um fator de compressão definido como "1" (sem compactação).

Na 4ª linha, salvamos os dados JPG com um determinado caminho. "Atomicamente: true" significa que o arquivo é salvo como uma peça e isso nos garante que a operação será bem -sucedida.

NB: Você pode usar outro NSBITMAPIMAGEFILETYPE na 3ª linha, para salvar sua imagem em outro formato. Tem bastante:

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

etc.

Para ajudar no código de plataforma cruzada, implementei uma versão deUIImagePNGRepresentation() que funciona no Mac (e usa 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

Uso:

if let data = UIImagePNGRepresentation(myImage) {
    do { try data.write(to: url, options: [.atomic]) }
    catch let error { print("Error writing image (\(error))") }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top