Як я можу зберегти NSImage як новий файл (png, jpg, ...) у певному каталозі?
Як я можу зберегти NSImage як новий файл (png, jpg, ...) у певному каталозі?
Відповіді:
Зробіть щось подібне:
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
обхідне рішення: NSCGImageSnapshotRep, як отримати bitmapData
Ви можете додати категорію до NSImage, як це
@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
Виклик "TIFFRepresentation" є важливим, інакше ви можете не отримати дійсне зображення.
Не впевнений у решті вас, але я вважаю за краще їсти свою повну енчиладу. Те, що описано вище, спрацює, і нічого страшного в цьому немає, але я виявив, що кілька речей залишились поза увагою. Тут я виділю свої спостереження:
Отже, якщо ви хочете перетворити це зображення, ви дійсно хочете програти все це? Якщо ви хочете з’їсти повноцінне харчування, давайте читайте далі ...
Іноді, і я маю на увазі, іноді немає нічого кращого, ніж хороший розвиток старої школи. Так, це означає, що нам доведеться трохи попрацювати!
Давайте розпочнемо:
Я створюю категорію в NSData. Це методи класу, оскільки ви хочете, щоб ці речі були безпечними для потоків, і немає нічого безпечнішого, ніж розміщення ваших речей у стеку. Існує два типи методів, один для виведення зображень, що не є багатосторінковими, і один для виведення багатосторінкових зображень.
Список окремих зображень: JPG, PNG, BMP, JPEG-2000
Список декількох зображень: PDF, GIF, TIFF
Спочатку створіть змінний простір даних у пам'яті.
NSMutableData * imageData = [NSMutableData data];
По-друге, отримайте CGImageSourceRef. Так, вже некрасиво звучить, чи не так. Це не так погано, продовжуймо йти далі ... Ви дійсно хочете, щоб вихідне зображення не було поданням чи шматком даних NSImage. Однак у нас є невеличка проблема. Джерело може бути несумісним, тому переконайтеся, що ви перевіряєте свій UTI на основі перелічених у CGImageSourceCopyTypeIdentifiers ()
Деякий код:
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 );
Зачекайте секунду, чому там NSImage? Ну, є деякі формати, які не мають метаданих, і CGImageSource не підтримує, але це дійсні зображення. Прикладом є зображення PICT у старому стилі.
Тепер у нас є CGImageSourceRef, переконайтесь, що він не нульовий, і тоді давайте тепер отримаємо CGImageDestinationRef. Нічого собі, всі ці посилання для відстеження. Поки що ми на 2!
Ми будемо використовувати цю функцію: CGImageDestinationCreateWithData ()
Третя парама - кількість зображень, які потрібно зберегти. Для окремих файлів зображень це 1, інакше ви можете просто використовувати наступне:
CGImageSourceGetCount (imageSource);
4-й парам - нуль.
Перевірте, чи є у вас цей CGImageDestinationRef, а тепер давайте додамо в нього наші зображення з джерела ... це також включатиме всі / всі метадані та зберігатиме роздільну здатність.
Для кількох зображень ми циклу:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
Для одного зображення це один рядок коду з індексом 0:
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
Гаразд, доопрацюй, що запише на диск або в контейнер даних:
CGImageDestinationFinalize( imageDest );
Тож, щоб „Змінні дані” з самого початку в нас були всі наші дані зображень та метадані.
Ми вже закінчили? Майже навіть при вивозі сміття вам доведеться прибирати! Запам’ятайте два посилання - один для джерела та один для пункту призначення, тому зробіть CFRelease ()
Тепер ми закінчили, і у вас вийде конвертоване зображення, що зберігає всі свої метадані, роздільну здатність тощо ...
Мої методи категорії NSData виглядають так:
+ (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;
А як щодо зміни розміру або ICO / ICNS? Це на інший день, але в підсумку ви спочатку вирішуєте зменшення розміру ...
Промийте та повторіть для кількох зображень, вбудованих у джерело.
Єдина різниця між ICO та ICNS полягає в тому, що одне - це одне зображення, а інше - кілька зображень в одному файлі. Б'юсь об заклад, ви можете здогадатися, що є яке ?! ;-) Для цих форматів вам доведеться зменшити розмір до певного розміру, інакше виникне ПОМИЛКА. Хоча процес точно такий самий, коли ви використовуєте відповідний UTI, але зміна розміру трохи суворіша.
Добре, сподіваюся, це допомагає іншим, і ти такий же ситий, як і я зараз!
Оппс, забув згадати. Коли ви отримуєте об'єкт NSData, робіть із ним як завгодно, наприклад, writeToFile, writeToURL або, чорт, створіть інший NSImage, якщо хочете.
Щасливого кодування!
Свіфт 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)")
}
}
}
self.self
замість self
? Чи є якась різниця?
<<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
}
}
}
Стрімкий стиль:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}
Щоб допомогти з крос-платформним кодом, я впровадив версію, UIImagePNGRepresentation()
яка працює на Mac (і використовує 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
Використання:
if let data = UIImagePNGRepresentation(myImage) {
do { try data.write(to: url, options: [.atomic]) }
catch let error { print("Error writing image (\(error))") }
}
Ще один гарантовано підхід до роботи за допомогою SWIFT:
У мене є "Колодязь зображень", куди користувач може скинути будь-яке зображення. І цей "Колодязь зображення" має властивість зображення (типу NSImage), доступ до якої здійснюється через розетку:
@IBOutlet weak var imageWell: NSImageView!
І код, який зберігає це зображення (ви можете помістити його всередину дії кнопки):
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)
}
У 1-му рядку даного коду ми перевіряємо, чи є в нашому Image Well зображення.
У 2-му рядку ми робимо растрове зображення цього зображення.
У 3-му рядку ми перетворюємо нашу BitmapRepresentation у тип JPG з коефіцієнтом стиснення, встановленим на "1" (без стиснення).
У 4-му рядку ми зберігаємо дані JPG із заданим шляхом. "atomically: true" означає, що файл зберігається як цілісний фрагмент, і це гарантує нам, що операція буде успішною.
Примітка. Ви можете використовувати інший NSBitmapImageFileType у 3-му рядку, щоб зберегти зображення в іншому форматі. Він має багато:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
тощо
Розчин OBJC зі стисненням та без нього
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];
}
}
}