Переглянути джерело

Refactor for image drawing

onevcat 7 роки тому
батько
коміт
23de76f6eb

+ 2 - 2
Demo/Kingfisher-Demo.xcodeproj/project.pbxproj

@@ -834,7 +834,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -886,7 +886,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;

+ 2 - 2
Kingfisher.xcodeproj/project.pbxproj

@@ -1452,7 +1452,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1504,7 +1504,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 10.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.11;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;

+ 1 - 11
Sources/Image/Filter.swift

@@ -71,11 +71,7 @@ public struct Filter {
             let filter = CIFilter(name: "CISourceOverCompositing")!
             filter.setValue(colorImage, forKey: kCIInputImageKey)
             filter.setValue(input, forKey: kCIInputBackgroundImageKey)
-            #if swift(>=4.0)
             return filter.outputImage?.cropped(to: input.extent)
-            #else
-            return filter.outputImage?.cropping(to: input.extent)
-            #endif
         })
     }
     
@@ -88,15 +84,9 @@ public struct Filter {
             let paramsColor = [kCIInputBrightnessKey: brightness,
                                kCIInputContrastKey: contrast,
                                kCIInputSaturationKey: saturation]
-            
             let paramsExposure = [kCIInputEVKey: inputEV]
-            #if swift(>=4.0)
             let blackAndWhite = input.applyingFilter("CIColorControls", parameters: paramsColor)
             return blackAndWhite.applyingFilter("CIExposureAdjust", parameters: paramsExposure)
-            #else
-            let blackAndWhite = input.applyingFilter("CIColorControls", withInputParameters: paramsColor)
-            return blackAndWhite.applyingFilter("CIExposureAdjust", withInputParameters: paramsExposure)
-            #endif
         })
     }
 }
