Skip to content

Commit

Permalink
Merge pull request onevcat#1060 from xspyhack/master
Browse files Browse the repository at this point in the history
Refactor AnimatedImageView
  • Loading branch information
onevcat authored Dec 18, 2018
2 parents b246f77 + 9f1b968 commit 1c87dfe
Show file tree
Hide file tree
Showing 3 changed files with 332 additions and 192 deletions.
20 changes: 11 additions & 9 deletions Sources/Image/GIFAnimatedImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,7 @@ class GIFAnimatedImage {
gifDuration = .infinity
} else {
// Get current animated GIF frame duration
guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil)
as? [String: Any] else
{
return nil
}

let gifInfo = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any]
gifDuration += GIFAnimatedImage.getFrameDuration(from: gifInfo)
gifDuration += GIFAnimatedImage.getFrameDuration(from: imageSource, at: i)
}
images.append(KingfisherWrapper.image(cgImage: imageRef, scale: options.scale, refImage: nil))
if options.onlyFirstFrame { break }
Expand All @@ -104,7 +97,7 @@ class GIFAnimatedImage {
self.duration = gifDuration
}

//Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary.
// Calculates frame duration for a gif frame out of the kCGImagePropertyGIFDictionary dictionary.
static func getFrameDuration(from gifInfo: [String: Any]?) -> TimeInterval {
let defaultFrameDuration = 0.1
guard let gifInfo = gifInfo else { return defaultFrameDuration }
Expand All @@ -116,4 +109,13 @@ class GIFAnimatedImage {
guard let frameDuration = duration else { return defaultFrameDuration }
return frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : defaultFrameDuration
}

// Calculates frame duration at a specific index for a gif from an `imageSource`.
static func getFrameDuration(from imageSource: CGImageSource, at index: Int) -> TimeInterval {
guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, index, nil)
as? [String: Any] else { return 0.0 }

let gifInfo = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any]
return getFrameDuration(from: gifInfo)
}
}
74 changes: 74 additions & 0 deletions Sources/Image/ImageDrawing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -525,3 +525,77 @@ extension KingfisherWrapper where Base: Image {
}
#endif
}

extension CGImage: KingfisherCompatible {}
/// High Performance Image Resizing
/// @see https://nshipster.com/image-resizing/
extension KingfisherWrapper where Base: CGImage {
var size: CGSize {
return CGSize(width: CGFloat(base.width), height: CGFloat(base.height))
}

/// Resizes `base` CGImage to a CGImage of new size, respecting the given content mode.
///
/// - Parameters:
/// - targetSize: The target size in point.
/// - contentMode: Content mode of output image should be.
/// - Returns: A CGImage with new size.
#if os(iOS) || os(tvOS)
public func resize(to size: CGSize, for contentMode: UIView.ContentMode) -> CGImage {
switch contentMode {
case .scaleAspectFit:
return resize(to: size, for: .aspectFit)
case .scaleAspectFill:
return resize(to: size, for: .aspectFill)
default:
return resize(to: size)
}
}
#endif

// MARK: - Resize
/// Resizes `base` CGImage to a CGImage with new size.
///
/// - Parameter size: The target size in point.
/// - Returns: A CGImage with new size.
public func resize(to size: CGSize) -> CGImage {
let alphaInfo = base.alphaInfo.rawValue & CGBitmapInfo.alphaInfoMask.rawValue
var hasAlpha = false
if alphaInfo == CGImageAlphaInfo.premultipliedLast.rawValue
|| alphaInfo == CGImageAlphaInfo.premultipliedFirst.rawValue
|| alphaInfo == CGImageAlphaInfo.first.rawValue
|| alphaInfo == CGImageAlphaInfo.last.rawValue {
hasAlpha = true
}

var bitmapInfo = CGImageByteOrderInfo.order32Little.rawValue
bitmapInfo |= hasAlpha ? CGImageAlphaInfo.premultipliedFirst.rawValue : CGImageAlphaInfo.noneSkipFirst.rawValue

guard let context = CGContext(data: nil,
width: Int(size.width),
height: Int(size.height),
bitsPerComponent: base.bitsPerComponent,
bytesPerRow: base.bytesPerRow,
space: base.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
bitmapInfo: bitmapInfo) else
{
return base
}

let rect = CGRect(origin: .zero, size: size)
context.interpolationQuality = .high
context.draw(base, in: rect)
return context.makeImage() ?? base
}

/// Resizes `base` CGImage to a CGImage of new size, respecting the given content mode.
///
/// - Parameters:
/// - targetSize: The target size in point.
/// - contentMode: Content mode of output image should be.
/// - Returns: A CGImage with new size.
public func resize(to targetSize: CGSize, for contentMode: ContentMode) -> CGImage {
let newSize = size.kf.resize(to: targetSize, for: contentMode)
return resize(to: newSize)
}
}
Loading

0 comments on commit 1c87dfe

Please sign in to comment.