金属视图(MTKView)图纸尺寸问题

时间:2018-05-18 17:16:50

标签: ios xcode view metal

这里我有一个MTKView并在相机上运行一个简单的CIFilter。这很好。

问题

在较旧的设备上'自拍相机,如iPhone 5,iPad Air,饲料在较小的区域上绘制。 更新:发现当发生这种情况时,送到MTKView的CMSampleBuffer尺寸较小。我想每个更新中的纹理需要按比例放大吗?

 import UIKit
 import MetalPerformanceShaders
 import MetalKit
 import AVFoundation

    final class MetalObject: NSObject, MTKViewDelegate {

        private var metalBufferView            : MTKView?
        private var metalDevice                = MTLCreateSystemDefaultDevice()
        private var metalCommandQueue          : MTLCommandQueue!
        private var metalSourceTexture         : MTLTexture?
        private var context                    : CIContext?
        private var filter                     : CIFilter?


        init(with frame: CGRect, filterType: Int, scaledUp: Bool) {
            super.init()

            self.metalCommandQueue = self.metalDevice!.makeCommandQueue()
            self.metalBufferView = MTKView(frame: frame, device: self.metalDevice)
            self.metalBufferView!.framebufferOnly = false
            self.metalBufferView!.isPaused = true

            self.metalBufferView!.contentScaleFactor = UIScreen.main.nativeScale
            self.metalBufferView!.delegate = self
            self.context = CIContext()


        }


        final func update (sampleBuffer: CMSampleBuffer) {

            var textureCache : CVMetalTextureCache?
            CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, self.metalDevice!,  nil, &textureCache)
            var cameraTexture: CVMetalTexture?

            guard
                let cameraTextureCache = textureCache,
                let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
                    return
            }

            let cameraTextureWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0)
            let cameraTextureHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0)
            CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                      cameraTextureCache,
                                                      pixelBuffer,
                                                      nil,
                                                      MTLPixelFormat.bgra8Unorm,
                                                      cameraTextureWidth,
                                                      cameraTextureHeight,
                                                      0,
                                                      &cameraTexture)

            if let cameraTexture = cameraTexture,
                let metalTexture = CVMetalTextureGetTexture(cameraTexture) {
                self.metalSourceTexture = metalTexture
                self.metalBufferView!.draw()
            }


        }

        //MARK: - Metal View Delegate
        final func draw(in view: MTKView) {

            guard let currentDrawable = self.metalBufferView!.currentDrawable,
                let sourceTexture = self.metalSourceTexture
                else {  return  }

            let commandBuffer = self.metalCommandQueue!.makeCommandBuffer()
            var inputImage = CIImage(mtlTexture: sourceTexture)!.applyingOrientation(self.orientationNumber)

            if self.showFilter {
                self.filter!.setValue(inputImage, forKey: kCIInputImageKey)
                inputImage = filter!.outputImage!
            }


            self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: inputImage.extent, colorSpace: self.colorSpace!)

            commandBuffer.present(currentDrawable)
            commandBuffer.commit()

        }


        final func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {

        }
    }

观察

  • 仅在旧设备的自拍相机上发生
  • 新设备上的自拍相机很好 当问题发生时,新内容会在较小的区域内绘制(向左上方移动),后置摄像头的旧内容仍然在新内容之外。
  • 约束和金属视图的大小/位置都很好。
  • self.metalBufferView!.contentScaleFactor = UIScreen.main.nativeScale 解决Plus设备上奇怪的扩展问题。

1 个答案:

答案 0 :(得分:2)

看起来旧设备上前置(自拍)相机的分辨率较低,因此如果您想要使用全宽或高度,则需要缩放视频。由于您已经在使用CIContext和Metal,因此您可以简单地指示渲染调用将图像绘制到您喜欢的任何矩形。

draw方法中,执行self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: inputImage.extent, colorSpace: self.colorSpace!) - bounds参数是目标矩形,其中将呈现图像。目前,您正在使用图像范围,这意味着图像将不会缩放。

要向上缩放视频,请改用显示矩形。您只需使用metalBufferView.bounds,因为这将是您的显示视图的大小。您最终会得到self.context!.render(inputImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: self.metalBufferView.bounds, colorSpace: self.colorSpace!)

如果图像和视图的宽高比不同(宽度/高度是宽高比),则必须计算正确的大小,以保留图像的宽高比。要做到这一点,你最终会得到这样的代码:

CGRect dest = self.metalBufferView.bounds;
CGSize imageSize = inputImage.extent.size;
CGSize viewSize = dest.size; 
double imageAspect = imageSize.width / imageSize.height;
double viewAspect = viewSize.width / viewSize.height;
if (imageAspect > viewAspect) {
    // the image is wider than the view, adjust height
    dest.size.height = 1/imageAspect * dest.size.width;
} else {
    // the image is taller than the view, adjust the width
    dest.size.width = imageAspect * dest.size.height;

    // center the tall image
    dest.origin.x = (viewSize.width - dest.size.width) / 2;
}

希望这很有用,如果有什么不起作用或澄清会有所帮助,请告诉我。