【问题标题】:iOS PNG Alpha channel is solid when converting UIImage to CGImage for OpenGL将 UIImage 转换为 CGImage for OpenGL 时 iOS PNG Alpha 通道是稳定的
【发布时间】:2014-01-04 18:30:16
【问题描述】:

我有几个具有 alpha 透明度的 PNG 图像。这些在用于一般 Quartz 内容(例如在 UI 中)时会正确显示。然而,当我去获取图像并将其放入 OpenGL 时,我发现我得到的数据具有 255 的稳定 alpha。我使用的似乎是通常的 UIImage 转换为 CGImage 并渲染到上下文中。我将在下面发布相关的代码。

我做了很多搜索,最后偶然发现了一种解决方法,我想在这里分享,但它很慢,因为它必须遍历每个像素,并将原始 alpha 复制到 alpha 通道中。我很想知道解决这个问题的方法。我没有在我的构建中切换任何 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 的实体精灵。如果我执行上述操作,我的精灵会以透明度正确绘制。

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

想法?谢谢!

【问题讨论】:

  • 如果 CGDataProviderCopyData() 为您提供正确的原始数据,为什么不将其用于您的纹理,而不是使用 CGContextDrawImage() 重绘它呢?你不应该需要后者,或者你的循环。
  • 好点....我应该试试。我确实尝试过以其他方式使用它,但它给出了一个错误,因为它当然是只读的,但也限制了我传入......我忘记了它是什么......好主意......我应该提到这段代码来自苹果。有一个示例有它和正确的代码来读取没有 pre-mult alpha 的文件。虽然我真正的问题仍然是为什么那是正确的而 CGImage 是不正确的......
  • CGBitmapContextCreate() 仅支持预乘 alpha(至少在 iOS 上是这样),但 CGDataProviderCopyData() 从图像中提取原始像素数据,我认为通常不会预乘。
  • 那行得通,我不得不将 glTexImage2D 中的第二个格式三元组更改为 GL_BGRA,因为 CopyData 的格式是我的图像的格式。所以我想至少这真的很快,更干净,并且不需要上下文等......
  • 哦,如前所述,这是来自苹果的 GLImageProcessing 示例……它具有从 RGB 到 BGR 等转换的正确代码……可在此处获得:developer.apple.com/library/ios/samplecode/GLImageProcessing/…

标签: ios opengl-es png alpha cgimage


【解决方案1】:

它看到 CGImageGetDataProvider(CGImage) 进行 alpha 预乘。 我有一个红色 = 255 和 alpha = 128 的 PNG,加载的像素有红色 = 128。好难过……

【讨论】:

    【解决方案2】:

    好吧,我想我会根据我的发现和推测自己回答这个问题……

    首先,UIImage 和 CGI​​mage 提供的数据将预乘 alpha。如果您使用的是经过优化的 PNG,就是这样。在那一点上,阿尔法似乎毫无用处。基于 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 操作,这仍然适用于正确提供的数据。不要忘记释放......我忘记了这一点,因为内存泄漏......呵呵。

    希望这对某人有所帮助……

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-13
      • 2016-06-11
      • 2021-12-28
      • 1970-01-01
      相关资源
      最近更新 更多