Caps-iOS/CapCollector/Extensions/UIImage+Extensions.swift
christophhagen 8892d04f62 Lots of updates
- Add unlock
- Update Sorting menu
- Prepare to load multiple tile images
- New logging
- Calculate thumbnails and colors before schowing grid
2020-06-18 22:55:51 +02:00

162 lines
5.2 KiB
Swift

//
// UIImageExtensions.swift
// CapFinder
//
// Created by User on 13.02.18.
// Copyright © 2018 User. All rights reserved.
//
import Foundation
import UIKit
extension UIImage {
static func templateImage(named: String) -> UIImage {
return UIImage(named: named)!.withRenderingMode(.alwaysTemplate)
}
/**
Resize an image to the target size
*/
func resize(to targetSize: CGSize) -> UIImage {
let rect = CGRect(x: 0, y: 0, width: targetSize.width, height: targetSize.height)
UIGraphicsBeginImageContextWithOptions(targetSize, false, 1.0)
self.draw(in: rect)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
/**
Crop an image to a square, centered around the middle of the frame
- parameter factor: The crop factor of the resulting image
- returns: The cropped image
*/
func crop(factor: CGFloat) -> UIImage {
let width = self.size.width * factor
return crop(to: width)
}
/**
Crop an image to a square, centered around the middle of the frame
- parameter size: The height and width of the resulting image
- returns: The cropped image
*/
func crop(to size: CGFloat) -> UIImage {
var rect = CGRect(
x: (self.size.height - size) / 2,
y: (self.size.width - size) / 2,
width: size,
height: size)
rect.origin.x *= scale
rect.origin.y *= scale
rect.size.width *= scale
rect.size.height *= scale
let imageRef = cgImage!.cropping(to: rect)
return UIImage(cgImage: imageRef!, scale: scale, orientation: imageOrientation)
}
/// The (circular) image masked by a circle
var circleMasked: UIImage? {
let width = size.width
let height = size.height
let isPortrait = height > width
let breadth = min(width, height)
let breadthSize = CGSize(width: breadth, height: breadth)
let breadthRect = CGRect(origin: .zero, size: breadthSize)
UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)
defer { UIGraphicsEndImageContext() }
let x = isPortrait ? 0 : floor((width - height) / 2)
let y = isPortrait ? floor((height - width) / 2) : 0
let rect = CGRect(origin: CGPoint( x: x, y: y), size: breadthSize)
guard let cgImage = cgImage?.cropping(to: rect) else {
return nil
}
UIBezierPath(ovalIn: breadthRect).addClip()
UIImage(cgImage: cgImage, scale: 1, orientation: imageOrientation).draw(in: breadthRect)
return UIGraphicsGetImageFromCurrentImageContext()
}
var averageColor: UIColor? {
let image = ciImage ?? CIImage(cgImage: cgImage!)
return image.averageColor
}
}
extension CIImage {
func averageColor(context: CIContext) -> UIColor? {
let extentVector = CIVector(
x: extent.origin.x,
y: extent.origin.y,
z: extent.size.width,
w: extent.size.height)
guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: self, kCIInputExtentKey: extentVector]) else {
log("Failed to create filter")
return nil
}
guard let outputImage = filter.outputImage else {
log("Failed get filter output")
return nil
}
var bitmap = [UInt8](repeating: 0, count: 4)
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4,
bounds: CGRect(x: 0, y: 0, width: 1, height: 1),
format: .RGBA8, colorSpace: nil)
return UIColor(
red: saturate(bitmap[0]),
green: saturate(bitmap[1]),
blue: saturate(bitmap[2]),
alpha: CGFloat(bitmap[3]) / 255)
}
var averageColor: UIColor? {
let extentVector = CIVector(
x: extent.origin.x,
y: extent.origin.y,
z: extent.size.width,
w: extent.size.height)
guard let filter = CIFilter(name: "CIAreaAverage", parameters: [kCIInputImageKey: self, kCIInputExtentKey: extentVector]) else {
log("Failed to create filter")
return nil
}
guard let outputImage = filter.outputImage else {
log("Failed get filter output")
return nil
}
var bitmap = [UInt8](repeating: 0, count: 4)
guard let null = kCFNull else {
return nil
}
let context = CIContext(options: [.workingColorSpace: null])
context.render(outputImage, toBitmap: &bitmap, rowBytes: 4,
bounds: CGRect(x: 0, y: 0, width: 1, height: 1),
format: .RGBA8, colorSpace: nil)
let color = UIColor(
red: saturate(bitmap[0]),
green: saturate(bitmap[1]),
blue: saturate(bitmap[2]),
alpha: CGFloat(bitmap[3]) / 255)
return color
}
}
/// Map expected range 75-200 to 0-255
private func saturate(_ component: UInt8) -> CGFloat {
return max(min(CGFloat(component) * 2 - 150, 255), 0) / 255
}
extension CIImage: Logger { }