Skip to content

Commit 37345a2

Browse files
authored
Merge pull request onevcat#2347 from yeatse/avoid-unneeded-recreation
Avoid recreating animated image representations when reloaded with identical options
2 parents 8769e7c + ef4a4fc commit 37345a2

File tree

4 files changed

+31
-3
lines changed

4 files changed

+31
-3
lines changed

Sources/General/KingfisherManager.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -601,8 +601,8 @@ public class KingfisherManager: @unchecked Sendable {
601601
// TODO: Optimize it when we can use async across all the project.
602602
@Sendable func checkResultImageAndCallback(_ inputImage: KFCrossPlatformImage) {
603603
var image = inputImage
604-
if image.kf.imageFrameCount != nil && image.kf.imageFrameCount != 1, let data = image.kf.animatedImageData {
605-
// Always recreate animated image representation since it is possible to be loaded in different options.
604+
if image.kf.imageFrameCount != nil && image.kf.imageFrameCount != 1, options.imageCreatingOptions != image.kf.imageCreatingOptions, let data = image.kf.animatedImageData {
605+
// Recreate animated image representation when loaded in different options.
606606
// https://github.com/onevcat/Kingfisher/issues/1923
607607
image = options.processor.process(item: .data(data), options: options) ?? .init()
608608
}

Sources/Image/GIFAnimatedImage.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import Foundation
2828
import ImageIO
2929

3030
/// Represents a set of image creation options used in Kingfisher.
31-
public struct ImageCreatingOptions {
31+
public struct ImageCreatingOptions: Equatable {
3232

3333
/// The target scale of the image that needs to be created.
3434
public var scale: CGFloat

Sources/Image/Image.swift

+9
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import UniformTypeIdentifiers
4747
nonisolated(unsafe) private let animatedImageDataKey = malloc(1)!
4848
nonisolated(unsafe) private let imageFrameCountKey = malloc(1)!
4949
nonisolated(unsafe) private let imageSourceKey = malloc(1)!
50+
nonisolated(unsafe) private let imageCreatingOptionsKey = malloc(1)!
5051
#if os(macOS)
5152
nonisolated(unsafe) private let imagesKey = malloc(1)!
5253
nonisolated(unsafe) private let durationKey = malloc(1)!
@@ -55,6 +56,7 @@ nonisolated(unsafe) private let durationKey = malloc(1)!
5556
private let animatedImageDataKey = malloc(1)!
5657
private let imageFrameCountKey = malloc(1)!
5758
private let imageSourceKey = malloc(1)!
59+
private let imageCreatingOptionsKey = malloc(1)!
5860
#if os(macOS)
5961
private let imagesKey = malloc(1)!
6062
private let durationKey = malloc(1)!
@@ -68,6 +70,11 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
6870
set { setRetainedAssociatedObject(base, animatedImageDataKey, newValue) }
6971
}
7072

73+
private(set) var imageCreatingOptions: ImageCreatingOptions? {
74+
get { return getAssociatedObject(base, imageCreatingOptionsKey) }
75+
set { setRetainedAssociatedObject(base, imageCreatingOptionsKey, newValue) }
76+
}
77+
7178
public var imageFrameCount: Int? {
7279
get { return getAssociatedObject(base, imageFrameCountKey) }
7380
set { setRetainedAssociatedObject(base, imageFrameCountKey, newValue) }
@@ -359,6 +366,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
359366
image?.kf.animatedImageData = source.data
360367
image?.kf.imageFrameCount = source.frameCount
361368
image?.kf.frameSource = source
369+
image?.kf.imageCreatingOptions = options
362370
return image
363371
#else
364372

@@ -390,6 +398,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
390398
}
391399

392400
image?.kf.imageFrameCount = source.frameCount
401+
image?.kf.imageCreatingOptions = options
393402
return image
394403
#endif
395404
}

Tests/KingfisherTests/KingfisherManagerTests.swift

+19
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,25 @@ class KingfisherManagerTests: XCTestCase {
13521352
waitForExpectations(timeout: 3, handler: nil)
13531353
}
13541354

1355+
func testAnimatedImageShouldNotRecreateWithSameOptions() {
1356+
let exp = expectation(description: #function)
1357+
let url = testURLs[0]
1358+
let data = testImageGIFData
1359+
stub(url, data: data)
1360+
let p = SimpleProcessor()
1361+
manager.retrieveImage(with: url, options: [.processor(p), .onlyLoadFirstFrame]) { result in
1362+
XCTAssertTrue(p.processed)
1363+
XCTAssertTrue(result.value!.image.creatingOptions!.onlyFirstFrame)
1364+
p.processed = false
1365+
self.manager.retrieveImage(with: url, options: [.processor(p), .onlyLoadFirstFrame]) { result in
1366+
XCTAssertFalse(p.processed)
1367+
XCTAssertTrue(result.value!.image.creatingOptions!.onlyFirstFrame)
1368+
exp.fulfill()
1369+
}
1370+
}
1371+
waitForExpectations(timeout: 3, handler: nil)
1372+
}
1373+
13551374
func testMissingResourceOfLivePhotoFound() {
13561375
let resource = KF.ImageResource(downloadURL: LivePhotoURL.mov)
13571376
let source = LivePhotoSource(resources: [resource])

0 commit comments

Comments
 (0)