【问题标题】:MTKView displaying CIImage goes wrong when image changes size图像更改大小时显示 CIImage 的 MTKView 出错
【发布时间】:2019-04-02 04:03:53
【问题描述】:

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

我遇到的问题是,只有图像覆盖的视图部分,在本例中为下面代码中的scaledImage,实际上是重绘的。这意味着如果新图像比以前的图像小,我仍然可以看到旧图像的一部分从它后面伸出。

有没有办法在包含的图像大小发生变化时清除drawable的纹理内容,以免发生这种情况?

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()
}

【问题讨论】:

  • 您不需要使用MTKView 来对图像应用过滤器。您可以在UIImageView 的层上设置过滤器。
  • 来自 CALayer 上的 Apple 文档:This property is not supported on layers in iOS.
  • 哦,对了。对不起。
  • 没有问题,无论如何感谢您的输入:)

标签: ios swift metal


【解决方案1】:

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】:

    您可以做的是告诉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

    【讨论】:

      【解决方案3】:

      我有以下可行的解决方案,但我对它不太满意,因为它感觉很老套,所以请有人告诉我一个更好的方法!

      我正在做的是跟踪图像大小何时发生变化并设置一个名为needsRefresh 的标志。在视图的draw() 函数中,我检查是否设置了这个标志,如果是,我从视图的clearColor 创建一个新的 CIImage,它的大小与可绘制对象相同,并在继续图像之前渲染它我真的想渲染。

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

      这是附加的绘图代码:

          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
          }   
      }
      

      【讨论】:

      • 你可以做类似的事情,除了使用一个渲染命令编码器,它以可绘制的纹理为目标,加载动作.clearclearColor与视图相同,并存储动作.store。您不必对它编码任何命令。只需创建它,然后在其上结束编码。这将清除可绘制对象的纹理。
      • 感谢您的建议。我正朝着使用纯 Metal 的速度前进,而不是 CIFilter 和 Metal 的令人不舒服的大杂烩。我用普通的 MTKView 和渲染委托替换了我奇怪的子类,并使用编码器直接处理纹理。应该会好很多,尽管这意味着我必须从纹理转换回合适的文件输出。
      猜你喜欢
      • 2021-11-25
      • 2019-07-13
      • 2021-07-18
      • 1970-01-01
      • 2023-01-29
      • 1970-01-01
      • 1970-01-01
      • 2017-08-30
      • 1970-01-01
      相关资源
      最近更新 更多