123 lines
3.7 KiB
Swift
123 lines
3.7 KiB
Swift
//
|
|
// 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<NSURL, UIImage>()
|
|
|
|
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
|
|
}
|
|
}
|