Просмотр исходного кода

Add API to copy Kingfisher state between images

onevcat 1 месяц назад
Родитель
Сommit
1fb225ac97
2 измененных файлов с 64 добавлено и 0 удалено
  1. 21 0
      Sources/Image/Image.swift
  2. 43 0
      Tests/KingfisherTests/ImageCacheTests.swift

+ 21 - 0
Sources/Image/Image.swift

@@ -137,6 +137,27 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage {
         set { setRetainedAssociatedObject(base, imageSourceKey, newValue) }
     }
 
+    /// Copies Kingfisher internal image states from `base` to a `target` image.
+    ///
+    /// This includes the embedded animated image data and related metadata that are used by Kingfisher for caching and
+    /// animated image rendering. It is useful when a custom processor creates and returns a new image instance from
+    /// an animated image in `.image` branch.
+    ///
+    /// - Important: This method does not make the `target` image animated by itself. It only propagates Kingfisher's
+    ///   internal metadata so the cache can preserve the original animated bytes when possible.
+    ///
+    /// - Parameter target: The target image to which the internal states will be copied.
+    public func copyKingfisherState(to target: KFCrossPlatformImage) {
+        target.kf.animatedImageData = animatedImageData
+        target.kf.imageFrameCount = imageFrameCount
+        target.kf.frameSource = frameSource
+        target.kf.imageCreatingOptions = imageCreatingOptions
+        #if os(macOS)
+        target.kf.images = images
+        target.kf.duration = duration
+        #endif
+    }
+
     // Bitmap memory cost with bytes.
     var cost: Int {
         let pixel = Int(size.width * size.height * scale * scale)

+ 43 - 0
Tests/KingfisherTests/ImageCacheTests.swift

@@ -232,6 +232,49 @@ class ImageCacheTests: XCTestCase {
 
         waitForExpectations(timeout: 3, handler: nil)
     }
+
+    func testCopyKingfisherStateShouldKeepEmbeddedGIFDataForDiskCache() {
+        struct TestProcessor: ImageProcessor {
+            let identifier: String = "com.onevcat.KingfisherTests.TestProcessor.CopyState"
+            func process(item: ImageProcessItem, options: KingfisherParsedOptionsInfo) -> KFCrossPlatformImage? {
+                switch item {
+                case .image(let image):
+                    #if os(macOS)
+                    guard let cgImage = image.kf.cgImage else { return image }
+                    let newImage = KFCrossPlatformImage(cgImage: cgImage, size: image.kf.size)
+                    image.kf.copyKingfisherState(to: newImage)
+                    return newImage
+                    #else
+                    guard let cgImage = image.cgImage else { return image }
+                    let newImage = KFCrossPlatformImage(cgImage: cgImage, scale: image.scale, orientation: image.imageOrientation)
+                    image.kf.copyKingfisherState(to: newImage)
+                    return newImage
+                    #endif
+                case .data(let data):
+                    return DefaultImageProcessor.default.process(item: .data(data), options: options)
+                }
+            }
+        }
+
+        let exp = expectation(description: #function)
+        let image = KingfisherWrapper<KFCrossPlatformImage>.animatedImage(data: testImageGIFData, options: .init())!
+        XCTAssertEqual(image.kf.gifRepresentation()?.kf.imageFormat, .GIF)
+
+        let options = KingfisherParsedOptionsInfo([.processor(TestProcessor())])
+        let key = "test-gif-copy-state"
+        cache.store(image, original: nil, forKey: key, options: options, toDisk: true) { _ in
+            do {
+                let storedKey = key.computedKey(with: TestProcessor().identifier)
+                let storedData = try self.cache.diskStorage.value(forKey: storedKey)
+                XCTAssertEqual(storedData?.kf.imageFormat, .GIF)
+            } catch {
+                XCTFail("Unexpected error: \(error)")
+            }
+            exp.fulfill()
+        }
+
+        waitForExpectations(timeout: 3, handler: nil)
+    }
     
     func testStoreMultipleImages() {
         let exp = expectation(description: #function)