135 lines
4.7 KiB
Swift
135 lines
4.7 KiB
Swift
//
|
|
// Gif.swift
|
|
// SwiftGif
|
|
//
|
|
// Created by Arne Bahlo on 07.06.14.
|
|
// Copyright (c) 2014 Arne Bahlo. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import ImageIO
|
|
|
|
// MARK: - REFACTOR
|
|
|
|
extension UIImageView {
|
|
public func loadGif(name: String) {
|
|
DispatchQueue.global().async {
|
|
let image = UIImage.gif(name: name)
|
|
DispatchQueue.main.async { self.image = image }
|
|
}
|
|
}
|
|
public func loadGif(asset: String) {
|
|
DispatchQueue.global().async {
|
|
let image = UIImage.gif(asset: asset)
|
|
DispatchQueue.main.async { self.image = image }
|
|
}
|
|
}
|
|
}
|
|
|
|
extension UIImage {
|
|
public class func gif(data: Data) -> UIImage? {
|
|
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else { return nil }
|
|
return .animatedImageWithSource(source)
|
|
}
|
|
public class func gif(url: String) -> UIImage? {
|
|
guard let bundleURL = URL(string: url) else { return nil }
|
|
guard let imageData = try? Data(contentsOf: bundleURL) else { return nil }
|
|
return gif(data: imageData)
|
|
}
|
|
public class func gif(name: String) -> UIImage? {
|
|
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else { return nil }
|
|
guard let imageData = try? Data(contentsOf: bundleURL) else { return nil }
|
|
return gif(data: imageData)
|
|
}
|
|
public class func gif(asset: String) -> UIImage? {
|
|
guard let dataAsset = NSDataAsset(name: asset) else { return nil }
|
|
return gif(data: dataAsset.data)
|
|
}
|
|
internal class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
|
|
var delay = 0.1
|
|
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
|
|
let gifPropertiesPointer = UnsafeMutablePointer<UnsafeRawPointer?>.allocate(capacity: 0)
|
|
defer { gifPropertiesPointer.deallocate() }
|
|
let unsafePointer = Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()
|
|
if CFDictionaryGetValueIfPresent(cfProperties, unsafePointer, gifPropertiesPointer) == false { return delay }
|
|
let gifProperties: CFDictionary = unsafeBitCast(gifPropertiesPointer.pointee, to: CFDictionary.self)
|
|
var delayObject: AnyObject = unsafeBitCast(
|
|
CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
|
|
to: AnyObject.self
|
|
)
|
|
if delayObject.doubleValue == 0 {
|
|
delayObject = unsafeBitCast(
|
|
CFDictionaryGetValue(gifProperties, Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()),
|
|
to: AnyObject.self
|
|
)
|
|
}
|
|
if let delayObject = delayObject as? Double, delayObject > 0 {
|
|
delay = delayObject
|
|
} else {
|
|
delay = 0.1
|
|
}
|
|
return delay
|
|
}
|
|
internal class func gcdForPair(_ lhs: Int?, _ rhs: Int?) -> Int {
|
|
var lhs = lhs
|
|
var rhs = rhs
|
|
if rhs == nil || lhs == nil {
|
|
if rhs != nil {
|
|
return rhs!
|
|
} else if lhs != nil {
|
|
return lhs!
|
|
} else {
|
|
return 0
|
|
}
|
|
}
|
|
if lhs! < rhs! {
|
|
let ctp = lhs
|
|
lhs = rhs
|
|
rhs = ctp
|
|
}
|
|
var rest: Int
|
|
while true {
|
|
rest = lhs! % rhs!
|
|
if rest == 0 {
|
|
return rhs!
|
|
} else {
|
|
lhs = rhs
|
|
rhs = rest
|
|
}
|
|
}
|
|
}
|
|
internal class func gcdForArray(_ array: [Int]) -> Int {
|
|
if array.isEmpty { return 1 }
|
|
var gcd = array[0]
|
|
for val in array { gcd = UIImage.gcdForPair(val, gcd) }
|
|
return gcd
|
|
}
|
|
internal class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
|
|
let count = CGImageSourceGetCount(source)
|
|
var images = [CGImage]()
|
|
var delays = [Int]()
|
|
for index in 0..<count {
|
|
if let image = CGImageSourceCreateImageAtIndex(source, index, nil) {
|
|
images.append(image)
|
|
}
|
|
let delaySeconds = UIImage.delayForImageAtIndex(Int(index), source: source)
|
|
delays.append(Int(delaySeconds * 1000.0))
|
|
}
|
|
let duration: Int = {
|
|
var sum = 0
|
|
for val: Int in delays { sum += val }
|
|
return sum
|
|
}()
|
|
let gcd = gcdForArray(delays)
|
|
var frames = [UIImage]()
|
|
var frame: UIImage
|
|
var frameCount: Int
|
|
for index in 0..<count {
|
|
frame = UIImage(cgImage: images[Int(index)])
|
|
frameCount = Int(delays[Int(index)] / gcd)
|
|
for _ in 0..<frameCount { frames.append(frame) }
|
|
}
|
|
return .animatedImage(with: frames, duration: Double(duration) / 1000.0)
|
|
}
|
|
}
|