「ピクセル密度」ってナニよ?

 さて。
 端末でアプリを実行すると、「画像表示(View)が拡大されてしまう」不具合について。
 (参照:「画面サイズ設定」ってナニよ?)
 原因は、画面サイズだと思ったんだけど、そうではなかったみたい。
 振り出しに戻る…。
 でも、ヒントはあった。
 それが「ピクセル密度」

「ピクセル密度」ってナニよ?

 ピクセル密度は、「1dotを何pixelで表すか?」というもの。
 何の役に立つのかというと、「表示されるものは、どの環境でも、同じ大きさで表示される」。
 17インチモニターでも、15インチモニターでも、そこに表示されるものは、同じ大きさになるってワケ。

 じゃ、なんで、画像が拡大表示しまうのか?
 それは画面と画像のピクセル密度が合っていないから。
 画像のピクセル密度が、画面の密度より低いため、拡大表示されてしまうんだ。
 PhotoShopやGIMPなんかで、印刷解像度を低くした時と似てる。
 「ピクセル密度 = 印刷解像度」みたいに捉えるとわかりやすいかもね。
 Androidでの表示は、WYSWYGの仕組みに近いんだ。

ピクセル密度を調べる

 ピクセル密度は、下記の方法で調べられる。
 実行すると、ログにピクセル密度が書き出されるよ。

	DisplayMetrics metrics = getResources().getDisplayMetrics();

	Log.d( "TEST", "density=" + metrics.density );

 DisplayMetrics では、画面に関する他の情報も取得できる。

	DisplayMetrics metrics = getResources().getDisplayMetrics();
	
	Log.d( "TEST", "density=" + metrics.density );
	Log.d( "TEST", "densityDpi=" + metrics.densityDpi );
	Log.d( "TEST", "scaledDensity=" + metrics.scaledDensity );
	Log.d( "TEST", "widthPixels=" + metrics.widthPixels );
	Log.d( "TEST", "heightPixels=" + metrics.heightPixels );
	Log.d( "TEST", "xDpi=" + metrics.xdpi );
	Log.d( "TEST", "yDpi=" + metrics.ydpi );

 ちなみに。
 「画面サイズ設定ってナニよ?」で画面サイズを調べた時、画面サイズは 533×400 だった。
 それぞれに、ピクセル密度:1.5 をかけると、800×600 になる。
 ちょうど、normalScreens での値とlargeScreens での値になるね。

画像リソースのピクセル密度設定(画像解像度設定)

 拡大表示される原因は、ピクセル密度の違いにあることがわかった。
 じゃ、画面のピクセル密度に、画像のピクセル密度を合わせればいいワケだ。
 でもソレってどうやるの…?

 印刷解像度をいじくったリソースを使用してもダメ。
 ピクセル密度は、画像リソースだけで行う設定じゃないんだ。

 ピクセル密度の設定は、Eclipseプロジェクトでも行うんだ。
 正確には、設定ではないんだけどね。(^_^;

 大抵、画像リソースは、drawable ディレクトリに置くよね?
 それと同じで、それぞれのピクセル密度のディレクトリに、最適化した画像リソースを置くんだ。

	drawable-ldpi … 120dpi( low )
	drawable-mdpi … 160dpi( medium )
	drawable-hdpi … 240dpi( high )

 たとえば、medium で533x400dot の画像の場合、drawable-hdpi に800x600dot に拡大した画像リソースを置く。
 そうすることで、動作環境がdensityDpi = 240(high)だったら、画像リソースはdrawable-hdpi から使用される。
 Android は動作環境に合わせて、それぞれから最適なピクセル密度のリソースを使用してくれるって仕組み。

 最適なリソースがない場合、Android が自動的に拡縮調整してくれる。
 ただし、基準はmedium(160dpi)。
 つまり、drawable にだけ置くと、リソースをmedium として拡縮処理をしてしまう。
 high(240dpi)の環境で表示すると、拡大表示されてしまうんだね。

 これは便利な仕組みなんだけど…ゲームなんかでは困った仕組みだよ。
 ゲームの場合、テレビのようにdpi調整はしないで欲しい…
 この場合、drawable-nodpi ディレクトリにリソースを置く。

	drawable-nodpi … dpi調整をしない

 今回、drawable ディレクトリにあったリソースを、drawable-nodpi へ移すことで、拡大表示されてしまう不具合は解消されたよ。

プログラムからピクセル密度(画像解像度)を設定する

 ピクセル密度は、リソース・ディレクトリで対応できることがわかった。
 でも、ソレってデータがリソースの時だけ。

 画像データを外部参照する時は…?

 外部参照だけじゃなく、プログラム側からピクセル密度を設定したい場合もあるよね。
 そこでピクセル密度を設定できるか、ざっと調べてみたよ。

◆Canvas

	int	 getDensity()
	void	 setDensity(int density)

http://developer.android.com/reference/android/graphics/Canvas.html

◆Bitmap

	int 	getDensity()
	void 	setDensity(int density)

	int 	getScaledHeight(int targetDensity)
	int 	getScaledWidth(int targetDensity)

http://developer.android.com/reference/android/graphics/Bitmap.html

◆BitmapDrawable

	void	 setTargetDensity(int density)
	void	 setTargetDensity(DisplayMetrics metrics)
	void	 setTargetDensity(Canvas canvas)

http://developer.android.com/reference/android/graphics/drawable/BitmapDrawable.html

 以上からすると、今回の不具合には、Viewで使ってるCanvas にピクセル密度を設定すればいいみたい。
 そこでこんな風↓にしてみたよ。

	Canvas canvas;
	Bitmap scrImg = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );
	
	scrImg.setDensity( 160 );
	Log.d("TEST", "scrImg density = " + scrImg.getDensity() );
	
	Canvas scrImg_canv = new Canvas( scrImg );
