iOS渐变二维码之CALayer遮罩实现篇

作者: handyTOOL | 来源:发表于2017-08-15 16:52 被阅读573次

    欢迎访问我的个人主页获取本文示例代码

    前言

    工作中有一个需求,将服务端已经生成好的黑白二维码变成渐变的二维码,具体效果如下。



    本文将介绍如何使用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实现渐变二维码的效果。

    补充

    为了方便不会Swift的同学,我在示例中增加了OC版本,有兴趣的同学可以点击文章顶部获取示例的链接获取新的示例。OC版本涉及的文件有ColorfulQRCodeOCVerView.hColorfulQRCodeOCVerView.m

    相关文章

      网友评论

      • Tangdixi:感觉可以用 OpenGL 来抠出原纹理中的黑色,然后跟渐变纹理做 mix,跟绿幕原理差不多~
        不过来用做这个好像没必要 ...
        handyTOOL:https://www.jianshu.com/p/8c909f515e78 我用metal实现过一个版本,在shader里动态生成渐变纹理和原图mix
      • 35378e2877b2:为什么要渐变呢:flushed:
        handyTOOL:@小小小小洁要减肥 但是产品觉得酷呀:ghost:
        35378e2877b2:@handyTOOL 并不酷啊……
        handyTOOL:@小小小小洁要减肥 觉得炫酷呗:ghost:
      • 照亮黑夜的曙光:博主 能写一份OC的学习一下吗 不会Swift
        照亮黑夜的曙光:@handyTOOL 感谢博主 mark:smile:
        handyTOOL:@_SX OC版本已完成,看文末的补充。
        handyTOOL:可以,写好了我链接发到文章里
      • 瞬csr:let imageData = UnsafeMutableRawPointer.allocate(bytes: Int(width * height * bytesPerPixel), alignedTo: 8)
        这句话如果用oc应该怎么写
        handyTOOL:@瞬csr OC版本已完成,看文末的补充。
        瞬csr:@handyTOOL :smiley: 好的谢啦
        handyTOOL:其实就是对应OC的malloc,我这两天会写一个OC版本的例子,你要是有兴趣可以看一下,到时候我会通知你。

      本文标题:iOS渐变二维码之CALayer遮罩实现篇

      本文链接:https://www.haomeiwen.com/subject/rrlvrxtx.html