64位RGBA UIImage? CGBitmapInfo为64位

问题描述 投票:0回答:1

我正在尝试使用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

ios swift colors uiimage metal
1个回答
0
投票

答案是使用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位数据块应以小端顺序处理[...]例如,当使用byteOrder16Little值为指定RGB格式的图像时使用16位对于每个组件和每像素48位,您的数据提供程序为每个像素提供R,G,B组件的数据,但每个颜色组件值都以小端顺序[...]以便在使用byteOrder16Little时获得最佳性能,图像的像素大小或组件大小必须是16位。

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

(谢谢@warrenm!)

© www.soinside.com 2019 - 2024. All rights reserved.