Swiftでの画像の扱い方：準備編

## はじめに

個人プロジェクトにおいてSwiftで画像を色々ごにょごにょしたかったので，勢いではなくちゃんと使うために（ちゃんと調査しつつ）記録しておこうと思いました。

（心の声：複数の画像を扱うためのクラスがあってめんどくさいしややこしいねん！）

## 画像に関するクラス

### UIImage

* [UIKitフレームワーク](https://developer.apple.com/documentation/coregraphics)のクラス
    * アプリ内での画像データを管理するためのもの
        * アプリ内に表示される画像は基本的にUIImageのデータとして用意すると思っておいたらいい
* イメージデータには様々なフォーマットがあるので，使ってるプラットフォームでサポートされているすべてのフォーマーットに対応するよう作られている。感謝

### CIImage

* [CoreImageフレームワーク](https://developer.apple.com/documentation/coreimage)のクラス
* CoreImageフレームワークは静止画像と動画像の高性能処理を実現する画像処理および解析技術らしい（英語不安）
* 元画像に，何らかの処理をして生成された存在
    * CIContextやCIColorとセットで使用し，CoreImageフィルタを使って処理
    * 元データとして紐づけられた画像（やデータ）は存在するが，CIImageオブジェクト自体はいわゆる画像ではないというところが要注意とか書いてました
* Appleのドキュメントは「レシピ」のようなものと言っているが，事前に「画像にどんな処理をするか」を決めておき，実際に画面に表示する段階でフィルタを一気に適応するという動きになるっぽい
    * "lazy evaluation"て表現があるので遅延評価なんでしょう
* CIImageオブジェクトは不変
    * スレッド間で安全に共有が可能

### CGImage

* [CoreGraphicsフレームワーク](https://developer.apple.com/documentation/coregraphics)のクラス
    * 軽量な2Dレンダリングを行います（by Apple)
    * パスベースの描画やアンチエイリアス処理，グラデーション処理なんかを担当できる
        * 処理の計算を行うのがお仕事
* CGImageはbitmap画像です
    * もしくはイメージマスク

## 画像の利用

```Swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let image = UIImage(named: "filename") // ちゃちゃっとプロジェクトのに追加した画像をファイル名で読み込む。便利

        let ciImageView = UIImageView(image: image)
        ciImageView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height / 2)
        ciImageView.contentMode = .scaleAspectFit
        
        self.view.addSubview(ciImageView)
        
        let cgImageView = UIImageView(image: image)
        cgImageView.frame = CGRect(x: 0, y: self.view.frame.height / 2, width: self.view.frame.width, height: self.view.frame.height / 2)
        cgImageView.contentMode = .scaleAspectFit

        self.view.addSubview(cgImageView)
       
        // ここから実験
        let ciImage = image?.ciImage
        let cgImage = image?.cgImage
        
        if let ciImage = ciImage {
            let uiImageFromCIImage = UIImage(ciImage: ciImage)
            let cgImageFromCIImage = createCGImage(from: ciImage)
            
            cgImageView.image = uiImageFromCIImage
        } else {
            print("??????")
        }
        
        if let cgImage = cgImage {
            let uiImageFromCGImage = UIImage(cgImage: cgImage)
            let ciImageFromCGImage = CIImage(cgImage: cgImage)
            
            cgImageView.image = uiImageFromCGImage
        }
    }

    func createCGImage(from source: CIImage) -> CGImage? {
        let context = CIContext(options: nil)
        let cgImage = context.createCGImage(source, from: source.extent)
        
        return cgImage
    }
}
```

UIImageからの変換は対応する`.ciImage`と`.cgImage`が用意されています。それぞれの説明が"The underlying Core Image data."と"The underlying Quartz image data."なので，UIImageがCIImageやCGImageをベースに作られているというイメージが持てます。
ですが，このコードだと，`.ciImage`がうまく反応しません。かわりに`CIImage(image: UIImage)`だとうまく動くのですが。
※原因わかったら追記しときます

一方，UIImageの作成には新しいインスタンスを作成することになっています。

CIImageの作成は，CGImageのインスタンスを渡せば作成できますが，CGImageの作成には一手間必要。

まずは，画像処理結果のレンダリングと画像解析を行うための評価用のコンテキストを作成し，そのコンテキストに元画像と画像の範囲を指定するための四角を引数に渡して作成するという手順になります。

## まとめ

* アプリの画像は，表面にUIとして利用されるものと，内部の計算（処理）に使用されるものは別
    * 画面に表示するためには，どっちが上なのか，とか，ｘ，ｙのデータなの画像そのものに必要ないデータが必要になるからしょうがない
* 切り替えは割と簡単にできる
    * UIImageには元データが存在するのでプロパティのように取得できる
        * が，うまく取れない時があるので，結局各クラスのインスタンスを生成して利用してしまいがちな私
    * CIImageとCGImageは元データなので新しいUIImageのインスタンス生成が必要

## 心に誓ったこと

* 「知ってるつもり」は厳禁
    * 改めてやってみるとうまくいかないとか普通にある
* 今後，どんな加工や変換ができるかまとめていきたいと心に誓いました