更改大小后,MTKView显示CIImage出错

时间:2018-10-29 08:55:12

标签: ios swift metal

我正在使用显示CIImage的相当标准的MTKView子类,目的是使用CIFilters快速更新。 draw()函数的代码如下。

我遇到的问题是,实际上仅重绘了图像所覆盖的视图部分,在本例中为scaledImage。这意味着,如果新图像小于前一个图像,我仍然可以看到旧图像的某些部分从后面露出来。

当所包含图像的大小发生变化时,是否有办法清除可绘制对象纹理的内容,从而不会发生这种情况?

override func draw() {
    guard let image = image,
        let targetTexture = currentDrawable?.texture,
        let commandBuffer = commandQueue.makeCommandBuffer()
        else { return }

    let bounds = CGRect(origin: CGPoint.zero, size: drawableSize)

    let scaleX = drawableSize.width / image.extent.width
    let scaleY = drawableSize.height / image.extent.height
    let scale = min(scaleX, scaleY)

    let width = image.extent.width * scale
    let height = image.extent.height * scale
    let originX = (bounds.width - width) / 2
    let originY = (bounds.height - height) / 2

    let scaledImage = image
        .transformed(by: CGAffineTransform(scaleX: scale, y: scale))
        .transformed(by: CGAffineTransform(translationX: originX, y: originY))

    ciContext.render(
        scaledImage,
        to: targetTexture,
        commandBuffer: commandBuffer,
        bounds: bounds,
        colorSpace: colorSpace
    )

    guard let drawable = currentDrawable else { return }
    commandBuffer.present(drawable)
    commandBuffer.commit()
    super.draw()
}

3 个答案:

答案 0 :(得分:0)

我有以下可行的解决方案,但由于它很笨拙,我对此不太满意,所以请有人告诉我一种更好的解决方法!

我正在做的是跟踪图像大小何时更改并设置一个名为needsRefresh的标志。然后,在视图的draw()函数中,检查是否设置了此标志,如果是,则从视图的clearColor创建一个新的CIImage,其大小与drawable相同,并在继续之前进行渲染到我实际想要渲染的图像。

这似乎效率很低,因此欢迎更好的解决方案!

这是其他绘图代码:

    if needsRefresh {
        let color = UIColor(mtkColor: clearColor)
        let clearImage = CIImage(cgImage: UIImage.withColor(color, size: bounds.size)!.cgImage!)
        ciContext.render(
            clearImage,
            to: targetTexture,
            commandBuffer: commandBuffer,
            bounds: bounds,
            colorSpace: colorSpace
        )
        needsRefresh = false
    }

以下是UIColor扩展名,用于转换纯色:

extension UIColor {
    convenience init(mtkColor: MTLClearColor) {
        let red = CGFloat(mtkColor.red)
        let green = CGFloat(mtkColor.green)
        let blue = CGFloat(mtkColor.blue)
        let alpha = CGFloat(mtkColor.alpha)
        self.init(red: red, green: green, blue: blue, alpha: alpha)
    }
}

最后,是一个小扩展,用于从给定的背景色创建CIImage:

extension UIImage {
    static func withColor(_ color: UIColor, size: CGSize = CGSize(width: 10, height: 10)) -> UIImage? {
        UIGraphicsBeginImageContext(size)
        guard let context = UIGraphicsGetCurrentContext() else { return nil }
        context.setFillColor(color.cgColor)
        context.fill(CGRect(origin: .zero, size: size))
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return image
    }   
}

答案 1 :(得分:0)

MTKView具有clearColor属性,您可以像这样使用:

clearColor = MTLClearColorMake(1, 1, 1, 1)

或者,如果您想使用CIImage,则可以使用CIColor创建一个,就像这样:

CIImage(color: CIColor(red: 1, green: 1, blue: 1)).cropped(to: CGRect(origin: .zero, size: drawableSize))

答案 2 :(得分:0)

您可以做的是告诉ciContext在绘制纹理之前先清除纹理。 所以在您这样做之前,

ciContext.render(
        scaledImage,
        to: targetTexture,
        commandBuffer: commandBuffer,
        bounds: bounds,
        colorSpace: colorSpace)

首先清除上下文。

let renderDestination = CIRenderDestination(
            width: Int(sizeToClear.width),
            height: Int(sizeToClear.height),
            pixelFormat: colorPixelFormat,
            commandBuffer: nil) { () -> MTLTexture in
            return drawable.texture
        }
do {
    try ciContext.startTask(toClear: renderDestination)
} catch {

}

有关更多信息,请参见documentation