この記事は ソフトウェア エンジニア、Peiyong Lin による Android Developers Blog の記事 "Wide Color Photos Are Coming to Android: Things You Need to Know to be Prepared" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
現在の Android のカラー チャンネルあたり 8 ビットの sRGB 色域では、ディスプレイやカメラの技術を十分に活かせない領域にさしかかっています。そこで Android では、ワイドカラー フォトをエンドツーエンドで実現する、つまりビット数を増やして色域を上げる作業を進めています。これが意味するのは、ユーザーが豊かな色彩のシーンを撮影し、ワイドカラーの写真を友人と共有したり、スマートフォンに表示したりできるようになるということです。そして Android Q ではこれらを現実に近づける取り組みが始まり、ワイドカラー フォトが登場します。そのため、アプリが広色域に対応することがとても重要になります。本記事では、アプリが広色域に対応しているか、それを表示できるかを確認する方法について説明します。また、広色域の写真に対応するために必要な手順も説明します。
本題に入る前に、なぜワイドカラー フォトが必要なのか、考えてみましょう。モバイル機器のディスプレイ パネルやカメラ センサーは、年々進化を続けています。新しくリリースされるスマートフォンは、キャリブレーション済みのディスプレイ パネルを搭載することが多くなるでしょう。その中には、広色域に対応したものもあります。最新のカメラ センサーは、sRGB よりも広い色域でシーンを撮影できるので、広色域の写真が生成されます。この 2 つを合わせると、エンドツーエンドで実世界の鮮やかな色を表現する写真が実現できます。
技術的に説明すると、皆さんのアプリに sRGB よりも広い色域を持つ
ICC プロファイル(Display P3、Adobe RGB など)が埋め込まれた写真が加わることになります。ユーザーにとっては、写真のリアルさが増すことになります。
Display P3
sRGB
Display P3
sRGB
上に示すのは、同じシーンをそれぞれ Display P3 と SRGB で撮影したイメージです。この記事をキャリブレーション済みの広色域対応ディスプレイで読んでいる方は、両者に大きな違いがあることに気づくはずです。
カラーテスト
アプリの対応状況を確認できる、2 種類のテストがあります。私たちは、1 つ目をカラー コレクトネス テスト、2 つ目をワイドカラー テストと呼んでいます。
カラー コレクトネス テスト: アプリが広色域に対応しているか
広色域に対応したアプリは、色を能動的に管理します。つまり、あるイメージが与えられたとき、アプリは常にカラースペースをチェックし、広色域を表示できるかに基づいて変換します。そのため、たとえアプリが広色域を扱えなくても、色のずれが発生することはなく、sRGB 色域を使ってイメージを正しく表示できます。
次に示すのは、Display P3 ICC プロファイルが埋め込まれたイメージを正しい色で表示した例です。
しかし、色が正しくないアプリでは、カラースペースを正しく変換せずにイメージを操作したり、表示したりすることになります。その結果、色のずれが発生します。たとえば、下のようなイメージになります。全体的に色あせて、色がずれたように見えます。
ワイドカラー テスト: アプリが広色域を表示できるか
広色域を表示できるアプリは、広色域のイメージが与えられた場合、sRGB カラースペースに含まれない色を表示できます。次に示すのは、アプリが広色域を表示できるかをテストするために使えるイメージです。表示できる場合、赤い Android ロゴが見えます。このテストは、Pixel 3 や Samsung Galaxy S10 などの広色域対応端末で実行する必要があります。
対応が必要になる点
広色域写真に対応するには、少なくともアプリが広色域対応テスト(カラー コレクトネス テスト)に合格しなければなりません。アプリが広色域対応テストに合格したら、それは何よりです!しかし合格しなかった場合のために、広色域に対応する手順を示します。
将来の保証を含め、広色域対応のために重要な点は、アプリが外部イメージを取得する際に、それが sRGB カラースペースであると仮定しないことです。つまり、アプリはデコードしたイメージのカラースペースをチェックし、必要な場合には変換しなければなりません。これを行わないと、色のずれが発生し、パイプラインの途中でカラー プロファイルが破棄されることになります。
必須: 正しい色を使う
少なくとも、正しい色を使う必要があります。アプリが広色域を採用しない場合、すべてのイメージを sRGB カラースペースにデコードしたいはずです。そのためには、BitmapFactory か ImageDecoder を使います。
BitmapFactory を使う
API 26 で
BitmapFactory.Option に
inPreferredColorSpace を追加しました。これを使うと、デコードしたビットマップのターゲット カラースペースを指定することができます。あるファイルをデコードする場合、色を管理するための一般的なスニペットは次のようになります。
final BitmapFactory.Options options = new BitmapFactory.Options();
// Decode this file to sRGB color space.
options.inPreferredColorSpace = ColorSpace.get(Named.SRGB);
Bitmap bitmap = BitmapFactory.decodeFile(FILE_PATH, options);
ImageDecoder を使う
Android P(API レベル 28)で ImageDecoder を導入しました。これは、イメージをデコードする最新の手法です。apk を API レベル 28 以上にアップグレードする場合は、BitmapFactory および BitmapFactory.Option API を使う代わりに、こちらを使うことをおすすめします。
次に示すのは、ImageDecoder#decodeBitmap API を使ってイメージを sRGB ビットマップにデコードするスニペットです。
ImageDecoder.Source source =
ImageDecoder.createSource(FILE_PATH);
try {
bitmap = ImageDecoder.decodeBitmap(source,
new ImageDecoder.OnHeaderDecodedListener() {
@Override
public void onHeaderDecoded(ImageDecoder decoder,
ImageDecoder.ImageInfo info,
ImageDecoder.Source source) {
decoder.setTargetColorSpace(ColorSpace.get(Named.SRGB));
}
});
} catch (IOException e) {
// handle exception.
}
ImageDecoder には、最終的なビットマップを得る前に、エンコードされているビットマップのカラースペースを把握できるというメリットもあります。これを行うには、
ImageDecoder.OnHeaderDecodedListener を渡して
ImageDecoder.ImageInfo#getColorSpace() をチェックします。アプリでのカラースペースの扱い方によっては、これを使ってエンコードされているコンテンツのカラースペースをチェックし、別のターゲット カラースペースを設定することもできます。
ImageDecoder.Source source =
ImageDecoder.createSource(FILE_PATH);
try {
bitmap = ImageDecoder.decodeBitmap(source,
new ImageDecoder.OnHeaderDecodedListener() {
@Override
public void onHeaderDecoded(ImageDecoder decoder,
ImageDecoder.ImageInfo info,
ImageDecoder.Source source) {
ColorSpace cs = info.getColorSpace();
// Do something...
}
});
} catch (IOException e) {
// handle exception.
}
詳しい使用方法については、
こちらから ImageDecoder API を参照してください。
既知のバッド プラクティス
典型的なバッド プラクティスには次のようなものがありますが、これに限られるわけではありません。
- 常に sRGB カラースペースを前提とする
- 必要な変換を行わずにイメージをテクスチャとしてアップロードする
- 圧縮時に ICC プロファイルを無視する
これらは、いずれもユーザーが検知できる重大な結果、すなわち色のずれを引き起こします。たとえば、次に示すのは、色が正しくないアプリのコード スニペットです。
// This is bad, don't do it!
final BitmapFactory.Options options = new BitmapFactory.Options();
final Bitmap bitmap = BitmapFactory.decodeFile(FILE_PATH, options);
glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES31.GL_RGBA, bitmap.getWidth(),
bitmap.getHeight(), 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, bitmap,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE);
ビットマップをテクスチャとしてアップロードする前にカラースペースをチェックしていないので、カラー コレクトネス テストで紹介した色がずれたイメージができあがります。
省略可能: ワイドカラーを表示できるようにする
イメージを多用するアプリでイメージを正しく扱うには、以上の必須の変更点に加えて、イメージを完全な色域で表示するための追加手順を組み込みます。具体的には、マニフェストで広色域モードを有効にするか、Display P3 サーフェスを作成します。
アクティビティで広色域を有効にするには、AndroidManifest.xml ファイルで
colorMode 属性を
wideColorGamut に設定します。これは、ワイドカラー モードを有効にするすべてのアクティビティで行う必要があります。
android:colorMode="wideColorGamut"
カラーモードは、アクティビティのプログラムで設定することもできます。これを行うには、
setColorMode(int) メソッドに
COLOR_MODE_WIDE_COLOR_GAMUT を渡します。
ワイドカラー コンテンツに加えて広色域コンテンツを描画するには、描画先となる広色域サーフェスを作成します。たとえば OpenGL では、最初にアプリで次の拡張機能をチェックする必要があります。
次に、サーフェスを作成する際に、カラースペースとして Display P3 をリクエストします。次のコード スニペットをご覧ください。
private static final int EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT = 0x3490;
public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
EGLConfig config, Object nativeWindow) {
EGLSurface surface = null;
try {
int attribs[] = {
EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_DISPLAY_P3_PASSTHROUGH_EXT,
egl.EGL_NONE
};
surface = egl.eglCreateWindowSurface(display, config, nativeWindow, attribs);
} catch (IllegalArgumentException e) {}
return surface;
}
ネイティブ コードで広色域を採用する方法について詳しく説明した
投稿もご覧ください。
イメージ ライブラリ用の API デザイン ガイドライン
最後に、イメージのデコードやエンコードを行うライブラリを所有またはメンテナンスしている方は、少なくともカラー コレクトネス テストに合格する必要があります。ライブラリを最新化する場合、API を拡張して色を管理する際に次の 2 つのことを強くおすすめします。
- 新しい API を設定する場合や、既存の API を拡張する場合は、パラメータとして明示的に ColorSpace を受け取るようにすることを強くおすすめします。カラースペースをハードコーディングするよりも、明示的に ColorSpace パラメータを使う方が将来性が高くなります。
- すべての従来の API では、ビットマップを明示的に sRGB カラースペースにデコードすることを強くおすすめします。昔はカラー マネジメントが存在しなかったので、Android 8.0(API レベル 26)までの Android はすべてを暗黙的に sRGB として扱います。これにより、ユーザーは下位互換性を維持することができます。
開発が終わったら、上のセクションに戻って 2 つのカラーテストを実行してください。
Reviewed by
Yuichi Araki - Developer Relations Team