[FIX] GIFHeavy crash

This commit is contained in:
Taras Nikulin
2021-04-16 11:25:50 +02:00
parent 246762e4fe
commit 26c715dfbc
7 changed files with 304 additions and 74 deletions

View File

@@ -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">

View File

@@ -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)
}
}

View File

@@ -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;
};

View File

@@ -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 */,

View 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
}
}

View File

@@ -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

View File

@@ -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() {