How to create 8-bit PNG with transparency from NSBitmapImageRep?

I have a 32-bit NSBitmapImageRep that has an alpha channel with substantially 1-bit values ​​(pixels are either on or off).

I want to save this bitmap as an 8-bit PNG file with transparency. If I use the -representationUsingType:properties: NSBitmapImageRep and pass it to NSPNGFileType , a 32-bit PNG is created, which I don't want.

I know that 8-bit PNGs can be read, they open in Preview without problems, but is it possible to write this type of PNG file using any Mac OS X built-in APIs? I am glad, if necessary, to go down to Core Image or even QuickTime. A quick analysis of CGImage documents showed nothing obvious.

EDIT: I started generosity on this matter, if someone can provide working source code that accepts 32-bit NSBitmapImageRep and writes 256-color PNG with 1-bit transparency, it's yours.

+6
image cocoa png macos nsbitmapimagerep
source share
5 answers

pngnq (and the new pngquant , which achieves higher quality) is BSD-style, so you can just include it in your program. No need to create a separate task.

+1
source share

What about pnglib ? It is really lightweight and easy to use.

+2
source share

Great link for working with lower level APIs: Quartz Programming

Some of the code below is based on examples from this book.

Note. This is not verified code, intended only for a starting point ....

 - (NSBitmapImageRep*)convertImageRep:(NSBitmapImageRep*)startingImage{ CGImageRef anImage = [startingImage CGImage]; CGContextRef bitmapContext; CGRect ctxRect; size_t bytesPerRow, width, height; width = CGImageGetWidth(anImage); height = CGImageGetHeight(anImage); ctxRect = CGRectMake(0.0, 0.0, width, height); bytesPerRow = (width * 4 + 63) & ~63; bitmapData = calloc(bytesPerRow * height, 1); bitmapContext = createRGBBitmapContext(width, height, TRUE); CGContextDrawImage (bitmapContext, ctxRect, anImage); //Now extract the image from the context CGImageRef bitmapImage = nil; bitmapImage = CGBitmapContextCreateImage(bitmapContext); if(!bitmapImage){ fprintf(stderr, "Couldn't create the image!\n"); return nil; } NSBitmapImageRep *newImage = [[NSBitmapImageRep alloc] initWithCGImage:bitmapImage]; return newImage; } 

Context Creation Function:

 CGContextRef createRGBBitmapContext(size_t width, size_t height, Boolean needsTransparentBitmap) { CGContextRef context; size_t bytesPerRow; unsigned char *rasterData; //minimum bytes per row is 4 bytes per sample * number of samples bytesPerRow = width*4; //round up to nearest multiple of 16. bytesPerRow = COMPUTE_BEST_BYTES_PER_ROW(bytesPerRow); int bitsPerComponent = 2; // to get 256 colors (2xRGBA) //use function 'calloc' so memory is initialized to 0. rasterData = calloc(1, bytesPerRow * height); if(rasterData == NULL){ fprintf(stderr, "Couldn't allocate the needed amount of memory!\n"); return NULL; } // uses the generic calibrated RGB color space. context = CGBitmapContextCreate(rasterData, width, height, bitsPerComponent, bytesPerRow, CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), (needsTransparentBitmap ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst) ); if(context == NULL){ free(rasterData); fprintf(stderr, "Couldn't create the context!\n"); return NULL; } //Either clear the rect or paint with opaque white, if(needsTransparentBitmap){ CGContextClearRect(context, CGRectMake(0, 0, width, height)); }else{ CGContextSaveGState(context); CGContextSetFillColorWithColor(context, getRGBOpaqueWhiteColor()); CGContextFillRect(context, CGRectMake(0, 0, width, height)); CGContextRestoreGState(context); } return context; } 

Using:

 NSBitmapImageRep *startingImage; // assumed to be previously set. NSBitmapImageRep *endingImageRep = [self convertImageRep:startingImage]; // Write out as data NSData *outputData = [endingImageRep representationUsingType:NSPNGFileType properties:nil]; // somePath is set elsewhere [outputData writeToFile:somePath atomically:YES]; 
+1
source share

One thing to try is to create an NSBitmapImageRep with 8 bits, and then copy the data to it.

In fact, it will be a lot of work, since you will have to calculate the color index table yourself.

0
source share

CGImageDestination is your person for recording low-level images, but I do not know if it supports this specific ability.

0
source share

All Articles