Skip to content

Instantly share code, notes, and snippets.

@devonc
Last active November 7, 2019 20:07
Show Gist options
  • Save devonc/a54ffb3541a7499ade3d to your computer and use it in GitHub Desktop.
Save devonc/a54ffb3541a7499ade3d to your computer and use it in GitHub Desktop.

Revisions

  1. devonc revised this gist Jul 18, 2015. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion UIImage+ImageEffects_Swift2.swift
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    // Original from Apple https://developer.apple.com/library/ios/samplecode/UIImageEffects/Listings/UIImageEffects_UIImageEffects_m.html#//apple_ref/doc/uid/DTS40013396-UIImageEffects_UIImageEffects_m
    import Accelerate
    import UIKit

    @@ -76,7 +77,6 @@ extension UIImage {
    ]

    let divisor: CGFloat = 256
    let matrixSize = floatingPointSaturationMatrix.count
    let saturationMatrix = floatingPointSaturationMatrix.map {
    return Int16(round($0 * divisor))
    }
  2. devonc revised this gist Jul 18, 2015. 1 changed file with 136 additions and 0 deletions.
    136 changes: 136 additions & 0 deletions UIImage+ImageEffects_Swift2.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    import Accelerate
    import UIKit

    extension UIImage {
    func applyLightEffect() -> UIImage? {
    return applyBlur(radius: 30, tintColor: UIColor(white: 1, alpha: 0.3))
    }

    func applyExtraLightEffect() -> UIImage? {
    return applyBlur(radius: 20, tintColor: UIColor(white: 0.97, alpha: 0.82))
    }

    func applyDarkEffect() -> UIImage? {
    return applyBlur(radius: 20, tintColor: UIColor(white: 0.11, alpha: 0.73))
    }

    func applyTintEffect(color tintColor: UIColor) -> UIImage? {
    return applyBlur(radius: 10, tintColor: tintColor.colorWithAlphaComponent(0.6), saturationDeltaFactor: -1)
    }

    func applyBlur(radius blurRadius: CGFloat, tintColor: UIColor? = nil, saturationDeltaFactor: CGFloat = 1.8, maskImage: UIImage? = nil) -> UIImage? {
    if size.width < 1 || size.height < 1 {
    print(String(format: "*** error: invalid size: %.2f x %.2f. Both dimensions must be >= 1: \(self)", size.width, size.height))
    return nil
    }

    if CGImage == nil {
    print("*** error: image must be backed by a CGImage: \(self)")
    return nil
    }

    if let maskImage = maskImage where maskImage.CGImage == nil {
    print("*** error: maskImage must be backed by a CGImage: \(maskImage)")
    return nil
    }

    let imageRect = CGRect(origin: CGPointZero, size: size)
    var effectImage = self

    let hasBlur = Float(blurRadius) > FLT_EPSILON
    let hasSaturationChange = Float(abs(saturationDeltaFactor - 1)) > FLT_EPSILON

    if hasBlur || hasSaturationChange {
    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let effectInContext = UIGraphicsGetCurrentContext()
    CGContextScaleCTM(effectInContext, 1, -1)
    CGContextTranslateCTM(effectInContext, 0, -size.height)
    CGContextDrawImage(effectInContext, imageRect, CGImage)
    var effectInBuffer = vImage_Buffer(data: CGBitmapContextGetData(effectInContext), height: UInt(CGBitmapContextGetHeight(effectInContext)), width: UInt(CGBitmapContextGetWidth(effectInContext)), rowBytes: CGBitmapContextGetBytesPerRow(effectInContext))

    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let effectOutContext = UIGraphicsGetCurrentContext()
    var effectOutBuffer = vImage_Buffer(data: CGBitmapContextGetData(effectOutContext), height: UInt(CGBitmapContextGetHeight(effectOutContext)), width: UInt(CGBitmapContextGetWidth(effectOutContext)), rowBytes: CGBitmapContextGetBytesPerRow(effectOutContext))

    if hasBlur {
    let inputRadius = blurRadius * UIScreen.mainScreen().scale
    var radius = UInt32(floor(inputRadius * 3.0 * CGFloat(sqrt(2 * M_PI)) / 4 + 0.5))
    if radius % 2 != 1 {
    ++radius // force radius to be odd so that the three box-blur methodology works.
    }

    let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
    vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    }

    var effectImageBuffersAreSwapped = false
    if hasSaturationChange {
    let s = saturationDeltaFactor
    let floatingPointSaturationMatrix = [
    0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
    0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
    0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
    0, 0, 0, 1
    ]

    let divisor: CGFloat = 256
    let matrixSize = floatingPointSaturationMatrix.count
    let saturationMatrix = floatingPointSaturationMatrix.map {
    return Int16(round($0 * divisor))
    }

    if hasBlur {
    vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
    effectImageBuffersAreSwapped = true
    } else {
    vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
    }
    }

    if !effectImageBuffersAreSwapped {
    effectImage = UIGraphicsGetImageFromCurrentImageContext()
    }
    UIGraphicsEndImageContext()

    if effectImageBuffersAreSwapped {
    effectImage = UIGraphicsGetImageFromCurrentImageContext()
    }
    UIGraphicsEndImageContext()
    }

    // Set up output context.
    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let outputContext = UIGraphicsGetCurrentContext()
    CGContextScaleCTM(outputContext, 1, -1)
    CGContextTranslateCTM(outputContext, 0, -size.height)

    // Draw base image.
    CGContextDrawImage(outputContext, imageRect, CGImage)

    // Draw effect image.
    if hasBlur {
    CGContextSaveGState(outputContext)
    if let image = maskImage {
    CGContextClipToMask(outputContext, imageRect, image.CGImage)
    }
    CGContextDrawImage(outputContext, imageRect, effectImage.CGImage)
    CGContextRestoreGState(outputContext)
    }

    // Add in color tint.
    if let tintColor = tintColor {
    CGContextSaveGState(outputContext)
    CGContextSetFillColorWithColor(outputContext, tintColor.CGColor)
    CGContextFillRect(outputContext, imageRect)
    CGContextRestoreGState(outputContext)
    }

    // Output image is ready.
    let outputImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return outputImage
    }
    }
  3. devonc created this gist Apr 16, 2015.
    136 changes: 136 additions & 0 deletions UIImage+ImageEffects.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,136 @@
    import Accelerate
    import UIKit

    public extension UIImage {
    public func applyLightEffect() -> UIImage? {
    return applyBlur(radius: 30, tintColor: UIColor(white: 1, alpha: 0.3))
    }

    public func applyExtraLightEffect() -> UIImage? {
    return applyBlur(radius: 20, tintColor: UIColor(white: 0.97, alpha: 0.82))
    }

    public func applyDarkEffect() -> UIImage? {
    return applyBlur(radius: 20, tintColor: UIColor(white: 0.11, alpha: 0.73))
    }

    public func applyTintEffect(color tintColor: UIColor) -> UIImage? {
    return applyBlur(radius: 10, tintColor: tintColor.colorWithAlphaComponent(0.6), saturationDeltaFactor: -1)
    }

    public func applyBlur(radius blurRadius: CGFloat, tintColor: UIColor? = nil, saturationDeltaFactor: CGFloat = 1.8, maskImage: UIImage? = nil) -> UIImage? {
    if size.width < 1 || size.height < 1 {
    println(String(format: "*** error: invalid size: %.2f x %.2f. Both dimensions must be >= 1: \(self)", size.width, size.height))
    return nil
    }

    if CGImage == nil {
    println("*** error: image must be backed by a CGImage: \(self)")
    return nil
    }

    if let maskImage = maskImage where maskImage.CGImage == nil {
    println("*** error: maskImage must be backed by a CGImage: \(maskImage)")
    return nil
    }

    let imageRect = CGRect(origin: CGPointZero, size: size)
    var effectImage = self

    let hasBlur = Float(blurRadius) > FLT_EPSILON
    let hasSaturationChange = Float(abs(saturationDeltaFactor - 1)) > FLT_EPSILON

    if hasBlur || hasSaturationChange {
    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let effectInContext = UIGraphicsGetCurrentContext()
    CGContextScaleCTM(effectInContext, 1, -1)
    CGContextTranslateCTM(effectInContext, 0, -size.height)
    CGContextDrawImage(effectInContext, imageRect, CGImage)
    var effectInBuffer = vImage_Buffer(data: CGBitmapContextGetData(effectInContext), height: UInt(CGBitmapContextGetHeight(effectInContext)), width: UInt(CGBitmapContextGetWidth(effectInContext)), rowBytes: CGBitmapContextGetBytesPerRow(effectInContext))

    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let effectOutContext = UIGraphicsGetCurrentContext()
    var effectOutBuffer = vImage_Buffer(data: CGBitmapContextGetData(effectOutContext), height: UInt(CGBitmapContextGetHeight(effectOutContext)), width: UInt(CGBitmapContextGetWidth(effectOutContext)), rowBytes: CGBitmapContextGetBytesPerRow(effectOutContext))

    if hasBlur {
    let inputRadius = blurRadius * UIScreen.mainScreen().scale
    var radius = UInt32(floor(inputRadius * 3.0 * CGFloat(sqrt(2 * M_PI)) / 4 + 0.5))
    if radius % 2 != 1 {
    ++radius // force radius to be odd so that the three box-blur methodology works.
    }

    let imageEdgeExtendFlags = vImage_Flags(kvImageEdgeExtend)
    vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    vImageBoxConvolve_ARGB8888(&effectOutBuffer, &effectInBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    vImageBoxConvolve_ARGB8888(&effectInBuffer, &effectOutBuffer, nil, 0, 0, radius, radius, nil, imageEdgeExtendFlags)
    }

    var effectImageBuffersAreSwapped = false
    if hasSaturationChange {
    let s = saturationDeltaFactor
    let floatingPointSaturationMatrix = [
    0.0722 + 0.9278 * s, 0.0722 - 0.0722 * s, 0.0722 - 0.0722 * s, 0,
    0.7152 - 0.7152 * s, 0.7152 + 0.2848 * s, 0.7152 - 0.7152 * s, 0,
    0.2126 - 0.2126 * s, 0.2126 - 0.2126 * s, 0.2126 + 0.7873 * s, 0,
    0, 0, 0, 1
    ]

    let divisor: CGFloat = 256
    let matrixSize = count(floatingPointSaturationMatrix)
    let saturationMatrix = map(floatingPointSaturationMatrix) {
    return Int16(round($0 * divisor))
    }

    if hasBlur {
    vImageMatrixMultiply_ARGB8888(&effectOutBuffer, &effectInBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
    effectImageBuffersAreSwapped = true
    } else {
    vImageMatrixMultiply_ARGB8888(&effectInBuffer, &effectOutBuffer, saturationMatrix, Int32(divisor), nil, nil, vImage_Flags(kvImageNoFlags))
    }
    }

    if !effectImageBuffersAreSwapped {
    effectImage = UIGraphicsGetImageFromCurrentImageContext()
    }
    UIGraphicsEndImageContext()

    if effectImageBuffersAreSwapped {
    effectImage = UIGraphicsGetImageFromCurrentImageContext()
    }
    UIGraphicsEndImageContext()
    }

    // Set up output context.
    UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.mainScreen().scale)
    let outputContext = UIGraphicsGetCurrentContext()
    CGContextScaleCTM(outputContext, 1, -1)
    CGContextTranslateCTM(outputContext, 0, -size.height)

    // Draw base image.
    CGContextDrawImage(outputContext, imageRect, CGImage)

    // Draw effect image.
    if hasBlur {
    CGContextSaveGState(outputContext)
    if let image = maskImage {
    CGContextClipToMask(outputContext, imageRect, image.CGImage)
    }
    CGContextDrawImage(outputContext, imageRect, effectImage.CGImage)
    CGContextRestoreGState(outputContext)
    }

    // Add in color tint.
    if let tintColor = tintColor {
    CGContextSaveGState(outputContext)
    CGContextSetFillColorWithColor(outputContext, tintColor.CGColor)
    CGContextFillRect(outputContext, imageRect)
    CGContextRestoreGState(outputContext)
    }

    // Output image is ready.
    let outputImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return outputImage
    }
    }