//	scrImg_canv.setDensity( 160 );
	Log.d("TEST", "Canvas density = " + scrImg_canv.getDensity() );

 Canvas に直接、設定するのではなく、元になるBitmap の密度を設定してみた。
 Bitmap の設定がCanvas に引き継がれるか、確かめたかったため。
 実行してみると、設定値はちゃんと引き継がれていた。

 表示する画像データは、drawable ディレクトリに置いた画像リソース。
 medium(160dpi) と認識されるので、setDensity で設定する値も160にしてある。

 以上で、拡大表示される不具合は解消できたよ。

 この設定は、他のことにも使えそう。
 たとえば、動的に値を変えて、拡縮操作の代わりにするとか。
 まぁ、setBounds の方が使い勝手はいいから、意味はないだろうけどね。(^_^;

Posted in Bitmap, ピクセル密度. Tags: , , , , , , , , . 「ピクセル密度」ってナニよ? はコメントを受け付けていません。 »

Bitmap の回転

 この何日か、スプライトの制御コマンドを作っているよ。
 で、その流れで、回転コマンドを作ってみることにした。

 Android での画像の回転は、Bitmap にかけるものらしい。
 BitmapDrawable には、回転に相当するものが見当たらない。
 これにはちょっと面食らっちゃったよ。
 スプライトは半透明を使うことを想定して、BitmapDrawable を描画するようにしてたから。

 仕方ないので、回転角を変更するたびに、Bitmap からBitmapDrawable を生成しなおすことにしてみたよ。
 処理落ちが気になるところだけど、やってみないことにはわからないね。

Matrix

 Bitmap の回転には、Matrix クラスを使う。
「Matrix に回転設定をして、Bitmap に適用する」みたいな感じ。
 Matrix クラスは回転の他にも、サイズ変更なんかもあって、色々と多彩なことができるみたい。

 で。回転は、postRotate method を使って、角度を設定する。
 ↓こんなテストmethod を作ってみた。

	protected Bitmap bitmap = null;
	protected pos_X = 0;
	protected pos_Y = 0;
	protected width = 0;
	protected height = 0;
	protected BitmapDrawable img = null;
	//--

	protected float roll = 0.0f;
	protected void rotate(){
		Matrix matrix = new Matrix();
		roll += 5.0f;
		matrix.postRotate( roll );  // 角度設定
		
		Bitmap _bitmap = Bitmap.createBitmap( bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
		img = new BitmapDrawable( _bitmap );	// Drawable変換
		
		int w = _bitmap.getWidth();
		int h = _bitmap.getHeight();
		
		pos_X = pos_X +( ( width - w ) /2 );
		pos_Y = pos_Y +( ( height - h ) /2 );
		
		width = w;
		height = h;	
	}
	//--

 このmethod は、実行するたびに、bitmapを5度ずつ右回転させて、BitmapDrawable を作り出している。
 bitmap とimg は、別method であらかじめ読み込んでいて、表示位置やサイズ等もその時に設定している。
 実描画も別method で行っている。
 ちなみに、実描画でBitmap を描画するなら、BitmapDrawable にする必要はないね。

 実行すると、こんな感じ。

 実行速度は、まぁ、こんなモンかな…って感じ。
 エミュレータ上なのでハッキリいえないけど、サイズを考慮すると遅いのは仕方ないやね。

by the way…

 18行目からの処理は、表示位置と幅の更新。

 位置更新をしないと、Bitmap のセンターで回転されず、宇宙遊泳してるみたいになってしまう。
 ソレはソレでおもしろかったけどね。(^_^;

 幅の更新は、描画をDrawable で行っているため。
 更新しないと、↓な感じに変形してしまう。(^_^;

 なぜDrawable で描画しているのかというと、半透明表示を行いたいから。
 いまわかっている時点では、半透明表示には、Drawable を使うしかないみたいなんだ。
 Bitmap のpaint にalpha マスクをかければいいような気もするけど…まだ試してない。
 拡縮もDrawable 上でやってたんで、作り直しだよ…。(-_-;

Posted in Bitmap, Matrix. Tags: , , , . Bitmap の回転 はコメントを受け付けていません。 »