0

Hello and thanks for reading! I'm trying to write an NSImage object to a file on the disk but am having trouble as some of the 'image properties' are lost when i extract an NSData object from the original NSImage.

To illustrate the problem I have the below code. The original NSImage object is called 'image' which i pass to imageDump (output at the end), i then extract an NSData object from this in order to write it to file but doing so seems to remove some of the image properties. Again i call imageDump (output again at the end) but this time notice how some of the properties are removed. For example CGImageGetBitsPerPixel: starts at 32 but on the second call to imageDump it's now 24?

How do i prevent these image properties from being removed when i extract the NSData object prior to writing it to disk?

NSLog(@"/n PRINTING THE IMAGE PASSED TO THIS METHOD\n");
[self imageDump:[image CGImageForProposedRect:NULL context:NULL hints:NULL]];

NSData *imageData =  [image TIFFRepresentation];
NSImage *montysImage = [[NSImage alloc ] initWithData:imageData];

NSLog(@"/n PRINTING THE IMAGE THAT WAS FIRST CONVERTED TO NSDATA AND THEN BACK\n");
[self imageDump:[montysImage CGImageForProposedRect:NULL context:NULL hints:NULL]];

-(void)imageDump:(CGImageRef)cgimage  {


    size_t width  = CGImageGetWidth(cgimage);
    size_t height = CGImageGetHeight(cgimage);

    size_t bpr = CGImageGetBytesPerRow(cgimage);
    size_t bpp = CGImageGetBitsPerPixel(cgimage);
    size_t bpc = CGImageGetBitsPerComponent(cgimage);
    size_t bytes_per_pixel = bpp / bpc;

    CGBitmapInfo info = CGImageGetBitmapInfo(cgimage);

    NSString *file = @"file";

    NSLog(
          @"\n"
          "===== %@ =====\n"
          "CGImageGetHeight: %d\n"
          "CGImageGetWidth:  %d\n"
          "CGImageGetColorSpace: %@\n"
          "CGImageGetBitsPerPixel:     %d\n"
          "CGImageGetBitsPerComponent: %d\n"
          "CGImageGetBytesPerRow:      %d\n"
          "CGImageGetBitmapInfo: 0x%.8X\n"
          "  kCGBitmapAlphaInfoMask     = %s\n"
          "  kCGBitmapFloatComponents   = %s\n"
          "  kCGBitmapByteOrderMask     = %s\n"
          "  kCGBitmapByteOrderDefault  = %s\n"
          "  kCGBitmapByteOrder16Little = %s\n"
          "  kCGBitmapByteOrder32Little = %s\n"
          "  kCGBitmapByteOrder16Big    = %s\n"
          "  kCGBitmapByteOrder32Big    = %s\n",
          file,
          (int)width,
          (int)height,
          CGImageGetColorSpace(cgimage),
          (int)bpp,
          (int)bpc,
          (int)bpr,
          (unsigned)info,
          (info & kCGBitmapAlphaInfoMask)     ? "YES" : "NO",
          (info & kCGBitmapFloatComponents)   ? "YES" : "NO",
          (info & kCGBitmapByteOrderMask)     ? "YES" : "NO",
          (info & kCGBitmapByteOrderDefault)  ? "YES" : "NO",
          (info & kCGBitmapByteOrder16Little) ? "YES" : "NO",
          (info & kCGBitmapByteOrder32Little) ? "YES" : "NO",
          (info & kCGBitmapByteOrder16Big)    ? "YES" : "NO",
          (info & kCGBitmapByteOrder32Big)    ? "YES" : "NO"  );



    CGDataProviderRef provider = CGImageGetDataProvider(cgimage);
    NSData* data = (__bridge id)CGDataProviderCopyData(provider);
    //[data autorelease];
    const uint8_t* bytes = [data bytes];

    printf("Pixel Data:\n");
    for(size_t row = 0; row < height; row++)
    {
        for(size_t col = 0; col < width; col++)
        {
            const uint8_t* pixel = &bytes[row * bpr + col * bytes_per_pixel];

            printf("(");
            for(size_t x = 0; x < 3; x++) // 3 WAS -> bytes_per_pixel - changed to remove the alpha component.
            {
                printf("%.2X", pixel[x]);
                if( x < 3 - 1 ) // 3 WAS -> bytes_per_pixel - changed to remove the alpha component.
                    printf(",");
            }

            printf(")");
            if( col < width - 1 )
                printf(", ");
        }

        printf("\n");
    }
}

Console output:

2013-11-06 22:52:01.082 LibraryBuilder[20792:303] /n PRINTING THE IMAGE PASSED TO THIS METHOD
2013-11-06 22:52:01.082 LibraryBuilder[20792:303] 
===== file =====
CGImageGetHeight: 6
CGImageGetWidth:  7
CGImageGetColorSpace: <CGColorSpace 0x60000003b840> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD)
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      64
CGImageGetBitmapInfo: 0x00002006
  kCGBitmapAlphaInfoMask     = YES
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = YES
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = YES
  kCGBitmapByteOrder16Big    = YES
  kCGBitmapByteOrder32Big    = NO
