1

I want to read a PNG file such that I can:

a) Access the raw bitmap data of the file, with no color space adjustment or alpha premultiply.

b) Based on that bitmap, display bit slices (any single bit of R, G, B, or A, across the whole image) in an image in the window. If I have the bitmap I can find the right bits, but what can I stuff them into to get them onscreen?

c) After some modification of the bitplanes, write a new PNG file, again with no adjustments.

This is only for certain specific images. The PNG is not expected to have any data other than simply RGBA-32.

From reading some similar questions here, I'm suspecting NSBitmapImageRep for the file read/write, and drawing in an NSView for the onscreen part. Does this sound right?

Abizern
  • 146,289
  • 39
  • 203
  • 257
Jerry B
  • 444
  • 6
  • 14

2 Answers2

1

1.) You can use NSBitmapImageRep's -bitmapData to get the raw pixel data. Unfortunately, CG (NSBitmapImageRep's backend) does not support native unpremultiplication so you would have to unpremultiply yourself. The colorspace used in this will be the same as present in the file. Here is how to unpremultiply the image data:

NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
    NSInteger width = [imageRep pixelsWide];
    NSInteger height = [imageRep pixelsHigh];

    unsigned char *bytes = [imageRep bitmapData];
    for (NSUInteger y = 0; y < width * height * 4; y += 4) { // bgra little endian + alpha first
        uint8_t a, r, g, b;

        if (imageRep.bitmapFormat & NSAlphaFirstBitmapFormat) {
            a = bytes[y];
            r = bytes[y+1];
            g = bytes[y+2];
            b = bytes[y+3];
        } else {
            r = bytes[y+0];
            g = bytes[y+1];
            b = bytes[y+2];
            a = bytes[y+3];
        }

        // unpremultiply alpha if there is any
        if (a > 0) {
            if (!(imageRep.bitmapFormat & NSAlphaNonpremultipliedBitmapFormat)) {
                float factor = 255.0f/a;
                b *= factor;
                g *= factor;
                r *= factor;
            }
        } else {
            b = 0;
            g = 0;
            r = 0;
        }
        bytes[y]=a; // for argb
        bytes[y+1]=r;
        bytes[y+2]=g;
        bytes[y+3]=b;
    }

2.) I couldn't think of a simple way to do this. You could make your own image drawing method that loops through the raw image data and generates a new image based on the values. Refer above to see how to start doing it.

3.) Here is a method to get a CGImage from raw data places (you can write the png to a file using native CG functions or convert it to NSBitmapImageRep if CG makes you uncomfortable)

static CGImageRef cgImageFrom(NSData *data, uint16_t width, uint16_t height) {
CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)data);
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaFirst;

CGImageRef cgImage = CGImageCreate(width, height, 8, 32, 4 * width, colorSpace, bitmapInfo, provider, NULL, NO, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return cgImage;
}

You can create the NSData object out of the raw data object with +dataWithBytes:length:

Alex Zielenski
  • 3,591
  • 1
  • 26
  • 44
  • 1
    Ouch. Removing the premultiply is going to create serious rounding error, especially where the alpha is small. That will ruin the point of accessing the bitmap. I need to both load and save the **exact** bitmap values that are in the files, which are not premultiplied. CGImageCreateWithPNGDataProvider seems like it would support this, as CGImage has an accessor for whether it's premultiplied. CGImageDestinationCreateWithURL sounds like it will write the PNG. – Jerry B Nov 05 '11 at 00:59
  • Yes if the data is already unpremultiplied in the original image then CGImage/NSBitmapImageRep will treat it as such. Images drawn into a context and then initialized as a new image in memory will not be unpremultiplied as it is not supported. Also, during unpremultiplication, lossiness is inevitable. – Alex Zielenski Nov 05 '11 at 16:32
  • The thing is, the **original** data, in the PNG file, is **not** premultiplied. That's the data I want to load, modify and save. But if I'm reading the references right, NSBitmapImageRep (the only class I can see how to access the bitmap from) will premultiply when it gets initialized with the data from the file. If I can't find an easy way in Cocoa to reach that data, I'm thinking that Python's PyPNG module is my best bet. – Jerry B Nov 05 '11 at 22:28
  • Yeah, I considered that too. I actually managed to get what I want out of Python in just a few hours. :) – Jerry B Nov 07 '11 at 22:41
0

I haven't ever worked in this area, but you may be able to use Image IO for this.

Jon Hess
  • 14,237
  • 1
  • 47
  • 51
  • Image IO handles transferring the image between the file & a CGImageRef, apparently without premultiplying, so that would cover #1 & #3. I can feed a CGImage to all sorts of filters, or send it to the screen, but see no way to access the bitmap data. :( – Jerry B Nov 05 '11 at 14:06