diff --git a/Sources/General/KingfisherManager.swift b/Sources/General/KingfisherManager.swift index f9429f0af..c2e9d8c4a 100644 --- a/Sources/General/KingfisherManager.swift +++ b/Sources/General/KingfisherManager.swift @@ -601,7 +601,7 @@ public class KingfisherManager: @unchecked Sendable { // TODO: Optimize it when we can use async across all the project. @Sendable func checkResultImageAndCallback(_ inputImage: KFCrossPlatformImage) { var image = inputImage - if image.kf.imageFrameCount != nil && image.kf.imageFrameCount != 1, let data = image.kf.animatedImageData { + if image.kf.imageFrameCount != nil && image.kf.imageFrameCount != 1, options.imageCreatingOptions != image.kf.imageCreatingOptions, let data = image.kf.animatedImageData { // Always recreate animated image representation since it is possible to be loaded in different options. // https://github.com/onevcat/Kingfisher/issues/1923 image = options.processor.process(item: .data(data), options: options) ?? .init() diff --git a/Sources/Image/GIFAnimatedImage.swift b/Sources/Image/GIFAnimatedImage.swift index f373829ee..c8086bb1a 100644 --- a/Sources/Image/GIFAnimatedImage.swift +++ b/Sources/Image/GIFAnimatedImage.swift @@ -28,7 +28,7 @@ import Foundation import ImageIO /// Represents a set of image creation options used in Kingfisher. -public struct ImageCreatingOptions { +public struct ImageCreatingOptions: Equatable { /// The target scale of the image that needs to be created. public var scale: CGFloat diff --git a/Sources/Image/Image.swift b/Sources/Image/Image.swift index 7568ffbf3..f773d5b82 100644 --- a/Sources/Image/Image.swift +++ b/Sources/Image/Image.swift @@ -47,6 +47,7 @@ import UniformTypeIdentifiers nonisolated(unsafe) private let animatedImageDataKey = malloc(1)! nonisolated(unsafe) private let imageFrameCountKey = malloc(1)! nonisolated(unsafe) private let imageSourceKey = malloc(1)! +nonisolated(unsafe) private let imageCreatingOptionsKey = malloc(1)! #if os(macOS) nonisolated(unsafe) private let imagesKey = malloc(1)! nonisolated(unsafe) private let durationKey = malloc(1)! @@ -55,6 +56,7 @@ nonisolated(unsafe) private let durationKey = malloc(1)! private let animatedImageDataKey = malloc(1)! private let imageFrameCountKey = malloc(1)! private let imageSourceKey = malloc(1)! +private let imageCreatingOptionsKey = malloc(1)! #if os(macOS) private let imagesKey = malloc(1)! private let durationKey = malloc(1)! @@ -68,6 +70,11 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage { set { setRetainedAssociatedObject(base, animatedImageDataKey, newValue) } } + private(set) var imageCreatingOptions: ImageCreatingOptions? { + get { return getAssociatedObject(base, imageCreatingOptionsKey) } + set { setRetainedAssociatedObject(base, imageCreatingOptionsKey, newValue) } + } + public var imageFrameCount: Int? { get { return getAssociatedObject(base, imageFrameCountKey) } set { setRetainedAssociatedObject(base, imageFrameCountKey, newValue) } @@ -359,6 +366,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage { image?.kf.animatedImageData = source.data image?.kf.imageFrameCount = source.frameCount image?.kf.frameSource = source + image?.kf.imageCreatingOptions = options return image #else @@ -390,6 +398,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage { } image?.kf.imageFrameCount = source.frameCount + image?.kf.imageCreatingOptions = options return image #endif } diff --git a/Tests/KingfisherTests/KingfisherManagerTests.swift b/Tests/KingfisherTests/KingfisherManagerTests.swift index f4e279239..7380d3cd3 100644 --- a/Tests/KingfisherTests/KingfisherManagerTests.swift +++ b/Tests/KingfisherTests/KingfisherManagerTests.swift @@ -1352,6 +1352,25 @@ class KingfisherManagerTests: XCTestCase { waitForExpectations(timeout: 3, handler: nil) } + func testAnimatedImageShouldNotRecreateWithSameOptions() { + let exp = expectation(description: #function) + let url = testURLs[0] + let data = testImageGIFData + stub(url, data: data) + let p = SimpleProcessor() + manager.retrieveImage(with: url, options: [.processor(p), .onlyLoadFirstFrame]) { result in + XCTAssertTrue(p.processed) + XCTAssertTrue(result.value!.image.creatingOptions!.onlyFirstFrame) + p.processed = false + self.manager.retrieveImage(with: url, options: [.processor(p), .onlyLoadFirstFrame]) { result in + XCTAssertFalse(p.processed) + XCTAssertTrue(result.value!.image.creatingOptions!.onlyFirstFrame) + exp.fulfill() + } + } + waitForExpectations(timeout: 3, handler: nil) + } + func testMissingResourceOfLivePhotoFound() { let resource = KF.ImageResource(downloadURL: LivePhotoURL.mov) let source = LivePhotoSource(resources: [resource])