Pixel Data:
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (EA,D9,C2)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)

2013-11-06 22:52:01.122 LibraryBuilder[20792:303] /n PRINTING THE IMAGE THAT WAS FIRST CONVERTED TO NSDATA AND THEN BACK
2013-11-06 22:52:01.122 LibraryBuilder[20792:303] 
===== file =====
CGImageGetHeight: 6
CGImageGetWidth:  7
CGImageGetColorSpace: <CGColorSpace 0x618000032f40> (kCGColorSpaceICCBased; kCGColorSpaceModelRGB; Color LCD)
CGImageGetBitsPerPixel:     24
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      18
CGImageGetBitmapInfo: 0x00000000
  kCGBitmapAlphaInfoMask     = NO
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = NO
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = NO
  kCGBitmapByteOrder16Big    = NO
  kCGBitmapByteOrder32Big    = NO
Pixel Data:
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (C2,D9,EA)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3), (B3,B3,B3)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
(F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7), (F7,F7,F7)
David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
Monty
  • 117
  • 1
  • 12
  • What's wrong with doing it like this? http://stackoverflow.com/questions/3038820/how-to-save-a-nsimage-as-a-new-file – jbat100 Nov 06 '13 at 12:28
  • "For example CGImageGetBitsPerPixel: starts at 32 but on the second call to imageDump it's now 24?" This says that you've lost the transparency info, likely because you saved the image specifying a format that doesn't support transparency. I can't see anywhere where you show how the image was dumped. – Hot Licks Nov 06 '13 at 12:33
  • How about NSData *imageData = [image TIFFRepresentationUsingCompression: NSTIFFCompressionNone factor: 0.0f]; instead of NSData *imageData = [image TIFFRepresentation];? – yoninja Nov 06 '13 at 12:56
  • I'm pretty sure that the damage is being done on the save, not the reload. But the OP doesn't show us how he saved the image. – Hot Licks Nov 06 '13 at 17:18
  • @jbat100 Yes that's essentially what i'm doing but when i pull the NSData object some of the image properties are lost. The call to imageDump is purely for illustrative purposes. – Monty Nov 06 '13 at 21:07
  • @HotLicks Thanks. In my code there is a method i've included called imageDump. It's purely used for illustrative purposes to show the loss of data. Also the change in BitsPerPixel isn't the only data being lost. Did you notice the Console output at the end of the code? – Monty Nov 06 '13 at 21:10
  • @yoninja Thanks. I've given that a go and the image data is still lost. – Monty Nov 06 '13 at 21:15
  • @HotLicks The damage is being done when the NSData object is being pulled. I haven't included the save call in the above code as the problem is happening prior to that. – Monty Nov 06 '13 at 21:17
  • Show us how you save the file!!!! If you don't specify the right options when saving the image then the transparency info will be lost. – Hot Licks Nov 06 '13 at 21:20
  • @HotLicks here you go [imageData writeToFile:fileName atomically:NO]; If you trace through my above code though you will notice that the information is lost when i pull out the NSData object and then recreate an NSImage, this all happens before i write the image to file. – Monty Nov 07 '13 at 05:17
  • `[image TIFFRepresentation]` -- TIFF is RGB. – Hot Licks Nov 07 '13 at 07:40
  • @HotLicks ok thanks, yes that makes sense. Any idea how to get it into the original NSImage object format? (Output 1 from console above) Surely there must be a way to save an image which having to go to RGB? – Monty Nov 07 '13 at 12:41
  • Check out the link in the comment by @jbat100. – Hot Licks Nov 07 '13 at 17:24

1 Answers1

0

It’s not clear any data was actually lost. There are lots of ways to represent the exact same image, and you’re doing a bunch of contortions to dump the data which might modify its format (without changing how it draws). Like, it doesn’t matter if an image is stored BRG or RGB or BGR, but those WILL modify how your dump looks.

In your instance, if your original image has a transparency channel but it’s always 100% opaque, then the -TIFFRepresentation call might (reasonably) decide not to write out the transparency channel, so when you read it back in it’ll be gone. But that doesn’t change how the image draws.

You don’t show the alpha channel in your dump so it’s not clear if you’re losing any data, but everything else looks normal.

Wil Shipley
  • 9,343
  • 35
  • 59
  • Thanks for your comment Will and I believe you are right. I ended up taking a different approach after weeks of frustration. I now load the tiff image back on the screen in an NSImageview and recapture it and the doing this seems to generate the same image properties as i originally found. – Monty Jan 20 '14 at 02:14