Kaynağa Gözat

Merge pull request #567 from onevcat/fix/blur-image-crash

Fix blur image crash
Wei Wang 9 yıl önce
ebeveyn
işleme
0c4813cf89
1 değiştirilmiş dosya ile 66 ekleme ve 87 silme
  1. 66 87
      Sources/Image.swift

+ 66 - 87
Sources/Image.swift

@@ -460,57 +460,42 @@ extension Kingfisher where Base: Image {
             let h = Int(size.height)
             let rowBytes = Int(CGFloat(cgImage.bytesPerRow))
             
-            let inDataPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: rowBytes * Int(h))
-            inDataPointer.initialize(to: 0)
-            defer {
-                inDataPointer.deinitialize()
-                inDataPointer.deallocate(capacity: rowBytes * Int(h))
-        }
-            
-            let bitmapInfo = cgImage.bitmapInfo.fixed
-            guard let context = CGContext(data: inDataPointer,
-                                          width: w,
-                                          height: h,
-                                          bitsPerComponent: cgImage.bitsPerComponent,
-                                          bytesPerRow: rowBytes,
-                                          space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
-                                          bitmapInfo: bitmapInfo.rawValue) else
-            {
+            func createEffectBuffer(_ context: CGContext) -> vImage_Buffer {
+                let data = context.data
+                let width = vImagePixelCount(context.width)
+                let height = vImagePixelCount(context.height)
+                let rowBytes = context.bytesPerRow
+                
+                return vImage_Buffer(data: data, height: height, width: width, rowBytes: rowBytes)
+            }
+
+            guard let context = beginContext() else {
                 assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
                 return base
             }
+            defer { endContext() }
             
-            context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
-            
+            #if !os(macOS)
+            context.scaleBy(x: 1.0, y: -1.0)
+            context.translateBy(x: 0, y: -size.height)
+            #endif
             
-            var inBuffer = vImage_Buffer(data: inDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes)
+            context.draw(cgImage, in: CGRect(x: 0, y: 0, width: w, height: h))
             
-            let outDataPointer = UnsafeMutablePointer<UInt8>.allocate(capacity: rowBytes * Int(h))
-            outDataPointer.initialize(to: 0)
-            defer {
-                outDataPointer.deinitialize()
-                outDataPointer.deallocate(capacity: rowBytes * Int(h))
-        }
+            var inBuffer = createEffectBuffer(context)
             
-            var outBuffer = vImage_Buffer(data: outDataPointer, height: vImagePixelCount(h), width: vImagePixelCount(w), rowBytes: rowBytes)
+            guard let outContext = beginContext() else {
+                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
+                return base
+            }
+            defer { endContext() }
+            var outBuffer = createEffectBuffer(outContext)
             
             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: cgImage.bitsPerComponent,
-                                             bytesPerRow: rowBytes,
-                                             space: cgImage.colorSpace ?? CGColorSpaceCreateDeviceRGB(),
-                                             bitmapInfo: bitmapInfo.rawValue) else
-            {
-                assertionFailure("[Kingfisher] Failed to create CG context for blurring image.")
-                return base
-            }
-            
             #if os(macOS)
                 let result = outContext.makeImage().flatMap { fixedForRetinaPixel(cgImage: $0, to: size) }
             #else
@@ -615,13 +600,13 @@ extension Kingfisher where Base: Image {
             return base
         }
         let colorSpace = CGColorSpaceCreateDeviceRGB()
-        let bitmapInfo = imageRef.bitmapInfo.fixed
-        
-        guard let context = CGContext(data: nil, width: imageRef.width, height: imageRef.height, bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue) else {
+        guard let context = beginContext() else {
             assertionFailure("[Kingfisher] Decoding fails to create a valid context.")
             return base
         }
         
+        defer { endContext() }
+        
         let rect = CGRect(x: 0, y: 0, width: imageRef.width, height: imageRef.height)
         context.draw(imageRef, in: rect)
         let decompressedImageRef = context.makeImage()
@@ -721,24 +706,48 @@ extension CGSizeProxy {
     }
 }
 
-extension CGBitmapInfo {
-    var fixed: CGBitmapInfo {
-        var fixed = self
-        let alpha = (rawValue & CGBitmapInfo.alphaInfoMask.rawValue)
-        if alpha == CGImageAlphaInfo.none.rawValue {
-            fixed.remove(.alphaInfoMask)
-            fixed = CGBitmapInfo(rawValue: fixed.rawValue | CGImageAlphaInfo.noneSkipFirst.rawValue)
-        } else if !(alpha == CGImageAlphaInfo.noneSkipFirst.rawValue) || !(alpha == CGImageAlphaInfo.noneSkipLast.rawValue) {
-            fixed.remove(.alphaInfoMask)
-            fixed = CGBitmapInfo(rawValue: fixed.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
-        }
-        return fixed
-    }
-}
-
-
 extension Kingfisher where Base: Image {
     
+    func beginContext() -> CGContext? {
+        #if os(macOS)
+            guard let rep = NSBitmapImageRep(
+                bitmapDataPlanes: nil,
+                pixelsWide: Int(size.width),
+                pixelsHigh: Int(size.height),
+                bitsPerSample: cgImage?.bitsPerComponent ?? 8,
+                samplesPerPixel: 4,
+                hasAlpha: true,
+                isPlanar: false,
+                colorSpaceName: NSCalibratedRGBColorSpace,
+                bytesPerRow: 0,
+                bitsPerPixel: 0) else
+            {
+                assertionFailure("[Kingfisher] Image representation cannot be created.")
+                return nil
+            }
+            rep.size = size
+            NSGraphicsContext.saveGraphicsState()
+            guard let context = NSGraphicsContext(bitmapImageRep: rep) else {
+                assertionFailure("[Kingfisher] Image contenxt cannot be created.")
+                return nil
+            }
+            
+            NSGraphicsContext.setCurrent(context)
+            return context.cgContext
+        #else
+            UIGraphicsBeginImageContextWithOptions(size, false, scale)
+            return UIGraphicsGetCurrentContext()
+        #endif
+    }
+    
+    func endContext() {
+        #if os(macOS)
+            NSGraphicsContext.restoreGraphicsState()
+        #else
+            UIGraphicsEndImageContext()
+        #endif
+    }
+    
     func draw(cgImage: CGImage?, to size: CGSize, draw: ()->()) -> Image {
         #if os(macOS)
         guard let rep = NSBitmapImageRep(
@@ -791,36 +800,6 @@ extension Kingfisher where Base: Image {
     #endif
 }
 
-
-extension CGContext {
-    static func createARGBContext(from imageRef: CGImage) -> CGContext? {
-        
-        let w = imageRef.width
-        let h = imageRef.height
-        let bytesPerRow = w * 4
-        let colorSpace = CGColorSpaceCreateDeviceRGB()
-        
-        let data = malloc(bytesPerRow * h)
-        defer {
-            free(data)
-        }
-        
-        let bitmapInfo = imageRef.bitmapInfo.fixed
-        
-        // Create the bitmap context. We want pre-multiplied ARGB, 8-bits
-        // per component. Regardless of what the source image format is
-        // (CMYK, Grayscale, and so on) it will be converted over to the format
-        // specified here.
-        return CGContext(data: data,
-                         width: w,
-                         height: h,
-                         bitsPerComponent: imageRef.bitsPerComponent,
-                         bytesPerRow: bytesPerRow,
-                         space: colorSpace,
-                         bitmapInfo: bitmapInfo.rawValue)
-    }
-}
-
 extension Double {
     var isEven: Bool {
         return truncatingRemainder(dividingBy: 2.0) == 0