Browse Source

[FIX] GIFHeavy crash

Taras Nikulin 4 years ago
parent
commit
26c715dfbc

+ 47 - 7
Demo/Demo/Kingfisher-Demo/Base.lproj/Main.storyboard

@@ -299,9 +299,33 @@
                                             <segue destination="UXA-j3-Wgu" kind="show" id="7cS-7z-3m8"/>
                                             <segue destination="UXA-j3-Wgu" kind="show" id="7cS-7z-3m8"/>
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
-                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="xxW-va-fvw">
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="24U-er-jWn">
                                         <rect key="frame" x="0.0" y="336" width="414" height="44"/>
                                         <rect key="frame" x="0.0" y="336" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="24U-er-jWn" id="6Tk-ch-QY4">
+                                            <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="GIF Heavy" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6fi-xZ-lfj">
+                                                    <rect key="frame" x="20" y="11.666666666666664" width="78" height="21"/>
+                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                                                    <nil key="textColor"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstItem="6fi-xZ-lfj" firstAttribute="leading" secondItem="6Tk-ch-QY4" secondAttribute="leading" constant="20" symbolic="YES" id="W1l-7A-hsC"/>
+                                                <constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="6fi-xZ-lfj" secondAttribute="trailing" constant="20" symbolic="YES" id="lIC-bI-T9j"/>
+                                                <constraint firstItem="6fi-xZ-lfj" firstAttribute="centerY" secondItem="6Tk-ch-QY4" secondAttribute="centerY" id="wzN-WH-KFB"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                        <connections>
+                                            <segue destination="nf1-Ij-kCs" kind="show" id="pIB-zv-l3J"/>
+                                        </connections>
+                                    </tableViewCell>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="xxW-va-fvw">
+                                        <rect key="frame" x="0.0" y="380" width="414" height="44"/>
+                                        <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xxW-va-fvw" id="JDa-HW-wqO">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="xxW-va-fvw" id="JDa-HW-wqO">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <autoresizingMask key="autoresizingMask"/>
                                             <autoresizingMask key="autoresizingMask"/>
@@ -324,7 +348,7 @@
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3Fa-Qk-aCx">
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="3Fa-Qk-aCx">
-                                        <rect key="frame" x="0.0" y="380" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="424" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3Fa-Qk-aCx" id="mHo-fO-aOl">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="3Fa-Qk-aCx" id="mHo-fO-aOl">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@@ -348,7 +372,7 @@
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="cln-Yy-v33">
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="cln-Yy-v33">
-                                        <rect key="frame" x="0.0" y="424" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="468" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="cln-Yy-v33" id="uVh-9Y-8Dr">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="cln-Yy-v33" id="uVh-9Y-8Dr">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@@ -372,7 +396,7 @@
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="KH8-JQ-Aa3">
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="KH8-JQ-Aa3">
-                                        <rect key="frame" x="0.0" y="468" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="512" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KH8-JQ-Aa3" id="pOx-M5-JGf">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KH8-JQ-Aa3" id="pOx-M5-JGf">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@@ -396,7 +420,7 @@
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="2c2-O7-4OG">
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="2c2-O7-4OG">
-                                        <rect key="frame" x="0.0" y="512" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="556" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2c2-O7-4OG" id="Mjl-hg-ebT">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="2c2-O7-4OG" id="Mjl-hg-ebT">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@@ -420,7 +444,7 @@
                                         </connections>
                                         </connections>
                                     </tableViewCell>
                                     </tableViewCell>
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="TIF-8x-GLM">
                                     <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="TIF-8x-GLM">
-                                        <rect key="frame" x="0.0" y="556" width="414" height="44"/>
+                                        <rect key="frame" x="0.0" y="600" width="414" height="44"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <autoresizingMask key="autoresizingMask"/>
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIF-8x-GLM" id="ykx-Ds-PkP">
                                         <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TIF-8x-GLM" id="ykx-Ds-PkP">
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
                                             <rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
@@ -697,7 +721,23 @@
                 </viewController>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="hAo-Ts-6oW" userLabel="First Responder" sceneMemberID="firstResponder"/>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="hAo-Ts-6oW" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
             </objects>
