31

画像に対していくつかの操作を行っています。完了したら、画像を 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];
}

このコードは機能していますが、Retina mac に問題があります。NSBitmapImageRep オブジェクトを印刷すると、異なるサイズとピクセル rect が得られ、イメージがディスクに保存されると、サイズが 2 倍になります。

$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>

元のサイズを維持したいので、ピクセルサイズが網膜スケールを気にしないように強制するように結びました。

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

今回は 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>

これを修正して元のピクセルサイズを維持する方法はありますか?

4

6 に答える 6

49

を持っていて、NSImageそれをイメージ ファイルとしてファイル システムに保存したい場合は、絶対に!を使用しないでください。画面に表示されるように決定された新しい画像を作成します。したがって、画面のプロパティを使用します。通常の画面では 72 dpi、Retina画面では 144 dpi です。あなたが望むもののために、私は次のコードを提案します:lockFocuslockFocuslockFocus

+ (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];
}
于 2013-07-07T08:58:11.077 に答える
16

NSImagelockFocusは解像度を認識し、Retinaスクリーンを搭載したシステムでは HiDPI グラフィックス コンテキストを使用します。
イニシャライザに渡す画像のサイズNSBitmapImageRepはポイント単位です (ピクセル単位ではありません)。したがって、150.0 ポイント幅の画像は、@2x コンテキストで 300 水平ピクセルを使用します。

convertRectToBacking:またはbackingScaleFactor:を使用して、@2x コンテキストを補正できます。(私はそれを試しませんでした)、または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
于 2013-07-07T08:00:40.730 に答える
5

このコードは web で見つけましたが、網膜で動作します。ここに貼り付けてください。希望は誰かを助けることができます。

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]; 
于 2015-03-20T08:44:57.317 に答える
4

誰かがこのスレッドに出くわした場合に備えて。これは、デバイスに関係なく1xサイズ(image.size)で画像を迅速に保存するという仕事をする、確かに欠陥のあるソリューションです。

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
    }
}
于 2015-08-19T21:41:12.263 に答える
0

拡張機能を処理する書き込みとオフスクリーン画像の描画 (方法 2) を含む、OS X に対する私の 2 セント。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)
        }
    }
}
于 2016-09-13T14:04:10.733 に答える