web-dev-qa-db-fra.com

Comment enregistrer un NSImage en tant que nouveau fichier

Comment puis-je enregistrer un NSImage en tant que nouveau fichier (png, jpg, ...) dans un certain répertoire?

72
burki

Faites quelque chose comme ça:

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

Vous pouvez ajouter une catégorie à NSImage comme ceci

@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

L'appel à "TIFFRepresentation" est essentiel sinon vous risquez de ne pas obtenir une image valide.

161
Pegolon

Je ne suis pas sûr du reste d'entre vous, mais je préfère manger mon enchilada complète. Ce qui est décrit ci-dessus fonctionnera et rien ne va mal, mais j'ai trouvé quelques choses omises. Ici, je vais souligner mes observations:

  • Tout d'abord, la meilleure représentation est fournie, qui semble être de 72 DPI, même si la résolution de l'image est supérieure à celle-ci. Vous perdez donc la résolution
  • Deuxièmement, qu'en est-il des images multipages telles que celles des GIF animés ou des PDF. Allez-y, essayez un GIF animé, vous trouverez l'animation perdue
  • Enfin, toutes les métadonnées telles que EXIF, GPS, etc ... seront perdues.

Donc, si vous voulez convertir cette image, voulez-vous vraiment perdre tout cela? Si vous souhaitez manger votre repas complet, alors lisez la suite ...

Parfois et je veux dire parfois, il n'y a rien de mieux qu'un bon vieux développement scolaire. Oui, cela signifie que nous devons faire un peu de travail!

Commençons:

Je crée une catégorie dans NSData. Ce sont des méthodes de classe parce que vous voulez que ces choses soient thread-safe et il n'y a rien de plus sûr que de mettre vos trucs sur la pile. Il existe deux types de méthodes, l'une pour la sortie d'images non multipages et l'autre pour la sortie d'images multipages.

Liste d'images individuelles: JPG, PNG, BMP, JPEG-2000

Liste de plusieurs images: PDF, GIF, TIFF

Créez d'abord un espace de données mutable en mémoire.

NSMutableData * imageData    = [NSMutableData data];

Obtenez ensuite un CGImageSourceRef. Ouais, ça a l'air laid, n'est-ce pas? Ce n'est pas si mal, continuons ... Vous voulez vraiment que l'image source ne soit pas une représentation ou un bloc de données NSImage. Cependant, nous avons un petit problème. La source n'est peut-être pas compatible, alors assurez-vous de vérifier votre UTI par rapport à ceux répertoriés dans CGImageSourceCopyTypeIdentifiers ()

Du code:

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

Attendez une seconde, pourquoi NSImage est-il là? Eh bien, il existe des formats qui n'ont pas de métadonnées et CGImageSource ne prend pas en charge, mais ce sont des images valides. Un exemple est les images PICT à l'ancienne.

Maintenant, nous avons un CGImageSourceRef, assurez-vous qu'il n'est pas nul, puis obtenons maintenant un CGImageDestinationRef. Wow toutes ces références à suivre. Jusqu'à présent, nous sommes à 2!

Nous utiliserons cette fonction: CGImageDestinationCreateWithData ()

  • Le 1er paramètre est votre imageData (cast CFMutableDataRef)
  • 2nd Param est votre sortie UTI, rappelez-vous la liste ci-dessus. (par exemple, kUTTypePNG)
  • 3e paramètre est le nombre d'images à enregistrer. Pour les fichiers d'image unique, c'est 1 sinon vous pouvez simplement utiliser ce qui suit:

    CGImageSourceGetCount (imageSource);

  • 4ème Param est nul.

Vérifiez si vous avez ce CGImageDestinationRef et maintenant ajoutons nos images de la source dedans ... cela inclura également toutes les métadonnées et conservera la résolution.

Pour plusieurs images, nous bouclons:

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

Pour une seule image, c'est une ligne de code à l'index 0:

CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);

Ok, finalisez-le qui l'écrit sur le disque ou le conteneur de données:

CGImageDestinationFinalize( imageDest );

Pour que Mutable Data depuis le début contienne toutes nos données d'image et métadonnées maintenant.

Avons-nous finit? Presque, même avec la collecte des ordures, vous devez nettoyer! N'oubliez pas deux références, une pour la source et une pour la destination, alors faites un CFRelease ()

Maintenant, nous avons terminé et vous vous retrouvez avec une image convertie conservant toutes ses métadonnées, sa résolution, etc.

Mes méthodes de catégorie NSData ressemblent à ceci:

+ (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'en est-il du redimensionnement ou de l'ICO/ICNS? C'est pour un autre jour, mais en résumé, vous abordez d'abord le redimensionnement ...

  1. Créez un contexte avec une nouvelle taille: CGBitmapContextCreate ()
  2. Obtenez la référence d'une image à partir de l'index: CGImageSourceCreateImageAtIndex ()
  3. Obtenez une copie des métadonnées: CGImageSourceCopyPropertiesAtIndex ()
  4. Dessinez l'image dans le contexte: CGContextDrawImage ()
  5. Obtenez l'image redimensionnée à partir du contexte: CGBitmapContextCreateImage ()
  6. Ajoutez maintenant l'image et les métadonnées à la référence Dest: CGImageDestinationAddImage ()

Rincez et répétez pour plusieurs images incorporées dans la source.

La seule différence entre un ICO et ICNS est que l'un est une image unique tandis que l'autre est plusieurs images dans un fichier. Je parie que vous pouvez deviner lequel est lequel?! ;-) Pour ces formats, vous devez redimensionner à une taille particulière sinon une ERREUR s'ensuivra. Le processus est cependant exactement le même lorsque vous utilisez le bon UTI, mais le redimensionnement est un peu plus strict.

J'espère que cela aide les autres et que vous êtes aussi rassasié que moi maintenant!

Opps, j'ai oublié de mentionner. Lorsque vous obtenez l'objet NSData, faites comme vous le souhaitez, comme writeToFile, writeToURL ou créez un autre NSImage si vous le souhaitez.

Bon codage!

33
Arvin

enregistrer au format PNG à l'aide de 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
        }
    }
}
14
neoneye

Solution 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)")
        }
    }
}
12
Charlton Provatas

Style rapide:

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

Pour aider avec le code multiplateforme, j'ai implémenté une version deUIImagePNGRepresentation() qui s'exécute sur Mac (et utilise 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

Usage:

if let data = UIImagePNGRepresentation(myImage) {
    do { try data.write(to: url, options: [.atomic]) }
    catch let error { print("Error writing image (\(error))") }
}
1
Robin Stewart

Encore une approche de travail garantie avec Swift:

J'ai une "image bien" où l'utilisateur peut déposer n'importe quelle image. Et ce "Image Well" possède une propriété d'image (de type NSImage) accessible via une sortie:

@IBOutlet weak var imageWell: NSImageView!

Et le code qui enregistre cette image (vous pouvez la mettre dans l'action du bouton) est:

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

Dans la 1ère ligne du code donné, nous vérifions si notre puits d'image a une image.

Dans la 2ème ligne, nous faisons une représentation bitmap de cette image.

Dans la 3ème ligne, nous convertissons notre BitmapRepresentation en un type JPG avec un facteur de compression réglé sur "1" (pas de compression).

Dans la 4ème ligne, nous enregistrons les données JPG avec un chemin donné. "atomically: true" signifie que le fichier est enregistré en une seule pièce et cela nous assure que l'opération sera réussie.

N.B.: Vous pouvez utiliser un autre NSBitmapImageFileType dans la 3ème ligne, pour enregistrer votre image dans un autre format. Il y en a plein:

NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType

etc.

1
German G.