-            <point key="canvasLocation" x="4172" y="679"/>
+            <point key="canvasLocation" x="4206" y="646"/>
+        </scene>
+        <!--Heavy View Controller-->
+        <scene sceneID="dgP-4L-Cr8">
+            <objects>
+                <viewController id="nf1-Ij-kCs" customClass="GIFHeavyViewController" customModule="Kingfisher_Demo" customModuleProvider="target" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="oSl-Bn-mXh">
+                        <rect key="frame" x="0.0" y="0.0" width="414" height="736"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <viewLayoutGuide key="safeArea" id="Mz6-v4-I52"/>
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                    </view>
+                    <navigationItem key="navigationItem" id="vKv-4p-kQh"/>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="596-19-5UM" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="5070" y="679"/>
         </scene>
         </scene>
         <!--Detail Image View Controller-->
         <!--Detail Image View Controller-->
         <scene sceneID="wpT-qC-cq3">
         <scene sceneID="wpT-qC-cq3">

+ 80 - 0
Demo/Demo/Kingfisher-Demo/ViewControllers/GIFHeavyViewController.swift

@@ -0,0 +1,80 @@
+//
+//  GIFHeavyViewController.swift
+//  Kingfisher
+//
+//  Created by taras on 16/04/2021.
+//
+//  Copyright (c) 2021 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import UIKit
+import Kingfisher
+
+class GIFHeavyViewController: UIViewController {
+    let stackView = UIStackView()
+    let imageView_1 = AnimatedImageView()
+    let imageView_2 = AnimatedImageView()
+    let imageView_3 = AnimatedImageView()
+    let imageView_4 = AnimatedImageView()
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        stackView.translatesAutoresizingMaskIntoConstraints = false
+        
+        view.addSubview(stackView)
+        
+        if #available(iOS 11.0, *) {
+            NSLayoutConstraint.activate([
+                stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
+                stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
+                stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
+                stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
+            ])
+        } else {
+            NSLayoutConstraint.activate([
+                stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+                stackView.topAnchor.constraint(equalTo: view.topAnchor),
+                stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+                stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
+            ])
+        }
+        
+        stackView.axis = .vertical
+        stackView.distribution = .fillEqually
+        
+        stackView.addArrangedSubview(imageView_1)
+        stackView.addArrangedSubview(imageView_2)
+        stackView.addArrangedSubview(imageView_3)
+        stackView.addArrangedSubview(imageView_4)
+        
+        imageView_1.contentMode = .scaleAspectFit
+        imageView_2.contentMode = .scaleAspectFit
+        imageView_3.contentMode = .scaleAspectFit
+        imageView_4.contentMode = .scaleAspectFit
+        
+        let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/GIF/GifHeavy.gif")
+
+        imageView_1.kf.setImage(with: url)
+        imageView_2.kf.setImage(with: url)
+        imageView_3.kf.setImage(with: url)
+        imageView_4.kf.setImage(with: url)
+    }
+}

+ 4 - 0
Demo/Kingfisher-Demo.xcodeproj/project.pbxproj

@@ -30,6 +30,7 @@
 		4BE855592320F9D300FE4205 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855572320F9D300FE4205 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		4BE855592320F9D300FE4205 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855572320F9D300FE4205 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		4BE855612320F9DE00FE4205 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855602320F9DE00FE4205 /* Kingfisher.framework */; };
 		4BE855612320F9DE00FE4205 /* Kingfisher.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855602320F9DE00FE4205 /* Kingfisher.framework */; };
 		4BE855622320F9DE00FE4205 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855602320F9DE00FE4205 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
 		4BE855622320F9DE00FE4205 /* Kingfisher.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4BE855602320F9DE00FE4205 /* Kingfisher.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		769F07F126298E2E00610767 /* GIFHeavyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 769F07F026298E2E00610767 /* GIFHeavyViewController.swift */; };
 		C959EEE622874DC600467A10 /* ProgressiveJPEGViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */; };
 		C959EEE622874DC600467A10 /* ProgressiveJPEGViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */; };
 		D10AC99821A300C9005F057C /* ProcessorCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */; };
 		D10AC99821A300C9005F057C /* ProcessorCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */; };
 		D12E0C951C47F91800AC98AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */; };
 		D12E0C951C47F91800AC98AD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */; };
