Browse Source

Merge pull request #529 from xspyhack/master

Fix preload animated image and improve performance
Wei Wang 9 years ago
parent
commit
ea94cc4b2b
2 changed files with 35 additions and 17 deletions
  1. 30 10
      Sources/AnimatedImageView.swift
  2. 5 7
      Sources/ImageView+Kingfisher.swift

+ 30 - 10
Sources/AnimatedImageView.swift

@@ -162,7 +162,7 @@ open class AnimatedImageView: UIImageView {
         if let imageSource = image?.kf.imageSource?.imageRef {
         if let imageSource = image?.kf.imageSource?.imageRef {
             animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount)
             animator = Animator(imageSource: imageSource, contentMode: contentMode, size: bounds.size, framePreloadCount: framePreloadCount)
             animator?.needsPrescaling = needsPrescaling
             animator?.needsPrescaling = needsPrescaling
-            animator?.prepareFrames()
+            animator?.prepareFramesAsynchronously()
         }
         }
         didMove()
         didMove()
     }
     }
@@ -217,6 +217,10 @@ class Animator {
     
     
     var contentMode = UIViewContentMode.scaleToFill
     var contentMode = UIViewContentMode.scaleToFill
     
     
+    private lazy var preloadQueue: DispatchQueue = {
+        return DispatchQueue(label: "com.onevcat.Kingfisher.Animator.preloadQueue")
+    }()
+    
     /**
     /**
      Init an animator with image source reference.
      Init an animator with image source reference.
      
      
@@ -235,12 +239,18 @@ class Animator {
     }
     }
     
     
     func frame(at index: Int) -> Image? {
     func frame(at index: Int) -> Image? {
-        return animatedFrames[index].image
+        return animatedFrames[safe: index]?.image
+    }
+    
+    func prepareFramesAsynchronously() {
+        preloadQueue.async { [weak self] in
+            self?.prepareFrames()
+        }
     }
     }
     
     
-    func prepareFrames() {
+    private func prepareFrames() {
         frameCount = CGImageSourceGetCount(imageSource)
         frameCount = CGImageSourceGetCount(imageSource)
-
+        
         if let properties = CGImageSourceCopyProperties(imageSource, nil),
         if let properties = CGImageSourceCopyProperties(imageSource, nil),
             let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
             let gifInfo = (properties as NSDictionary)[kCGImagePropertyGIFDictionary as String] as? NSDictionary,
             let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int
             let loopCount = gifInfo[kCGImagePropertyGIFLoopCount as String] as? Int
@@ -253,12 +263,12 @@ class Animator {
         animatedFrames = (0..<frameToProcess).reduce([]) { $0 + pure(prepareFrame(at: $1))}
         animatedFrames = (0..<frameToProcess).reduce([]) { $0 + pure(prepareFrame(at: $1))}
     }
     }
     
     
-    func prepareFrame(at index: Int) -> AnimatedFrame {
+    private func prepareFrame(at index: Int) -> AnimatedFrame {
         guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
         guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
             return AnimatedFrame.null
             return AnimatedFrame.null
         }
         }
         
         
-        let frameDuration = imageSource.kf.GIFProperties(at: index).flatMap {
+        let frameDuration = imageSource.kf.gifProperties(at: index).flatMap {
             gifInfo -> Double? in
             gifInfo -> Double? in
             
             
             let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
             let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as Double?
@@ -304,17 +314,27 @@ class Animator {
         currentFrameIndex = currentFrameIndex % animatedFrames.count
         currentFrameIndex = currentFrameIndex % animatedFrames.count
         
         
         if animatedFrames.count < frameCount {
         if animatedFrames.count < frameCount {
-            animatedFrames[lastFrameIndex] = prepareFrame(at: currentPreloadIndex)
-            currentPreloadIndex += 1
-            currentPreloadIndex = currentPreloadIndex % frameCount
+            preloadFrameAsynchronously(at: lastFrameIndex)
         }
         }
         return true
         return true
     }
     }
+    
+    private func preloadFrameAsynchronously(at index: Int) {
+        preloadQueue.async { [weak self] in
+            self?.preloadFrame(at: index)
+        }
+    }
+    
+    private func preloadFrame(at index: Int) {
+        animatedFrames[index] = prepareFrame(at: currentPreloadIndex)
+        currentPreloadIndex += 1
+        currentPreloadIndex = currentPreloadIndex % frameCount
+    }
 }
 }
 
 
 extension CGImageSource: KingfisherCompatible { }
 extension CGImageSource: KingfisherCompatible { }
 extension Kingfisher where Base: CGImageSource {
 extension Kingfisher where Base: CGImageSource {
-    func GIFProperties(at index: Int) -> [String: Double]? {
+    func gifProperties(at index: Int) -> [String: Double]? {
         let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary?
         let properties = CGImageSourceCopyPropertiesAtIndex(base, index, nil) as Dictionary?
         return properties?[kCGImagePropertyGIFDictionary] as? [String: Double]
         return properties?[kCGImagePropertyGIFDictionary] as? [String: Double]
     }
     }

+ 5 - 7
Sources/ImageView+Kingfisher.swift

@@ -74,7 +74,7 @@ extension Kingfisher where Base: ImageView {
         
         
         setWebURL(resource.downloadURL)
         setWebURL(resource.downloadURL)
 
 
-        if shouldPreloadAllGIF() {
+        if base.shouldPreloadAllGIF() {
             options.append(.preloadAllGIFData)
             options.append(.preloadAllGIFData)
         }
         }
         
         
@@ -138,10 +138,6 @@ extension Kingfisher where Base: ImageView {
     public func cancelDownloadTask() {
     public func cancelDownloadTask() {
         imageTask?.downloadTask?.cancel()
         imageTask?.downloadTask?.cancel()
     }
     }
-    
-    func shouldPreloadAllGIF() -> Bool {
-        return true
-    }
 }
 }
 
 
 // MARK: - Associated Object
 // MARK: - Associated Object
@@ -285,6 +281,8 @@ extension ImageView {
     fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) }
     fileprivate func kf_setImageTask(_ task: RetrieveImageTask?) { kf.setImageTask(task) }
     @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setWebURL")
     @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.setWebURL")
     fileprivate func kf_setWebURL(_ url: URL) { kf.setWebURL(url) }
     fileprivate func kf_setWebURL(_ url: URL) { kf.setWebURL(url) }
-    @available(*, deprecated, message: "Extensions directly on image views are deprecated.", renamed: "kf.shouldPreloadAllGIF")
-    func shouldPreloadAllGIF() -> Bool { return kf.shouldPreloadAllGIF() }
+}
+
+extension ImageView {
+    func shouldPreloadAllGIF() -> Bool { return true }
 }
 }