// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2019-2020 Threema GmbH // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License, version 3, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import Foundation @objc class ThreemaQRCodeGenerator: NSObject { @objc class func generate(qrString: String, dimension: Int) -> UIImage? { guard let qrFilter = CIFilter(name: "CIQRCodeGenerator") else { return nil } let qrData = qrString.data(using: String.Encoding.ascii) qrFilter.setValue(qrData, forKey: "inputMessage") let scale = dimension/39 let qrTransform = CGAffineTransform.init(scaleX: CGFloat(scale), y: CGFloat(scale)) guard let qrImage = qrFilter.outputImage?.transformed(by: qrTransform) else { return nil } guard let logo = BundleUtil.imageNamed("QRLogo").cgImage else { return convert(cmage: qrImage) } guard let threemaQr: CIImage = qrImage.combined(with: CIImage(cgImage: logo)) else { return convert(cmage: qrImage) } return convert(cmage: threemaQr) } class func convert(cmage:CIImage) -> UIImage { let context:CIContext = CIContext.init(options: nil) let cgImage:CGImage = context.createCGImage(cmage, from: cmage.extent)! let image:UIImage = UIImage.init(cgImage: cgImage) return image } } extension CIImage { /// Inverts the colors and creates a transparent image by converting the mask to alpha. /// Input image should be black and white. var transparent: CIImage? { return inverted?.blackTransparent } /// Inverts the colors. var inverted: CIImage? { guard let invertedColorFilter = CIFilter(name: "CIColorInvert") else { return nil } invertedColorFilter.setValue(self, forKey: "inputImage") return invertedColorFilter.outputImage } /// Converts all black to transparent. var blackTransparent: CIImage? { guard let blackTransparentFilter = CIFilter(name: "CIMaskToAlpha") else { return nil } blackTransparentFilter.setValue(self, forKey: "inputImage") return blackTransparentFilter.outputImage } /// Applies the given color as a tint color. func tinted(using color: UIColor) -> CIImage? { guard let transparentQRImage = transparent, let filter = CIFilter(name: "CIMultiplyCompositing"), let colorFilter = CIFilter(name: "CIConstantColorGenerator") else { return nil } let ciColor = CIColor(color: color) colorFilter.setValue(ciColor, forKey: kCIInputColorKey) let colorImage = colorFilter.outputImage filter.setValue(colorImage, forKey: kCIInputImageKey) filter.setValue(transparentQRImage, forKey: kCIInputBackgroundImageKey) return filter.outputImage! } /// Combines the current image with the given image centered. func combined(with image: CIImage) -> CIImage? { guard let combinedFilter = CIFilter(name: "CISourceOverCompositing") else { return nil } let centerTransform = CGAffineTransform(translationX: extent.midX - (image.extent.size.width / 2), y: extent.midY - (image.extent.size.height / 2)) combinedFilter.setValue(image.transformed(by: centerTransform), forKey: "inputImage") combinedFilter.setValue(self, forKey: "inputBackgroundImage") return combinedFilter.outputImage! } }