Swift 和 OC 下的二维码生成,可加颜色,加logo

如果觉得代码有更好的实现方式或者优化方案,请留言,大家共同交流一哈

Swift

swift全部代码,可直接拖入项目使用

透明度一直设置不成功,所以注释掉了,如果各位大神实现,请不吝赐教。

import UIKit
import CoreImage
import CoreGraphics

/// 像素,每个像素包含红,蓝,绿,透明度
public struct CWPixel {
    // 完整像素值
    var value: UInt32
    
    // 红色值
    var red: UInt8 {
        get {
            return UInt8(value & 0xFF)
        }
        set {
            value = UInt32(newValue) | (value & 0xFFFFFF00)
        }
    }
    
    // 绿色
    var green: UInt8 {
        get {
            return UInt8((value >> 8) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 8) | (value & 0xFFFF00FF)
        }
    }
    
    // 蓝色
    var blue: UInt8 {
        get {
            return UInt8((value >> 16) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 16) | (value & 0xFF00FFFF)
        }
    }
    
    // 透明
    var alpha: UInt8 {
        get {
            return UInt8((value >> 24) & 0xFF)
        }
        set {
            value = (UInt32(newValue) << 24) | (value & 0x00FFFFFF)
        }
    }
    
    // 是否为白色
    var isWhite: Bool {
        get {
            return value == 0xFFFFFFFF
        }
    }
}

// MARK: - 生成二维码
extension UIImage {
    
    class func cwQRCodeImage(content: String,
                             logo: UIImage? = nil,
                             logoFrame: CGRect = CGRect.zero,
                             size: CGFloat = -1,
                             highCorrection: Bool = false,
                             tintColor: UIColor? = nil) -> UIImage? {
        var retImage: UIImage? = nil
        var openHighCorrection = highCorrection
        
        // 如果要添加logo或者绘制颜色,自动开启高容错
        if logo != nil || tintColor != nil {
            openHighCorrection = true
        }
        let correctionLevel = openHighCorrection ? "H" : "M"
        
        // 生成二维码并设置纠错级别
        let outputImage = qrCodeOutputImage(content: content,
                                            correctionLevel: correctionLevel)
        if let output = outputImage {
            // 调整尺寸之后的二维码
            retImage = adjustHDQRCode(outputImage: output,
                                      size:size)
        }
        
        // 二维码着色
        if let mTintColor = tintColor {
            retImage = retImage?.cwModifyTintColor(tintColor: mTintColor)
        }
        
        // 二维码添加logo
        if let mLogo = logo {
            retImage = retImage?.cwAddLogoImage(image: mLogo,
                                                frame: logoFrame)
        }
        
        return retImage
    }
    
    
    /// 生成原始二维码CIImage
    ///
    /// - Parameters:
    ///   - content: 二维码包含的内容
    ///   - correctionLevel: 二维码纠错级别
    ///   ,调高纠错级别可以往上面加一个头像或者变色,但是识别速度也会降低
    /// - correctionLevel:
    ///   - 二维码纠错级别,通过调高二维码的纠错级别可以往上面加一个头像或者变色,但是识别速度也会降低
    ///   - L(低) 7%的码字可以被恢复。
    ///   - M(中) 15%的码字可以被恢复。
    ///   - Q(四分)25%的码字可以被恢复。
    ///   - H(高) 30%的码字可以被恢复。
    private class func qrCodeOutputImage(content: String,
                                         correctionLevel: String) -> CIImage? {
        // 初始化filter
        let data = content.data(using: .utf8)
        let qrFilter = CIFilter(name: "CIQRCodeGenerator")
        // 设置内容
        qrFilter?.setValue(data, forKey: "inputMessage")
        qrFilter?.setValue(correctionLevel, forKey: "inputCorrectionLevel")
        return qrFilter?.outputImage
    }
    
    /// 调整二维码的尺寸并返回高清图片
    /// - Code:
    ///   - let transform = CGAffineTransform(scaleX: 5, y: 5)
    ///   - outputImage?.transformed(by: transform)
    ///
    /// - Parameters:
    ///   - outputImage: CIFilter生成的二维码
    ///   - size: 放大之后的尺寸
    /// - Returns: 返回调整之后的图片
    private class func adjustHDQRCode(outputImage: CIImage,
                                      size: CGFloat) -> UIImage? {
        
        let integralRect: CGRect = outputImage.extent.integral
        // 设置默认放大5倍
        var scale: CGFloat = 5.0
        if size != -1 {
            // 需要计算出最佳缩放比例
            scale = min(size/integralRect.width, size/integralRect.height)
        }
        
        let width = integralRect.width * scale
        let height = integralRect.height * scale
        
        let colorSpaceRef = CGColorSpaceCreateDeviceGray()
        let bitmapRef = CGContext(data: nil,
                                  width: Int(width),
                                  height: Int(height),
                                  bitsPerComponent: 8,
                                  bytesPerRow: 0,
                                  space: colorSpaceRef,
                                  bitmapInfo: CGImageAlphaInfo.none.rawValue)
        
        let context = CIContext(options: nil)
        
        //放大重新绘制
        if let bitmapImage = context.createCGImage(outputImage,
                                                   from: integralRect) {
            bitmapRef?.interpolationQuality = .none
            bitmapRef?.scaleBy(x: scale, y: scale)
            bitmapRef?.draw(bitmapImage, in: integralRect)
        }
        
        let scaledImage: CGImage! = bitmapRef?.makeImage()
        return UIImage(cgImage: scaledImage)
    }
    
