【问题标题】:64-bit RGBA UIImage? CGBitmapInfo for 64-bit64位RGBA UIImage? 64 位的 CGBitmapInfo
【发布时间】:2023-03-03 08:06:23
【问题描述】:

我正在尝试从 iOS 上的金属纹理保存具有 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

【问题讨论】:

    标签: ios swift colors uiimage metal


    【解决方案1】:

    答案是使用 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 位的含义:

    值 byteOrder16Little 向 Quartz 指定数据提供程序提供的每个 16 位数据块应按小端顺序处理 [...] 例如,当对指定 RGB 格式的图像使用 byteOrder16Little 值时每个组件 16 位和每个像素 48 位,您的数据提供程序为每个像素提供数据,其中组件按 R、G、B 顺序排列,但每个颜色分量值都是小端顺序 [...] 为了获得最佳性能使用 byteOrder16Little 时,图像的像素大小或分量大小必须为 16 位。

    所以对于 rgba16 中的 64 位图像,像素大小为 64 位,但组件大小为 16 位。它工作得很好:)

    (感谢@warrenm!)

    【讨论】:

      猜你喜欢
      • 2019-07-09
      • 2013-03-24
      • 2015-05-01
      • 2012-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-11
      • 2019-03-18
      相关资源
      最近更新 更多