Jelajahi Sumber

Merge pull request #1163 from onevcat/fix/orientation-fix

Fix image normalization orientation
Wei Wang 6 tahun lalu
induk
melakukan
881cc93b9b

+ 54 - 5
Sources/Image/Image.swift

@@ -121,12 +121,61 @@ extension KingfisherWrapper where Base: Image {
     /// This method will try to redraw an image with orientation and scale considered.
     public var normalized: Image {
         // prevent animated image (GIF) lose it's images
-        guard images == nil else { return base }
+        guard images == nil else { return base.copy() as! Image }
         // No need to do anything if already up
-        guard base.imageOrientation != .up else { return base }
-    
-        return draw(to: size) { _ in
-            base.draw(in: CGRect(origin: .zero, size: size))
+        guard base.imageOrientation != .up else { return base.copy() as! Image }
+
+        return draw(to: size, inverting: true, refImage: Image()) {
+            fixOrientation(in: $0)
+        }
+    }
+
+    func fixOrientation(in context: CGContext) {
+
+        var transform = CGAffineTransform.identity
+
+        let orientation = base.imageOrientation
+
+        switch orientation {
+        case .down, .downMirrored:
+            transform = transform.translatedBy(x: size.width, y: size.height)
+            transform = transform.rotated(by: .pi)
+        case .left, .leftMirrored:
+            transform = transform.translatedBy(x: size.width, y: 0)
+            transform = transform.rotated(by: .pi / 2.0)
+        case .right, .rightMirrored:
+            transform = transform.translatedBy(x: 0, y: size.height)
+            transform = transform.rotated(by: .pi / -2.0)
+        case .up, .upMirrored:
+            break
+        #if compiler(>=5)
+        @unknown default:
+            break
+        #endif
+        }
+
+        //Flip image one more time if needed to, this is to prevent flipped image
+        switch orientation {
+        case .upMirrored, .downMirrored:
+            transform = transform.translatedBy(x: size.width, y: 0)
+            transform = transform.scaledBy(x: -1, y: 1)
+        case .leftMirrored, .rightMirrored:
+            transform = transform.translatedBy(x: size.height, y: 0)
+            transform = transform.scaledBy(x: -1, y: 1)
+        case .up, .down, .left, .right:
+            break
+        #if compiler(>=5)
+        @unknown default:
+            break
+        #endif
+        }
+
+        context.concatenate(transform)
+        switch orientation {
+        case .left, .leftMirrored, .right, .rightMirrored:
+            context.draw(cgImage!, in: CGRect(x: 0, y: 0, width: size.height, height: size.width))
+        default:
+            context.draw(cgImage!, in: CGRect(x: 0, y: 0, width: size.width, height: size.height))
         }
     }
     #endif

+ 2 - 2
Sources/Image/ImageDrawing.swift

@@ -499,7 +499,7 @@ extension KingfisherWrapper where Base: Image {
         #endif
     }
     
-    func draw(to size: CGSize, inverting: Bool = false, scale: CGFloat? = nil, draw: (CGContext) -> Void) -> Image {
+    func draw(to size: CGSize, inverting: Bool = false, scale: CGFloat? = nil, refImage: Image? = nil, draw: (CGContext) -> Void) -> Image {
         let targetScale = scale ?? self.scale
         guard let context = beginContext(size: size, scale: targetScale, inverting: inverting) else {
             assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
@@ -510,7 +510,7 @@ extension KingfisherWrapper where Base: Image {
         guard let cgImage = context.makeImage() else {
             return base
         }
-        return KingfisherWrapper.image(cgImage: cgImage, scale: targetScale, refImage: base)
+        return KingfisherWrapper.image(cgImage: cgImage, scale: targetScale, refImage: refImage ?? base)
     }
     
     #if os(macOS)

+ 1 - 1
Sources/Utility/String+MD5.swift

@@ -2,7 +2,7 @@
 //  String+MD5.swift
 //  Kingfisher
 //
-//  Created by Wei Wang on 18//25.
+//  Created by Wei Wang on 18/09/25.
 //
 //  Copyright (c) 2019 Wei Wang <onevcat@gmail.com>
 //

+ 10 - 1
Tests/KingfisherTests/ImageExtensionTests.swift

@@ -287,7 +287,16 @@ class ImageExtensionTests: XCTestCase {
         // No need to normalize up orientation image.
         let normalImage = testImage
         XCTAssertEqual(normalImage.imageOrientation, .up)
-        XCTAssertEqual(normalImage, testImage)
+        XCTAssertEqual(normalImage.kf.normalized, testImage)
+
+        let colorImage = UIImage.from(color: .red, size: CGSize(width: 100, height: 200))
+        let rotatedImage = UIImage(cgImage: colorImage.cgImage!, scale: colorImage.scale, orientation: .right)
+
+        XCTAssertEqual(rotatedImage.imageOrientation, .right)
+
+        let rotatedNormalizedImage = rotatedImage.kf.normalized
+        XCTAssertEqual(rotatedNormalizedImage.imageOrientation, .up)
+        XCTAssertEqual(rotatedNormalizedImage.size, CGSize(width: 200, height: 100))
         #endif
     }
     

+ 16 - 0
Tests/KingfisherTests/KingfisherTestHelper.swift

@@ -196,6 +196,22 @@ extension Image {
     }
 }
 
+#if os(iOS) || os(tvOS)
+import UIKit
+extension Image {
+    static func from(color: Color, size: CGSize) -> Image {
+        let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
+        UIGraphicsBeginImageContext(rect.size)
+        let context = UIGraphicsGetCurrentContext()
+        context!.setFillColor(color.cgColor)
+        context!.fill(rect)
+        let img = UIGraphicsGetImageFromCurrentImageContext()
+        UIGraphicsEndImageContext()
+        return img!
+    }
+}
+#endif
+
 extension Data {
     init(fileName: String) {
         let comp = fileName.components(separatedBy: ".")