Wie kann ich ein NSImage als neue Datei (png, jpg, ...) in einem bestimmten Verzeichnis speichern?
Wie kann ich ein NSImage als neue Datei (png, jpg, ...) in einem bestimmten Verzeichnis speichern?
Antworten:
Mach so etwas:
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
[* representationUsingType:properties:]: unrecognized selector sent to instance
ist die Problemumgehung
Sie können NSImage wie folgt eine Kategorie hinzufügen
@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
Der Aufruf von "TIFFRepresentation" ist wichtig, da Sie sonst möglicherweise kein gültiges Bild erhalten.
Ich bin mir nicht sicher über den Rest von euch, aber ich esse lieber meine volle Enchilada. Was oben beschrieben wurde, wird funktionieren und nichts ist daran falsch, aber ich habe ein paar Dinge ausgelassen. Hier werde ich meine Beobachtungen hervorheben:
Wenn Sie dieses Bild konvertieren möchten, möchten Sie das alles wirklich verlieren? Wenn Sie Ihre volle Mahlzeit essen möchten, dann lesen Sie weiter ...
Manchmal und ich meine manchmal gibt es nichts Besseres als eine gute alte Schulentwicklung. Ja das heißt, wir müssen ein bisschen arbeiten!
Lass uns anfangen:
Ich erstelle eine Kategorie in NSData. Dies sind Klassenmethoden, weil Sie möchten, dass diese Dinge threadsicher sind und es nichts Sichereres gibt, als Ihre Sachen auf den Stapel zu legen. Es gibt zwei Arten von Methoden, eine für die Ausgabe von nicht mehrseitigen Bildern und eine für die Ausgabe von mehrseitigen Bildern.
Liste der Einzelbilder: JPG, PNG, BMP, JPEG-2000
Liste mehrerer Bilder: PDF, GIF, TIFF
Erstellen Sie zunächst einen veränderlichen Datenraum im Speicher.
NSMutableData * imageData = [NSMutableData data];
Zweitens erhalten Sie ein CGImageSourceRef. Ja, es klingt schon hässlich, nicht wahr? Es ist nicht so schlimm, lass uns weitermachen ... Sie möchten wirklich, dass das Quellbild keine Darstellung oder ein NSImage-Datenblock ist. Wir haben jedoch ein kleines Problem. Die Quelle ist möglicherweise nicht kompatibel. Überprüfen Sie daher Ihre UTI anhand der in CGImageSourceCopyTypeIdentifiers () aufgeführten Quellen.
Etwas 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 );
Warten Sie eine Sekunde, warum ist NSImage dort? Nun, es gibt einige Formate, die keine Metadaten haben und CGImageSource nicht unterstützt, aber dies sind gültige Bilder. Ein Beispiel sind PICT-Bilder im alten Stil.
Jetzt haben wir ein CGImageSourceRef, stellen Sie sicher, dass es nicht Null ist, und lassen Sie uns jetzt ein CGImageDestinationRef erhalten. Wow, all diese Schiedsrichter sind im Auge zu behalten. Bisher sind wir bei 2!
Wir werden diese Funktion verwenden: CGImageDestinationCreateWithData ()
3. Parameter ist die Anzahl der zu speichernden Bilder. Für Einzelbilddateien ist dies 1, andernfalls können Sie einfach Folgendes verwenden:
CGImageSourceGetCount (imageSource);
4. Param ist Null.
Überprüfen Sie, ob Sie über dieses CGImageDestinationRef verfügen, und fügen Sie nun unsere Bilder aus der Quelle hinzu. Dies schließt auch alle Metadaten ein und behält die Auflösung bei.
Für mehrere Bilder schleifen wir:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
Für ein einzelnes Bild ist es eine Codezeile bei Index 0:
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
Ok, finalisieren Sie es, das es auf die Festplatte oder den Datencontainer schreibt:
CGImageDestinationFinalize( imageDest );
Damit Mutable Data von Anfang an alle unsere Bilddaten und Metadaten enthält.
Sind wir schon fertig? Fast, auch mit Müllabfuhr muss man aufräumen! Denken Sie an zwei Refs, einen für die Quelle und einen für das Ziel. Führen Sie also eine CFRelease () durch.
Jetzt sind wir fertig und am Ende erhalten Sie ein konvertiertes Bild, das alle Metadaten, Auflösungen usw. beibehält.
Meine NSData-Kategoriemethoden sehen folgendermaßen aus:
+ (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;
Was ist mit Größenänderung oder ICO / ICNS? Dies ist für einen anderen Tag, aber zusammenfassend befassen Sie sich zuerst mit der Größenänderung ...
Spülen und wiederholen Sie dies für mehrere in die Quelle eingebettete Bilder.
Der einzige Unterschied zwischen einem ICO und einem ICNS besteht darin, dass eines ein einzelnes Bild ist, während das andere mehrere Bilder in einer Datei enthält. Wetten, dass Sie erraten können, welches welches ist?! ;-) Für diese Formate müssen Sie die Größe auf eine bestimmte Größe verkleinern, da sonst ein FEHLER auftritt. Der Prozess ist zwar genau der gleiche, bei dem Sie die richtige UTI verwenden, aber die Größenänderung ist etwas strenger.
Ok, hoffe das hilft anderen da draußen und du bist so voll wie ich jetzt!
Opps, vergessen zu erwähnen. Wenn Sie das NSData-Objekt erhalten, tun Sie, was Sie möchten, z. B. writeToFile, writeToURL oder erstellen Sie ein anderes NSImage, wenn Sie möchten.
Viel Spaß beim Codieren!
Swift 4.2 Lösung
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)")
}
}
}
self.self
anstelle von verwenden self
? Gibt es einen Unterschied?
<<Class-Name> <Memory-Address>>
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
}
}
}
Schneller Stil:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}
Um mit plattformübergreifendem Code zu helfen, habe ich eine Version implementiert UIImagePNGRepresentation()
, die auf einem Mac ausgeführt wird (und verwendet wird 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
Verwendung:
if let data = UIImagePNGRepresentation(myImage) {
do { try data.write(to: url, options: [.atomic]) }
catch let error { print("Error writing image (\(error))") }
}
Nur noch ein garantierter Arbeitsansatz mit SWIFT:
Ich habe einen "Image Well", in dem der Benutzer jedes Bild ablegen kann. Und dieser "Image Well" hat eine Bildeigenschaft (vom Typ NSImage), auf die über eine Steckdose zugegriffen wird:
@IBOutlet weak var imageWell: NSImageView!
Und der Code, der dieses Bild speichert (Sie können es in die Schaltflächenaktion einfügen), lautet:
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)
}
In der ersten Zeile des angegebenen Codes prüfen wir, ob unser Image Well ein Bild enthält.
In der 2. Zeile machen wir eine Bitmap-Darstellung dieses Bildes.
In der 3. Zeile konvertieren wir unsere BitmapRepresentation in einen JPG-Typ mit einem Komprimierungsfaktor von "1" (keine Komprimierung).
In der 4. Zeile speichern wir die JPG-Daten unter einem bestimmten Pfad. "atomar: wahr" bedeutet, dass die Datei als ein Stück gespeichert wird und dass wir sicher sind, dass die Operation erfolgreich sein wird.
NB: Sie können einen anderen NSBitmapImageFileType in der 3. Zeile verwenden, um Ihr Bild in einem anderen Format zu speichern. Es hat viel:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
etc.
Objektive Lösung mit und ohne Komprimierung
NSImage* image;
// No compression
NSData* data = [image TIFFRepresentation];
// With compression
NSData* data = [image
TIFFRepresentationUsingCompression:NSTIFFCompressionLZW
factor:0.5f];
if (data != nil) {
NSBitmapImageRep* bitmap = [[NSBitmapImageRep alloc] initWithData:data];
if (bitmap != nil) {
NSData* bitmapData = [bitmap
representationUsingType:NSBitmapImageFileTypePNG
properties:@{}];
if (bitmapData != nil) {
[bitmapData
writeToFile:<file_path>
atomically:true];
}
}
}