渐变二维码

获取示例代码

前言

工作中有一个需求,将服务端已经生成好的黑白二维码变成渐变的二维码,具体效果如下。 本文将介绍如何使用CALayer的Mask实现渐变二维码的效果。下面是我们需要处理的二维码图片。

原理

苹果对CALayer的Mask(遮罩)是这样描述的,被遮罩的Layer位于Mask不透明的部分才会被显示。所以我们的基本思路是让二维码白色的部分变的完全透明,黑色的部分不透明,然后把处理后的图作为一个渐变Layer的Mask。这样黑色部分将会显示渐变Layer上对应的部分,白色部分将显示渐变Layer后面Layer的颜色。

处理原始二维码图片

我们将原始二维码图片转化成遮罩需要的图片。首先我们把图片数据抽取出来,格式化为ARGB格式的像素数据。具体做法就是生成一个ARGB空间的CGContext,将原始二维码图片绘制上去。然后像素数据就会在imageData中了。

let bitsPerComponent = 8
let bytesPerPixel = 4
let width:Int = Int(image.size.width)
let height:Int = Int(image.size.height)
let imageData = UnsafeMutableRawPointer.allocate(bytes: Int(width * height * bytesPerPixel), alignedTo: 8)

// 将原始黑白二维码图片绘制到像素格式为ARGB的图片上,绘制后的像素数据在imageData中。
let colorSpace = CGColorSpaceCreateDeviceRGB()
let imageContext = CGContext.init(data: imageData, width: Int(image.size.width), height: Int(image.size.height), bitsPerComponent: bitsPerComponent, bytesPerRow: width * bytesPerPixel, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue )
UIGraphicsPushContext(imageContext!)
imageContext?.translateBy(x: 0, y: CGFloat(height))
imageContext?.scaleBy(x: 1, y: -1)
image.draw(in: CGRect.init(x: 0, y: 0, width: width, height: height))
UIGraphicsPopContext()

接下来我们将二维码白色的部分变的完全透明,黑色的部分不透明。我们遍历所有像素,根据像素的Red值,设置像素的Alpha值。如果R大于100,我们认为是白色,所以将Alpha设为0,全透明。如果R小于100,我们认为是黑色,所以将Alpha设为255,不透明。

// 根据每个像素R通道的值修改Alpha通道的值,当Red大于100,则将Alpha置为0,反之置为255
for row in 0..<height {
    for col in 0..<width {
        let offset = row * width * bytesPerPixel + col * bytesPerPixel
        let r = imageData.load(fromByteOffset: offset + 1, as: UInt8.self)
        let alpha:UInt8 = r > 100 ? 0 : 255
        imageData.storeBytes(of: alpha, toByteOffset: offset, as: UInt8.self)
    }
}

设置遮罩

最后我们创建渐变Layer,然后把处理后的二维码图片作为遮罩设置给渐变Layer。

lazy var gradientLayer: CAGradientLayer = {
    let layer = CAGradientLayer.init()
    layer.colors = [UIColor.red.cgColor, UIColor.orange.cgColor, UIColor.cyan.cgColor]
    self.layer.addSublayer(layer)
    layer.frame = self.bounds
    return layer
}()

// 设置黑白二维码图片
func setQRCodeImage(qrcodeImage: UIImage) {
    let imageMask = genQRCodeImageMask(grayScaleQRCodeImage: qrcodeImage)
    maskLayer.contents = imageMask
    maskLayer.frame = self.bounds
    self.gradientLayer.mask = maskLayer
}

总结

我们主要利用CALayer的遮罩特性完成了渐变二维码的效果。涉及到CoreGraphics和UIKit等相关知识。当然我们还有其他方式实现这一效果,比如OpenGL ES或者Metal,我将在另一篇文章中介绍如何使用Metal实现渐变二维码的效果。

Updated: