今回も比較的基本的な話。
5月26日の記事で書いたように、OpenGL ESでは原則的に非正方形テクスチャは使えず、正方形でも一辺の長さが2のべき乗である必要がある。じゃあテクスチャ用画像を合成で作成してしまおうという話だった。前回で2のべき乗に関する計算をするメソッドを作成したので、今回はそれを使って実際に画像合成のサンプルを作成してみる。
実際にプロパティでも見ていただくとわかるが、この画像のサイズは240px×149px。なので256px×256pxの正方形にくるんでやれば良いことになる。画像の合成を行うコードはこんな感じになる。
public Bitmap makeTexturableBmp(Resources res, int resId){ //画像サイズに応じてビットマップ作成 Bitmap org = BitmapFactory.decodeResource(res, resId); Bitmap ret = null; if(org!=null){ int w = org.getWidth(); int h = org.getHeight(); if(w==h && MathUtil.isPowerOfTwo(w)){ //正方形で一辺が2のべき乗ピクセルならそのまま使う ret = org; } else { //ダメなら2のべき乗正方形で覆ったビットマップを作成する //w*hの画像を覆える最小の2のべき乗正方形を算出 int len = MathUtil.padToPowerOfTwo(w,h); Bitmap wrapper = Bitmap.createBitmap(len, len, Bitmap.Config.ARGB_8888); Canvas offScreen = new Canvas(wrapper); offScreen.drawColor(0x99990000); // ← 確認のために色を塗る offScreen.drawBitmap(org, 0f, 0f, null); ret = wrapper; org.recycle(); //ちゃんと消しておく } } return ret; }
MathUtilというクラスは5月26日の記事で載せた静的メソッドを集めたクラスである。条件に合う正方形ビットマップを作成し、Canvasオブジェクトをオフスクリーンとして合成を行って返すだけ。面倒くさいのは条件分岐のところなのだが、MathUtilを作っておいたのでさくっと書けた。あとはこのメソッドにリソースを食わせてあげればいい。
ImageView img = (ImageView) findViewById(R.id.img); img.setImageBitmap(makeTexturableBmp(getResources(),R.drawable.qbsample));
今回はアクティビティのonCreateで適当に描画して確かめる。実機(Xperia arc)の実行結果SSを撮ってみた。
ちゃんと256の正方形でくるまれた画像になっている。実際は色を塗らずに作成し、このビットマップをまるまるテクスチャとして登録した後、クリッピングすればよい。テクスチャ用に作ったのにテクスチャとして確認してないのは、OpenGLのコードを書くと長くなってしまうので。
さて、ここまでだらだら書いておいてナンだが、ぶっちゃけこの方法はあまり効率がよくない。
無駄が多いのは明らかだろう。例えばQB画像の横幅が257pxだっただけで、ラッパーの正方形は512px四方になってしまうのである。空白の部分にメモリを食うのは無駄だ(多分、そんな都合良い最適化とか行われないだろうし?)。ただでさえメモリの少ないAndroidでそんな無駄を許していいだろうか、まぁ状況次第だろうか。
そうなるともう少し広大な正方形を用意して、複数の画像をなるべく隙間無くマージしよう、という話になる。しかしこれは難問だ。今回のようにあらかじめサイズが分かっている画像リソースをマージするならうまいパッキング方法を自分で考えられるが、例えば、任意に指定された画像ファイルをマージするとなると……数学的な難問になってくる。
まぁ本当に任意のサイズの長方形を正方形にパッキングするアルゴリズムというのはあまりに大変なので、そのへんを上手く避けたソリューションを考えなければならない。次回はそんな記事を書く…かもしれない。