Skip to content

Instantly share code, notes, and snippets.

@dhoerl
Last active June 27, 2023 04:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dhoerl/c4b62696496f4a644239a950a1320cef to your computer and use it in GitHub Desktop.
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
// 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
}
@dhoerl
Copy link
Author

dhoerl commented Apr 26, 2020

I recently tripped on this CIImage filter (new in iOS11):

let newImage = origImage.oriented(CGImagePropertyOrientation(image.imageOrientation))
guard let cgImage = context.createCGImage(newImage, from: newImage.extent) else { fatalError() }
let showImage = UIImage(cgImage: cgImage)

It creates much smaller memory spikes than the above code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment