如何从NSBitmapImageRep创建具有透明度的8位PNG?

时间:2010-03-03 04:11:02

标签: cocoa macos image png nsbitmapimagerep

我有一个32位NSBitmapImageRep,其alpha通道基本上是1位值(像素打开或关闭)。

我想将此位图保存为具有透明度的8位PNG文件。如果我使用-representationUsingType:properties:的{​​{1}}方法并传入NSBitmapImageRep,则会创建一个32位的PNG,这不是我想要的。

我知道可以读取8位PNG,它们在预览中打开没有问题,但是是否可以使用任何内置的Mac OS X API编写这种类型的PNG文件?如果有必要,我很高兴下载到Core Image甚至QuickTime。对NSPNGFileType文档的粗略检查没有发现任何明显的内容。

修改 我已经开始对这个问题表示赏心悦目,如果有人可以提供一个32位CGImage的工作源代码并写入一个透明度为1位的256色PNG,那就是你的了。

5 个答案:

答案 0 :(得分:2)

pnglib怎么样?它非常轻巧,易于使用。

答案 1 :(得分:1)

使用低级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];

答案 2 :(得分:1)

pngnq(和new pngquant达到更高的质量)具有BSD风格的许可证,因此您可以将其包含在您的程序中。无需将其作为单独的任务生成。

答案 3 :(得分:0)

要尝试的一件事是创建一个8位的NSBitmapImageRep,然后将数据复制到它。

这实际上需要做很多工作,因为你必须自己计算颜色索引表。

答案 4 :(得分:0)

CGImageDestination是你的低级图像写作的人,但我不知道它是否支持这种特定的能力。