我有一个 32 位
NSBitmapImageRep
,它有一个基本上具有 1 位值的 Alpha 通道(像素要么打开,要么关闭)。
我想将此位图保存为具有透明度的 8 位 PNG 文件。如果我使用
-representationUsingType:properties:
的NSBitmapImageRep
方法并传入NSPNGFileType
,则会创建一个32位PNG,这不是我想要的。
我知道可以读取 8 位 PNG,它们在预览中打开没有任何问题,但是是否可以使用任何内置的 Mac OS X API 写入这种类型的 PNG 文件?如果有必要,我很乐意使用 Core Image 甚至 QuickTime。粗略检查
CGImage
文档并没有发现任何明显的信息。
编辑: 我已经开始对这个问题进行悬赏,如果有人可以提供使用 32 位
NSBitmapImageRep
并编写具有 1 位透明度的 256 色 PNG 的工作源代码,那么它就是你的。
pnglib
怎么样? 它真的很轻且易于使用。
pngnq(以及实现更高质量的new pngquant),因此您可以将其包含在您的程序中。无需作为单独的任务生成。
使用较低级别 API 的一个很好的参考是 Programming With Quartz
下面的一些代码基于该书中的示例。
注意:这是未经测试的代码,仅作为起点......
- (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;
}
上下文创建功能:
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;
}
用法是:
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];
要尝试的一件事是创建一个 8 位的 NSBitmapImageRep,然后将数据复制到其中。
这实际上需要大量工作,因为您必须自己计算颜色索引表。
CGImageDestination
是你进行低级图像写作的人,但我不知道它是否支持这种特定能力。