フォースダークを支える技術でダークテーマ用colors.xmlを生成する

## TD;DL
- Android #2 アドベントカレンダーの15日目の記事です。
- 下記レポジトリのスクリプトを実行すると生成できます。
https://github.com/sdsd08013/dark_color_gen

- layoutのcolor指定方法に強く依存するので基本的にはこれだけでダークテーマ対応が済むことはありません。

## はじめに
Android10から導入された機能にフォースダークというものがある。開発者向けオプションにあるフォースダークのオーバーライドという項目がそれにあたり、このオプションをONにした状態で既存のアプリを起動すると比較的高いクオリティでダークテーマを適用する技術である。この記事ではフォースダークを支える技術を利用して既存のcolors.xmlからダークテーマ用のcolors.xmlを生成する。

## フォースダークを支える技術

#### Lab色空間
Lab色空間はCIE1931色空間をベースとして定義された色空間で明度を示す次元Lと補色a*bにより表される。開発時に利用されることで馴染みがあるRGB色空間と比べると次元Lが特徴的なパラメータでこれは人間の視覚の明度に近く設計されている。
<img src="https://upload.wikimedia.org/wikipedia/commons/2/21/Lab_color_space.png" width="200">
[1]

#### Skia
Googleにより買収されたSkia incにより開発されていたchromiumやfirefoxでも採用実績のある2Dレンダリングを目的としたグラフィックライブラリ。
Android 3 Honyecombまでレンダリングにデフォルトで使用されていたがパフォーマンス上の理由からレンダリング処理は後述するHwuiに置き換えられた。レンダリング用の構造体などは引き続きSkia定義のものが利用されている。

#### Hwui
おそらくHardwareUIの略。
Android 3 Honyecombでハードウェア アクセラレーテッドのオプションとして追加されIceCream Sandwitch以降デフォルトとなったグラフィックライブラリ。Android端末が高解像度になったことやアプリケーションでリッチなアニメーションが要求されるようになったといった背景でSkiaでは表現できないグラフィックを表現するために導入された。

## Androidにおけるフォースダークの実現[3]
Androidでは以下のような手順を踏みダークテーマへ変換しグラフィックレンダリングを行っている、各行説明すると長くなるのでここでは省略する。

#### -> updateForceDarkMode

https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewRootImpl.java#4449

#### -> setForceDark
https://android.googlesource.com/platform/frameworks/base/+/master/graphics/java/android/graphics/HardwareRenderer.java#512

#### -> applyColorTransform
https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/CanvasTransform.cpp#68

#### -> transformColor
https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/CanvasTransform.cpp#57

#### -> makeDark
https://android.googlesource.com/platform/frameworks/base/+/master/libs/hwui/CanvasTransform.cpp#46

非ダークテーマのRGBカラーをダークテーマRGBカラーへと変換する処理でフォースダークの実体はこのコード。
RGB色空間をLab色空間に変換し必要があれば明度を下げてRGB色空間へ再度変換する。

```cpp
static SkColor makeDark(SkColor color) {
    Lab lab = sRGBToLab(color);
    float invertedL = std::min(110 - lab.L, 100.0f);
    if (invertedL < lab.L) {
        lab.L = invertedL;
        return LabToSRGB(lab, SkColorGetA(color));
    } else {
        return color;
    }
}
```

## ダークテーマ用colors.xmlの生成
Skiaについては共有ライブラリを比較的簡単に生成できる[2]ので良いのだが、Hwuiについてはandroidのソースコードが巨大すぎてコンパイルするのも大変そうだったので該当ファイルだけ抽出してヘッダーファイルを書き直す対応をした。

```
macOS Mojava
バージョン 10.14.3
```

```zsh
clang++ --std=c++14 \
    -I ~/skia/include/core \
    -I ~/skia/include/config \
    -I ~/skia/include/utils \
    -I ~/skia/include/gpu \
    -I ~/skia \
    -I transform.h \
    -I Color.h \
    -I ColorSpace.h \
    -L ~/skia/out/Static \
    -lskia -lz transform.cpp Color.cpp ColorSpace.cpp main.cpp
```

実際のコードは以下
https://github.com/sdsd08013/dark_color_gen

#### 結果
以下droidkaigi2018の公式アプリのcolors.xmlからダークテーマ用のcolors.xmlを生成してビルドした結果である。それっぽい色味にはなるがやはりこれだけだと完成度は低いなという印象で、実際のAndroidのフォースダークと比べても、リアルタイムでダークテーマ用のカラーを生成してレンダリングができるフォースダークのダークテーマのほうが完成度が高い。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56262/fc4d75ef-b87e-7785-6b3a-ec4e6f288dff.png" width="200"/><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/56262/581b6ebd-c84f-0254-c579-1e4c580d0af9.png" width="200"/>

## refs
[[1]wiki Lab色空間](https://ja.wikipedia.org/wiki/Lab%E8%89%B2%E7%A9%BA%E9%96%93)
[[2]skia公式](https://skia.org/user/build)
[[3]android Git repositories](https://android.googlesource.com/?format=HTML)
[Learning about the Android graphics subsystem](https://www.imgtec.com/blog/learning-about-the-android-graphics-subsystem/)