首页 > 解决方案 > 在 Metal 中渲染后翻转图片

问题描述

我正在尝试在 MTKView 中渲染动画并遇到一个奇怪的问题。在模拟器中,我看到翻转的图片,但在设备上一切都很好。我还使用相同的方法渲染视频,并且图片在设备和模拟器上具有相同的方向。

这是重现我的问题的最简化示例:

class TestVC: UIViewController {
    var output: MTKView!
    let device: MTLDevice
    let context: CIContext
    let colorSpace = CGColorSpaceCreateDeviceRGB()
    let queue: MTLCommandQueue?
    let image: CIImage

    init() {
        let defaultDevice = MTLCreateSystemDefaultDevice()!
        self.device = defaultDevice
        self.context = CIContext(mtlDevice: defaultDevice)
        self.queue = defaultDevice.makeCommandQueue()
        self.image = CIImage(image: UIImage(named: "lena.png")!)!
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .red
        makeOutputView()
    }

    func makeOutputView() {
        let side = min(view.frame.size.width, view.frame.size.height) * 0.9
        let viewFrame = CGRect(x: 0, y: 0, width: side, height: side)
        output = MTKView(frame: viewFrame, device: device)
        view.addSubview(output)
        output.center = view.center
        output.framebufferOnly = false
        output.delegate = self
    }
}

extension TestVC: MTKViewDelegate {
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {}

    func draw(in view: MTKView) {
        let buffer = queue?.makeCommandBuffer()
        guard let drawable = view.currentDrawable else { return }
        let dpi = UIScreen.main.nativeScale
        let width = view.bounds.width * dpi
        let height = view.bounds.height * dpi
        let rect = CGRect(x: 0, y: 0, width: width, height: height)
        let extent = image.extent
        let xScale = extent.width > 0 ? width  / extent.width  : 1
        let yScale = extent.height > 0 ? height / extent.height : 1
        let scale = max(xScale, yScale)
        let tx = (width - extent.width * scale) / 2
        let ty = (height - extent.height * scale) / 2
        let transform = CGAffineTransform(a: scale, b: 0, c: 0, d: scale, tx: tx, ty: ty)
        let filter = CIFilter(name: "CIAffineTransform",
                              parameters: ["inputImage": image, "inputTransform": transform])!
        let scaledImage = filter.outputImage!
        context.render(scaledImage,
                       to: drawable.texture,
                       commandBuffer: buffer,
                       bounds: rect,
                       colorSpace: colorSpace)
        buffer?.present(drawable)
        buffer?.commit()
    }
}

这就是我所看到的: 在此处输入图像描述

标签: iosswiftmetal

解决方案


此问题与使用 Mac 上使用不同坐标系的 GPU 的模拟器有关。一个快速的解决方法是在模拟器中运行图像时翻转图像。

var scaledImage = filter.outputImage!
    
#if targetEnvironment(simulator)
    scaledImage = scaledImage.transformed(by: CGAffineTransform(scaleX: 1, y: -1))
        .transformed(by: CGAffineTransform(translationX: 0, y: scaledImage.extent.height))
#endif

context.render(scaledImage,
          to: drawable.texture,
          commandBuffer: buffer,
          bounds: rect,
          colorSpace: colorSpace)
 buffer?.present(drawable)
 buffer?.commit()

推荐阅读