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

问题描述 投票:0回答:5

我有一个 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 的工作源代码,那么它就是你的。

cocoa macos image png nsbitmapimagerep
5个回答
2
投票

pnglib
怎么样? 它真的很轻且易于使用。


1
投票

pngnq(以及实现更高质量的new pngquant),因此您可以将其包含在您的程序中。无需作为单独的任务生成。


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];

0
投票

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

这实际上需要大量工作,因为您必须自己计算颜色索引表。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.