Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix blur image crash #567

Merged
merged 3 commits into from
Jan 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.