onevcat пре 9 година
родитељ
комит
d1ab52f444
2 измењених фајлова са 125 додато и 0 уклоњено
  1. 105 0
      Sources/Image.swift
  2. 20 0
      Sources/ImageProcessor.swift

+ 105 - 0
Sources/Image.swift

@@ -43,6 +43,10 @@ private var animatedImageDataKey: Void?
 import ImageIO
 import CoreGraphics
 
+#if os(iOS) || os(macOS) || os(tvOS)
+import Accelerate
+#endif
+
 // MARK: - Image Properties
 extension Image {
 #if os(macOS)
@@ -492,6 +496,107 @@ extension CGSize {
     }
 }
 
+extension Image {
+    func kf_blurred(withRadius radius: CGFloat, blurScaling: CGFloat = 1.0) -> Image {
+        #if os(watchOS)
+        return self
+        #else
+        guard let imageRef = cgImage else {
+            assertionFailure("[Kingfisher] Blur only works for CG-based image.")
+            return self
+        }
+        
+        // http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
+        // let d = floor(s * 3*sqrt(2*pi)/4 + 0.5)
+        // if d is odd, use three box-blurs of size 'd', centered on the output pixel.
+        let s = max(radius, 2.0)
+        // We will do blur on a resized image (*0.5), so the blur radius could be half as well.
+        var targetRadius = floor((Double(s * 3.0) * sqrt(2 * M_PI) / 4.0 + 0.5))
+
+        if targetRadius.isEven {
+            targetRadius += 1
+        }
+        
+        let iterations: Int
+        if radius < 0.5 {
+            iterations = 1
+        } else if radius < 1.5 {
+            iterations = 2
+        } else {
+            iterations = 3
+        }
+            
+        let w = Int(kf_size.width * blurScaling)
+        let h = Int(kf_size.height * blurScaling)
+        let rowBytes = Int(CGFloat(imageRef.bytesPerRow) * blurScaling)
+        
+        let inDataPointer = malloc(rowBytes * Int(h))
+        defer {
+            free(inDataPointer)
+        }
+        
+        guard let context = CGContext(data: inDataPointer,
+                                      width: w,
+                                      height: h,
+                                      bitsPerComponent: imageRef.bitsPerComponent,
+                                      bytesPerRow: rowBytes,
+                                      space: imageRef.colorSpace!,
+                                      bitmapInfo: imageRef.bitmapInfo.rawValue) else
+        {
+            assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+            return self
+        }
+        
+        context.draw(imageRef, in: CGRect(x: 0, y: 0, width: w, height: h))
+        
+        
+        var inBuffer = vImage_Buffer(data: inDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes)
+        
+        let outDataPointer = malloc(rowBytes * Int(h))
+        defer {
+            free(outDataPointer)
+        }
+            
+        var outBuffer = vImage_Buffer(data: outDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes)
+        
+        for _ in 0 ..< iterations {
+            vImageBoxConvolve_ARGB8888(&inBuffer, &outBuffer, nil, 0, 0, UInt32(targetRadius), UInt32(targetRadius), nil, vImage_Flags(kvImageEdgeExtend))
+            (inBuffer, outBuffer) = (outBuffer, inBuffer)
+        }
+
+        guard let outContext = CGContext(data: inDataPointer,
+                                         width: w,
+                                         height: h,
+                                         bitsPerComponent: imageRef.bitsPerComponent,
+                                         bytesPerRow: rowBytes,
+                                         space: imageRef.colorSpace!,
+                                         bitmapInfo: imageRef.bitmapInfo.rawValue) else
+        {
+            assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+            return self
+        }
+        
+        #if os(macOS)
+        let result = outContext.makeImage().flatMap { Image(cgImage: $0, size: size) }
+        #else
+        let result = outContext.makeImage().flatMap { Image(cgImage: $0) }
+        #endif
+        guard let blurredImage = result else {
+            assertionFailure("Can not make an resized image within context.")
+            return self
+        }
+        
+        return blurredImage
+        #endif
+    }
+}
+
+extension Double {
+    var isEven: Bool {
+        return truncatingRemainder(dividingBy: 2.0) == 0
+    }
+}
+
 
 /// Reference the source image reference
 class ImageSource {

+ 20 - 0
Sources/ImageProcessor.swift

@@ -83,6 +83,26 @@ public struct ResizingImageProcessor: ImageProcessor {
     }
 }
 
+public struct BlurImageProcessor: ImageProcessor {
+    public let blurRadius: CGFloat
+    public let blurScaling: CGFloat
+    
+    public init(blurRadius: CGFloat, blurScaling: CGFloat = 1.0) {
+        self.blurRadius = blurRadius
+        self.blurScaling = blurScaling
+    }
+    
+    public func process(item: ImageProcessItem, options: KingfisherOptionsInfo) -> Image? {
+        switch item {
+        case .image(let image):
+            let radius = blurRadius * options.scaleFactor
+            return image.kf_blurred(withRadius: radius)
+        case .data(_):
+            return (DefaultProcessor() |> self).process(item: item, options: options)
+        }
+    }
+}
+
 infix operator |>: DefaultPrecedence
 public func |>(left: ImageProcessor, right: ImageProcessor) -> ImageProcessor {
     return left.append(another: right)