Reputation: 9351
I'm using a fairly standard subclass of MTKView
that displays a CIImage, for the purposes of rapidly updating using CIFilters. Code for the draw()
function is below.
The problem I have is that only the portions of the view that are covered by the image, in this case scaledImage
in the code below, are actually redrawn. This means that if the new image is smaller than the previous image, I can still see portions of the old image poking out from behind it.
Is there a way to clear the content of the drawable's texture when the size of the contained image changes, so that this does not happen?
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()
}
Upvotes: 1
Views: 1401
Reputation: 195
What you can do is tell the ciContext
to clear the texture before you draw to it.
So before you do this,
ciContext.render(
scaledImage,
to: targetTexture,
commandBuffer: commandBuffer,
bounds: bounds,
colorSpace: colorSpace)
clear the context by first.
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 {
}
For more info see the documentation
Upvotes: 2
Reputation: 4622
MTKView
has clearColor
property which you can use like so:
clearColor = MTLClearColorMake(1, 1, 1, 1)
Alternatively, if you'd like to use CIImage
, you can just create one with CIColor
like so:
CIImage(color: CIColor(red: 1, green: 1, blue: 1)).cropped(to: CGRect(origin: .zero, size: drawableSize))
Upvotes: 1
Reputation: 9351
I have the following solution which works, but I'm not very pleased with it as it feels hacky so please, somebody tell me a better way of doing it!
What I'm doing is tracking when the size of the image changes and setting a flag called needsRefresh
. During the view's draw()
function I then check to see if this flag is set and, if it is, I create a new CIImage from the view's clearColor
that's the same size as the drawable and render this before continuing to the image I actually want to render.
This seems really inefficient, so better solutions are welcome!
Here's the additional drawing code:
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
}
Here's the UIColor
extension for converting the clear color:
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)
}
}
And finally, a small extension for creating CIImages from a given background colour:
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
}
}
Upvotes: 0