ios - 64 位 RGBA UIImage? 64 位的 CGBitmapInfo

标签 ios swift colors uiimage metal

我正在尝试从 iOS 上的 Metal 纹理中保存具有 P3 色彩空间的 16 位深度 PNG 图像。纹理的pixelformat = .rgba16Unorm,我用这段代码提取数据

func dataProviderRef() -> CGDataProvider? {
    let pixelCount = width * height
    var imageBytes = [UInt8](repeating: 0, count: pixelCount * bytesPerPixel)
    let region = MTLRegionMake2D(0, 0, width, height)
    getBytes(&imageBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    return CGDataProvider(data: NSData(bytes: &imageBytes, length: pixelCount * bytesPerPixel * MemoryLayout<UInt8>.size))
}

我发现在 iOS 上保存 PNG 图像的方法是先创建一个 UIImage,然后为了初始化它,我需要创建一个 CGImage。问题是我不知道要传递给 CGIBitmapInfo 的内容。在 documentation我可以看到您可以为 32 位格式指定 byteOrder,但不能为 64 位格式指定。

我用来将纹理转换为 UIImage 的函数是这个,

extension UIImage {
  public convenience init?(texture: MTLTexture) {
    guard let rgbColorSpace = texture.defaultColorSpace else {
        return nil
    }
    let bitmapInfo:CGBitmapInfo = [CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue)]

    guard let provider = texture.dataProviderRef() else {
        return nil
    }
    guard let cgim = CGImage(
        width: texture.width,
        height: texture.height,
        bitsPerComponent: texture.bitsPerComponent,
        bitsPerPixel: texture.bitsPerPixel,
        bytesPerRow: texture.bytesPerRow,
        space: rgbColorSpace,
        bitmapInfo: bitmapInfo,
        provider: provider,
        decode: nil,
        shouldInterpolate: false,
        intent: .defaultIntent
        )
    else {
        return nil
    }
    self.init(cgImage: cgim)
  }
}

请注意,“纹理”使用的是 MTLTexture 中不存在的一系列属性。为了方便起见,我创建了一个简单的扩展。我想唯一有趣的一点是色彩空间,目前很简单,

public extension MTLTexture {
  var defaultColorSpace: CGColorSpace? {
    get {
        switch pixelFormat {
        case .rgba16Unorm:
            return CGColorSpace(name: CGColorSpace.displayP3)
        default:
            return CGColorSpaceCreateDeviceRGB()
        }
    }
  }
}

看起来我用上面的代码创建的图像是每个像素采样 4 个字节,而不是 8 个。所以我显然最终得到了一个看起来很有趣的图像......

如何创建适当的 CGBitmapInfo?这可能吗?

附注如果您想查看完整的代码和示例,都在 github 中:https://github.com/endavid/VidEngine/tree/master/SampleColorPalette

最佳答案

答案是使用 byteOrder16。例如,我为此替换了上面代码中的 bitmapInfo,

    let isFloat = texture.bitsPerComponent == 16
    let bitmapInfo:CGBitmapInfo = [isFloat ? .byteOrder16Little : .byteOrder32Big, CGBitmapInfo(rawValue: CGImageAlphaInfo.last.rawValue)]

(alpha 也可以预乘)。

SDK documentation没有提供很多提示来说明为什么会这样,但是这本书Programming with Quartz对这 16 位的含义有很好的解释:

The value byteOrder16Little specifies to Quartz that each 16-bit chunk of data supplied by your data provider should be treated in little endian order [...] For example, when using a value of byteOrder16Little for an image that specifies RGB format with 16 bits per component and 48 bits per pixel, your data provider supplies the data for each pixel where the components are ordered R, G, B, but each color component value is in little-endian order [...] For best performance when using byteOrder16Little, either the pixel size or the component size of the image must be 16 bits.

因此对于 rgba16 的 64 位图像,像素大小为 64 位,但组件大小为 16 位。效果很好:)

(感谢@warrenm!)

关于ios - 64 位 RGBA UIImage? 64 位的 CGBitmapInfo,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48998969/

相关文章:

ios - 可通过继承进行编码

ios - 项目名称必须与 iTunes Connect 应用程序名称匹配吗?

javascript - HSV颜色的有效范围是多少

javascript - 如何使用Alpha输入计算两种颜色的柔光混合值?

java - 在小程序中选择自定义颜色

ios - 当插入到 View Controller View 的层次结构中时,UIView 不会放置在 Collection View 单元格的正上方

ios - 如何保护 AWS REST 后端以进行移动访问

swift - 无法将类型 'Swift._SwiftDeferredNSArray' (0x11241ab30) 的值转换为 'NSMutableArray' (0x1109ef598)

macos - 使用 CLI 中的参数运行 Mac OS 应用程序

swift - 当按下开始按钮时,游戏分数会随着时间的推移而增加