Last active
June 27, 2023 04:34
-
-
Save dhoerl/c4b62696496f4a644239a950a1320cef to your computer and use it in GitHub Desktop.
Process Image Data in any orientation and make a UIImage in an "up" orientation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Based on https://gist.github.com/schickling/b5d86cb070130f80bb40#gistcomment-2894406 | |
func image(data: Data, orientation: UIImage.Orientation = .up) -> UIImage? { | |
let context: CGContext | |
let width: CGFloat | |
let height: CGFloat | |
func defaultImage() -> UIImage? { | |
return UIImage(data: data) | |
} | |
do { | |
guard | |
let imageSource = CGImageSourceCreateWithData(data as CFData, nil), | |
let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as NSDictionary?, | |
let orientation = CGImagePropertyOrientation(rawValue: properties[kCGImagePropertyOrientation] as? UInt32 ?? 1), | |
let image = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) | |
else { return defaultImage() } | |
guard orientation != .up else { return UIImage(cgImage: image) } | |
let imageOrientation = UIImage.Orientation(orientation) | |
let bytesPerRow: Int | |
switch imageOrientation { | |
case .left, .leftMirrored, .right, .rightMirrored: | |
width = CGFloat(image.height) | |
height = CGFloat(image.width) | |
bytesPerRow = ((Int(width)+15)/16) * 16 * (image.bitsPerPixel/8) | |
default: | |
width = CGFloat(image.width) | |
height = CGFloat(image.height) | |
bytesPerRow = image.bytesPerRow | |
} | |
guard let _context = CGContext(data: nil, | |
width: Int(width), | |
height: Int(height), | |
bitsPerComponent: image.bitsPerComponent, | |
bytesPerRow: bytesPerRow, | |
space: image.colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)!, | |
bitmapInfo: image.bitmapInfo.rawValue) | |
else { return defaultImage() } | |
context = _context | |
let drawRect: CGRect | |
var transform: CGAffineTransform = CGAffineTransform.identity | |
switch imageOrientation { | |
case .down, .downMirrored: | |
transform = transform.translatedBy(x: width, y: height) | |
transform = transform.rotated(by: CGFloat.pi) | |
case .left, .leftMirrored: | |
transform = transform.translatedBy(x: width, y: 0) | |
transform = transform.rotated(by: CGFloat.pi / 2.0) | |
case .right, .rightMirrored: | |
transform = transform.translatedBy(x: 0, y: height) | |
transform = transform.rotated(by: CGFloat.pi / -2.0) | |
case .up, .upMirrored: | |
break | |
@unknown default: | |
break | |
} | |
// Flip image one more time if needed to, this is to prevent flipped image | |
switch imageOrientation { | |
case .upMirrored, .downMirrored: | |
transform = transform.translatedBy(x: width, y: 0) | |
transform = transform.scaledBy(x: -1, y: 1) | |
case .leftMirrored, .rightMirrored: | |
transform = transform.translatedBy(x: height, y: 0) | |
transform = transform.scaledBy(x: -1, y: 1) | |
case .up, .down, .left, .right: | |
break | |
@unknown default: | |
break | |
} | |
context.concatenate(transform) | |
switch imageOrientation { | |
case .left, .leftMirrored, .right, .rightMirrored: | |
drawRect = CGRect(x: 0, y: 0, width: height, height: width) | |
default: | |
drawRect = CGRect(x: 0, y: 0, width: width, height: height) | |
} | |
context.draw(image, in: drawRect) | |
// image released | |
} | |
guard let newImage = context.makeImage() else { return defaultImage() } | |
let uiImage = UIImage(cgImage: newImage, scale: 1, orientation: .up) | |
return uiImage | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I recently tripped on this CIImage filter (new in iOS11):
It creates much smaller memory spikes than the above code.