Android のグラデーション品質を向上させる
Android の描画リソースによるグラデーションは粗い。
表示領域を広げてゆくと 16bit カラーのディスプレイが主流の頃によく見られたマッハ バンドのような縞が発生してしまう。仕事で作ったアプリでグラデーションを多用したのだが、それをタブレット端末でテストした時にこの縞が気になってた。
はじめは省メモリやパフォーマンスの観点から Android OS が品質を制限しているのだと諦めていた。しかし気まぐれに「Android gradient quality」かでググってみたら以下の記事を見つけた。
記事中のサンプルコードでは Activity.getWindow メソッドで得られたウィンドウに対し、Window.setFormat メソッドを呼び出している。パラメータは PixelFormat.RGBA_8888。リファレンスには説明がないけれど名前から察するにアルファ ブレンドを持つ 32bit カラーなのだろう。どうやらこれで Activity
の描画品質を変更できるらしい。
ちなみに Android 2.1 時点の Window.setFormat
は以下のように実装されている。
/**
* Set the format of window, as per the PixelFormat types. This overrides
* the default format that is selected by the Window based on its
* window decorations.
*
* @param format The new window format (see PixelFormat). Use
* PixelFormat.UNKNOWN to allow the Window to select
* the format.
*
* @see PixelFormat
*/
public void setFormat(int format) {
final WindowManager.LayoutParams attrs = getAttributes();
if (format != PixelFormat.UNKNOWN) {
attrs.format = format;
mHaveWindowFormat = true;
} else {
attrs.format = mDefaultWindowFormat;
mHaveWindowFormat = false;
}
if (mCallback != null) {
mCallback.onWindowAttributesChanged(attrs);
}
}
規定の描画品質に何が設定されているのかは Window.getAttributes メソッドから得られたインスタンスから分かる。実際にデバッグ実行して値を調べたところ WindowManager.LayoutParams.format に書かれているとおり PixelFormat.OPAQUE
が設定されていた。リファレンスに System chooses an opaque format (no alpha bits required)
と説明されているが、これは 24bit カラーということなのだろうか。
ともあれ描画品質を RGBA_8888
に設定することで滑らかなグラデーションを描けることが判明した。ここまでで調査完了なのだけど、せっかくなので品質の変更がどれだけ描画に影響を及ぼすのかを検証するサンプル アプリを作成してみた。
はじめに表示される画面では描画品質と色を選択する。ちなみにこの画面には RGBA_8888 を設定している。
品質は使用頻度の高そうな RGB_565
と RGBA_8888、ビットマップ操作時にメモリ節約のために選ばれることの多い RGBA_4444 を入れてみた。検証的には RGB_565 を OPAQUE にしたほうがよかったかも。色は Paint.NET の標準パレット上から私が好んで利用する配色を持ってきた。
メニューを選択して Show!! と書かれたボタンを押すと選んだ内容に応じたグラデーションが表示される。
タイトルバー的な部分の光沢グラデーションも画像ではなく描画リソースで定義している。スクリーンショットのとおり RGBA_8888
以外の品質はかなり低い。
最後にサンプル アプリのプロジェクトを公開しておく。
Android 2.1 update 1 (API Level 7) でビルド、エミュレータと初代 Xperia (SO-01B) にて動作確認した。
Xperia だと RGBA_4444
モード時に Activity
が真っ黒になる。同一バージョンの Android エミュレータでは表示できているので端末のディスプレイ ドライバによってはサポートされていないモードがあるのかもしれない。