@@ -168,6 +169,7 @@
 		4BE8555A2320F9D800FE4205 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4BE8555A2320F9D800FE4205 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4BE8555B2320F9D800FE4205 /* KingfisherSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KingfisherSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4BE8555B2320F9D800FE4205 /* KingfisherSwiftUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = KingfisherSwiftUI.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4BE855602320F9DE00FE4205 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		4BE855602320F9DE00FE4205 /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		769F07F026298E2E00610767 /* GIFHeavyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFHeavyViewController.swift; sourceTree = "<group>"; };
 		C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressiveJPEGViewController.swift; sourceTree = "<group>"; };
 		C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressiveJPEGViewController.swift; sourceTree = "<group>"; };
 		D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessorCollectionViewController.swift; sourceTree = "<group>"; };
 		D10AC99721A300C9005F057C /* ProcessorCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessorCollectionViewController.swift; sourceTree = "<group>"; };
 		D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
 		D12E0C8C1C47F91800AC98AD /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
@@ -361,6 +363,7 @@
 				D1FAB06E21A853E600908910 /* HighResolutionCollectionViewController.swift */,
 				D1FAB06E21A853E600908910 /* HighResolutionCollectionViewController.swift */,
 				D1F06F3221AA4292000B1C38 /* DetailImageViewController.swift */,
 				D1F06F3221AA4292000B1C38 /* DetailImageViewController.swift */,
 				D1F06F3621AAEACF000B1C38 /* GIFViewController.swift */,
 				D1F06F3621AAEACF000B1C38 /* GIFViewController.swift */,
+				769F07F026298E2E00610767 /* GIFHeavyViewController.swift */,
 				D1F06F3821AAF1EE000B1C38 /* IndicatorCollectionViewController.swift */,
 				D1F06F3821AAF1EE000B1C38 /* IndicatorCollectionViewController.swift */,
 				D1E4CF5321BACBA6004D029D /* ImageDataProviderCollectionViewController.swift */,
 				D1E4CF5321BACBA6004D029D /* ImageDataProviderCollectionViewController.swift */,
 				C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */,
 				C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */,
@@ -680,6 +683,7 @@
 				4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */,
 				4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */,
 				D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */,
 				D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */,
 				D1F06F3721AAEACF000B1C38 /* GIFViewController.swift in Sources */,
 				D1F06F3721AAEACF000B1C38 /* GIFViewController.swift in Sources */,
+				769F07F126298E2E00610767 /* GIFHeavyViewController.swift in Sources */,
 			);
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			runOnlyForDeploymentPostprocessing = 0;
 		};
 		};

+ 4 - 0
Kingfisher.xcodeproj/project.pbxproj

@@ -22,6 +22,7 @@
 		4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD821662189FD330084CC21 /* SessionDataTask.swift */; };
 		4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD821662189FD330084CC21 /* SessionDataTask.swift */; };
 		4BE688F722FD513100B11168 /* NSButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6AD215D2BB50013BA68 /* NSButton+Kingfisher.swift */; };
 		4BE688F722FD513100B11168 /* NSButton+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6AD215D2BB50013BA68 /* NSButton+Kingfisher.swift */; };
 		4BE688F822FD513700B11168 /* WKInterfaceImage+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6AF215D2BB50013BA68 /* WKInterfaceImage+Kingfisher.swift */; };
 		4BE688F822FD513700B11168 /* WKInterfaceImage+Kingfisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = D12AB6AF215D2BB50013BA68 /* WKInterfaceImage+Kingfisher.swift */; };
+		76FB4FD2262D773E006D15F8 /* GraphicsContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76FB4FD1262D773E006D15F8 /* GraphicsContext.swift */; };
 		C9286407228584EB00257182 /* ImageProgressive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9286406228584EB00257182 /* ImageProgressive.swift */; };
 		C9286407228584EB00257182 /* ImageProgressive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9286406228584EB00257182 /* ImageProgressive.swift */; };
 		D1132C9725919F69003E528D /* KFOptionsSetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1132C9625919F69003E528D /* KFOptionsSetter.swift */; };
 		D1132C9725919F69003E528D /* KFOptionsSetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1132C9625919F69003E528D /* KFOptionsSetter.swift */; };
 		D11D9B72245FA6F700C5A0AE /* RetryStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11D9B71245FA6F700C5A0AE /* RetryStrategy.swift */; };
 		D11D9B72245FA6F700C5A0AE /* RetryStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = D11D9B71245FA6F700C5A0AE /* RetryStrategy.swift */; };
