Search code examples
objective-cimagemacospngretina-display

How to save a retina image, NSBitmapImageRep


I am trying to create something that emulates that mac osx built in screen shot tool, where if you take a screenshot in a retina monitor (320x240), the image that results opens at the same size as it was taken, but it is retina in that the dpi is 144.

However, no matter what I do I can't seem to do the same thing. I can save a image with the exact same metadata, specifications (as far as I can see), and it just opens up at double size.

Here is the data from the built in screenshot image https://i.sstatic.net/tQBNo.png

Here is the data from the image I produce https://i.sstatic.net/F6jTg.png

The only difference I see is that the XMP exif data is different. But for one, I can't seem to figure out how to change it, and 2, I don't see how that would make sense anyways so I think I'm still not seeing something that makes a retina image.

I theorize that there is some kind of a hidden flag that I'm missing that tells Preview to open up the builtin screenshot with 320x240 instead of it's pixel size, 640x480

And here is my code. The NSImageEXIFData does not seem to be sticking

   NSBitmapImageRep *bmpImageRep = [[NSBitmapImageRep alloc]
                                    initWithBitmapDataPlanes:NULL
                                    pixelsWide:640
                                    pixelsHigh:480
                                    bitsPerSample:8
                                    samplesPerPixel:4
                                    hasAlpha:YES
                                    isPlanar:NO
                                    colorSpaceName:NSCalibratedRGBColorSpace
                                    bitmapFormat:NSAlphaFirstBitmapFormat
                                    bytesPerRow:0
                                    bitsPerPixel:0
                                    ];


   bmpImageRep.size=NSMakeSize(320,240);

   id profile = [NSData dataWithContentsOfFile: @"/Library/ColorSync/Profiles/Displays/Color LCD-FD6E9053-53B7-5224-5892-F9F7EC52CEF3.icc"];
   [bmpImageRep setProperty:NSImageColorSyncProfileData withValue:profile];

   NSDictionary *exif = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithInt:640], kCGImagePropertyExifPixelXDimension, [NSNumber numberWithInt:480], kCGImagePropertyExifPixelYDimension, nil];
   [bmpImageRep setProperty:NSImageEXIFData withValue:exif];

   NSGraphicsContext *bitmapContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:bmpImageRep];
   [NSGraphicsContext saveGraphicsState];
   [NSGraphicsContext setCurrentContext: bitmapContext];

   NSRect bgRect = NSMakeRect(0,0,320,240);
   [myImage drawInRect:bgRect fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];

   [NSGraphicsContext restoreGraphicsState];


   NSData* pngData= [bmpImageRep representationUsingType:NSPNGFileType
                               properties:nil];
   return pngData;

Solution

  • That screenshot information appears to be stored in the file-system's extended attributes rather than the specific format's image metadata. Those extended attributes will be duplicated along with a file copy or open/save (as long as the application does not explicitly change the attributes) but not with a content-only copy (e.i. create new file and replicate original file's content).

    You can use the xattr command line tool to list the extended attributes of a file. Here is what you would expect for a screenshot:

    com.apple.metadata:kMDItemIsScreenCapture com.apple.metadata:kMDItemScreenCaptureGlobalRect com.apple.metadata:kMDItemScreenCaptureType

    And sys/xattr.h is the header that contains the functions necessary to manipulate the extended attributes of a file.