mirror of
https://github.com/zhigang1992/Kingfisher.git
synced 2026-04-30 13:02:33 +08:00
[FIX] GIFHeavy crash
This commit is contained in:
@@ -299,9 +299,33 @@
|
||||
<segue destination="UXA-j3-Wgu" kind="show" id="7cS-7z-3m8"/>
|
||||
</connections>
|
||||
</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"/>
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
@@ -324,7 +348,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -348,7 +372,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -372,7 +396,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -396,7 +420,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -420,7 +444,7 @@
|
||||
</connections>
|
||||
</tableViewCell>
|
||||
<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"/>
|
||||
<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"/>
|
||||
@@ -697,7 +721,23 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="hAo-Ts-6oW" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</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>
|
||||
<!--Detail Image View Controller-->
|
||||
<scene sceneID="wpT-qC-cq3">
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
D10AC99821A300C9005F057C /* ProcessorCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D10AC99721A300C9005F057C /* ProcessorCollectionViewController.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; };
|
||||
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; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -361,6 +363,7 @@
|
||||
D1FAB06E21A853E600908910 /* HighResolutionCollectionViewController.swift */,
|
||||
D1F06F3221AA4292000B1C38 /* DetailImageViewController.swift */,
|
||||
D1F06F3621AAEACF000B1C38 /* GIFViewController.swift */,
|
||||
769F07F026298E2E00610767 /* GIFHeavyViewController.swift */,
|
||||
D1F06F3821AAF1EE000B1C38 /* IndicatorCollectionViewController.swift */,
|
||||
D1E4CF5321BACBA6004D029D /* ImageDataProviderCollectionViewController.swift */,
|
||||
C959EEE522874DC600467A10 /* ProgressiveJPEGViewController.swift */,
|
||||
@@ -680,6 +683,7 @@
|
||||
4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */,
|
||||
D1A1CCA321A1879600263AD8 /* MainViewController.swift in Sources */,
|
||||
D1F06F3721AAEACF000B1C38 /* GIFViewController.swift in Sources */,
|
||||
769F07F126298E2E00610767 /* GIFHeavyViewController.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD821662189FD330084CC21 /* SessionDataTask.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 */; };
|
||||
76FB4FD2262D773E006D15F8 /* GraphicsContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 76FB4FD1262D773E006D15F8 /* GraphicsContext.swift */; };
|
||||
C9286407228584EB00257182 /* ImageProgressive.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9286406228584EB00257182 /* ImageProgressive.swift */; };
|
||||
D1132C9725919F69003E528D /* KFOptionsSetter.swift in Sources */ = {isa = PBXBuildFile; fileRef = D1132C9625919F69003E528D /* KFOptionsSetter.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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
@@ -353,6 +355,7 @@
|
||||
D12AB6A6215D2BB50013BA68 /* Filter.swift */,
|
||||
D12AB6A7215D2BB50013BA68 /* Placeholder.swift */,
|
||||
D12AB6A8215D2BB50013BA68 /* GIFAnimatedImage.swift */,
|
||||
76FB4FD1262D773E006D15F8 /* GraphicsContext.swift */,
|
||||
);
|
||||
path = Image;
|
||||
sourceTree = "<group>";
|
||||
@@ -818,6 +821,7 @@
|
||||
D12AB6E0215D2BB50013BA68 /* Filter.swift in Sources */,
|
||||
4BE688F722FD513100B11168 /* NSButton+Kingfisher.swift in Sources */,
|
||||
D12AB6C4215D2BB50013BA68 /* Resource.swift in Sources */,
|
||||
76FB4FD2262D773E006D15F8 /* GraphicsContext.swift in Sources */,
|
||||
D8FCF6A821C5A0E500F9ABC0 /* RedirectHandler.swift in Sources */,
|
||||
D1A37BDE215D34E8009B39B7 /* ImageDrawing.swift in Sources */,
|
||||
4BD821672189FD330084CC21 /* SessionDataTask.swift in Sources */,
|
||||
|
||||
88
Sources/Image/GraphicsContext.swift
Normal file
88
Sources/Image/GraphicsContext.swift
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,21 +290,22 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
|
||||
|
||||
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.")
|
||||
return base
|
||||
}
|
||||
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
|
||||
endContext()
|
||||
GraphicsContext.end()
|
||||
|
||||
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.")
|
||||
return base
|
||||
}
|
||||
defer { endContext() }
|
||||
defer { GraphicsContext.end() }
|
||||
var outBuffer = createEffectBuffer(outContext)
|
||||
|
||||
for _ in 0 ..< iterations {
|
||||
@@ -457,55 +458,42 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
if images != nil { return base }
|
||||
#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 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()
|
||||
#else
|
||||
UIGraphicsEndImageContext()
|
||||
#endif
|
||||
}
|
||||
|
||||
func draw(
|
||||
to size: CGSize,
|
||||
inverting: Bool = false,
|
||||
@@ -515,11 +503,12 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
|
||||
) -> KFCrossPlatformImage
|
||||
{
|
||||
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.")
|
||||
return base
|
||||
}
|
||||
defer { endContext() }
|
||||
defer { GraphicsContext.end() }
|
||||
let useRefImage = draw(context)
|
||||
guard let cgImage = context.makeImage() else {
|
||||
return base
|
||||
|
||||
@@ -170,8 +170,7 @@ open class AnimatedImageView: UIImageView {
|
||||
// A display link that keeps calling the `updateFrame` method on every screen refresh.
|
||||
private lazy var displayLink: CADisplayLink = {
|
||||
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.isPaused = true
|
||||
return displayLink
|
||||
@@ -258,12 +257,14 @@ open class AnimatedImageView: UIImageView {
|
||||
// Reset the animator.
|
||||
private func reset() {
|
||||
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 animator = Animator(
|
||||
imageSource: imageSource,
|
||||
contentMode: contentMode,
|
||||
size: targetSize,
|
||||
imageSize: image.kf.size,
|
||||
imageScale: image.kf.scale,
|
||||
framePreloadCount: framePreloadCount,
|
||||
repeatCount: repeatCount,
|
||||
preloadQueue: preloadQueue)
|
||||
@@ -371,6 +372,9 @@ extension AnimatedImageView {
|
||||
public class Animator {
|
||||
private let size: CGSize
|
||||
|
||||
private let imageSize: CGSize
|
||||
private let imageScale: CGFloat
|
||||
|
||||
/// The maximum count of image frames that needs preload.
|
||||
public let maxFrameCount: Int
|
||||
|
||||
@@ -451,20 +455,33 @@ extension AnimatedImageView {
|
||||
/// - source: The reference of animated image.
|
||||
/// - mode: Content mode 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.
|
||||
/// - repeatCount: The repeat count should this animator uses.
|
||||
/// - preloadQueue: Dispatch queue used for preloading images.
|
||||
init(imageSource source: CGImageSource,
|
||||
contentMode mode: UIView.ContentMode,
|
||||
size: CGSize,
|
||||
imageSize: CGSize,
|
||||
imageScale: CGFloat,
|
||||
framePreloadCount count: Int,
|
||||
repeatCount: RepeatCount,
|
||||
preloadQueue: DispatchQueue) {
|
||||
self.imageSource = source
|
||||
self.contentMode = mode
|
||||
self.size = size
|
||||
self.imageSize = imageSize
|
||||
self.imageScale = imageScale
|
||||
self.maxFrameCount = count
|
||||
self.maxRepeatCount = repeatCount
|
||||
self.preloadQueue = preloadQueue
|
||||
|
||||
GraphicsContext.begin(size: imageSize, scale: imageScale)
|
||||
}
|
||||
|
||||
deinit {
|
||||
GraphicsContext.end()
|
||||
}
|
||||
|
||||
/// Gets the image frame of a given index.
|
||||
@@ -520,22 +537,30 @@ extension AnimatedImageView {
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
Reference in New Issue
Block a user