Comment puis-je enregistrer une image NSImage comme un nouveau fichier (png, jpg, ...) dans un certain répertoire ?
Mais TIFF est RGB et jette toute information de transparence.
Comment puis-je enregistrer une image NSImage comme un nouveau fichier (png, jpg, ...) dans un certain répertoire ?
Vous pourriez 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.
Non, le format TIFF convient parfaitement aux images alpha et multi-pages. Qu'est-ce qui vous fait penser que ce n'est pas le cas ? C'est ce qu'Apple recommande comme format de destination pour les images rétiniennes (TIFF multi-représentation).
Le format TIFF doit supporter l'alpha et la transparence fr.wikipedia.org/wiki/Transparency_(graphic)
Vous devez obtenir la représentation de l'image bitmap d'une manière plus fiable que de supposer qu'il s'agit de la première représentation. Vous ne pouvez pas non plus supposer qu'il y en a une ; si l'image a été chargée à partir d'un PDF, par exemple, il y aura un NSPDFImageRep, et non un NSBitmapImageRep.
Si on obtient un [* representationUsingType:properties:]: unrecognized selector sent to instance
la solution de contournement est ici : NSCGImageSnapshotRep, comment obtenir les bitmapData
Je ne sais pas pour le reste d'entre vous, mais je préfère manger toute l'enchilada. Ce qui est décrit ci-dessus fonctionnera et il n'y a rien de mal à cela, mais j'ai trouvé quelques oublis. Je vais souligner ici mes observations :
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 dis bien parfois, il n'y a rien de mieux que le bon vieux développement à l'ancienne. Oui, cela signifie que nous devons travailler un peu !
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, une pour la sortie d'images non multi-pages et une pour la sortie d'images multi-pages.
Liste des images uniques : JPG, PNG, BMP, JPEG-2000
Liste d'images multiples : PDF, GIF, TIFF
Créez d'abord un espace de données mutable en mémoire.
NSMutableData * imageData = [NSMutableData data];
Ensuite, obtenez un CGImageSourceRef. Oui, ça semble déjà moche, n'est-ce pas ? Ce n'est pas si grave, continuons... Vous voulez vraiment l'image source, pas une représentation ou un morceau de données NSImage. Cependant, nous avons un petit problème. La source peut ne pas être compatible, donc assurez-vous de vérifier votre UTI avec ceux listés par CGImageSourceCopyTypeIdentifiers().
Un certain 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 là ? Et bien il y a certains formats qui n'ont pas de métadonnées et que CGImageSource ne supporte pas, mais ce sont des images valides. Un exemple est l'ancien style d'images PICT.
Maintenant, nous avons un CGImageSourceRef, assurez-vous qu'il n'est pas nul et ensuite, obtenons un CGImageDestinationRef. Wow, toutes ces références à suivre. Jusqu'à présent, nous en sommes à 2 !
Nous allons utiliser cette fonction : CGImageDestinationCreateWithData()
1er Param est votre imageData (cast CFMutableDataRef)
Le deuxième paramètre est votre UTI de sortie, rappelez-vous la liste ci-dessus. (par exemple kUTTypePNG)
Le troisième paramètre est le nombre d'images à sauvegarder. Pour les fichiers d'images uniques, ce est 1, sinon vous pouvez simplement utiliser ce qui suit :
CGImageSourceGetCount( imageSource ) ;
Le 4e paramètre est nul.
Vérifiez si vous avez ce CGImageDestinationRef et maintenant ajoutons-y nos images de la source... ceci inclura également toutes les métadonnées et conservera la résolution.
Pour les images multiples, 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 et écrivez-le sur le disque ou le conteneur de données :
CGImageDestinationFinalize( imageDest );
Ainsi, les données mutables du début contiennent maintenant toutes les données et métadonnées de l'image.
Avons-nous déjà terminé ? Presque, même avec la collecte des déchets, il faut faire le ménage ! Rappelez-vous que vous avez deux Ref, un pour la source et un pour la destination, donc faites un CFRelease().
Maintenant nous avons terminé et ce que vous obtenez est 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é, il faut d'abord s'attaquer au redimensionnement...
Rincer et répéter pour les images multiples intégrées dans la source.
La seule différence entre une ICO et une ICNS est que l'une est une image unique tandis que l'autre est constituée de plusieurs images dans un seul 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 exactement le même lorsque vous utilisez le bon UTI, mais le redimensionnement est un peu plus strict.
J'espère que cela aidera d'autres personnes et que vous serez aussi rassasiés que moi maintenant !
Opps, j'ai oublié de mentionner. Lorsque vous obtenez l'objet NSData, faites-en ce que vous voulez, comme writeToFile, writeToURL, ou même créer un autre NSImage si vous le souhaitez.
Bon codage !
C'est un bon début, certainement meilleur que les autres réponses (même si celles-ci sont mieux notées) mais ce n'est pas complet. Tout d'abord, la logique telle qu'elle est présentée ici ne traitera pas les fichiers PDF par elle-même (CGImageDestinationAddImageFromSource ne fonctionnera pas puisque la source de l'image sera nulle). Les fichiers PDF et les autres sources d'images non copiables nécessitent un traitement spécial (pour obtenir les images via des vignettes ou pour copier les données d'une autre manière). Deuxièmement, il ne copiera aucune méta-donnée par lui-même, vous devez le faire explicitement en récupérant le dictionnaire de propriétés de la source d'image et en l'ajoutant à la destination de l'image.
Deux questions s'il vous plaît. 1) comment pouvez-vous être sûr que [anImage TIFFRepresentation] renvoie toujours quelque chose ? n'y a-t-il pas des NSImage qui n'ont pas de TIFFRepresentation ? 2) Pourriez-vous fournir un extrait de code fonctionnel pour au moins une (disons JPEPDataFromURL) de ces fonctions dans leur intégralité ? J'ai essayé de suivre vos instructions, et au moins pour moi - cela ne fonctionne pas (j'essaie d'enregistrer une trame de données vidéo entrante en tant que JPEG sur le disque, et mon entrée va du CMSampleBuffer de AVSession à travers un NSImage - et j'essaie d'utiliser votre code pour enregistrer cela dans un fichier.
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
}
}
}
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)")
}
}
}
Pouvez-vous expliquer pourquoi vous utilisez self.self
au lieu de self
? Y a-t-il une différence ?
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.
4 votes
J'ai ajouté une prime puisque quelqu'un a appelé la première option un hack laid et je ne peux pas sembler trouver facilement une réponse apparemment correcte et définitive sur google, plus de vote/réponses s'il vous plaît.