You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

139 lines
4.4 KiB

// The MIT License (MIT)
//
// Copyright (c) 2015-2021 Alexander Grebenyuk (github.com/kean).
#if !os(macOS)
import UIKit
#else
import Cocoa
#endif
#if os(watchOS)
import WatchKit
#endif
import ImageIO
// MARK: - ImageEncoding
public protocol ImageEncoding {
/// Encodes the given image.
func encode(_ image: PlatformImage) -> Data?
/// An optional method which encodes the given image container.
func encode(_ container: ImageContainer, context: ImageEncodingContext) -> Data?
}
public extension ImageEncoding {
func encode(_ container: ImageContainer, context: ImageEncodingContext) -> Data? {
self.encode(container.image)
}
}
// MARK: - ImageEncoder
public typealias ImageEncoder = ImageEncoders.Default
/// Image encoding context used when selecting which encoder to use.
public struct ImageEncodingContext {
public let request: ImageRequest
public let image: PlatformImage
public let urlResponse: URLResponse?
}
// MARK: - ImageEncoders
public enum ImageEncoders {}
// MARK: - ImageEncoders.Default
public extension ImageEncoders {
/// A default adaptive encoder which uses best encoder available depending
/// on the input image and its configuration.
struct Default: ImageEncoding {
public var compressionQuality: Float
/// Set to `true` to switch to HEIF when it is available on the current hardware.
/// `false` by default.
public var isHEIFPreferred = false
public init(compressionQuality: Float = 0.8) {
self.compressionQuality = compressionQuality
}
public func encode(_ image: PlatformImage) -> Data? {
guard let cgImage = image.cgImage else {
return nil
}
let type: ImageType
if cgImage.isOpaque {
if isHEIFPreferred && ImageEncoders.ImageIO.isSupported(type: .heic) {
type = .heic
} else {
type = .jpeg
}
} else {
type = .png
}
let encoder = ImageEncoders.ImageIO(type: type, compressionRatio: compressionQuality)
return encoder.encode(image)
}
}
}
// MARK: - ImageEncoders.ImageIO
public extension ImageEncoders {
/// An Image I/O based encoder.
///
/// Image I/O is a system framework that allows applications to read and
/// write most image file formats. This framework offers high efficiency,
/// color management, and access to image metadata.
struct ImageIO: ImageEncoding {
public let type: ImageType
public let compressionRatio: Float
/// - parameter format: The output format. Make sure that the format is
/// supported on the current hardware.s
/// - parameter compressionRatio: 0.8 by default.
public init(type: ImageType, compressionRatio: Float = 0.8) {
self.type = type
self.compressionRatio = compressionRatio
}
private static let lock = NSLock()
private static var availability = [ImageType: Bool]()
/// Retuns `true` if the encoding is available for the given format on
/// the current hardware. Some of the most recent formats might not be
/// available so its best to check before using them.
public static func isSupported(type: ImageType) -> Bool {
lock.lock()
defer { lock.unlock() }
if let isAvailable = availability[type] {
return isAvailable
}
let isAvailable = CGImageDestinationCreateWithData(
NSMutableData() as CFMutableData, type.rawValue as CFString, 1, nil
) != nil
availability[type] = isAvailable
return isAvailable
}
public func encode(_ image: PlatformImage) -> Data? {
let data = NSMutableData()
let options: NSDictionary = [
kCGImageDestinationLossyCompressionQuality: compressionRatio
]
guard let source = image.cgImage,
let destination = CGImageDestinationCreateWithData(
data as CFMutableData, type.rawValue as CFString, 1, nil
) else {
return nil
}
CGImageDestinationAddImage(destination, source, options)
CGImageDestinationFinalize(destination)
return data as Data
}
}
}