Gifu/Source/Helpers/ImageSourceHelpers.swift

71 lines
2.7 KiB
Swift
Raw Normal View History

2014-09-06 14:12:09 +00:00
import ImageIO
import MobileCoreServices
2015-01-23 00:02:08 +00:00
import UIKit
2014-09-06 14:12:09 +00:00
typealias GIFProperties = [String: Double]
let defaultDuration: Double = 0
2014-09-06 14:12:09 +00:00
2015-01-23 00:02:08 +00:00
/// Retruns the duration of a frame at a specific index using an image source (an `CGImageSource` instance).
///
/// - returns: A frame duration.
func CGImageFrameDuration(with imageSource: CGImageSource, atIndex index: Int) -> TimeInterval {
2014-12-13 10:35:00 +00:00
if !imageSource.isAnimatedGIF { return 0.0 }
2014-09-06 14:12:09 +00:00
guard let GIFProperties = imageSource.properties(at: index),
let duration = frameDuration(with: GIFProperties),
let cappedDuration = capDuration(with: duration)
2016-04-24 10:19:13 +00:00
else { return defaultDuration }
2014-09-06 14:12:09 +00:00
2016-04-24 10:19:13 +00:00
return cappedDuration
2014-12-13 10:35:00 +00:00
}
2014-09-06 14:12:09 +00:00
2015-01-23 00:02:08 +00:00
/// Ensures that a duration is never smaller than a threshold value.
///
/// - returns: A capped frame duration.
func capDuration(with duration: Double) -> Double? {
if duration < 0 { return .none }
2017-04-04 09:11:33 +00:00
let threshold = 0.02 - Double.ulpOfOne
2014-12-13 10:35:00 +00:00
let cappedDuration = duration < threshold ? 0.1 : duration
return cappedDuration
}
2015-01-23 00:02:08 +00:00
/// Returns a frame duration from a `GIFProperties` dictionary.
///
/// - returns: A frame duration.
func frameDuration(with properties: GIFProperties) -> Double? {
2016-03-30 22:15:14 +00:00
guard let unclampedDelayTime = properties[String(kCGImagePropertyGIFUnclampedDelayTime)],
let delayTime = properties[String(kCGImagePropertyGIFDelayTime)]
else { return .none }
2014-12-13 10:35:00 +00:00
return duration(withUnclampedTime: unclampedDelayTime, andClampedTime: delayTime)
2014-12-13 10:35:00 +00:00
}
2015-01-23 00:02:08 +00:00
/// Calculates frame duration based on both clamped and unclamped times.
///
/// - returns: A frame duration.
func duration(withUnclampedTime unclampedDelayTime: Double, andClampedTime delayTime: Double) -> Double {
2014-12-13 10:35:00 +00:00
let delayArray = [unclampedDelayTime, delayTime]
return delayArray.filter({ $0 >= 0 }).first ?? defaultDuration
2014-12-13 10:35:00 +00:00
}
2015-01-23 00:02:08 +00:00
/// An extension of `CGImageSourceRef` that add GIF introspection and easier property retrieval.
extension CGImageSource {
2015-01-23 00:02:08 +00:00
/// Returns whether the image source contains an animated GIF.
///
/// - returns: A boolean value that is `true` if the image source contains animated GIF data.
2014-12-13 10:35:00 +00:00
var isAnimatedGIF: Bool {
let isTypeGIF = UTTypeConformsTo(CGImageSourceGetType(self) ?? "" as CFString, kUTTypeGIF)
2014-12-13 10:35:00 +00:00
let imageCount = CGImageSourceGetCount(self)
return isTypeGIF != false && imageCount > 1
2014-09-06 14:12:09 +00:00
}
2015-01-23 00:02:08 +00:00
/// Returns the GIF properties at a specific index.
///
/// - parameter index: The index of the GIF properties to retrieve.
/// - returns: A dictionary containing the GIF properties at the passed in index.
func properties(at index: Int) -> GIFProperties? {
guard let imageProperties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as? [String: AnyObject] else { return nil }
return imageProperties[String(kCGImagePropertyGIFDictionary)] as? GIFProperties
2014-12-13 10:35:00 +00:00
}
2014-09-06 14:12:09 +00:00
}