【问题标题】:Capturing MTKView texture to UIImageView efficiently有效地将 MTKView 纹理捕获到 UIImageView
【发布时间】:2018-12-07 05:51:47
【问题描述】:

我想捕获最近更新到 UIImageView 的 MTKView 部分的内容。我下面的一段代码来完成这个任务:

let cicontext = CIContext(mtlDevice: self.device!) // set up once along with rest of renderPipeline

...

let lastSubStrokeCIImage = CIImage(mtlTexture: lastDrawableDisplayed.texture, options: nil)!.oriented(CGImagePropertyOrientation.downMirrored)
let bboxChunkSubCurvesScaledAndYFlipped = CGRect(...) // get bounding box of region just drawn
let imageCropCG = cicontext.createCGImage(lastSubStrokeCIImage, from: bboxStrokeAccumulatingScaledAndYFlipped)

// Now that we have a CGImage of just the right size, we have to do the following expensive operations before assigning to a UIIView

UIGraphicsBeginImageContextWithOptions(bboxStrokeAccumulating.size, false, 0)  // open a bboxKeyframe-sized context
UIGraphicsGetCurrentContext()?.translateBy(x: 0, y: bboxStrokeAccumulating.height)
UIGraphicsGetCurrentContext()?.scaleBy(x: 1.0, y: -1.0)
UIGraphicsGetCurrentContext()?.draw(imageCropCG!, in: CGRect(x: 0, y: 0 , width: bboxStrokeAccumulating.width, height: bboxStrokeAccumulating.height))

// Okay, finally we create a CALayer to be a container for what we've just drawn

let layerStroke = CALayer()
layerStroke.frame = bboxStrokeAccumulating
layerStroke.contents = UIGraphicsGetImageFromCurrentImageContext()?.cgImage
UIGraphicsEndImageContext()

strokeView.layer.sublayers = nil  // empty out strokeView
strokeView.layer.addSublayer(layerStroke) // add our hard-earned drawing in its latest state

因此,此代码有效,但效率不高,并且当 bboxStrokeAccumulating 变得非常大时会使应用程序滞后。谁能提出更有效的替代方案?

【问题讨论】:

    标签: uiimageview core-graphics uigraphicscontext metalkit


    【解决方案1】:

    对于 swift 4.0,

    let lastDrawableDisplayed = metalView?.currentDrawable?.texture
    
    if let imageRef = lastDrawableDisplayed?.toImage() {
        let uiImage:UIImage = UIImage.init(cgImage: imageRef)
    }
    
    extension MTLTexture {
    
        func bytes() -> UnsafeMutableRawPointer {
            let width = self.width
            let height   = self.height
            let rowBytes = self.width * 4
            let p = malloc(width * height * 4)
    
            self.getBytes(p!, bytesPerRow: rowBytes, from: MTLRegionMake2D(0, 0, width, height), mipmapLevel: 0)
    
            return p!
        }
    
        func toImage() -> CGImage? {
            let p = bytes()
    
            let pColorSpace = CGColorSpaceCreateDeviceRGB()
    
            let rawBitmapInfo = CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
            let bitmapInfo:CGBitmapInfo = CGBitmapInfo(rawValue: rawBitmapInfo)
    
            let selftureSize = self.width * self.height * 4
            let rowBytes = self.width * 4
            let releaseMaskImagePixelData: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
                return
            }
            let provider = CGDataProvider(dataInfo: nil, data: p, size: selftureSize, releaseData: releaseMaskImagePixelData)
            let cgImageRef = CGImage(width: self.width, height: self.height, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: rowBytes, space: pColorSpace, bitmapInfo: bitmapInfo, provider: provider!, decode: nil, shouldInterpolate: true, intent: CGColorRenderingIntent.defaultIntent)!
    
            return cgImageRef
        }
    }
    

    您可以将 uiImage 设置为您的 uiImageView

    【讨论】:

    • 谢谢 Ankit。我研究了这个选项,但不确定它是否能节省任何性能。据我所知,您建议的 toImage() 方法与我的代码开头的 CIImage(mtlTexture) 行执行相同的工作。 (顺便说一句,我没有得到阿尔法)。我仍然需要做边界框数学来获得已绘制的区域。我会调查是否可以修改 toImage() 来自定义宽度/高度,并捕获 alpha 值
    猜你喜欢
    • 1970-01-01
    • 2017-10-05
    • 1970-01-01
    • 1970-01-01
    • 2019-07-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-26
    相关资源
    最近更新 更多