特定のディレクトリに NSImage を新しいファイル (png、jpg、...) として保存するにはどうすればよいですか?
9 に答える
このように 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」への呼び出しは不可欠です。そうしないと、有効な画像を取得できない場合があります。
次のようにします。
NSBitmapImageRep *imgRep = [[image representations] objectAtIndex: 0];
NSData *data = [imgRep representationUsingType: NSPNGFileType properties: nil];
[data writeToFile: @"/path/to/file.png" atomically: NO];
あなたの残りの部分についてはわかりませんが、私は完全なエンチラーダを食べることを好みます. 上記で説明したことは機能し、何も問題はありませんが、省略されていることがいくつかあります。ここで、私の所見を強調します。
- 最初に、画像の解像度がそれより大きい場合でも 72 DPI のように見える最適な表現が提供されます。だからあなたは解像度を失っています
- 次に、アニメーション GIF や PDF などの複数ページの画像についてはどうでしょう。先に進み、アニメーション GIF を試してみてください。アニメーションが失われていることがわかります
- 最後に、EXIF、GPS などのメタデータはすべて失われます。
では、その画像を変換したい場合、本当にすべてを失いたいのでしょうか? しっかりと食事をしたい場合は、読み進めてください...
時には、古き良き学校の開発に勝るものはないこともあります。ええ、それは私たちが少し仕事をしなければならないことを意味します!
始めましょう:
NSData でカテゴリを作成します。これらをスレッド セーフにする必要があり、自分のものをスタックに置くよりも安全なことはないため、これらはクラス メソッドです。非マルチページ画像出力用とマルチページ画像出力用の 2 種類の方法があります。
単一画像のリスト: 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 ができました。nil でないことを確認してから、CGImageDestinationRef を取得しましょう。これらすべての参照を追跡する必要があります。これまでのところ、私たちは2です!
この関数を使用します: CGImageDestinationCreateWithData()
- 最初のパラメータは imageData です (キャスト CFMutableDataRef)
- 2 番目のパラメータは出力 UTI です。上記のリストを思い出してください。(例: kUTTypePNG)
3 番目のパラメータは、保存する画像の数です。単一の画像ファイルの場合、これは 1 です。それ以外の場合は、次のように単純に使用できます。
CGImageSourceGetCount( imageSource );
4 番目のパラメータは nil です。
この CGImageDestinationRef があるかどうかを確認し、ソースから画像を追加しましょう...これには、任意/すべてのメタデータも含まれ、解像度が保持されます。
複数の画像の場合、ループします:
for ( NSUInteger i = 0; i < count; ++i )
CGImageDestinationAddImageFromSource( imageDest, imageSource, i, nil );
単一の画像の場合、インデックス 0 の 1 行のコードです。
CGImageDestinationAddImageFromSource( imageDest, imageSource, 0, nil);
OK、ディスクまたはデータ コンテナーに書き込むファイナライズ:
CGImageDestinationFinalize( imageDest );
そのため、最初のミュータブル データには、すべての画像データとメタデータが含まれています。
まだ終わってないか?ほとんど、ガベージ コレクションがあってもクリーンアップする必要があります。ソース用と宛先用の 2 つの Ref を覚えているので、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はどうですか?これは別の日にしますが、要約すると、最初にサイズ変更に取り組みます...
- 新しいサイズでコンテキストを作成します: CGBitmapContextCreate()
- インデックスから画像参照を取得します: CGImageSourceCreateImageAtIndex()
- メタデータのコピーを取得します: CGImageSourceCopyPropertiesAtIndex()
- コンテキストに画像を描画します: CGContextDrawImage()
- コンテキストからサイズ変更された画像を取得します: CGBitmapContextCreateImage()
- イメージとメタデータを Dest Ref に追加します: CGImageDestinationAddImage()
ソースに埋め込まれた複数の画像をすすぎ、繰り返します。
ICO と ICNS の唯一の違いは、一方が単一の画像であり、他方が 1 つのファイル内の複数の画像であることです。どれがどれか当てられるかな?! ;-) これらの形式の場合、特定のサイズに縮小する必要があります。そうしないと、エラーが発生します。プロセスは適切な UTI を使用する場合とまったく同じですが、サイズ変更はもう少し厳密です。
これが他の人の助けになることを願っています。あなたは今の私と同じくらい満腹です!
おっと、言い忘れました。NSData オブジェクトを取得したら、writeToFile、writeToURL など、必要に応じてそれを処理するか、必要に応じて別の NSImage を作成します。
ハッピーコーディング!
swift3 を使用して PNG として保存する
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
}
}
}
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)")
}
}
}
迅速なスタイル:
if let imgRep = image?.representations[0] as? NSBitmapImageRep
{
if let data = imgRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [:])
{
data.writeToFile("/path/to/file.png", atomically: false)
}
}
SWIFT を使用して動作することが保証されているもう 1 つのアプローチ:
ユーザーが任意の画像をドロップできる「Image Well」があります。そして、この「Image Well」には、アウトレット経由でアクセスできる (NSImage 型の) image プロパティがあります。
@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」は、ファイルが 1 つのピースとして保存され、操作が成功することを保証することを意味します。
注意: 3行目に別の NSBitmapImageFileType を使用して、画像を別の形式で保存できます。それはたくさんあります:
NSBitmapImageFileType.NSBMPFileType
NSBitmapImageFileType.NSGIFFileType
NSBitmapImageFileType.NSPNGFileType
等
圧縮あり/なしのオブジェクト ソリューション
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];
}
}
}