I am creating video from images and adding overlay to them. Problem is when I try to add CATextLayer to video. Text is pixelated, take a look at the image
This is the code used to generate CATextLayer:
private func generateTextLayer(for text: String, at frame: CGRect, in layer: CALayer) -> CATextLayer {
let textLayer = CATextLayer()
textLayer.frame = frame.integral
textLayer.contentsScale = UIScreen.main.scale
textLayer.isWrapped = true
textLayer.isHidden = true
textLayer.foregroundColor = UIColor.white.cgColor
textLayer.backgroundColor = UIColor.clear.cgColor
let fontHeight = resolution.fontHeight
textLayer.font = UIFont.systemFont(ofSize: fontHeight)
textLayer.fontSize = fontHeight
textLayer.string = text
textLayer.truncationMode = .end
textLayer.alignmentMode = .center
textLayer.contentsScale = UIScreen.main.scale
layer.addSublayer(textLayer)
return textLayer
}
And this code is used to generate CALayer with image:
private func generateImageLayer(for image: CGImage?, at frame: CGRect, in layer: CALayer) -> CALayer {
let imageLayer = CALayer()
imageLayer.frame = frame.integral
imageLayer.contents = image
imageLayer.contentsGravity = .resizeAspectFill
imageLayer.isHidden = true
imageLayer.backgroundColor = UIColor.clear.cgColor
layer.addSublayer(imageLayer)
return imageLayer
}
Image layer is hidden due to animation of different image layers in generated video. This is code used for layer generation:
let size = backgroundTrack.naturalSize.applying(backgroundTrack.preferredTransform)
let parentlayer = CALayer()
parentlayer.frame = CGRect(origin: .zero, size: size).integral
parentlayer.backgroundColor = UIColor.black.cgColor
parentlayer.isOpaque = true
let backgroundVideoLayer = CALayer()
backgroundVideoLayer.frame = parentlayer.bounds
backgroundVideoLayer.backgroundColor = UIColor.black.cgColor
backgroundVideoLayer.isOpaque = true
parentlayer.addSublayer(backgroundVideoLayer)
var currentTime: Double = 0
for source in videoSources {
// This will generate blurred background layer
let blurrLayer = generateImageLayer(for: source.blurredImage, at: backgroundVideoLayer.bounds, in: backgroundVideoLayer)
blurrLayer.contentsGravity = .resizeAspectFill
// Adds image to blurrLayer
if let image = source.image {
let frame = calculateFrame(for: image.size)
let imageLayer = generateImageLayer(for: image.cgImage, at: frame, in: blurrLayer)
imageLayer.isHidden = false
Animations.fadeInOut(layer: blurrLayer, beginTime: currentTime, duration: duration.value)
}
// Adds CATextLayer
if let text = source.title {
let frame = parentlayer.bounds.offsetBy(dx: 0, dy: 100)
let titleLayer = generateTextLayer(for: text, at: frame, in: parentlayer)
Animations.fadeInOut(layer: titleLayer, beginTime: currentTime, duration: duration.value)
}
currentTime += duration.value
}
I tried setting rasterizationScale to UIScreen.main.scale, shouldRasterize to true, contentsScale to UIScreen.main.scale, all kind of different options, even tried snapshoting UILabel and setting it as image (even saved image locally and checked it, quality was good), but result is again pixelated writing.
Animate fadeInOut is basically three animations, one is setting hidden param to true or false and another one is adding CATransition reveal animation for entry and exit transition between layers.
static func fadeInOut(layer: CALayer, beginTime: Double, duration: Double, skipEntry: Bool = false) {
let t = beginTime < 1 ? -0.0001 : beginTime
let d = beginTime < 1 ? duration + 0.0001 : duration - 0.0001
if !skipEntry {
let entryTransition = CATransition()
entryTransition.beginTime = t
entryTransition.duration = 0.5
entryTransition.type = CATransitionType.reveal
entryTransition.subtype = CATransitionSubtype.fromTop
entryTransition.isRemovedOnCompletion = false
layer.add(entryTransition, forKey: "entry")
}
let exitTransition = CATransition()
exitTransition.beginTime = d - 0.5
exitTransition.duration = 0.5
exitTransition.startProgress = 1
exitTransition.endProgress = 0
exitTransition.type = CATransitionType.reveal
exitTransition.subtype = CATransitionSubtype.fromTop
exitTransition.isRemovedOnCompletion = false
layer.add(exitTransition, forKey: "exit")
let fadeInOutAnimation = CABasicAnimation(keyPath: "hidden")
fadeInOutAnimation.fromValue = false
fadeInOutAnimation.toValue = false
fadeInOutAnimation.beginTime = t
fadeInOutAnimation.duration = d
fadeInOutAnimation.isRemovedOnCompletion = false
layer.add(fadeInOutAnimation, forKey: "opacity")
}
Before marking my question as already seen and answered, I've spent lot of time trying to google possible results and tried many things, but results are still the same.