19

イメージ上でいくつかの操作をしています。終了後、イメージをディスクに保存します。私は、次のことをやっている:NSImage(網膜の問題)からPNGファイルを保存する方法

+ (void)saveImage:(NSImage *)image atPath:(NSString *)path { 

    [image lockFocus] ; 
    NSBitmapImageRep *imageRepresentation = [[NSBitmapImageRep alloc] initWithFocusedViewRect:NSMakeRect(0.0, 0.0, image.size.width, image.size.height)] ; 
    [image unlockFocus] ; 

    NSData *data = [imageRepresentation representationUsingType:NSPNGFileType properties:nil]; 
    [data writeToFile:path atomically:YES]; 
} 

このコードが動作しているが、問題は、私はNSBitmapImageRepオブジェクトを印刷する場合、私はさまざまなサイズとピクセルRECTを取得し、私のイメージがディスク上に保存されている場合、網膜マックであり、

imageRepresentation.pixelsWide = image.size.width; 
imageRepresentation.pixelsHigh = image.size.height; 

私が手にこの時間:

$0 = 0x0000000100413890 NSBitmapImageRep 0x100413890 Size={300, 300} ColorSpace=sRGB IEC61966-2.1 colorspace BPS=8 BPP=32 Pixels=600x600 Alpha=YES Planar=NO Format=0 CurrentBacking=<CGImageRef: 0x100414830> 

は私が元のサイズを維持したいと、網膜の規模についての世話をしないようにピクセルサイズを強制的に結ば:それは2倍の大きさです右のサイズ私はNSBitmapImageRepオブジェクトを印刷するが、私は私のファイルを保存するとき、私はまだ同じ問題を取得する場合:

$0 = 0x0000000100413890 NSBitmapImageRep 0x100413890 Size={300, 300} ColorSpace=sRGB IEC61966-2.1 colorspace BPS=8 BPP=32 Pixels=300x300 Alpha=YES Planar=NO Format=0 CurrentBacking=<CGImageRef: 0x100414830> 

任意のアイデアはどのようにこれを修正し、オリジナルのピクセルサイズを維持するの?

答えて

35

をファイルシステムは、決してを使用するlockFocuslockFocusは、画面を表示するために決定され、何も表示されない新しい画像を作成する。したがって、lockFocusは、画面のプロパティを使用します。の場合は72 dpi、のスクリーンの場合は72 dpi、の場合は144 dpiの網膜の画面が使用されます。あなたが望むものについては、私は次のコードを提案します:

+ (void)saveImage:(NSImage *)image atPath:(NSString *)path { 

    CGImageRef cgRef = [image CGImageForProposedRect:NULL 
              context:nil 
               hints:nil]; 
    NSBitmapImageRep *newRep = [[NSBitmapImageRep alloc] initWithCGImage:cgRef]; 
    [newRep setSize:[image size]]; // if you want the same resolution 
    NSData *pngData = [newRep representationUsingType:NSPNGFileType properties:nil]; 
    [pngData writeToFile:path atomically:YES]; 
    [newRep autorelease]; 
} 
+2

' - [NSBitmapImageRep setSize:]'は10.10以降しか利用できないようです。おそらくそれがなぜマーベリックスでコードを試したときに、画像のサイズが変更されないのでしょうか?例外はスローされませんが...私はどのサイズを渡しても元のサイズと同じサイズのイメージを取得しています。 –

+0

@NicolasMiari 'newRep'のサイズが(10.9を目標にしていますが、10.10で動いている)ものに変更されていますが、ディスクに書き込まれたファイルにはまだ2倍の画像が含まれています。あなたは解決策を考案したことがありますか? – Dov

+0

はい、Weichselの答えは私のために働いた。 –

14

NSImageは、網膜の画面を持つシステム上でlockFocusを使用したときに、HiDPIグラフィックスコンテキストを使用します。
NSBitmapImageRepイニシャライザに渡す画像の大きさは、ピクセル単位ではなく、ポイント単位です。したがって、150.0ポイント幅の画像は、@ 2xの文脈で300の水平ピクセルを使用する。