    /// 改变二维码图片的像素的颜色
    private func cwModifyTintColor(tintColor: UIColor) -> UIImage {
        
        // 必须可以转换为CGImage
        guard let mCgImage = self.cgImage else {
            return self
        }
        
        // 将Float类型保存为Int进行地址操作
        let width = Int(self.size.width)
        let height = Int(self.size.height)
        
        // 创建一个大小为 4 * width * height 的缓存区
        let bytesPerRow = width * 4
        let byteSize = width * height
    
        // 初始化一个指针类型并分配空间
        let rgbImageBuf = UnsafeMutablePointer<CWPixel>.allocate(capacity: byteSize)
        
        
        // 初始化绘制,颜色空间跟随设备
        let colorSpaceRef = CGColorSpaceCreateDeviceRGB()
        // 用BGRA创建一个位图
        var bitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
        bitmapInfo = bitmapInfo | CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
        
        // 创建位图
        guard let context = CGContext(data: rgbImageBuf,
                                      width: width,
                                      height: height,
                                      bitsPerComponent: 8,
                                      bytesPerRow: bytesPerRow,
                                      space: colorSpaceRef,
                                      bitmapInfo: bitmapInfo)  else {
            return self
        }
        
        // 使用rgbImageBuf 填充 mCgImage
        context.draw(mCgImage, in: CGRect(origin: .zero, size: self.size))
        
        
       // 连续存储的内存集合
        var bufferPointer = UnsafeMutableBufferPointer<CWPixel>(start: rgbImageBuf,
                                                                count: byteSize)

        // 释放分配的空间
//        rgbImageBuf.deallocate()
        
        //获取红绿蓝色值
        var red: CGFloat = 0
        var green: CGFloat = 0
        var blue: CGFloat = 0
        var alpha: CGFloat = 0
        tintColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
        
        // 遍历像素,改变像素点颜色
        for currentPixel in 0..<byteSize {
            var pixel = bufferPointer[currentPixel]
            if !pixel.isWhite {
                //当不是白色的时候才使用
                pixel.red = UInt8(red * 255)
                pixel.green = UInt8(green * 255)
                pixel.blue = UInt8(blue * 255)
//                pixel.alpha = UInt8(alpha * 255)
                let retPixel = pixel
                bufferPointer[currentPixel] = retPixel
            }
        }
        
        let inputColorSpace = CGColorSpaceCreateDeviceRGB()
        var inputBitmapInfo: UInt32 = CGBitmapInfo.byteOrder32Big.rawValue
        let inputBytesPerRow = width * 4
        
        inputBitmapInfo |= CGImageAlphaInfo.premultipliedLast.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
        
        guard let imageContext = CGContext(data: bufferPointer.baseAddress,
                                           width: width,
                                           height: height,
                                           bitsPerComponent: 8,
                                           bytesPerRow: inputBytesPerRow,
                                           space: inputColorSpace,
                                           bitmapInfo: inputBitmapInfo,
                                           releaseCallback: nil,
                                           releaseInfo: nil) else {
            return self
        }
        
        guard let cgImage = imageContext.makeImage() else {
            return self
        }
        
        let image = UIImage(cgImage: cgImage)
        return image
    }
    
    /// 给二维码图片添加logo
    private func cwAddLogoImage(image: UIImage,
                                frame: CGRect) -> UIImage? {
        var resultImage: UIImage? = nil
        if frame != CGRect.zero {
            // 开始绘制
            UIGraphicsBeginImageContext(self.size)
            //绘制二维码图片
            self.draw(in: CGRect.init(origin: CGPoint.zero,
                                      size:self.size))
            //绘制Logo
            image.draw(in: frame)
            //获取绘制之后的image
            resultImage = UIGraphicsGetImageFromCurrentImageContext();
            //结束绘制
            UIGraphicsEndImageContext();
        }
        return resultImage
    }
}

OC

二维码生成

/**
 *
 *  @brief 生成二维码图片
 *
 *  @param content   内容
 *  @param width     二维码宽度,-1为自动
 *  @param highLevel 是否是高的纠错级别,设为YES,可往上添加logo
 *
 *  @return 生成的二维码图片
 */
