縮小画像をきれいに表示したい
動物将棋を作っていて「駒が小さい」という意見がありました。私の端末はQVGAなので現在の駒サイズでちょうど良い感じなのですが、世の中は画面の大きな端末だらけなので、QVGA用にサイズを合わせた駒サイズ(約50×50pixel)では小さくて、見えないというのはわかる気がします。
そこで、正月休みに大きな画面サイズにも対応できるように、動物将棋のプログラムを改造していました。考え方は簡単で、もともと大きな画像を用意しておき、表示時に画面サイズに合わせて小さくして表示しようというものです。
大きな画像の用意
実際に、250×250ピクセルの駒を用意しました。駒は下記のような感じです。ちなみに、駒がメロンなのは、動物将棋は卒業して、くだものしょうぶというゲームに代えて公開しようかなという考えからです。
駒は「すいか」「メロン」「バナナ」「みかん」「ぶどう」の5種類です。このサイズの駒を用意して、縮小して使おうと考えました。
駒を縮小する
現在の駒のサイズは、52×52ピクセルです。Luaridaの命令だと画像読み込み時に、下記のようにするだけです。
canvas.loadBmp( "/sdcard/luarida/melon.png", 0, 0, 51, 51 )
すると、下のような残念な画像になってしまいました。
この原因は、Luaridaのjavaプログラム内で縮小時にアンチエイリアスなどの処理を行っていないことが原因かなと思って、プログラムの見直しを始めました。
javaプログラム
javaのプログラムでは、画像をワークに取り込むときに、単純に下記のような感じで処理をしているだけです。
workcanvas.drawBitmap(bmp, new Rect(0, 0, w, h), new Rect(gx0, gy0, gx1+1, gy1+1), null);
そこで、テストプログラムを作り、縮小部分を下記のように改造してみました。
Matrix matrix = new Matrix(); float sc = 52f/250f; matrix.postScale( sc, sc ); Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix,true);
これで、scaledBitmapは52×52ピクセルの補正が効いた画像になっているはずです。ところが、結果は上とまったく同じでした。drawBitmap()は補正が掛かっているのですね。たぶん。
段階的に縮小する
もっと、元画像に近い画像に縮小できるように、いろいろと試してみましたが、きれいな画像を得ることができませんでした。仕方が無いので、自力でLanczos法やBi-Cubic法、Bi-Linear法のプログラムを実装し、パラメータを操作して、何とかきれいな画像を出せないかと試みましたが、結果的にはだめで、諦めました。
この試行錯誤で得た結果は、「一気に縮小してきれいな画像を得ようとするには限界がある」でした。
そこで、段階的に縮小する方向に考え方を代えました。例えば、250×250ピクセルの画像をn回縮小処理を繰り返して52×52ピクセルの画像を生成するという考え方です。この場合、n回で250ピクセルから52ピクセルに縮小するには、縮尺率scをいくつにすればいいかという話ですが、250*sc*sc*sc・・・n回 = 52ということなので、250*sc^n=52という式になります。
この式を解くと、sc = exp(ln(52/250)/n)となります。早速これをjavaでプログラムしてみました。
int wx = bmp.getWidth(); int newwx = 52; Matrix matrix = new Matrix(); int n = 3; //縮小回数 float sc = (float)Math.exp(Math.log((float)newwx/(float)wx)/(float)n); matrix.postScale( sc, sc ); Bitmap scaledBitmap = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix,true); for( int i=0; i<n-1; i++ ){ scaledBitmap = Bitmap.createBitmap(scaledBitmap, 0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight(), matrix,true); } workcanvas.drawBitmap(scaledBitmap, new Rect(0, 0, scaledBitmap.getWidth(), scaledBitmap.getHeight()), new Rect(0, 0, newwx, newwx), null);
プログラム自体は、matrix.postScale()によって設定した縮尺率の画像変換をループで繰り返すという単純なものです。結果は良好でした。
n=1の場合、今までと同じ結果です。
n=2の場合、少し変わりました。
n=3の場合、メロンの線が繋がってきました。
n=4の場合、元絵と同じイメージになりました。
n=5の場合、nが大きくなると、エッジがボケてきますね。
n=10の場合、n=10にしてみました。イメージは元絵に一番近いですが、全体的にボケていますね。
n=4くらいがいい感じでしょうか。