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

Add rect corner support for round image processor

onevcat 8 лет назад
Родитель
Сommit
c7cd28a24a
2 измененных файлов с 98 добавлено и 11 удалено
  1. 68 6
      Sources/Image.swift
  2. 30 5
      Sources/ImageProcessor.swift

+ 68 - 6
Sources/Image.swift

@@ -340,14 +340,17 @@ extension Kingfisher where Base: Image {
     // MARK: - Round Corner
     /// Create a round corner image based on `self`.
     ///
-    /// - parameter radius: The round corner radius of creating image.
-    /// - parameter size:   The target size of creating image.
+    /// - parameter radius:  The round corner radius of creating image.
+    /// - parameter size:    The target size of creating image.
+    /// - parameter corners: The target corners which will be applied rounding.
     ///
     /// - returns: An image with round corner of `self`.
     ///
     /// - Note: This method only works for CG-based image.
-    public func image(withRoundRadius radius: CGFloat, fit size: CGSize) -> Image {
-        
+    public func image(withRoundRadius radius: CGFloat,
+                      fit size: CGSize,
+                      roundingCorners corners: RectCorner = .all) -> Image
+    {   
         guard let cgImage = cgImage else {
             assertionFailure("[Kingfisher] Round corner image only works for CG-based image.")
             return base
@@ -356,7 +359,7 @@ extension Kingfisher where Base: Image {
         let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: size)
         return draw(cgImage: cgImage, to: size) {
             #if os(macOS)
-                let path = NSBezierPath(roundedRect: rect, xRadius: radius, yRadius: radius)
+                let path = NSBezierPath(roundedRect: rect, byRoundingCorners: corners, radius: radius)
                 path.windingRule = .evenOddWindingRule
                 path.addClip()
                 base.draw(in: rect)
@@ -365,7 +368,9 @@ extension Kingfisher where Base: Image {
                     assertionFailure("[Kingfisher] Failed to create CG context for image.")
                     return
                 }
-                let path = UIBezierPath(roundedRect: rect, byRoundingCorners: .allCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath
+                let path = UIBezierPath(roundedRect: rect,
+                                        byRoundingCorners: corners.uiRectCorner,
+                                        cornerRadii: CGSize(width: radius, height: radius)).cgPath
                 context.addPath(path)
                 context.clip()
                 base.draw(in: rect)
@@ -863,6 +868,63 @@ extension Float {
     }
 }
 
+#if os(macOS)
+extension NSBezierPath {
+    convenience init(roundedRect rect: NSRect, topLeftRadius: CGFloat, topRightRadius: CGFloat,
+         bottomLeftRadius: CGFloat, bottomRightRadius: CGFloat)
+    {
+        self.init()
+        
+        let maxCorner = min(rect.width, rect.height) / 2
+        
+        let radiusTopLeft = min(maxCorner, max(0, topLeftRadius))
+        let radiustopRight = min(maxCorner, max(0, topRightRadius))
+        let radiusbottomLeft = min(maxCorner, max(0, bottomLeftRadius))
+        let radiusbottomRight = min(maxCorner, max(0, bottomRightRadius))
+        
+        guard !NSIsEmptyRect(rect) else {
+            return
+        }
+        
+        let topLeft = NSMakePoint(NSMinX(rect), NSMaxY(rect));
+        let topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
+        let bottomRight = NSMakePoint(NSMaxX(rect), NSMinY(rect));
+        
+        move(to: NSMakePoint(NSMidX(rect), NSMaxY(rect)))
+        appendArc(from: topLeft, to: rect.origin, radius: radiusTopLeft)
+        appendArc(from: rect.origin, to: bottomRight, radius: radiusbottomLeft)
+        appendArc(from: bottomRight, to: topRight, radius: radiusbottomRight)
+        appendArc(from: topRight, to: topLeft, radius: radiustopRight)
+        close()
+    }
+    
+    convenience init(roundedRect rect: NSRect, byRoundingCorners corners: RectCorner, radius: CGFloat) {
+        let radiusTopLeft = corners.contains(.topLeft) ? radius : 0
+        let radiusTopRight = corners.contains(.topRight) ? radius : 0
+        let radiusBottomLeft = corners.contains(.bottomLeft) ? radius : 0
+        let radiusBottomRight = corners.contains(.bottomRight) ? radius : 0
+        
+        self.init(roundedRect: rect, topLeftRadius: radiusTopLeft, topRightRadius: radiusTopRight,
+                  bottomLeftRadius: radiusBottomLeft, bottomRightRadius: radiusBottomRight)
+    }
+}
+    
+#else
+extension RectCorner {
+    var uiRectCorner: UIRectCorner {
+        
+        var result: UIRectCorner = []
+        
+        if self.contains(.topLeft) { result.insert(.topLeft) }
+        if self.contains(.topRight) { result.insert(.topRight) }
+        if self.contains(.bottomLeft) { result.insert(.bottomLeft) }
+        if self.contains(.bottomRight) { result.insert(.bottomRight) }
+        
+        return result
+    }
+}
+#endif
+
 // MARK: - Deprecated. Only for back compatibility.
 extension Image {
     /**

+ 30 - 5
Sources/ImageProcessor.swift

@@ -27,7 +27,6 @@
 import Foundation
 import CoreGraphics
 
-
 /// The item which could be processed by an `ImageProcessor`
 ///
 /// - image: Input image
@@ -133,6 +132,26 @@ public struct DefaultImageProcessor: ImageProcessor {
     }
 }
 
+public struct RectCorner: OptionSet {
+    public let rawValue: Int
+    public static let topLeft = RectCorner(rawValue: 1 << 0)
+    public static let topRight = RectCorner(rawValue: 1 << 1)
+    public static let bottomLeft = RectCorner(rawValue: 1 << 2)
+    public static let bottomRight = RectCorner(rawValue: 1 << 3)
+    public static let all: RectCorner = [.topLeft, .topRight, .bottomLeft, .bottomRight]
+    
+    public init(rawValue: Int) {
+        self.rawValue = rawValue
+    }
+    
+    var cornerIdentifier: String {
+        if self == .all {
+            return ""
+        }
+        return "_\(rawValue)"
+    }
+}
+
 /// Processor for making round corner images. Only CG-based images are supported in macOS, 
 /// if a non-CG image passed in, the processor will do nothing.
 public struct RoundCornerImageProcessor: ImageProcessor {
@@ -144,6 +163,9 @@ public struct RoundCornerImageProcessor: ImageProcessor {
     /// Corner radius will be applied in processing.
     public let cornerRadius: CGFloat
     
+    /// The target corners which will be applied rounding.
+    public let roundingCorners: RectCorner
+    
     /// Target size of output image should be. If `nil`, the image will keep its original size after processing.
     public let targetSize: CGSize?
     
@@ -153,13 +175,16 @@ public struct RoundCornerImageProcessor: ImageProcessor {
     /// - parameter targetSize:   Target size of output image should be. If `nil`, 
     ///                           the image will keep its original size after processing.
     ///                           Default is `nil`.
-    public init(cornerRadius: CGFloat, targetSize: CGSize? = nil) {
+    /// - parameter corners:      The target corners which will be applied rounding. Default is `.all`.
+    public init(cornerRadius: CGFloat, targetSize: CGSize? = nil, roundingCorners corners: RectCorner = .all) {
         self.cornerRadius = cornerRadius
         self.targetSize = targetSize
+        self.roundingCorners = corners
+        
         if let size = targetSize {
-            self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size))"
+            self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)_\(size)\(corners.cornerIdentifier))"
         } else {
-            self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius))"
+            self.identifier = "com.onevcat.Kingfisher.RoundCornerImageProcessor(\(cornerRadius)\(corners.cornerIdentifier))"
         }
     }
     
@@ -175,7 +200,7 @@ public struct RoundCornerImageProcessor: ImageProcessor {
         switch item {
         case .image(let image):
             let size = targetSize ?? image.kf.size
-            return image.kf.image(withRoundRadius: cornerRadius, fit: size)
+            return image.kf.image(withRoundRadius: cornerRadius, fit: size, roundingCorners: roundingCorners)
         case .data(_):
             return (DefaultImageProcessor.default >> self).process(item: item, options: options)
         }