MTKTextureLoader使图像饱和

时间:2018-03-29 20:55:57

标签: ios swift nsview metal

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

Pretty image

然而,在我将CGImage转换为MTLTexture并将纹理转换回CGImage后,它看起来很可怕,如下所示:

enter image description here

以下是代码中的内容。

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

我有一个函数视图(),它允许我在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。

请注意,此纹理从未呈现过仅转换过的。

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

1 个答案:

答案 0 :(得分:3)

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

您可以使用原始CGImage的原生色彩空间来解决此问题,或者通过考虑您的图像数据是sRGB编码的事实来解决此问题。