@@ -142,6 +143,7 @@
 		4BCFF7A9219932390055AAC4 /* DiskStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorageTests.swift; sourceTree = "<group>"; };
 		4BCFF7A9219932390055AAC4 /* DiskStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorageTests.swift; sourceTree = "<group>"; };
 		4BD821612189FC0C0084CC21 /* SessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDelegate.swift; sourceTree = "<group>"; };
 		4BD821612189FC0C0084CC21 /* SessionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDelegate.swift; sourceTree = "<group>"; };
 		4BD821662189FD330084CC21 /* SessionDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDataTask.swift; sourceTree = "<group>"; };
 		4BD821662189FD330084CC21 /* SessionDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDataTask.swift; sourceTree = "<group>"; };
+		76FB4FD1262D773E006D15F8 /* GraphicsContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphicsContext.swift; sourceTree = "<group>"; };
 		C9286406228584EB00257182 /* ImageProgressive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProgressive.swift; sourceTree = "<group>"; };
 		C9286406228584EB00257182 /* ImageProgressive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProgressive.swift; sourceTree = "<group>"; };
 		C959EEE7228940FE00467A10 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		C959EEE7228940FE00467A10 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
 		D1132C9625919F69003E528D /* KFOptionsSetter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFOptionsSetter.swift; sourceTree = "<group>"; };
 		D1132C9625919F69003E528D /* KFOptionsSetter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KFOptionsSetter.swift; sourceTree = "<group>"; };
@@ -353,6 +355,7 @@
 				D12AB6A6215D2BB50013BA68 /* Filter.swift */,
 				D12AB6A6215D2BB50013BA68 /* Filter.swift */,
 				D12AB6A7215D2BB50013BA68 /* Placeholder.swift */,
 				D12AB6A7215D2BB50013BA68 /* Placeholder.swift */,
 				D12AB6A8215D2BB50013BA68 /* GIFAnimatedImage.swift */,
 				D12AB6A8215D2BB50013BA68 /* GIFAnimatedImage.swift */,
+				76FB4FD1262D773E006D15F8 /* GraphicsContext.swift */,
 			);
 			);
 			path = Image;
 			path = Image;
 			sourceTree = "<group>";
 			sourceTree = "<group>";
@@ -818,6 +821,7 @@
 				D12AB6E0215D2BB50013BA68 /* Filter.swift in Sources */,
 				D12AB6E0215D2BB50013BA68 /* Filter.swift in Sources */,
 				4BE688F722FD513100B11168 /* NSButton+Kingfisher.swift in Sources */,
 				4BE688F722FD513100B11168 /* NSButton+Kingfisher.swift in Sources */,
 				D12AB6C4215D2BB50013BA68 /* Resource.swift in Sources */,
 				D12AB6C4215D2BB50013BA68 /* Resource.swift in Sources */,
+				76FB4FD2262D773E006D15F8 /* GraphicsContext.swift in Sources */,
 				D8FCF6A821C5A0E500F9ABC0 /* RedirectHandler.swift in Sources */,
 				D8FCF6A821C5A0E500F9ABC0 /* RedirectHandler.swift in Sources */,
 				D1A37BDE215D34E8009B39B7 /* ImageDrawing.swift in Sources */,
 				D1A37BDE215D34E8009B39B7 /* ImageDrawing.swift in Sources */,
 				4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */,
 				4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */,

+ 88 - 0
Sources/Image/GraphicsContext.swift

