LoginSignup
14
10

More than 5 years have passed since last update.

UIImageをCGImageを利用してトリミングする場合は注意するという話

Posted at

はじめに

以前書いた CGImageは画像の向きを保持していない という記事で,cgImage については理解していたつもりでしたが,まだまだでした。

UIImage を CGImage を利用してトリミングした際に,再び CGImage にハマってしまったので記録しておきます。

UIImage と CGImage の関係

UIImage と CGImage は,以下の図のようになっています。CGImage は方向を持ちません。
ss 2019-02-08 at 14.43.11.png

UIImage を self.cgImage でトリミングする場合に起こり得ること

CGImage には,cropping(to rect: CGRect) -> CGImage? という,トリミング用の関数が用意されています。

しかし,そのまま image.cgImage?.cropping(to: rect) としてはいけません。

ss 2019-02-08 at 14.43.19.png

UIImage と CGImage の向き (size) が異なる場合,例え UIImage の size にフィットするような CGRect を用意しても,内部の CGImage が異なっていた場合,
CGImage のトリム範囲にズレが生じ,UIImage に戻したとしても,上手くトリミングがされません。

ss 2019-02-08 at 14.44.36.png
ss 2019-02-08 at 14.47.46.png

つまづきポイント

通常のトリミングでは,以下のようなコードになると思います。
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

14
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
10