如何使用Cocoa加载带有alpha的PNG?

时间:2009-10-29 16:36:31

标签: objective-c cocoa-touch opengl-es core-graphics

我正在开发一个iPhone OpenGL应用程序,我需要使用一些具有透明度的纹理。我已将图像保存为PNG。我已经拥有了将PNG加载为OpenGL纹理并渲染它们的所有代码。这适用于所有没有透明度的图像(所有alpha值都是1.0)。但是,现在我正在尝试加载并使用一些具有透明度(不同的alpha值)的PNG,我的纹理搞砸了,就像它加载数据不正确一样。

我很确定这是由于我的加载代码使用了一些Cocoa API。我会在这里发布相关代码。

在OSX / iPhone上加载PNG或支持透明度的任何图像格式的最佳方法是什么?这种方法感觉很迂回。将它渲染为CGContext并获取数据似乎很奇怪。

*加载*

CGImageRef CGImageRef_load(const char *filename) {
    NSString *path = [NSString stringWithFormat:@"%@/%s",
                      [[NSBundle mainBundle] resourcePath],
                      filename];
    UIImage *img = [UIImage imageWithContentsOfFile:path];
    if(img) return [img CGImage];
    return NULL;
}

unsigned char* CGImageRef_data(CGImageRef image) {
    NSInteger width = CGImageGetWidth(image);
    NSInteger height = CGImageGetHeight(image);
    unsigned char *data = (unsigned char*)malloc(width*height*4);

    CGContextRef context = CGBitmapContextCreate(data,
                                                 width, height,
                                                 8, width * 4,
                                                 CGImageGetColorSpace(image),
                                                 kCGImageAlphaPremultipliedLast);

    CGContextDrawImage(context,
                       CGRectMake(0.0, 0.0, (float)width, (float)height),
                       image);
    CGContextRelease(context);

    return data;
}

*上传*

(define (image-opengl-upload data width height)
  (let ((tex (alloc-opengl-image)))
    (glBindTexture GL_TEXTURE_2D tex)
    (glTexEnvi GL_TEXTURE_ENV GL_TEXTURE_ENV_MODE GL_DECAL)
    (glTexImage2D GL_TEXTURE_2D
                  0
                  GL_RGBA
                  width
                  height
                  0
                  GL_RGBA
                  GL_UNSIGNED_BYTE
                  (->void-array data))
    (glTexParameteri GL_TEXTURE_2D
                     GL_TEXTURE_MIN_FILTER
                     GL_LINEAR)
    (glTexParameteri GL_TEXTURE_2D
                     GL_TEXTURE_MAG_FILTER
                     GL_LINEAR)
    (glTexParameteri GL_TEXTURE_2D
                     GL_TEXTURE_WRAP_S
                     GL_CLAMP_TO_EDGE)
    (glTexParameteri GL_TEXTURE_2D
                     GL_TEXTURE_WRAP_T
                     GL_CLAMP_TO_EDGE)    
    (glBindTexture GL_TEXTURE_2D 0)
    tex))

4 个答案:

答案 0 :(得分:2)

明确......

使用Core Image加载纹理的最常见问题是它坚持将数据转换为预乘alpha格式。对于捆绑包中包含的PNG,实际上这是在构建过程中的预处理步骤中完成的。如果不考虑这一点会导致混合物体周围出现暗带。

考虑到这一点的方法是使用glBlendMode(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)代替glBlendMode(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)。如果你想将alpha通道用于常规混合之外的其他东西,你唯一的选择就是切换到不同的格式和加载器(由于骄傲建议,但出于不同的原因)。

ETA:在Mac OS X下也存在预复制问题,但预处理是特定于iPhone的。

答案 1 :(得分:1)

我对OpenGL一无所知,但Cocoa用NSImage / UIImage抽象了这个功能。

答案 2 :(得分:1)

在渲染之前,你的Core Graphics表面应该被清除为全零,所以我建议使用calloc而不是malloc,或者在malloc之后添加一个memset。

另外,我不确定您是否希望将TexEnv设置为 GL_DECAL 。您可能希望将其设置为默认值( GL_MODULATE )。

如果您想避免使用Core Graphics解码PNG图像,我建议您在PVR文件中加载。 PVR是一种非常简单的文件格式。 Imagination SDK附带了一个名为 PVRTexTool 的应用程序,可以轻松地将PNG转换为PVR。 SDK还包含一些示例代码,用于说明如何解析其文件格式。

答案 3 :(得分:0)

你可以使用PVR,但会有一些压缩瑕疵,所以我只推荐那些用于3D对象纹理,或者不需要PVR无法提供的特定细节水平的纹理,尤其是渐变。