我正在尝试从 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/