// // ImageGrayscalifier.swift // Tusker // // Created by Shadowfacts on 10/29/20. // Copyright © 2020 Shadowfacts. All rights reserved. // import UIKit struct ImageGrayscalifier { static let queue = DispatchQueue(label: "ImageGrayscalifier", qos: .userInitiated) private static let context = CIContext() private static let cache = NSCache() static func convertIfNecessary(url: URL?, image: UIImage) -> UIImage? { let grayscale = MainActor.runUnsafelyMaybeIntroducingDataRace { Preferences.shared.grayscaleImages } if grayscale, let source = image.cgImage { // todo: should this return the original image if conversion fails? return convert(url: url, cgImage: source) } else { return image } } static func convertIfNecessary(url: URL?, image: UIImage) async -> UIImage? { let grayscale = await MainActor.run { Preferences.shared.grayscaleImages } if grayscale, let source = image.cgImage { return await convert(url: url, cgImage: source) } else { return image } } static func convert(url: URL?, image: UIImage) -> UIImage? { if let url, let cached = cache.object(forKey: url as NSURL) { return cached } guard let cgImage = image.cgImage else { return nil } return doConvert(CIImage(cgImage: cgImage), url: url) } static func convert(url: URL?, image: UIImage) async -> UIImage? { if let url, let cached = cache.object(forKey: url as NSURL) { return cached } guard let cgImage = image.cgImage else { return nil } return await withCheckedContinuation { continuation in queue.async { continuation.resume(returning: doConvert(CIImage(cgImage: cgImage), url: url)) } } } static func convert(url: URL?, data: Data) -> UIImage? { if let url = url, let cached = cache.object(forKey: url as NSURL) { return cached } guard let source = CIImage(data: data) else { return nil } return doConvert(source, url: url) } static func convert(url: URL?, cgImage: CGImage) -> UIImage? { if let url = url, let cached = cache.object(forKey: url as NSURL) { return cached } return doConvert(CIImage(cgImage: cgImage), url: url) } static func convert(url: URL?, cgImage: CGImage) async -> UIImage? { if let url = url, let cached = cache.object(forKey: url as NSURL) { return cached } return await withCheckedContinuation { continuation in queue.async { continuation.resume(returning: doConvert(CIImage(cgImage: cgImage), url: url)) } } } private static func doConvert(_ source: CIImage, url: URL?) -> UIImage? { guard let filter = CIFilter(name: "CIColorMonochrome") else { return nil } filter.setValue(source, forKey: "inputImage") filter.setValue(CIColor(red: 0.85, green: 0.85, blue: 0.85), forKey: "inputColor") filter.setValue(1.0, forKey: "inputIntensity") guard let output = filter.outputImage, let cgImage = context.createCGImage(output, from: output.extent) else { return nil } let image = UIImage(cgImage: cgImage) if let url = url { cache.setObject(image, forKey: url as NSURL) } return image } }