views:

207

answers:

1

Hello,

I'm interested in loading indexed-color PNG images in my iPhone application. Once they're loaded, I want to access the images on a per-pixel basis. In particular, I want to get the index of the color (rather than the color itself) of individual pixels.

Unfortunately, there does not seem to be a way to access pixels via the UIImage class, let alone color index of a pixel. I'm also taking a look at Quartz2D-related APIs, but so far things look bleak.

I'd greatly appreciate any suggestions. I'm hoping I won't have to port the necessary code from libpng.

Thanks in advance!

UPDATE: I'm able to load the PNG using Quartz2D, but for some reason it automatically converts my indexed-color 8bit PNG to a 32-bit ARGB PNG. Any thoughts how I might prevent this?

UPDATE 2: The reason this is important is due to memory limitations. I'm trying to keep the raster from blowing up form eight bits per pixel to thirty two to avoid the overhead. If anyone has the magic answer for me, 100 points are yours!

+3  A: 

By loading the image as a CGImage rather than an UIImage, using CGImageCreateWithPNGDataProvider(), you might be able to get an indexed color space. See:

http://developer.apple.com/iphone/library/documentation/GraphicsImaging/Reference/CGColorSpace/Reference/reference.html

which lists CGColorSpaceCreateIndexed(), CGColorSpaceGetColorTable() and more. Use CGColorSpaceGetModel(CGImageGetColorSpace(img)) to see if the color space you end up with is an indexed one, then use CGImageGetDataProvider() to get a CGDataProviderRef, which you can use with CGDataProviderCopyData() to get to the actual bitmap data...

edit a bounty always gets things going. I tested and it just works. (sorry for the crappy handling, this is proof of concept of course)

NSString *path = [[[NSBundle mainBundle] resourcePath] 
            stringByAppendingPathComponent:@"test.png"];
printf("path: %s\n",[path UTF8String]);
NSData *file = [[NSFileManager defaultManager] contentsAtPath:path];
if ( !file ) printf("file failed\n");
CGDataProviderRef src = CGDataProviderCreateWithCFData(file);
if ( !src ) printf("image failed\n");
CGImageRef img = CGImageCreateWithPNGDataProvider(src, NULL, NO, kCGRenderingIntentDefault);
if ( !img ) printf("image failed\n");

printf("Color space model: %d, indexed=%d\n",
    CGColorSpaceGetModel(CGImageGetColorSpace(img)),
    kCGColorSpaceModelIndexed);

output:

path: /Users/..../638...8C12/test.app/test.png
Color space model: 5, indexed=5

qed?

ps. my test image is from libgd, through php, using

    $img = imagecreatefrompng("whateverimage.png");
    imagetruecolortopalette($img,false,256);
    header("Content-Type: image/png");
    imagepng($img);

which results in my case (b/w image) in

$ file test.png 
test.png: PNG image, 2000 x 300, 1-bit colormap, non-interlaced

edit^2 This is how you access the bitmap data. ASCII art ftw!

CGDataProviderRef data = CGImageGetDataProvider(img);
NSData *nsdata = (NSData *)(CGDataProviderCopyData(data));

char *rawbuf = malloc([nsdata length]);
if ( !rawbuf ) printf("rawbuf failed\n");
[nsdata getBytes:rawbuf];

int w = CGImageGetWidth(img);
int h = CGImageGetHeight(img);
int bpl = CGImageGetBytesPerRow(img);

printf("width: %d (%d bpl), height: %d, pixels: %d, bytes: %d\n",w,bpl,h,bpl*h,[nsdata length]);

if ( [nsdata length] != bpl*h )
{
    printf("%d pixels is not %d bytes, i may be crashing now...\n",bpl*h,[nsdata length]);
}

for ( int y=0;y<h; y++ )
{
    for ( int x=0;x<w; x++ )
    {
        char c = rawbuf[y*bpl+x];
        while ( !isalnum(c) ) c += 31; //whoa!
        printf("%c",c);
    }
    printf("\n");
}
mvds
Thanks, this definitely gives me some ideas. Unfortunately, so far I can only get CGImageCreateWithPNGDataProvider() to give me an RGB color space image. Perhaps it's doing some kind of conversion, since I happen to know the image is an indexed-color image already.
Tom
Are you doing "Compress PNG" in your compile step? This will convert your PNG to a RGB image. Make sure its turned off if your'e relying on the format of the PNG.
Joshua Weinberg
here it seems to work ok! even with png compression turned on.
mvds
added example to get the pixel buffer out.
mvds
note: the example assumes 8 bits per pixel, while 1 bit indexed color is stored as 1 bit per pixel and thus 8 pixels per byte.
mvds
Excellent! Thanks for all the help--I'll try this out very soon.
Tom
@tom, did it work?
mvds
@mvds--haven't been able to test it yet due to delays. Sorry! Thanks for all of your help though. I'm going to assume this is the right answer. :)
Tom
@Joshua Weinberg: where is this "Compress PNG" located? Is it some sort of target setting?
Tom
@Tom: Yes, its a project/target setting
Joshua Weinberg
@tom: thanks ;-)
mvds