Browse Source

Merge pull request #1189 from xspyhack/master

Improve animated image resizing (again?) and supports background decoding.
Wei Wang 6 năm trước cách đây
mục cha
commit
e351514b0e

+ 0 - 74
Sources/Image/ImageDrawing.swift

@@ -527,77 +527,3 @@ extension KingfisherWrapper where Base: Image {
     }
     }
     #endif
     #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)
-    }
-}

+ 21 - 9
Sources/Views/AnimatedImageView.swift

@@ -120,6 +120,10 @@ open class AnimatedImageView: UIImageView {
     /// Default is `true`.
     /// Default is `true`.
     public var needsPrescaling = true
     public var needsPrescaling = true
 
 
+    /// Decode the GIF frames in background thread before using. It will decode frames data and do a off-screen
+    /// rendering to extract pixel information in background. This can reduce the main thread CPU usage.
+    public var backgroundDecode = true
+
     /// The animation timer's run loop mode. Default is `RunLoop.Mode.common`.
     /// The animation timer's run loop mode. Default is `RunLoop.Mode.common`.
     /// Set this property to `RunLoop.Mode.default` will make the animation pause during UIScrollView scrolling.
     /// Set this property to `RunLoop.Mode.default` will make the animation pause during UIScrollView scrolling.
     public var runLoopMode = KFRunLoopModeCommon {
     public var runLoopMode = KFRunLoopModeCommon {
@@ -251,6 +255,7 @@ open class AnimatedImageView: UIImageView {
                 preloadQueue: preloadQueue)
                 preloadQueue: preloadQueue)
             animator.delegate = self
             animator.delegate = self
             animator.needsPrescaling = needsPrescaling
             animator.needsPrescaling = needsPrescaling
+            animator.backgroundDecode = backgroundDecode
             animator.prepareFramesAsynchronously()
             animator.prepareFramesAsynchronously()
             self.animator = animator
             self.animator = animator
         }
         }
@@ -361,6 +366,9 @@ extension AnimatedImageView {
         var isFinished: Bool = false
         var isFinished: Bool = false
 
 
         var needsPrescaling = true
         var needsPrescaling = true
+
+        var backgroundDecode = true
+
         weak var delegate: AnimatorDelegate?
         weak var delegate: AnimatorDelegate?
 
 
         // Total duration of one animation loop
         // Total duration of one animation loop
@@ -488,18 +496,22 @@ extension AnimatedImageView {
         }
         }
 
 
         private func loadFrame(at index: Int) -> UIImage? {
         private func loadFrame(at index: Int) -> UIImage? {
-            guard let image = CGImageSourceCreateImageAtIndex(imageSource, index, nil) else {
+            let options: [CFString: Any] = [
+                kCGImageSourceCreateThumbnailFromImageIfAbsent: true,
+                kCGImageSourceCreateThumbnailWithTransform: true,
+                kCGImageSourceShouldCacheImmediately: true,
+                kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height)
+            ]
+
+            let resize = needsPrescaling && size != .zero
+            guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource,
+                                                                index,
+                                                                resize ? options as CFDictionary : nil) else {
                 return nil
                 return nil
             }
             }
 
 
-            let scaledImage: CGImage
-            if needsPrescaling, size != .zero {
-                scaledImage = image.kf.resize(to: size, for: contentMode)
-            } else {
-                scaledImage = image
-            }
-
-            return Image(cgImage: scaledImage)
+            let image = Image(cgImage: cgImage)
+            return backgroundDecode ? image.kf.decoded : image
         }
         }
         
         
         private func updatePreloadedFrames() {
         private func updatePreloadedFrames() {