Gifu/Source/ImageSourceHelpers.swift

79 lines
2.8 KiB
Swift
Raw Normal View History

2014-09-06 14:12:09 +00:00
import ImageIO
import MobileCoreServices
2015-01-20 19:44:15 +00:00
import Runes
2015-01-23 00:02:08 +00:00
import UIKit
2014-09-06 14:12:09 +00:00
2015-01-23 00:02:08 +00:00
typealias GIFProperties = [String : Double]
2014-12-13 10:35:00 +00:00
private 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.
2014-09-06 14:12:09 +00:00
func CGImageSourceGIFFrameDuration(imageSource: CGImageSource, index: Int) -> NSTimeInterval {
2014-12-13 10:35:00 +00:00
if !imageSource.isAnimatedGIF { return 0.0 }
2014-09-06 14:12:09 +00:00
2014-12-13 10:35:00 +00:00
let duration = imageSource.GIFPropertiesAtIndex(UInt(index))
>>- durationFromGIFProperties
>>- capDuration
2014-09-06 14:12:09 +00:00
2014-12-13 10:35:00 +00:00
return duration ?? defaultDuration
}
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.
2014-12-13 10:35:00 +00:00
private func capDuration(duration: Double) -> Double? {
if duration < 0 { return .None }
2014-09-06 14:12:09 +00:00
let threshold = 0.02 - Double(FLT_EPSILON)
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.
2014-12-13 10:35:00 +00:00
private func durationFromGIFProperties(properties: GIFProperties) -> Double? {
let unclampedDelayTime = properties[String(kCGImagePropertyGIFUnclampedDelayTime)]
let delayTime = properties[String(kCGImagePropertyGIFDelayTime)]
return duration <^> unclampedDelayTime <*> delayTime
}
2015-01-23 00:02:08 +00:00
/// Calculates frame duration based on both clamped and unclamped times.
///
/// :returns: A frame duration.
2014-12-13 10:35:00 +00:00
private func duration(unclampedDelayTime: Double)(delayTime: Double) -> Double {
let delayArray = [unclampedDelayTime, delayTime]
return delayArray.filter(isPositive).first ?? defaultDuration
}
2014-09-06 14:12:09 +00:00
2015-01-23 00:02:08 +00:00
/// Checks if a `Double` value is positive.
///
/// :returns: A boolean value that is `true` if the tested value is positive.
2014-12-13 10:35:00 +00:00
private func isPositive(value: Double) -> Bool {
return value >= 0
}
2015-01-23 00:02:08 +00:00
/// An extension of `CGImageSourceRef` that add GIF introspection and easier property retrieval.
2014-12-13 10:35:00 +00:00
extension CGImageSourceRef {
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), kUTTypeGIF)
let imageCount = CGImageSourceGetCount(self)
return isTypeGIF != 0 && 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.
///
/// :param: index The index of the GIF properties to retrieve.
/// :returns: A dictionary containing the GIF properties at the passed in index.
2014-12-13 10:35:00 +00:00
func GIFPropertiesAtIndex(index: UInt) -> GIFProperties? {
if !isAnimatedGIF { return .None }
let imageProperties = CGImageSourceCopyPropertiesAtIndex(self, index, nil) as Dictionary
return imageProperties[String(kCGImagePropertyGIFDictionary)] as? GIFProperties
}
2014-09-06 14:12:09 +00:00
}