Skip to content

Commit

Permalink
Merge pull request #567 from onevcat/fix/blur-image-crash
Browse files Browse the repository at this point in the history
Fix blur image crash
  • Loading branch information
onevcat committed Jan 20, 2017
2 parents a3edece + b3bba01 commit 0c4813c
Showing 1 changed file with 66 additions and 87 deletions.
153 changes: 66 additions & 87 deletions Sources/Image.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 0c4813c

Please sign in to comment.