@@ -128,7 +118,7 @@ extension KingfisherClass where Base: Image {
         guard let outputImage = filter.transform(inputImage) else {
             return base
         }
-        
+
         guard let result = ciContext.createCGImage(outputImage, from: outputImage.extent) else {
             assertionFailure("[Kingfisher] Can not make an tint image within context.")
             return base

+ 17 - 27
Sources/Image/ImageDrawing.swift

@@ -266,7 +266,7 @@ extension KingfisherClass where Base: Image {
         var targetRadius = floor(s * 3.0 * sqrtPi2 / 4.0 + 0.5)
         
         if targetRadius.isEven { targetRadius += 1 }
-        
+
         // Determine necessary iteration count by blur radius.
         let iterations: Int
         if radius < 0.5 {
@@ -290,17 +290,16 @@ extension KingfisherClass where Base: Image {
             return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
         }
         
-        guard let context = beginContext(size: size, scale: scale, translated: true) else {
+        guard let context = beginContext(size: size, scale: scale, inverting: true) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
         }
-        defer { endContext() }
-        
         context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
+        endContext()
         
         var inBuffer = createEffectBuffer(context)
         
-        guard let outContext = beginContext(size: size, scale: scale, translated: true) else {
+        guard let outContext = beginContext(size: size, scale: scale, inverting: true) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
         }
@@ -444,32 +443,22 @@ extension KingfisherClass where Base: Image {
         #else
         if images != nil { return base }
         #endif
-        
-        guard let imageRef = self.cgImage else {
+
+        guard let imageRef = cgImage else {
             assertionFailure("[Kingfisher] Decoding only works for CG-based image.")
             return base
         }
-        
-        // Draw CGImage in a plain context with scale of 1.0.
-        guard let context = beginContext(
-            size: CGSize(width: imageRef.width, height: imageRef.height), scale: 1.0, translated: true) else
-        {
-            assertionFailure("[Kingfisher] Decoding fails to create a valid context.")
-            return base
+
+        let size = CGSize(width: CGFloat(imageRef.width) / scale, height: CGFloat(imageRef.height) / scale)
+        return draw(to: size, inverting: true, scale: scale) { context in
+            context.draw(imageRef, in: CGRect(origin: .zero, size: size))
         }
-        
-        defer { endContext() }
-        
-        let rect = CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height))
-        context.draw(imageRef, in: rect)
-        let decompressedImageRef = context.makeImage()
-        return KingfisherClass.image(cgImage: decompressedImageRef!, scale: scale, refImage: base)
     }
 }
 
 extension KingfisherClass where Base: Image {
     
-    func beginContext(size: CGSize, scale: CGFloat, translated: Bool = false) -> CGContext? {
+    func beginContext(size: CGSize, scale: CGFloat, inverting: Bool = false) -> CGContext? {
         #if os(macOS)
         guard let rep = NSBitmapImageRep(
             bitmapDataPlanes: nil,
@@ -498,7 +487,7 @@ extension KingfisherClass where Base: Image {
         #else
         UIGraphicsBeginImageContextWithOptions(size, false, scale)
         guard let context = UIGraphicsGetCurrentContext() else { return nil }
-        if translated { // If drawing a CGImage, we need to make context flipped.
+        if inverting { // If drawing a CGImage, we need to make context flipped.
             context.scaleBy(x: 1.0, y: -1.0)
             context.translateBy(x: 0, y: -size.height)
         }
@@ -514,8 +503,9 @@ extension KingfisherClass where Base: Image {
         #endif
     }
     
-    func draw(to size: CGSize, draw: (CGContext)->()) -> Image {
-        guard let context = beginContext(size: size, scale: scale) else {
+    func draw(to size: CGSize, inverting: Bool = false, scale: CGFloat? = nil, draw: (CGContext) -> Void) -> Image {
+        let targetScale = scale ?? self.scale
+        guard let context = beginContext(size: size, scale: targetScale, inverting: inverting) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
         }
@@ -524,7 +514,7 @@ extension KingfisherClass where Base: Image {
         guard let cgImage = context.makeImage() else {
             return base
         }
-        return KingfisherClass.image(cgImage: cgImage, scale: scale, refImage: base)
+        return KingfisherClass.image(cgImage: cgImage, scale: targetScale, refImage: base)
     }
     
     #if os(macOS)
@@ -534,7 +524,7 @@ extension KingfisherClass where Base: Image {
         let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
         
         return draw(to: self.size) { context in
-            image.draw(in: rect, from: NSRect.zero, operation: .copy, fraction: 1.0)
+            image.draw(in: rect, from: .zero, operation: .copy, fraction: 1.0)
         }
     }
     #endif

+ 10 - 15
Sources/Image/Placeholder.swift

@@ -25,9 +25,9 @@
 //  THE SOFTWARE.
 
 #if os(macOS)
-    import AppKit
+import AppKit
 #else
-    import UIKit
+import UIKit
 #endif
 
 
@@ -42,19 +42,16 @@ public protocol Placeholder {
     func remove(from imageView: ImageView)
 }
 
-/// Default implementation of an image placeholder. The image will be set or 
+/// Default implementation of an image placeholder. The image will be set or
 /// reset directly for `image` property of the image view.
-extension Placeholder where Self: Image {
-    
+extension Image: Placeholder {
     /// How the placeholder should be added to a given image view.
     public func add(to imageView: ImageView) { imageView.image = self }
-    
+
     /// How the placeholder should be removed from a given image view.
     public func remove(from imageView: ImageView) { imageView.image = nil }
 }
 
-extension Image: Placeholder {}
-
 /// Default implementation of an arbitrary view as placeholder. The view will be 
 /// added as a subview when adding and be removed from its super view when removing.
 ///
@@ -65,14 +62,12 @@ extension Placeholder where Self: View {
     /// How the placeholder should be added to a given image view.
     public func add(to imageView: ImageView) {
         imageView.addSubview(self)
-
         translatesAutoresizingMaskIntoConstraints = false
-        NSLayoutConstraint.activate([
-            NSLayoutConstraint(item: self, attribute: .centerX, relatedBy: .equal, toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0),
-            NSLayoutConstraint(item: self, attribute: .centerY, relatedBy: .equal, toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0),
-            NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: imageView, attribute: .height, multiplier: 1, constant: 0),
-            NSLayoutConstraint(item: self, attribute: .width, relatedBy: .equal, toItem: imageView, attribute: .width, multiplier: 1, constant: 0)
-            ])
+
+        centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
+        centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
+        heightAnchor.constraint(equalTo: imageView.heightAnchor).isActive = true
+        widthAnchor.constraint(equalTo: imageView.widthAnchor).isActive = true
     }
 
     /// How the placeholder should be removed from a given image view.

+ 4 - 2
Sources/Networking/ImageModifier.swift

@@ -26,8 +26,10 @@
 
 import Foundation
 
-/// An `ImageModifier` can be used to change properties on an Image in between
-/// cache serialization and use of the image.
+/// An `ImageModifier` can be used to change properties on an image in between
+/// cache serialization and use of the image. The modified returned image will be
+/// only used for current rendering purpose, the serialization data will not contain
+/// the changes applied by the `ImageModifier`.
 public protocol ImageModifier {
     /// Modify an input `Image`.
     ///

+ 25 - 20
Sources/Networking/ImagePrefetcher.swift

@@ -35,14 +35,16 @@
 /// Progress update block of prefetcher. 
 ///
 /// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
-/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while
+///                      downloading, encountered an error when downloading or the download not being started at all.
 /// - `completedResources`: An array of resources that are downloaded and cached successfully.
 public typealias PrefetcherProgressBlock = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
 
 /// Completion block of prefetcher.
 ///
 /// - `skippedResources`: An array of resources that are already cached before the prefetching starting.
-/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while downloading, encountered an error when downloading or the download not being started at all.
+/// - `failedResources`: An array of resources that fail to be downloaded. It could because of being cancelled while
+///                      downloading, encountered an error when downloading or the download not being started at all.
 /// - `completedResources`: An array of resources that are downloaded and cached successfully.
 public typealias PrefetcherCompletionHandler = ((_ skippedResources: [Resource], _ failedResources: [Resource], _ completedResources: [Resource]) -> Void)
 
@@ -75,25 +77,28 @@ public class ImagePrefetcher {
     private let manager: KingfisherManager
     
     private var finished: Bool {
-        return failedResources.count + skippedResources.count + completedResources.count == prefetchResources.count && tasks.isEmpty
+        let totalFinished = failedResources.count + skippedResources.count + completedResources.count
+        return totalFinished == prefetchResources.count && tasks.isEmpty
     }
-    
-    /**
-     Init an image prefetcher with an array of URLs.
-     
-     The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable. 
-     After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
-     The images already cached will be skipped without downloading again.
-     
-     - parameter urls:              The URLs which should be prefetched.
-     - parameter options:           A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
-     - parameter progressBlock:     Called every time an resource is downloaded, skipped or cancelled.
-     - parameter completionHandler: Called when the whole prefetching process finished.
-     
-     - Note: By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as 
-     the downloader and cache target respectively. You can specify another downloader or cache by using a customized `KingfisherOptionsInfo`.
-     Both the progress and completion block will be invoked in main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
-     */
+
+    /// Init an image prefetcher with an array of URLs.
+    ///
+    /// The prefetcher should be initiated with a list of prefetching targets. The URLs list is immutable.
+    /// After you get a valid `ImagePrefetcher` object, you could call `start()` on it to begin the prefetching process.
+    /// The images already cached will be skipped without downloading again.
+    ///
+    /// - Parameters:
+    ///   - urls: The URLs which should be prefetched.
+    ///   - options: A dictionary could control some behaviors. See `KingfisherOptionsInfo` for more.
+    ///   - progressBlock: Called every time an resource is downloaded, skipped or cancelled.
+    ///   - completionHandler: Called when the whole prefetching process finished.
+    ///
+    /// - Note:
+    /// By default, the `ImageDownloader.defaultDownloader` and `ImageCache.defaultCache` will be used as
+    /// the downloader and cache target respectively. You can specify another downloader or cache by using
+    /// a customized `KingfisherOptionsInfo`. Both the progress and completion block will be invoked in
+    /// main thread. The `CallbackDispatchQueue` in `optionsInfo` will be ignored in this method.
+
     public convenience init(urls: [URL],
                          options: KingfisherOptionsInfo? = nil,
                    progressBlock: PrefetcherProgressBlock? = nil,

+ 1 - 1
Sources/Networking/RequestModifier.swift

@@ -47,7 +47,7 @@ public struct AnyModifier: ImageDownloadRequestModifier {
         return block(request)
     }
     
-    public init(modify: @escaping (URLRequest) -> URLRequest? ) {
+    public init(modify: @escaping (URLRequest) -> URLRequest?) {
         block = modify
     }
 }