Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor UIImage extension #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 30 additions & 107 deletions GIF-Swift/iOSDevCenters+GIF.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,6 @@

import UIKit
import ImageIO
// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
// Consider refactoring the code to use the non-optional operators.
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}



extension UIImage {

public class func gifImageWithData(_ data: Data) -> UIImage? {
Expand All @@ -35,12 +20,11 @@ extension UIImage {
}

public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL? = URL(string: gifUrl)
else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
guard let bundleURL:URL = URL(string: gifUrl) else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL!) else {
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("image named \"\(gifUrl)\" into NSData")
return nil
}
Expand All @@ -49,10 +33,9 @@ extension UIImage {
}

public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
Expand All @@ -62,8 +45,9 @@ extension UIImage {
return gifImageWithData(imageData)
}

class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
private class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
let defaultDelay = 0.032 // 30 fps
Copy link

@sachinmeesho sachinmeesho Jun 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.032 value could be even less, maybe 0.001. In my case, per frame delay was 0.02 for a gif.

var delay = defaultDelay

let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
Expand All @@ -80,102 +64,41 @@ extension UIImage {
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}

delay = delayObject as! Double
guard let castedDelay = delayObject as? Double else { return delay }

if delay < 0.1 {
delay = 0.1
}
delay = castedDelay < defaultDelay ? defaultDelay : castedDelay

return delay
}

class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}

if a < b {
let c = a
a = b
b = c
}

var rest: Int
while true {
rest = a! % b!

if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}

class func gcdForArray(_ array: Array<Int>) -> Int {
private 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
return array.sorted(by: <).first ?? 1
}

class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
private class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
let images = (0..<count)
.compactMap({ CGImageSourceCreateImageAtIndex(source, $0, nil) })
let delays = images
.enumerated()
.map({ Int(UIImage.delayForImageAtIndex($0.offset,source: source) * 1000) })

for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}

let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}

let duration: Int = {
var sum = 0

for val: Int in delays {
sum += val
}

return sum
}()
let duration = delays.reduce(0, +)

let gcd = gcdForArray(delays)
var frames = [UIImage]()

var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)

for _ in 0..<frameCount {
frames.append(frame)
}
}

let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
let frames: [UIImage] = images
.map({ UIImage(cgImage: $0) })
.enumerated()
.map({
Array(repeating: $0.element, count: Int(delays[$0.offset] / gcd) )
})
.flatMap({ $0 })

let animation = UIImage.animatedImage(with: frames, duration: Double(duration) / 1000)

return animation
}
Expand Down