将 UIImage 转换为 OpenGL 的 CGImage 时,iOS PNG Alpha channel 是稳定的

标签 ios opengl-es png alpha cgimage

我有几个具有 alpha 透明度的 PNG 图像。这些在用于 UI 等一般 Quartz 内容时可以正确显示。但是,当我拍摄图像并将其导入 OpenGL 时,我发现我获得的数据具有 255 的固定 alpha。我使用的似乎是通常的 UIImage 转换为 CGImage 并渲染到上下文中。我将在下面发布相关的代码。

我进行了大量搜索,最终偶然发现了解决此问题的方法,我想在这里分享,但速度很慢,因为它必须遍历每个像素,并将原始 alpha 复制到 alpha channel 中。我很想知道解决这个问题的方法。我没有在我的构建中切换任何 PNG 选项(不确定在哪里设置)以查看它是否有帮助..

谢谢

首先,我从我的应用程序中获取一个 UIImage,我是这样添加的:

UIImage *image = [UIImage imageNamed:imageName];

然后我转换为 CGImageRef 并且我只像下面这样相关的行创建通常的上下文:

CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height,
                                             bitsPerComponent, bytesPerRow,
                                             CGImageGetColorSpace(spriteImage),
                                                   kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big);

然后接下来的两行是让我获得真正的 png 原始数据的魔法……我可能应该检查 RGBA 与其他格式,但我很懒……之后我绘制图像,然后返回并修复alpha... 是的,我知道我可以在一个循环中完成此操作并跳过字节,这现在更具可读性...

// THIS data actually HAS the alpha!!
CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(spriteImage));
GLubyte *pixels = (GLubyte *)CFDataGetBytePtr(data);

// 3
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);


// Sadly we now have to loop and fix the ALPHA directly from the raw PNG source
// data as otherwise we get wrong/missing alpha...
for (int y=0; y < height; ++y) {
    for (int x=0; x < width; ++x) {
        int byteIndex = (bytesPerRow * y) + (x * bytesPerPixel);

        spriteData[byteIndex + 3] = pixels[byteIndex +3 ];
    }
}

然后我执行通常的 glTexture 调用等......它工作正常..如果我省略上面的复制循环,我会得到没有 alpha 的实体 Sprite 。如果我执行上述操作,我的 sprite 将正确绘制并具有透明度。

我知道 iOS 所做的整个预乘的事情,但这似乎坏了......至少希望以上内容可以帮助某人,但必须有更好的方法???

想法?谢谢!

最佳答案

好吧,我想我会根据我的发现和猜测自己回答问题......

首先,UIImage 和 CGI​​mage 提供的数据将进行 alpha 预乘。如果您使用的是经过优化的 PNG,就是这样。似乎在这一点上 alpha 是无用的。基于 Apple 直接提供的示例 GLImageProcessing 代码,获得原始 PNG alpha 的正确方法似乎是使用 CGImageGetDataProvider 方法,如他们的示例中所示。这专门在没有任何处理的情况下获取图像。需要注意的是,数据很可能是 BGR 而不是 RGB 顺序,这在他们为优化而做时是有意义的。如果需要,他们的示例代码可以进行转换。就我而言,我这样做:

CGImageRef spriteImage = image.CGImage;
if (!spriteImage) {
    NSLog(@"Failed to load image");
    exit(1);
}

CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(CGImage));
pixels = (GLubyte *)CFDataGetBytePtr(data);

// … do other GL calls here like gl Gen and Bind…

// NOTE the reverse BGRA here for the 7th parameter only!:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)width, (int)height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pixels);

// Don't forget to release BOTH the CF data and the CGImage or you will get memory leaks!
CFRelease(data);        // Release the CF data

这对我有用,我得到了适当的 alpha,它又快又正确。请注意,我不必重新缩放我的图像……如果您关心这一点,那么您会想像往常一样执行通常的 CGBitmapContextCreate 和 ContextDraw 操作,它们仍然可以正确提供数据。也不要忘记释放……我忘记了这个,因为它正在泄漏内存……呵呵。

希望这对某人有帮助......

关于将 UIImage 转换为 OpenGL 的 CGImage 时,iOS PNG Alpha channel 是稳定的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20626629/

相关文章:

ios - 取消初始化加载 View Controller 的最佳方法?

iphone - 模态视图 Controller 隐藏标签栏

ffmpeg - 如何将一堆具有透明度的 PNG 转换为可使用 Keynote 播放的视频

php - 修复 php imagick 较暗的灰度 png

python - Python 3.x 中的 PIL ImageTk 等价物

iphone - 将触摸事件发送到堆叠在后面的 UIviewController

ios - 调度队列和异步 RNCryptor

java - 使用 queueEvent() 在渲染器和另一个类之间传递变量

android - 需要帮助理解 GLES 2.0 的 glDrawElements

ios - OpenGLES2.0 中的视频作为纹理