I've tried a bunch of techniques like using CIImage or copying the data into a buffer of uint_32ts, but they have all had their issues. The simplest I have found so far is:
extension MTLTexture {
func toImage() -> NSImage? {
assert (pixelFormat == .rgba32Float)
let rowByteCount = width * 16
let bytes = UnsafeMutablePointer<Float>.allocate(capacity: width * height * 4)
defer {
bytes.deallocate()
}
getBytes(bytes,
bytesPerRow: rowByteCount,
from: MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0),
size: MTLSize(width: width, height: height, depth: 1)),
mipmapLevel: 0)
let context = CGContext(data: bytes,
width: width,
height: height,
bitsPerComponent: 32,
bytesPerRow: rowByteCount,
space: CGColorSpaceCreateDeviceRGB(),
bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.floatComponents.rawValue)
guard let cgImage = context?.makeImage() else { print("Failed making cgImage"); return nil }
return NSImage(cgImage: cgImage, size: NSSize(width: width, height: height))
}
}
This creates an image of the correct size, but it is completely transparent. Sometimes when zooming in with Preview, an outline of certain features in the image will flicker, but once the image renders it disappears. I made sure to synchronize the image, and I have made sure that the bytes
buffer has non-zero values. I have also tried using a buffer of SIMD4<Float>
s instead of Float
s, but it produced the same result.