@@ -0,0 +1,88 @@
+//
+//  GraphicsContext.swift
+//  Kingfisher
+//
+//  Created by taras on 19/04/2021.
+//
+//  Copyright (c) 2021 Wei Wang <onevcat@gmail.com>
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
+import AppKit
+#endif
+#if canImport(UIKit)
+import UIKit
+#endif
+
+enum GraphicsContext {
+    static func begin(size: CGSize, scale: CGFloat) {
+        #if os(macOS)
+        NSGraphicsContext.saveGraphicsState()
+        #else
+        UIGraphicsBeginImageContextWithOptions(size, false, scale)
+        #endif
+    }
+    
+    static func current(size: CGSize, scale: CGFloat, inverting: Bool, cgImage: CGImage?) -> CGContext? {
+        #if os(macOS)
+        guard let rep = NSBitmapImageRep(
+            bitmapDataPlanes: nil,
+            pixelsWide: Int(size.width),
+            pixelsHigh: Int(size.height),
+            bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+            samplesPerPixel: 4,
+            hasAlpha: true,
+            isPlanar: false,
+            colorSpaceName: .calibratedRGB,
+            bytesPerRow: 0,
+            bitsPerPixel: 0) else
+        {
+            assertionFailure("[Kingfisher] Image representation cannot be created.")
+            return nil
+        }
+        rep.size = size
+        guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
+            assertionFailure("[Kingfisher] Image context cannot be created.")
+            return nil
+        }
+        
+        NSGraphicsContext.current = context
+        return context.cgContext
+        #else
+        guard let context = UIGraphicsGetCurrentContext() else {
+            return nil
+        }
+        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)
+        }
+        return context
+        #endif
+    }
+    
+    static func end() {
+        #if os(macOS)
+        NSGraphicsContext.restoreGraphicsState()
+        #else
+        UIGraphicsEndImageContext()
+        #endif
+    }
+}
+

+ 41 - 52
Sources/Image/ImageDrawing.swift

@@ -290,21 +290,22 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
             
             
             return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
             return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
         }
         }
-        
-        guard let context = beginContext(size: size, scale: scale, inverting: true) else {
+        GraphicsContext.begin(size: size, scale: scale)
+        guard let context = GraphicsContext.current(size: size, scale: scale, inverting: true, cgImage: cgImage) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
             return base
         }
         }
         context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
         context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
-        endContext()
+        GraphicsContext.end()
         
         
         var inBuffer = createEffectBuffer(context)
         var inBuffer = createEffectBuffer(context)
         
         
-        guard let outContext = beginContext(size: size, scale: scale, inverting: true) else {
+        GraphicsContext.begin(size: size, scale: scale)
+        guard let outContext = GraphicsContext.current(size: size, scale: scale, inverting: true, cgImage: cgImage) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
             return base
         }
         }
-        defer { endContext() }
+        defer { GraphicsContext.end() }
         var outBuffer = createEffectBuffer(outContext)
         var outBuffer = createEffectBuffer(outContext)
         
         
         for _ in 0 ..< iterations {
         for _ in 0 ..< iterations {
@@ -457,55 +458,42 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
             return true
             return true
         }
         }
     }
     }
-}
 
 
-extension KingfisherWrapper where Base: KFCrossPlatformImage {
-    
-    func beginContext(size: CGSize, scale: CGFloat, inverting: Bool = false) -> CGContext? {
-        #if os(macOS)
-        guard let rep = NSBitmapImageRep(
-            bitmapDataPlanes: nil,
-            pixelsWide: Int(size.width),
-            pixelsHigh: Int(size.height),
-            bitsPerSample: cgImage?.bitsPerComponent ?? 8,
-            samplesPerPixel: 4,
-            hasAlpha: true,
-            isPlanar: false,
-            colorSpaceName: .calibratedRGB,
-            bytesPerRow: 0,
-            bitsPerPixel: 0) else
-        {
-            assertionFailure("[Kingfisher] Image representation cannot be created.")
-            return nil
-        }
-        rep.size = size
-        NSGraphicsContext.saveGraphicsState()
-        guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
-            assertionFailure("[Kingfisher] Image context cannot be created.")
-            return nil
-        }
-        
-        NSGraphicsContext.current = context
-        return context.cgContext
-        #else
-        UIGraphicsBeginImageContextWithOptions(size, false, scale)
-        guard let context = UIGraphicsGetCurrentContext() else { return nil }
-        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)
-        }
-        return context
-        #endif
-    }
-    
-    func endContext() {
-        #if os(macOS)
-        NSGraphicsContext.restoreGraphicsState()
+    /// Returns decoded image of the `base` image at a given scale. It will draw the image in a plain context and
+    /// return the data from it. This could improve the drawing performance when an image is just created from
+    /// data but not yet displayed for the first time.
+    ///
+    /// - Parameter context: The context for frawing.
+    /// - Returns: The decoded image ready to be displayed.
+    ///
+    /// - Note: This method only works for CG-based image. The current image scale is kept.
+    ///         For any non-CG-based image or animated image, `base` itself is returned.
+    public func decoded(on context: CGContext) -> KFCrossPlatformImage {
+        // Prevent animated image (GIF) losing it's images
+        #if os(iOS)
+        if imageSource != nil { return base }
         #else
         #else
-        UIGraphicsEndImageContext()
+        if images != nil { return base }
         #endif
         #endif
+
+        guard let refImage = cgImage else {
+            assertionFailure("[Kingfisher] Decoding only works for CG-based image.")
+            return base
+        }
+
+        let size = CGSize(width: CGFloat(refImage.width) / scale, height: CGFloat(refImage.height) / scale)
+
+        context.draw(refImage, in: CGRect(origin: .zero, size: size))
+
+        guard let cgImage = context.makeImage() else {
+            return base
+        }
+
+        return KingfisherWrapper.image(cgImage: cgImage, scale: scale, refImage: base)
     }
     }
