MTKTextureLoader 使图像饱和

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

我正在尝试使用 MTKTextureLoader 将 CGImage 作为纹理加载。这是原图

然而,在我将 CGImage 转换为 MTLTexture 并将该纹理转换回 CGImage 之后,它看起来很糟糕,就像这样:

这是代码中发生的事情。

图像作为 CGImage 加载(我已经检查过,该图像确实具有完整的视觉质量)

我有一个函数 view() 允许我通过在 CALayer 中使用它来查看 NSImage,如下所示:

 func view() {
    .....
    imageView!.layer = CALayer()
    imageView!.layer!.contentsGravity = kCAGravityResizeAspectFill
    imageView!.layer!.contents = img
    imageView!.wantsLayer = true

所以我做了以下

let cg = CoolImage()
let ns = NSImage(cgImage: cg, size: Size(width: cg.width, height: cg.height))
view(image: ns)

并确保它具有完整的视觉保真度。

然后我像这样将 cg 图像加载到 MTLTexture 中

    let textureLoader = MTKTextureLoader(device: metalState.sharedDevice!)

    let options = [
        MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue | MTLTextureUsage.shaderWrite.rawValue | MTLTextureUsage.renderTarget.rawValue),
        MTKTextureLoader.Option.SRGB: false
    ]

    return ensure(try textureLoader.newTexture(cgImage: cg, options: options))

然后我将 MTLTexture 转换回 UIImage,如下所示:

    let texture = self
    let width = texture.width
    let height = texture.height
    let bytesPerRow = width * 4

    let data = UnsafeMutableRawPointer.allocate(bytes: bytesPerRow * height, alignedTo: 4)
    defer {
        data.deallocate(bytes: bytesPerRow * height, alignedTo: 4)
    }

    let region = MTLRegionMake2D(0, 0, width, height)
    texture.getBytes(data, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    var buffer = vImage_Buffer(data: data, height: UInt(height), width: UInt(width), rowBytes: bytesPerRow)

    var map: [UInt8] = [0, 1, 2, 3]
    if (pixelFormat == .bgra8Unorm) {
        map = [2, 1, 0, 3]
    }
    vImagePermuteChannels_ARGB8888(&buffer, &buffer, map, 0)

    guard let colorSpace = CGColorSpace(name: CGColorSpace.genericRGBLinear) else { return nil }
    guard let context = CGContext(data: data, width: width, height: height, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) else { return nil }
    guard let cgImage = context.makeImage() else { return nil }

    return NSImage(cgImage: cgImage, size: Size(width: width, height: height))

并查看了它。

生成的图像非常饱和,我相信这是因为 CGImage 到 MTLTexture 的转换,我在过去相当成功。

请注意,此纹理从未渲染过,只是经过转换。

你可能想知道为什么我要使用所有这些转换,这是一个很好的观点。我的实际管道不会像这样工作,但是它确实需要这些转换组件中的每一个都能顺利工作。这不是我的实际用例,只是为了说明问题。

ios swift metal nsview mtktextureloader
1个回答
9
投票

这里的问题不是从

CGImage
MTLTexture
的转换。问题是您假设源图像的色彩空间是线性的。更有可能的是,图像数据实际上是 sRGB 编码的,因此通过创建具有通用线性颜色空间的位图上下文,您错误地告诉 CG 它应该在显示之前对图像数据进行伽马编码,这会导致去饱和你看到了。

您可以通过使用原始颜色空间

CGImage
或以其他方式说明您的图像数据是 sRGB 编码这一事实来解决此问题。

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