Reputation: 3052
Trying to figure how image is being rotated or flipped when one gets cgImage
from UIImage
which is in portrait mode.
I.e. if I have a case, where image.size.width < image.size.height
and then call let cg = image.cgImage!
, I get cg.width > cg.height
(in fact, cg.width == image.size.height && cg.height == image.size.width
). I know about the difference in coordinates for CGImage
and UIImage
, but what I don't understand - which corner of UIImage
is taken as the origin for CGImage
and whether image is flipped somehow?
This messes up with my cropping code, where I simply calculate cropping rectangle for UIImage
but then trying to crop image by calling image.cgImage!.cropping(to: rect)
gives me unexpected results (wrong area is cropped). Does rect
need to be in CGImage
's coordinate system? I tried flipping it like this, but it doesn't help either:
swap(&croppingRect.origin.x, &croppingRect.origin.y)
swap(&croppingRect.size.width, &croppingRect.size.height)
Upvotes: 1
Views: 517
Reputation: 3052
For those, who might run into similar problems as I did, I post this Swift playground code, which helped me understand what's going on and how to tackle it:
import UIKit
let image : UIImage = UIImage(named: "down.JPG")!
image.size
image.imageOrientation.rawValue
let center = CGPoint(x: 0.3, y: 0.3)
let kx = (center.x > 0.5 ? (1-center.x) : center.x)
let ky = (center.y > 0.5 ? (1-center.y) : center.y)
let size = CGSize(width: image.size.width*kx, height: image.size.height*ky)
// cropRect is defined in UIImage coordinate system
// it is defined as a rectangle with center in "center", with proportions
// to the original image. size of rectangle is defined by the distance from
// the center to the nearest edge divided by 2
// this was chosen due to my specific needs and can be adjusted at will
// (just don't exceed limits of the original image)
var cropRect = CGRect(x: center.x*image.size.width-size.width/2,
y: center.y*image.size.height-size.height/2,
width: size.width,
height: size.height)
cropRect
if self.imageOrientation == .right || self.imageOrientation == .down || self.imageOrientation == .left
{
let k : CGFloat = (self.imageOrientation == .right ? -1 :(self.imageOrientation == .left ? 1 : 2))
let dy = (self.imageOrientation == .right ? self.size.width : (self.imageOrientation == .left ? 0 :self.size.height))
let dx = (self.imageOrientation == .down ? self.size.width : (self.imageOrientation == .left ? self.size.height : 0))
let rotate = CGAffineTransform(rotationAngle: k*90/180*CGFloat.pi)
let translate = CGAffineTransform(translationX: dx, y: dy)
cropRect = cropRect.applying(rotate).applying(translate)
}
let cgImage = image.cgImage!
cgImage.width
cgImage.height
cropRect
let cropped = cgImage.cropping(to: cropRect)
if cropped != nil
{
cropped!.width
cropped!.height
let croppedImage = UIImage(cgImage: cropped!, scale: image.scale, orientation: image.imageOrientation)
}
Take 4 pictures: "up.JPG", "down.JPG", "left.JPG" and "right.JPG" for all possible camera configurations and upload them to the Resources subfolder of your playground. Load them one by one and check what's happening to the arguments. This code helped me to come up with the working solution: when image has .right
or .down
orientation, apply affine transforms to the cropping rectangle in order to get desired cropping.
Upvotes: 1