はじめに
以前書いた CGImageは画像の向きを保持していない という記事で,cgImage については理解していたつもりでしたが,まだまだでした。
UIImage を CGImage を利用してトリミングした際に,再び CGImage にハマってしまったので記録しておきます。
UIImage と CGImage の関係
UIImage と CGImage は,以下の図のようになっています。CGImage は方向を持ちません。
UIImage を self.cgImage でトリミングする場合に起こり得ること
CGImage には,cropping(to rect: CGRect) -> CGImage?
という,トリミング用の関数が用意されています。
しかし,そのまま image.cgImage?.cropping(to: rect)
としてはいけません。
UIImage と CGImage の向き (size) が異なる場合,例え UIImage の size にフィットするような CGRect を用意しても,内部の CGImage が異なっていた場合,
CGImage のトリム範囲にズレが生じ,UIImage に戻したとしても,上手くトリミングがされません。
つまづきポイント
通常のトリミングでは,以下のようなコードになると思います。
CGImage から UIImage を生成する際に,orientation
プロパティが存在するので,ここで補正されたと思い込んでしまいます。
しかし実際は,cgImage?.cropping(to: rect)
の時点でズレが生じており,生成時に方向を正したとしても,思う方向と異なるトリミングがされた画像が返ってしまいます。
extension UIImage {
func cropping(to rect: CGRect) -> UIImage? {
guard let cgImage = self.cgImage?.cropping(to: rect) else { return nil }
let cropped: UIImage = UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
return cropped
}
}
どうすれば良いのか
UIImage からトリミングする際,cgImage に渡す CGRect を,UIImage が持つ方向に合わせて調整する必要があります。
初めに,横向きの状態を取得する UIImage.Orientation
の extension として isLandscape: Bool
を用意します。
今後追加された場合を考え,switch 文の case は網羅しておきます。
extension UIImage.Orientation {
/// 画像が横向きであるか
var isLandscape: Bool {
switch self {
case .up, .down, .upMirrored, .downMirrored:
return false
case .left, .right, .leftMirrored, .rightMirrored:
return true
}
}
}
次に,横向きであった際に回転させる CGRect
の extension,switched: CGRect
を用意します。
extension CGRect {
/// 反転させたサイズを返す
var switched: CGRect {
return CGRect(x: minY, y: minX, width: height, height: width)
}
}
UIImage の extension に合わせて向きを揃えるように書いて,完成です!
extension UIImage {
func cropping(to rect: CGRect) -> UIImage? {
let croppingRect: CGRect = imageOrientation.isLandscape ? rect.switched : rect
guard let cgImage: CGImage = self.cgImage?.cropping(to: croppingRect) else { return nil }
let cropped: UIImage = UIImage(cgImage: cgImage, scale: scale, orientation: imageOrientation)
return cropped
}
}
References