@ 2xのコンテキストを補正するには、convertRectToBacking:またはbackingScaleFactor:を使用できます。 (私はそれをしようとしなかった)、または明示的なピクセル寸法で描画コンテキストを作成し、以下のNSImageカテゴリ、使用することができます:あなたはNSImageを持っているとする画像ファイルとして保存したい場合は

@interface NSImage (SSWPNGAdditions) 

- (BOOL)writePNGToURL:(NSURL*)URL outputSizeInPixels:(NSSize)outputSizePx error:(NSError*__autoreleasing*)error; 

@end 

@implementation NSImage (SSWPNGAdditions) 

- (BOOL)writePNGToURL:(NSURL*)URL outputSizeInPixels:(NSSize)outputSizePx error:(NSError*__autoreleasing*)error 
{ 
    BOOL result = YES; 
    NSImage* scalingImage = [NSImage imageWithSize:[self size] flipped:NO drawingHandler:^BOOL(NSRect dstRect) { 
     [self drawAtPoint:NSMakePoint(0.0, 0.0) fromRect:dstRect operation:NSCompositeSourceOver fraction:1.0]; 
     return YES; 
    }]; 
    NSRect proposedRect = NSMakeRect(0.0, 0.0, outputSizePx.width, outputSizePx.height); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); 
    CGContextRef cgContext = CGBitmapContextCreate(NULL, proposedRect.size.width, proposedRect.size.height, 8, 4*proposedRect.size.width, colorSpace, kCGBitmapByteOrderDefault|kCGImageAlphaPremultipliedLast); 
    CGColorSpaceRelease(colorSpace); 
    NSGraphicsContext* context = [NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:NO]; 
    CGContextRelease(cgContext); 
    CGImageRef cgImage = [scalingImage CGImageForProposedRect:&proposedRect context:context hints:nil]; 
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)(URL), kUTTypePNG, 1, NULL); 
    CGImageDestinationAddImage(destination, cgImage, nil); 
    if(!CGImageDestinationFinalize(destination)) 
    { 
     NSDictionary* details = @{NSLocalizedDescriptionKey:@"Error writing PNG image"}; 
     [details setValue:@"ran out of money" forKey:NSLocalizedDescriptionKey]; 
     *error = [NSError errorWithDomain:@"SSWPNGAdditionsErrorDomain" code:10 userInfo:details]; 
     result = NO; 
    } 
    CFRelease(destination); 
    return result; 
} 

@end 
+0

私はさまざまな解決策を試しました(ここで受け入れられた回答を含む)。これが私が試した唯一の解決策です。ありがとう! – EsbenB

+0

私はあなたのソリューションを実装しています。 「isFlippedは廃止されました:OS X 10.6では最初に非推奨です」という警告が表示されます。私は警告を無視するか、より良い通話を取り除くべきですか? –

+0

また、 ''列挙型 'enum CGImageAlphaInfo'から別の列挙型 'CGBitmapInfo'(別名 'enum CGBitmapInfo') ''への暗黙の変換。これはより深刻な警告であるようです。私はこれら2つの列挙型の定義をチェックしましたが、それらは完全に異なっています。しかし、 'CGBitmapInfo'には 'premultiplied alpha'の定数はありません。 –

4

私は網上でこのコードを見つけ、それは網膜上で動作します。ここに貼り付けると、誰かを助けることができます。

NSImage *computerImage = [NSImage imageNamed:NSImageNameComputer]; 
NSInteger size = 256; 

NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] 
        initWithBitmapDataPlanes:NULL 
           pixelsWide:size 
           pixelsHigh:size 
          bitsPerSample:8 
          samplesPerPixel:4 
            hasAlpha:YES 
            isPlanar:NO 
          colorSpaceName:NSCalibratedRGBColorSpace 
           bytesPerRow:0 
           bitsPerPixel:0]; 
[rep setSize:NSMakeSize(size, size)]; 

