<NME> README.md
<BEF> <p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/10440878/a7c6e468-714b-11e5-9b12-baef482c37c1.png" height="100"/>

<p align="left">
<a href="https://cocoapods.org"><img src="https://img.shields.io/cocoapods/v/Nuke.svg"></a>
# Image Loading System
<a href="http://cocoadocs.org/docsets/Nuke"><img src="https://img.shields.io/cocoapods/p/Nuke.svg?style=flat)"></a>
</p>

Loading, processing, caching and [preheating](http://outscope.net/blog/image-preheating) images.

To get started check out http://kean.github.io/Nuke/ !


The framework is lean and compiles in under 2 seconds[¹](#footnote-1). Nuke has an automated test suite 2x the size of the codebase itself, ensuring excellent reliability. Every feature is carefully designed and optimized for [performance](https://kean.blog/post/nuke-9).

> **Fast LRU memory and disk cache** · **SwiftUI** · **Smart background decompression** · **Image processing** · **Resumable downloads** · **Intelligent deduplication** · **Request prioritization** · **Prefetching** · **Rate limiting** · **Progressive JPEG, HEIF, WebP, SVG, GIF** · **Alamofire** · **Combine** · **Async/Await**

## Sponsors

<a target="_blank" rel="noopener noreferrer" href="https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22#gh-light-mode-only"><img src="https://user-images.githubusercontent.com/1567433/175186173-64eb53cb-b5d6-4ed4-aaca-87dbbb0834ab.png#gh-light-mode-only" width="300px" alt="Stream Logo" style="max-width: 100%;"></a>
<a target="_blank" rel="noopener noreferrer" href="https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22#gh-dark-mode-only"><img src="https://user-images.githubusercontent.com/1567433/175562043-0ab82adc-e3c7-4c0b-8813-a7940ff41db8.png#gh-dark-mode-only" width="244px" alt="Stream Logo" style="max-width: 100%;"></a>

Nuke is proudly sponsored by [Stream](https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22), the leading provider in enterprise grade Feed & Chat APIs.

> [Support](https://github.com/sponsors/kean) Nuke on GitHub Sponsors.

## Documentation

Nuke is easy to learn and use thanks to documentation generated using DocC: [**Nuke**](https://kean-docs.github.io/nuke/documentation/nuke/getting-started/), [**NukeUI**](https://kean-docs.github.io/nukeui/documentation/nukeui/), [**NukeExtensions**](https://kean-docs.github.io/nukeextensions/documentation/nukeextensions/). Make sure to also check out [**Nuke Demo**](https://github.com/kean/NukeDemo).

> Upgrading from the previous version? Use a [**Migration Guide**](https://github.com/kean/Nuke/tree/master/Documentation/Migrations).

- Uses [NSURLSession](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/) with [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) support
- A single data task is used for multiple equivalent requests
- [Automated preheating](http://outscope.net/blog/image-preheating) of images close to the viewport
- Congestion control that prevents trashing the system with requests during fast scrolling
- Full featured extensions for UI components

##### Caching

## Extensions

The image pipeline is easy to customize and extend. Check out the following first-class extensions and packages built by the community.

|Name|Description|
|--|--|
|[**Alamofire Plugin**](https://github.com/kean/Nuke-Alamofire-Plugin)|Replace networking layer with [Alamofire](https://github.com/Alamofire/Alamofire)|
|[**NukeWebP**](https://github.com/makleso6/NukeWebP)| **Community**. [WebP](https://developers.google.com/speed/webp/) support, built by [Maxim Kolesnik](https://github.com/makleso6)|
|[**WebP Plugin**](https://github.com/ryokosuge/Nuke-WebP-Plugin)| **Community**. [WebP](https://developers.google.com/speed/webp/) support, built by [Ryo Kosuge](https://github.com/ryokosuge)|
|[**AVIF Plugin**](https://github.com/delneg/Nuke-AVIF-Plugin)| **Community**. [AVIF](https://caniuse.com/avif) support, built by [Denis](https://github.com/delneg)|
|[**Gifu Plugin**](https://github.com/kean/Nuke-Gifu-Plugin)|Use [Gifu](https://github.com/kaishin/Gifu) to load and display animated GIFs|
|[**RxNuke**](https://github.com/kean/RxNuke)|[RxSwift](https://github.com/ReactiveX/RxSwift) extensions for Nuke with examples|

## Minimum Requirements

| Nuke       | Date         | Swift       | Xcode      | Platforms                                     |
|------------|--------------|-------------|------------|-----------------------------------------------|
| Nuke 11.0  | Jul 20, 2022 | Swift 5.6   | Xcode 13.3 | iOS 13.0, watchOS 6.0, macOS 10.15, tvOS 13.0 |
| Nuke 10.0  | Jun 1, 2021  | Swift 5.3   | Xcode 12.0 | iOS 11.0, watchOS 4.0, macOS 10.13, tvOS 11.0 |

## License

Nuke is available under the MIT license. See the LICENSE file for more info.

----

> <a name="footnote-1">¹</a> Measured on MacBook Pro 14" 2021 (10-core M1 Pro)

<MSG> Merge branch 'develop'

<DFF> @@ -1,4 +1,4 @@
-<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/10440878/a7c6e468-714b-11e5-9b12-baef482c37c1.png" height="100"/>
+<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/13362579/7001aa94-dcd6-11e5-8c0d-5a4920008d04.png" height="150"/>
 
 <p align="left">
 <a href="https://cocoapods.org"><img src="https://img.shields.io/cocoapods/v/Nuke.svg"></a>
@@ -6,7 +6,7 @@
 <a href="http://cocoadocs.org/docsets/Nuke"><img src="https://img.shields.io/cocoapods/p/Nuke.svg?style=flat)"></a>
 </p>
 
-Loading, processing, caching and [preheating](http://outscope.net/blog/image-preheating) images.
+Loading, processing, caching and [**preheating**](http://outscope.net/blog/image-preheating) images.
 
 To get started check out http://kean.github.io/Nuke/ !
 
@@ -33,7 +33,7 @@ Nuke.taskWith(request) { response in
 - Uses [NSURLSession](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/) with [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) support
 - A single data task is used for multiple equivalent requests
 - [Automated preheating](http://outscope.net/blog/image-preheating) of images close to the viewport
-- Congestion control that prevents trashing the system with requests during fast scrolling
+- Prevents trashing the system with requests during fast scrolling
 - Full featured extensions for UI components
 
 ##### Caching

{'commit': 'diff --git a/.gitignore b/.gitignore\nindex 763ff3dc..fcdad8a5 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -1,4 +1,8 @@\n-# Xcode\n+## System\n+.DS_Store\n+\n+\n+## Xcode\n #\n build/\n *.pbxuser\n@@ -16,14 +20,40 @@ DerivedData\n *.hmap\n *.ipa\n *.xcuserstate\n-.DS_Store\n \n-# CocoaPods\n+\n+## Other\n+*.moved-aside\n+*.xcuserstate\n+\n+\n+## Playgrounds\n+timeline.xctimeline\n+playground.xcworkspace\n+\n+\n+## Swift Package Manager\n+#\n+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.\n+# Packages/\n+\n+.build/\n+\n+\n+## CocoaPods\n #\n # We recommend against adding the Pods directory to your .gitignore. However\n # you should judge for yourself, the pros and cons are mentioned at:\n-# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control\n+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control\n #\n \n Pods/\n Podfile.lock\n+\n+\n+## Carthage\n+#\n+# Add this line if you want to avoid checking in source code from Carthage dependencies.\n+# Carthage/Checkouts\n+\n+Carthage/Build\ndiff --git a/Nuke/Source/Core/ImageDecoder.swift b/Nuke/Source/Core/ImageDecoder.swift\nindex 46a26677..bb87a2c8 100644\n--- a/Nuke/Source/Core/ImageDecoder.swift\n+++ b/Nuke/Source/Core/ImageDecoder.swift\n@@ -12,6 +12,7 @@\n     import WatchKit\n #endif\n \n+\n /** Decodes data into an image object.\n  */\n public protocol ImageDecoding {\n@@ -20,6 +21,9 @@ public protocol ImageDecoding {\n     func decode(data: NSData, response: NSURLResponse?) -> Image?\n }\n \n+\n+private let lock = NSLock()\n+\n /** Decodes data into an image object. Image scale is set to the scale of the main screen.\n  */\n public class ImageDecoder: ImageDecoding {\n@@ -28,13 +32,23 @@ public class ImageDecoder: ImageDecoding {\n     public init() {}\n \n     /** Decodes data into an image object using native methods.\n-    */\n+     */\n     public func decode(data: NSData, response: NSURLResponse?) -> Image? {\n+        var image: Image?\n+        /* Image initializers are not considered thread safe:\n+        - https://github.com/AFNetworking/AFNetworking/issues/2572\n+        - https://github.com/Alamofire/AlamofireImage/issues/75\n+\n+        ImageLoader ensures thread safety of decoders by running them on NSOperationQueue with maxConcurrentOperationCount=1. However, users might either change this value, or user multiple ImageLoaders concurrently, which would break thread safety.\n+         */\n+        lock.lock()\n         #if os(OSX)\n-            return NSImage(data: data)\n+            image = NSImage(data: data)\n         #else\n-            return UIImage(data: data, scale: self.imageScale)\n+            image = UIImage(data: data, scale: self.imageScale)\n         #endif\n+        lock.unlock()\n+        return image\n     }\n \n     #if !os(OSX)\ndiff --git a/Nuke/Source/Core/ImageLoader.swift b/Nuke/Source/Core/ImageLoader.swift\nindex bff132c3..c34295db 100644\n--- a/Nuke/Source/Core/ImageLoader.swift\n+++ b/Nuke/Source/Core/ImageLoader.swift\n@@ -6,10 +6,10 @@ import Foundation\n \n // MARK: - ImageLoading\n \n-/** Performs loading of images for the given image tasks.\n+/** Performs loading of images for the given tasks.\n */\n public protocol ImageLoading: class {\n-    /** Managers that controls image loading.\n+    /** Manager that controls image loading.\n      */\n     weak var manager: ImageLoadingManager? { get set }\n     \n@@ -25,11 +25,11 @@ public protocol ImageLoading: class {\n      */\n     func cancelLoadingFor(task: ImageTask)\n     \n-    /** Compares requests for equivalence with regard to caching output image. This method is used for memory caching, in most cases there is no need for filtering out the dynamic part of the request (is there is any).\n+    /** Compares requests for equivalence with regard to caching output images. This method is used for memory caching, in most cases there is no need for filtering out the dynamic part of the request (is there is any).\n      */\n     func isCacheEquivalent(lhs: ImageRequest, to rhs: ImageRequest) -> Bool\n     \n-    /** Invalidates the receiver.\n+    /** Invalidates the receiver. This method gets called by the manager when it is invalidated.\n      */\n     func invalidate()\n     \n@@ -43,11 +43,11 @@ public protocol ImageLoading: class {\n /** Manages image loading.\n */\n public protocol ImageLoadingManager: class {\n-    /** Sent periodically to notify delegate of task progress.\n+    /** Sent periodically to notify the manager of the task progress.\n      */\n     func loader(loader: ImageLoading, task: ImageTask, didUpdateProgress progress: ImageTaskProgress)\n     \n-    /** Send when loading for task is completed.\n+    /** Sent when loading for the task is completed.\n      */\n     func loader(loader: ImageLoading, task: ImageTask, didCompleteWithImage image: Image?, error: ErrorType?, userInfo: Any?)\n }\n@@ -66,7 +66,10 @@ public struct ImageLoaderConfiguration {\n      */\n     public var processingQueue = NSOperationQueue(maxConcurrentOperationCount: 2)\n     \n-    /** Determines whether image loader should prevent excessive creation and resuming of session tasks, limiting the pressure on the system. Default value is true.\n+    /**\n+     Determines whether the image loader should prevent excessive creation and resuming of session tasks, limiting the pressure on the system. Default value is true.\n+     \n+     See CongestionController for a more detailed discussion.\n      */\n     public var congestionControlEnabled = true\n     \n@@ -87,23 +90,23 @@ public struct ImageLoaderConfiguration {\n /** Image loader customization endpoints.\n */\n public protocol ImageLoaderDelegate {\n-    /** Compares requests for equivalence with regard to loading image data. Requests should be considered equivalent if data loader can handle both requests with a single session task.\n+    /** Compares requests for equivalence with regard to loading image data. Requests should be considered equivalent if the loader can handle both requests with a single session task.\n      */\n     func loader(loader: ImageLoader, isLoadEquivalent lhs: ImageRequest, to rhs: ImageRequest) -> Bool\n     \n-    /** Compares requests for equivalence with regard to caching output image. This method is used for memory caching, in most cases there is no need for filtering out the dynamic part of the request (is there is any).\n+    /** Compares requests for equivalence with regard to caching output images. This method is used for memory caching, in most cases there is no need for filtering out the dynamic part of the request (is there is any).\n      */\n     func loader(loader: ImageLoader, isCacheEquivalent lhs: ImageRequest, to rhs: ImageRequest) -> Bool\n     \n     /** Returns processor for the given request and image.\n-     *     */\n+     */\n     func loader(loader: ImageLoader, processorFor: ImageRequest, image: Image) -> ImageProcessing?\n }\n \n /** Default implementation of ImageLoaderDelegate.\n  */\n public extension ImageLoaderDelegate {\n-    /** Returns processor for the given request by constructing decompressor based on request parameters and composing it with a processor from the request.\n+    /** Constructs image decompressor based on the request\'s target size and content mode (if decompression is allowed). Combined the decompressor with the processor provided in the request.\n      */\n     public func processorFor(request: ImageRequest, image: Image) -> ImageProcessing? {\n         var processors = [ImageProcessing]()\n@@ -125,9 +128,10 @@ public extension ImageLoaderDelegate {\n     }\n }\n \n-/** Default implementation of ImageLoaderDelegate.\n+/**\n+ Default implementation of ImageLoaderDelegate.\n  \n- The default implementation is provided in a class which allows methods to be overriden properly.\n+ The default implementation is provided in a class which allows methods to be overridden.\n  */\n public class ImageLoaderDefaultDelegate: ImageLoaderDelegate {\n     public init() {}\n@@ -143,7 +147,7 @@ public class ImageLoaderDefaultDelegate: ImageLoaderDelegate {\n             a.allowsCellularAccess == b.allowsCellularAccess\n     }\n     \n-    /** Compares request `URLs`, decompression parameters (`shouldDecompressImage`, `targetSize` and `contentMode`) and processors.\n+    /** Compares request `URL`s, decompression parameters (`shouldDecompressImage`, `targetSize` and `contentMode`), and processors.\n      */\n     public func loader(loader: ImageLoader, isCacheEquivalent lhs: ImageRequest, to rhs: ImageRequest) -> Bool {\n         guard lhs.URLRequest.URL == rhs.URLRequest.URL else {\n@@ -163,7 +167,7 @@ public class ImageLoaderDefaultDelegate: ImageLoaderDelegate {\n         }\n     }\n     \n-    /** Returns processor with combined image decompressor constructed based on request\'s target size and content mode, and image processor provided in image request.\n+    /** Constructs image decompressor based on the request\'s target size and content mode (if decompression is allowed). Combined the decompressor with the processor provided in the request.\n      */\n     public func loader(loader: ImageLoader, processorFor request: ImageRequest, image: Image) -> ImageProcessing? {\n         return self.processorFor(request, image: image)\n@@ -173,7 +177,9 @@ public class ImageLoaderDefaultDelegate: ImageLoaderDelegate {\n // MARK: - ImageLoader\n \n /**\n-Performs loading of images for the image tasks using objects conforming to `ImageDataLoading`, `ImageDecoding` and `ImageProcessing` protocols. Works in conjunction with the `ImageManager`.\n+Performs loading of images for the image tasks.\n+\n+This class uses multiple dependencies provided in its configuration. Image data is loaded using an object conforming to `ImageDataLoading` protocol. Image data is decoded via `ImageDecoding` protocol. Decoded images are processed by objects conforming to `ImageProcessing` protocols.\n \n - Provides a transparent loading, decoding and processing with a single completion signal\n - Reuses data tasks for equivalent image tasks\n@@ -204,18 +210,22 @@ public class ImageLoader: ImageLoading, CongestionControllerDelegate {\n     }\n     \n     public func resumeLoadingFor(task: ImageTask) {\n+        // Image loader performs all tasks asynchronously on its serial queue.\n         dispatch_async(self.queue) {\n             self.configuration.congestionControlEnabled ? self.controller.resume(task) : self._resumeLoadingFor(task)\n         }\n     }\n     \n     private func _resumeLoadingFor(task: ImageTask) {\n+        // ImageDataTasks wraps NSURLSessionTask\n+        // ImageLoader reuses ImageDataTasks for equivalent requests\n         let key = ImageRequestKey(task.request, owner: self)\n         var dataTask: ImageDataTask! = self.dataTasks[key]\n         if dataTask == nil {\n             dataTask = self.dataTaskWith(task.request, key: key)\n             self.dataTasks[key] = dataTask\n         } else {\n+            // Subscribing to the existing task, let the manager know about its progress\n             self.manager?.loader(self, task: task, didUpdateProgress: dataTask.progress)\n         }\n         self.loadStates[task] = ImageLoadState.Loading(dataTask)\n@@ -288,7 +298,9 @@ public class ImageLoader: ImageLoading, CongestionControllerDelegate {\n             self.configuration.congestionControlEnabled ? self.controller.suspend(task) : self._suspendLoadingFor(task)\n         }\n     }\n-    \n+\n+    /** Underlying data task is suspended when there are no executing tasks registered with it.\n+     */\n     private func _suspendLoadingFor(task: ImageTask) {\n         if let state = self.loadStates[task] {\n             switch state {\n@@ -298,7 +310,7 @@ public class ImageLoader: ImageLoading, CongestionControllerDelegate {\n             }\n         }\n     }\n-    \n+\n     public func cancelLoadingFor(task: ImageTask) {\n         dispatch_async(self.queue) {\n             self.configuration.congestionControlEnabled ? self.controller.cancel(task) : self._cancelLoadingFor(task)\n@@ -309,6 +321,7 @@ public class ImageLoader: ImageLoading, CongestionControllerDelegate {\n         if let state = self.loadStates[task] {\n             switch state {\n             case .Loading(let sessionTask):\n+                // Underlying data task is cancelled when there are no outstanding tasks (executing or suspended) registered with it.\n                 if sessionTask.cancel(task) {\n                     self.remove(sessionTask)\n                 }\n@@ -410,13 +423,12 @@ private protocol CongestionControllerDelegate: class {\n     func _cancelLoadingFor(task: ImageTask)\n }\n \n-/** The CongestionController serves multiple purposes:\n+/**\n+ The CongestionController serves multiple purposes:\n  - Prevents NSURLSession trashing\n  - Prevents excessive resuming and subsequent cancellation of tasks during the fast scrolling\n  \n  All it actually does is delay the `resume(task)` calls on ImageLoader. This is an alternative of having a limited number of concurrent NSURLSessionTasks, which we want to avoid in order to get the best on-disk caching and loading performance. The problem with limiting the number of concurrent tasks instead of using `HTTPMaximumConnectionsPerHost` is that when the queue is filled with networking requests, there is no way to retrieve cached responses from disk cache, and we want disk cache to always be accessible.\n- \n- This approach was borrowed from DFImageManager, and it was battle tested and proven really useful.\n  */\n private class CongestionController {\n     weak var delegate: CongestionControllerDelegate?\n@@ -456,6 +468,7 @@ private class CongestionController {\n     func setNeedsExecutePendingTasks() {\n         if !self.executing {\n             self.executing = true\n+            // Start executing one image task each `self.taskExecutionInterval` nsecs.\n             dispatch_after(dispatch_time(DISPATCH_TIME_NOW, self.taskExecutionInterval), self.queue) { [weak self] in\n                 self?.resumePendingTask()\n             }\ndiff --git a/Nuke/Source/Core/ImageManager.swift b/Nuke/Source/Core/ImageManager.swift\nindex 6ce71bca..e2b47686 100644\n--- a/Nuke/Source/Core/ImageManager.swift\n+++ b/Nuke/Source/Core/ImageManager.swift\n@@ -130,6 +130,7 @@ public class ImageManager {\n             switch task.request.memoryCachePolicy {\n             case .ReturnCachedImageElseLoad:\n                 if let response = self.responseForRequest(task.request) {\n+                    // FIXME: Should ImageResponse contain a `fastResponse` property?\n                     task.response = ImageResponse.Success(response.image, ImageResponseInfo(fastResponse: true, userInfo: response.userInfo))\n                     self.setState(.Completed, forTask: task)\n                     return\n@@ -182,7 +183,7 @@ public class ImageManager {\n         self.perform {\n             self.cancelTasks(requests.flatMap {\n                 return self.preheatingTasks[ImageRequestKey($0, owner: self)]\n-                })\n+            })\n         }\n     }\n     \n@@ -206,6 +207,7 @@ public class ImageManager {\n     private func executePreheatingTasksIfNeeded() {\n         self.needsToExecutePreheatingTasks = false\n         var executingTaskCount = self.executingTasks.count\n+        // FIXME: Use sorted dictionary\n         for task in (self.preheatingTasks.values.sort { $0.identifier < $1.identifier }) {\n             if executingTaskCount > self.configuration.maxConcurrentPreheatingTaskCount {\n                 break\ndiff --git a/Nuke/Source/Core/ImageMemoryCache.swift b/Nuke/Source/Core/ImageMemoryCache.swift\nindex fbe40aeb..d6e5024f 100644\n--- a/Nuke/Source/Core/ImageMemoryCache.swift\n+++ b/Nuke/Source/Core/ImageMemoryCache.swift\n@@ -122,8 +122,7 @@ public class ImageMemoryCache: ImageMemoryCaching {\n             return 1\n         #else\n             let imageRef = image.CGImage\n-            let bits = CGImageGetWidth(imageRef) * CGImageGetHeight(imageRef) * CGImageGetBitsPerPixel(imageRef)\n-            return bits / 8\n+            return CGImageGetBytesPerRow(imageRef) * CGImageGetHeight(imageRef)\n         #endif\n     }\n     \ndiff --git a/Nuke/Source/Core/ImageProcessor.swift b/Nuke/Source/Core/ImageProcessor.swift\nindex 569fd75a..175d6802 100644\n--- a/Nuke/Source/Core/ImageProcessor.swift\n+++ b/Nuke/Source/Core/ImageProcessor.swift\n@@ -87,6 +87,37 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n     return true\n }\n \n+\n+/** The `ImageProcessorWithClosure` is used for creating anonymous image filters.\n+ */\n+public class ImageProcessorWithClosure: ImageProcessing, Equatable {\n+    /** The identifier of the filter. Filters with equivalent closures should have the same identifiers.\n+     */\n+    public let identifier: String\n+\n+    /** A closure that performs image processing.\n+     */\n+    public let closure: Image -> Image?\n+\n+    /**\n+     Initializes the `ImageProcessorWithClosure` with the given identifier and closure.\n+\n+     - parameter identifier: The identifier of the filter. Filters with equivalent closures should have the same identifiers.\n+     */\n+    public init(identifier: String, closure: Image -> Image?) {\n+        self.identifier = identifier\n+        self.closure = closure\n+    }\n+\n+    public func process(image: Image) -> Image? {\n+        return self.closure(image)\n+    }\n+}\n+\n+public func ==(lhs: ImageProcessorWithClosure, rhs: ImageProcessorWithClosure) -> Bool {\n+    return lhs.identifier == rhs.identifier\n+}\n+\n #if !os(OSX)\n \n     // MARK: - ImageDecompressor\n@@ -139,11 +170,20 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n     }\n \n     private func decompress(image: UIImage, scale: Double) -> UIImage {\n-        let imageRef = image.CGImage\n+        guard let imageRef = image.CGImage else {\n+            return image\n+        }\n         let minification = CGFloat(min(scale, 1))\n         let size = CGSize(width: round(minification * CGFloat(CGImageGetWidth(imageRef))), height: round(minification * CGFloat(CGImageGetHeight(imageRef))))\n-        // See Quartz 2D Programming Guide and https://github.com/kean/Nuke/issues/35 for more info\n-        guard let contextRef = CGBitmapContextCreate(nil, Int(size.width), Int(size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), CGImageAlphaInfo.PremultipliedLast.rawValue) else {\n+\n+        // For more info see:\n+        // - Quartz 2D Programming Guide\n+        // - https://github.com/kean/Nuke/issues/35\n+        // - https://github.com/kean/Nuke/issues/57\n+        let alphaInfo = isOpaque(imageRef) ? CGImageAlphaInfo.NoneSkipLast : CGImageAlphaInfo.PremultipliedLast\n+        let bitmapInfo = CGBitmapInfo(rawValue: alphaInfo.rawValue)\n+\n+        guard let contextRef = CGBitmapContextCreate(nil, Int(size.width), Int(size.height), 8, 0, CGColorSpaceCreateDeviceRGB(), bitmapInfo.rawValue) else {\n             return image\n         }\n         CGContextDrawImage(contextRef, CGRect(origin: CGPointZero, size: size), imageRef)\n@@ -152,6 +192,13 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n         }\n         return UIImage(CGImage: decompressedImageRef, scale: image.scale, orientation: image.imageOrientation)\n     }\n+\n+    private func isOpaque(image: CGImageRef) -> Bool {\n+        let alpha = CGImageGetAlphaInfo(image)\n+        return alpha == .None ||\n+            alpha == .NoneSkipFirst ||\n+            alpha == .NoneSkipLast\n+    }\n #endif\n \n \n@@ -159,7 +206,7 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n \n // MARK: - CoreImage\n     \n-    let sharedCIContext = CIContext(options: [kCIContextPriorityRequestLow: true])\n+    private let sharedContext = CIContext(options: [kCIContextPriorityRequestLow: true])\n     \n     /** Core Image helper methods.\n      */\n@@ -172,7 +219,7 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n          - parameter context: Core Image context, uses shared context by default.\n          - parameter filter: Closure for applying image filter.\n          */\n-        public func nk_filter(context context: CIContext = sharedCIContext, closure: CoreImage.CIImage -> CoreImage.CIImage?) -> UIImage? {\n+        public func nk_filter(context context: CIContext = sharedContext, closure: CoreImage.CIImage -> CoreImage.CIImage?) -> UIImage? {\n             func inputImageForImage(image: Image) -> CoreImage.CIImage? {\n                 if let image = image.CGImage {\n                     return CoreImage.CIImage(CGImage: image)\n@@ -195,7 +242,7 @@ public func ==(lhs: ImageProcessorComposition, rhs: ImageProcessorComposition) -\n          - parameter context: Core Image context, uses shared context by default.\n          - parameter filter: Image filter. Function automatically sets input image on the filter.\n          */\n-        public func nk_filter(filter: CIFilter?, context: CIContext = sharedCIContext) -> UIImage? {\n+        public func nk_filter(filter: CIFilter?, context: CIContext = sharedContext) -> UIImage? {\n             guard let filter = filter else {\n                 return nil\n             }\ndiff --git a/README.md b/README.md\nindex 5d165166..db0b8db5 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/10440878/a7c6e468-714b-11e5-9b12-baef482c37c1.png" height="100"/>\n+<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/13362579/7001aa94-dcd6-11e5-8c0d-5a4920008d04.png" height="150"/>\n \n <p align="left">\n <a href="https://cocoapods.org"><img src="https://img.shields.io/cocoapods/v/Nuke.svg"></a>\n@@ -6,7 +6,7 @@\n <a href="http://cocoadocs.org/docsets/Nuke"><img src="https://img.shields.io/cocoapods/p/Nuke.svg?style=flat)"></a>\n </p>\n \n-Loading, processing, caching and [preheating](http://outscope.net/blog/image-preheating) images.\n+Loading, processing, caching and [**preheating**](http://outscope.net/blog/image-preheating) images.\n \n To get started check out http://kean.github.io/Nuke/ !\n \n@@ -33,7 +33,7 @@ Nuke.taskWith(request) { response in\n - Uses [NSURLSession](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/) with [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) support\n - A single data task is used for multiple equivalent requests\n - [Automated preheating](http://outscope.net/blog/image-preheating) of images close to the viewport\n-- Congestion control that prevents trashing the system with requests during fast scrolling\n+- Prevents trashing the system with requests during fast scrolling\n - Full featured extensions for UI components\n \n ##### Caching\n', 'commit_subject': "Merge branch 'develop'", 'repo_name': 'kean/Nuke', 'committer': {'name': 'kean', 'email': '7d79009c44fb8cc76c73cdb194cbb1e3b38c7a62@gmail.com', 'time_sec': '1456515850', 'tz_offset': '180', 'date': {'seconds': '1456515850'}}, 'commit_tree': 'b3e8398ebba964a2c55786e37eb30f6dd82243dc', 'commit_message': "Merge branch 'develop'\n", 'language_name': 'Swift', 'license': 'mit', 'hunks': '@@ -1,4 +1,4 @@\n-<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/10440878/a7c6e468-714b-11e5-9b12-baef482c37c1.png" height="100"/>\n+<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/13362579/7001aa94-dcd6-11e5-8c0d-5a4920008d04.png" height="150"/>\n \n <p align="left">\n <a href="https://cocoapods.org"><img src="https://img.shields.io/cocoapods/v/Nuke.svg"></a>\n@@ -6,7 +6,7 @@\n <a href="http://cocoadocs.org/docsets/Nuke"><img src="https://img.shields.io/cocoapods/p/Nuke.svg?style=flat)"></a>\n </p>\n \n-Loading, processing, caching and [preheating](http://outscope.net/blog/image-preheating) images.\n+Loading, processing, caching and [**preheating**](http://outscope.net/blog/image-preheating) images.\n \n To get started check out http://kean.github.io/Nuke/ !\n \n@@ -33,7 +33,7 @@ Nuke.taskWith(request) { response in\n - Uses [NSURLSession](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/) with [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) support\n - A single data task is used for multiple equivalent requests\n - [Automated preheating](http://outscope.net/blog/image-preheating) of images close to the viewport\n-- Congestion control that prevents trashing the system with requests during fast scrolling\n+- Prevents trashing the system with requests during fast scrolling\n - Full featured extensions for UI components\n \n ##### Caching\n', 'addition_count': 3, 'deletion_count': 3, 'src_file': 'a/README.md', 'tgt_file': 'b/README.md', 'file_extension': '.md', 'before_file': '<p align="left"><img src="https://cloud.githubusercontent.com/assets/1567433/10440878/a7c6e468-714b-11e5-9b12-baef482c37c1.png" height="100"/>\n\n<p align="left">\n<a href="https://cocoapods.org"><img src="https://img.shields.io/cocoapods/v/Nuke.svg"></a>\n# Image Loading System\n<a href="http://cocoadocs.org/docsets/Nuke"><img src="https://img.shields.io/cocoapods/p/Nuke.svg?style=flat)"></a>\n</p>\n\nLoading, processing, caching and [preheating](http://outscope.net/blog/image-preheating) images.\n\nTo get started check out http://kean.github.io/Nuke/ !\n\n\nThe framework is lean and compiles in under 2 seconds[¹](#footnote-1). Nuke has an automated test suite 2x the size of the codebase itself, ensuring excellent reliability. Every feature is carefully designed and optimized for [performance](https://kean.blog/post/nuke-9).\n\n> **Fast LRU memory and disk cache** · **SwiftUI** · **Smart background decompression** · **Image processing** · **Resumable downloads** · **Intelligent deduplication** · **Request prioritization** · **Prefetching** · **Rate limiting** · **Progressive JPEG, HEIF, WebP, SVG, GIF** · **Alamofire** · **Combine** · **Async/Await**\n\n## Sponsors\n\n<a target="_blank" rel="noopener noreferrer" href="https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22#gh-light-mode-only"><img src="https://user-images.githubusercontent.com/1567433/175186173-64eb53cb-b5d6-4ed4-aaca-87dbbb0834ab.png#gh-light-mode-only" width="300px" alt="Stream Logo" style="max-width: 100%;"></a>\n<a target="_blank" rel="noopener noreferrer" href="https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22#gh-dark-mode-only"><img src="https://user-images.githubusercontent.com/1567433/175562043-0ab82adc-e3c7-4c0b-8813-a7940ff41db8.png#gh-dark-mode-only" width="244px" alt="Stream Logo" style="max-width: 100%;"></a>\n\nNuke is proudly sponsored by [Stream](https://getstream.io/chat/sdk/swiftui/?utm_source=Nuke&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Nuke_July2022_SwiftUIChat_klmh22), the leading provider in enterprise grade Feed & Chat APIs.\n\n> [Support](https://github.com/sponsors/kean) Nuke on GitHub Sponsors.\n\n## Documentation\n\nNuke is easy to learn and use thanks to documentation generated using DocC: [**Nuke**](https://kean-docs.github.io/nuke/documentation/nuke/getting-started/), [**NukeUI**](https://kean-docs.github.io/nukeui/documentation/nukeui/), [**NukeExtensions**](https://kean-docs.github.io/nukeextensions/documentation/nukeextensions/). Make sure to also check out [**Nuke Demo**](https://github.com/kean/NukeDemo).\n\n> Upgrading from the previous version? Use a [**Migration Guide**](https://github.com/kean/Nuke/tree/master/Documentation/Migrations).\n\n- Uses [NSURLSession](https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSURLSession_class/) with [HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) support\n- A single data task is used for multiple equivalent requests\n- [Automated preheating](http://outscope.net/blog/image-preheating) of images close to the viewport\n- Congestion control that prevents trashing the system with requests during fast scrolling\n- Full featured extensions for UI components\n\n##### Caching\n\n## Extensions\n\nThe image pipeline is easy to customize and extend. Check out the following first-class extensions and packages built by the community.\n\n|Name|Description|\n|--|--|\n|[**Alamofire Plugin**](https://github.com/kean/Nuke-Alamofire-Plugin)|Replace networking layer with [Alamofire](https://github.com/Alamofire/Alamofire)|\n|[**NukeWebP**](https://github.com/makleso6/NukeWebP)| **Community**. [WebP](https://developers.google.com/speed/webp/) support, built by [Maxim Kolesnik](https://github.com/makleso6)|\n|[**WebP Plugin**](https://github.com/ryokosuge/Nuke-WebP-Plugin)| **Community**. [WebP](https://developers.google.com/speed/webp/) support, built by [Ryo Kosuge](https://github.com/ryokosuge)|\n|[**AVIF Plugin**](https://github.com/delneg/Nuke-AVIF-Plugin)| **Community**. [AVIF](https://caniuse.com/avif) support, built by [Denis](https://github.com/delneg)|\n|[**Gifu Plugin**](https://github.com/kean/Nuke-Gifu-Plugin)|Use [Gifu](https://github.com/kaishin/Gifu) to load and display animated GIFs|\n|[**RxNuke**](https://github.com/kean/RxNuke)|[RxSwift](https://github.com/ReactiveX/RxSwift) extensions for Nuke with examples|\n\n## Minimum Requirements\n\n| Nuke       | Date         | Swift       | Xcode      | Platforms                                     |\n|------------|--------------|-------------|------------|-----------------------------------------------|\n| Nuke 11.0  | Jul 20, 2022 | Swift 5.6   | Xcode 13.3 | iOS 13.0, watchOS 6.0, macOS 10.15, tvOS 13.0 |\n| Nuke 10.0  | Jun 1, 2021  | Swift 5.3   | Xcode 12.0 | iOS 11.0, watchOS 4.0, macOS 10.13, tvOS 11.0 |\n\n## License\n\nNuke is available under the MIT license. See the LICENSE file for more info.\n\n----\n\n> <a name="footnote-1">¹</a> Measured on MacBook Pro 14" 2021 (10-core M1 Pro)\n', 'message': "Merge branch 'develop'\n", 'author_name': 'kean', 'lang': 'md'}