I have a UIImage which contains a shape; the rest is transparent. I\'d like to get another UIImage by cropping out as much of the transparent part as possible, still retain
Improved from @Danny182's answer, i also added white-space (any pixels brighter than 0xe0e0e0) trimming for my own need.
Usage:
let newimage = UIImage(named: "XXX")!.trim()
import UIKit
extension UIImage {
func trim() -> UIImage {
let newRect = self.cropRect
if let imageRef = self.cgImage!.cropping(to: newRect) {
return UIImage(cgImage: imageRef)
}
return self
}
var cropRect: CGRect {
let cgImage = self.cgImage
let context = createARGBBitmapContextFromImage(inImage: cgImage!)
if context == nil {
return CGRect.zero
}
let height = CGFloat(cgImage!.height)
let width = CGFloat(cgImage!.width)
let rect = CGRect(x: 0, y: 0, width: width, height: height)
context?.draw(cgImage!, in: rect)
//let data = UnsafePointer(CGBitmapContextGetData(context))
guard let data = context?.data?.assumingMemoryBound(to: UInt8.self) else {
return CGRect.zero
}
var lowX = width
var lowY = height
var highX: CGFloat = 0
var highY: CGFloat = 0
let heightInt = Int(height)
let widthInt = Int(width)
//Filter through data and look for non-transparent pixels.
for y in (0 ..< heightInt) {
let y = CGFloat(y)
for x in (0 ..< widthInt) {
let x = CGFloat(x)
let pixelIndex = (width * y + x) * 4 /* 4 for A, R, G, B */
if data[Int(pixelIndex)] == 0 { continue } // crop transparent
if data[Int(pixelIndex+1)] > 0xE0 && data[Int(pixelIndex+2)] > 0xE0 && data[Int(pixelIndex+3)] > 0xE0 { continue } // crop white
if (x < lowX) {
lowX = x
}
if (x > highX) {
highX = x
}
if (y < lowY) {
lowY = y
}
if (y > highY) {
highY = y
}
}
}
return CGRect(x: lowX, y: lowY, width: highX - lowX, height: highY - lowY)
}
func createARGBBitmapContextFromImage(inImage: CGImage) -> CGContext? {
let width = inImage.width
let height = inImage.height
let bitmapBytesPerRow = width * 4
let bitmapByteCount = bitmapBytesPerRow * height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapData = malloc(bitmapByteCount)
if bitmapData == nil {
return nil
}
let context = CGContext (data: bitmapData,
width: width,
height: height,
bitsPerComponent: 8, // bits per component
bytesPerRow: bitmapBytesPerRow,
space: colorSpace,
bitmapInfo: CGImageAlphaInfo.premultipliedFirst.rawValue)
return context
}
}