[NSGraphicsContext saveGraphicsState]; 
[NSGraphicsContext setCurrentContext:[NSGraphicsContext  graphicsContextWithBitmapImageRep:rep]]; 
[computerImage drawInRect:NSMakeRect(0, 0, size, size) fromRect:NSZeroRect operation:NSCompositeCopy fraction:1.0]; 
[NSGraphicsContext restoreGraphicsState]; 

NSData *data = [rep representationUsingType:NSPNGFileType properties:nil]; 
+0

https://gist.github.com/mattstevens/4400775 1x画像として出力するための網膜MacでのNSImageのサイズ変更 – flowers

+0

これは質問に直接答えは出ていないようです – abarisone

4

このスレッドで誰かがつまずく。ここでは(法2を描画する拡張+オフスクリーン画像を処理し、書き込みなどのOS Xについて迅速

public func writeToFile(path: String, atomically: Bool = true) -> Bool{ 

    let bitmap = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(self.size.width), pixelsHigh: Int(self.size.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)! 
    bitmap.size = self.size 

    NSGraphicsContext.saveGraphicsState() 

    NSGraphicsContext.setCurrentContext(NSGraphicsContext(bitmapImageRep: bitmap)) 
    self.drawAtPoint(CGPoint.zero, fromRect: NSRect.zero, operation: NSCompositingOperation.CompositeSourceOver, fraction: 1.0) 
    NSGraphicsContext.restoreGraphicsState() 

    if let imagePGNData = bitmap.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: [NSImageCompressionFactor: 1.0]) { 
     return imagePGNData.writeToFile((path as NSString).stringByStandardizingPath, atomically: atomically) 
    } else { 
     return false 
    } 
} 
0

私の2セントにかかわらず、デバイスの1Xサイズ(image.size)で画像を保存する仕事をして欠陥のある解決策は確かにあります); NSGraphicsContext.currentContextDrawingToScreen()で確認できます。

func createCGImage() -> CGImage? { 

    //method 1 
    let image = NSImage(size: NSSize(width: bounds.width, height: bounds.height), flipped: true, drawingHandler: { rect in 
     self.drawRect(self.bounds) 
     return true 
    }) 
    var rect = CGRectMake(0, 0, bounds.size.width, bounds.size.height) 
    return image.CGImageForProposedRect(&rect, context: bitmapContext(), hints: nil) 


    //method 2 
    if let pdfRep = NSPDFImageRep(data: dataWithPDFInsideRect(bounds)) { 
     return pdfRep.CGImageForProposedRect(&rect, context: bitmapContext(), hints: nil) 
    } 
    return nil 
} 

func PDFImageData(filter: QuartzFilter?) -> NSData? { 
    return dataWithPDFInsideRect(bounds) 
} 

func bitmapContext() -> NSGraphicsContext? { 
    var context : NSGraphicsContext? = nil 
    if let imageRep = NSBitmapImageRep(bitmapDataPlanes: nil, 
             pixelsWide: Int(bounds.size.width), 
             pixelsHigh: Int(bounds.size.height), bitsPerSample: 8, 
             samplesPerPixel: 4, hasAlpha: true, isPlanar: false, 
             colorSpaceName: NSCalibratedRGBColorSpace, 
             bytesPerRow: Int(bounds.size.width) * 4, 
             bitsPerPixel: 32) { 
     imageRep.size = NSSize(width: bounds.size.width, height: bounds.size.height) 
     context = NSGraphicsContext(bitmapImageRep: imageRep) 
    } 
    return context 
} 

func writeImageData(view: MyView, destination: NSURL) { 
    if let dest = CGImageDestinationCreateWithURL(destination, imageUTType, 1, nil) { 
     let properties = imageProperties 
     let image = view.createCGImage()! 
     let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 
     dispatch_async(queue) { 
      CGImageDestinationAddImage(dest, image, properties) 
      CGImageDestinationFinalize(dest) 
     } 
    } 
} 
関連する問題