-    
+}
+
+extension KingfisherWrapper where Base: KFCrossPlatformImage {
     func draw(
     func draw(
         to size: CGSize,
         to size: CGSize,
         inverting: Bool = false,
         inverting: Bool = false,
@@ -515,11 +503,12 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
     ) -> KFCrossPlatformImage
     ) -> KFCrossPlatformImage
     {
     {
         let targetScale = scale ?? self.scale
         let targetScale = scale ?? self.scale
-        guard let context = beginContext(size: size, scale: targetScale, inverting: inverting) else {
+        GraphicsContext.begin(size: size, scale: targetScale)
+        guard let context = GraphicsContext.current(size: size, scale: targetScale, inverting: inverting, cgImage: cgImage) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
             return base
             return base
         }
         }
-        defer { endContext() }
+        defer { GraphicsContext.end() }
         let useRefImage = draw(context)
         let useRefImage = draw(context)
         guard let cgImage = context.makeImage() else {
         guard let cgImage = context.makeImage() else {
             return base
             return base

+ 39 - 14
Sources/Views/AnimatedImageView.swift

@@ -170,8 +170,7 @@ open class AnimatedImageView: UIImageView {
     // A display link that keeps calling the `updateFrame` method on every screen refresh.
     // A display link that keeps calling the `updateFrame` method on every screen refresh.
     private lazy var displayLink: CADisplayLink = {
     private lazy var displayLink: CADisplayLink = {
         isDisplayLinkInitialized = true
         isDisplayLinkInitialized = true
-        let displayLink = CADisplayLink(
-            target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate))
+        let displayLink = CADisplayLink(target: TargetProxy(target: self), selector: #selector(TargetProxy.onScreenUpdate))
         displayLink.add(to: .main, forMode: runLoopMode)
         displayLink.add(to: .main, forMode: runLoopMode)
         displayLink.isPaused = true
         displayLink.isPaused = true
         return displayLink
         return displayLink
@@ -258,12 +257,14 @@ open class AnimatedImageView: UIImageView {
     // Reset the animator.
     // Reset the animator.
     private func reset() {
     private func reset() {
         animator = nil
         animator = nil
-        if let imageSource = image?.kf.imageSource {
+        if let image = image, let imageSource = image.kf.imageSource {
             let targetSize = bounds.scaled(UIScreen.main.scale).size
             let targetSize = bounds.scaled(UIScreen.main.scale).size
             let animator = Animator(
             let animator = Animator(
                 imageSource: imageSource,
                 imageSource: imageSource,
                 contentMode: contentMode,
                 contentMode: contentMode,
                 size: targetSize,
                 size: targetSize,
+                imageSize: image.kf.size,
+                imageScale: image.kf.scale,
                 framePreloadCount: framePreloadCount,
                 framePreloadCount: framePreloadCount,
                 repeatCount: repeatCount,
                 repeatCount: repeatCount,
                 preloadQueue: preloadQueue)
                 preloadQueue: preloadQueue)
@@ -371,6 +372,9 @@ extension AnimatedImageView {
     public class Animator {
     public class Animator {
         private let size: CGSize
         private let size: CGSize
 
 
+        private let imageSize: CGSize
+        private let imageScale: CGFloat
+
         /// The maximum count of image frames that needs preload.
         /// The maximum count of image frames that needs preload.
         public let maxFrameCount: Int
         public let maxFrameCount: Int
 
 
@@ -451,20 +455,33 @@ extension AnimatedImageView {
         ///   - source: The reference of animated image.
         ///   - source: The reference of animated image.
         ///   - mode: Content mode of the `AnimatedImageView`.
         ///   - mode: Content mode of the `AnimatedImageView`.
         ///   - size: Size of the `AnimatedImageView`.
         ///   - size: Size of the `AnimatedImageView`.
+        ///   - imageSize: Size of the `KingfisherWrapper`.
+        ///   - imageScale: Scale of the `KingfisherWrapper`.
         ///   - count: Count of frames needed to be preloaded.
         ///   - count: Count of frames needed to be preloaded.
         ///   - repeatCount: The repeat count should this animator uses.
         ///   - repeatCount: The repeat count should this animator uses.
+        ///   - preloadQueue: Dispatch queue used for preloading images.
         init(imageSource source: CGImageSource,
         init(imageSource source: CGImageSource,
              contentMode mode: UIView.ContentMode,
              contentMode mode: UIView.ContentMode,
              size: CGSize,
              size: CGSize,
+             imageSize: CGSize,
+             imageScale: CGFloat,
              framePreloadCount count: Int,
              framePreloadCount count: Int,
              repeatCount: RepeatCount,
              repeatCount: RepeatCount,
              preloadQueue: DispatchQueue) {
              preloadQueue: DispatchQueue) {
             self.imageSource = source
             self.imageSource = source
             self.contentMode = mode
             self.contentMode = mode
             self.size = size
             self.size = size
+            self.imageSize = imageSize
+            self.imageScale = imageScale
             self.maxFrameCount = count
             self.maxFrameCount = count
             self.maxRepeatCount = repeatCount
             self.maxRepeatCount = repeatCount
             self.preloadQueue = preloadQueue
             self.preloadQueue = preloadQueue
+            
+            GraphicsContext.begin(size: imageSize, scale: imageScale)
+        }
+        
+        deinit {
+            GraphicsContext.end()
         }
         }
 
 
         /// Gets the image frame of a given index.
         /// Gets the image frame of a given index.
@@ -520,22 +537,30 @@ extension AnimatedImageView {
         }
         }
 
 
         private func loadFrame(at index: Int) -> UIImage? {
         private func loadFrame(at index: Int) -> UIImage? {
-            let options: [CFString: Any] = [
-                kCGImageSourceCreateThumbnailFromImageIfAbsent: true,
-                kCGImageSourceCreateThumbnailWithTransform: true,
-                kCGImageSourceShouldCacheImmediately: true,
-                kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height)
-            ]
-
             let resize = needsPrescaling && size != .zero
             let resize = needsPrescaling && size != .zero
-            guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource,
-                                                                index,
-                                                                resize ? options as CFDictionary : nil) else {
+            let options: [CFString: Any]?
+            if resize {
+                options = [
+                    kCGImageSourceCreateThumbnailFromImageIfAbsent: true,
+                    kCGImageSourceCreateThumbnailWithTransform: true,
+                    kCGImageSourceShouldCacheImmediately: true,
+                    kCGImageSourceThumbnailMaxPixelSize: max(size.width, size.height)
+                ]
+            } else {
+                options = nil
+            }
+
+            guard let cgImage = CGImageSourceCreateImageAtIndex(imageSource, index, options as CFDictionary?) else {
                 return nil
                 return nil
             }
             }
 
 
             let image = KFCrossPlatformImage(cgImage: cgImage)
             let image = KFCrossPlatformImage(cgImage: cgImage)
-            return backgroundDecode ? image.kf.decoded : image
+            
+            guard let context = GraphicsContext.current(size: imageSize, scale: imageScale, inverting: true, cgImage: cgImage) else {
+                return image
+            }
+            
+            return backgroundDecode ? image.kf.decoded(on: context) : image
         }
         }
         
         
         private func updatePreloadedFrames() {
         private func updatePreloadedFrames() {