+ (UIImage *)qrCodeImageWithContent:(NSString *)content width:(CGFloat)width highCorrectionLevel:(BOOL)highLevel
{
    UIImage *resultImage = nil;
    NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
    
    //创建filter
    CIFilter *QRFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
    // 设置内容
    [QRFilter setValue:data forKey:@"inputMessage"];
    //二维码纠错级别,通过调高二维码的纠错级别可以往上面加一个头像或者变色,但是识别速度也会降低
    /**
     L(低) 7%的码字可以被恢复。
     M(中) 15%的码字可以被恢复。
     Q(四分)25%的码字可以被恢复。
     H(高) 30%的码字可以被恢复。
     *
     */
    [QRFilter setValue:highLevel?@"H":@"M" forKey:@"inputCorrectionLevel"];
    CIImage *qrOutPutImage = QRFilter.outputImage;
    
    CGRect integralRect = CGRectIntegral(qrOutPutImage.extent);
    CGFloat scale = 5;
    if (width != -1) {
        //计算出缩放比例
        scale = MIN(width/CGRectGetWidth(integralRect), width/CGRectGetHeight(integralRect));
    }
    
    //计算出宽高
    size_t width = CGRectGetWidth(integralRect)*scale;
    size_t height = CGRectGetHeight(integralRect)*scale;
    
    //二维码生成的位图不清晰,所以需要放大绘制
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceGray();
    CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, colorSpaceRef, (CGBitmapInfo)kCGImageAlphaNone);
    CIContext *context = [CIContext contextWithOptions:nil];
    CGImageRef bitmapImage = [context createCGImage:qrOutPutImage fromRect:integralRect];
    CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
    CGContextScaleCTM(bitmapRef, scale, scale);
    CGContextDrawImage(bitmapRef, integralRect, bitmapImage);
    
    CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
    CGContextRelease(bitmapRef);
    CGImageRelease(bitmapImage);
    CGColorSpaceRelease(colorSpaceRef);
    
    resultImage = [UIImage imageWithCGImage:scaledImage];
    CGImageRelease(scaledImage);
    
    return resultImage;
}

添加logo,修改配色

//生成二维码图片,可插入logo,可调整配色
+ (UIImage *)qrCodeImageWithContent:(NSString *)content codeImageWidth:(CGFloat)width logo:(UIImage *)logo logoFrame:(CGRect)logoFrame color:(UIColor *)color
{
    //生成固定尺寸的二维码
    UIImage *image = [self qrCodeImageWithContent:content width:width highCorrectionLevel:YES];
    UIImage *resultImage = nil;
    if (image) {
        //修改配色
        int imageWidth = image.size.width;
        int imageHeight = image.size.height;
        size_t bytesPerRow = imageWidth * 4;
        uint32_t *rgbImageBuf = (uint32_t *)malloc(bytesPerRow * imageHeight);
        CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
        CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpaceRef, kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
        
        //获取红绿蓝色值
        CGFloat red = 0.0f;
        CGFloat green = 0.0f;
        CGFloat blue = 0.0f;
        CGFloat alpha = 0.0f;
        [color getRed:&red green:&green blue:&blue alpha:&alpha];
        
        //遍历像素,改变像素点颜色
        int pixelNum = imageWidth * imageHeight;
        uint32_t *pCurPtr = rgbImageBuf;
        for (int i = 0; i < pixelNum; i++, pCurPtr++) {
            if ((*pCurPtr & 0xFFFFFF00) < 0x99999900) {
                uint8_t *ptr = (uint8_t *)pCurPtr;
                ptr[3] = red*255;
                ptr[2] = green*255;
                ptr[1] = blue*255;
            }
            else
            {
                uint8_t *ptr = (uint8_t *)pCurPtr;
                ptr[0] = 0;
            }
        }
        
        //取出图片
        CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, ProviderReleaseData);
        CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpaceRef,
                                            kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,
                                            NULL, true, kCGRenderingIntentDefault);
        CGDataProviderRelease(dataProvider);
        resultImage = [UIImage imageWithCGImage:imageRef];
        CGImageRelease(imageRef);
        CGContextRelease(context);
        CGColorSpaceRelease(colorSpaceRef);
    }
    
    // 绘制logo
    if (resultImage) {
        //存在logo,且logoFrame不为空
        if (logo && !CGRectEqualToRect(logoFrame, CGRectZero)) {
            //开始绘制
            UIGraphicsBeginImageContext(resultImage.size);
            //绘制二维码图片
            [resultImage drawInRect:CGRectMake(0, 0, resultImage.size.width, resultImage.size.height)];
            //绘制Logo
            [logo drawInRect:logoFrame];
            //获取绘制之后的image
            resultImage = UIGraphicsGetImageFromCurrentImageContext();
            //结束绘制
            UIGraphicsEndImageContext();
        }
    }
    
    return resultImage;
}

//数据释放
void ProviderReleaseData (void *info, const void *data, size_t size){
    free((void*)data);
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,928评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,192评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,468评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,186评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,295评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,374评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,403评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,186评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,610评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,906评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,075评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,755评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,393评论 3 320
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,079评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,313评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,934评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,963评论 2 351

推荐阅读更多精彩内容