Androidででっかい画像ファイルを扱う

Androidのアプリで2Mピクセルとか以上の大きいファイルを扱おうとするとOutOfMemoryの例外で落ちてしまう場合があります。
もちろん端末のメモリサイズにも大きく影響するので一概にどれだけと言えないのですが・・・。

通常、画像を扱う場合「Bitmap」クラスを使います。
何も考えずに使ってしまうと画像をそのままのサイズでメモリに展開してしまってメモリオーバーになるわけです。
ですが、予めサイズなどの情報だけを取得してリサイズして展開することができます。

そんな方法があるよって事を以前から聞いていて実際の方法をどこかのサイトで見たのですが迷子になったので残しておきます。

サンプルの内容
1.画像サイズを取得してリサイズ時の係数を計算
  「BitmapFactory.Options」を使用して「BitmapFactory.decodeFile」の動作を調節します。
  「inJustDecodeBounds」パラメータで画像の情報だけをOptionsに入れてくれます。
  「inSampleSize」へ指定する値を計算します。
  inSampleSize = 1 の時、等倍
  inSampleSize > 1 小さくなる
  inSampleSize < 1 1と同じ動作(つまり等倍)
  んで、例えば、4の時、一辺が1/4になるので面積は1/16になります。
2.実際に画像を読み込む
  ImageViewとかにデータを渡す場合はここまででいいかも。
  自前でCanvasに描画する場合とかは続きが必要
3.表示エリアのサイズに合わせて具体的にサイズ調節
  1ではアバウトにしかサイズ調節できないので。


/**
 * ビットマップをロードする
 * @param file 開くビットマップのファイルクラス
 * @param view_width 表示領域の幅
 * @param view_height 表示領域の高さ
 * @return
 */
private Bitmap loadBitmap(File file, int view_width, int view_height){
	Bitmap ret = null;
    	
	if(file == null){
	}else{
		//画像
		BitmapFactory.Options option = new BitmapFactory.Options();
		Bitmap src = null;
		int sample_size = 0;
			
		//実際に読み込まないで情報だけ取得する
		//んで、スケールを決める
		option.inJustDecodeBounds = true;	
		BitmapFactory.decodeFile(file.getAbsolutePath(), option);
		if((option.outWidth * option.outHeight) > 1048576){
			//1Mピクセル超えてる
			double out_area = (double)(option.outWidth * option.outHeight) / 1048576.0;
			sample_size = (int) (Math.sqrt(out_area) + 1);
		}else{
			//小さいのでそのまま
			sample_size = 1;
		}
		
		//実際に読み込むモード
		option.inJustDecodeBounds = false;	
		//スケーリングする係数 
		option.inSampleSize = sample_size;
		//画像を読み込む
		src = BitmapFactory.decodeFile(file.getAbsolutePath(), option);
		if(src == null){
		}else{
			int src_width = src.getWidth();
			int src_height = src.getHeight();
			
			//表示利用域に合わせたサイズを計算
			float scale = getFitScale(view_width, view_height, src_width, src_height);
			
			//リサイズマトリクス
			Matrix matrix = new Matrix();
			matrix.postScale(scale, scale);
				
			//ビットマップ作成
			ret = Bitmap.createBitmap(src, 0, 0, src_width, src_height, matrix, true);
		}
	}

	return ret;
}

/**
  * ベストフィットなスケーリング率を求める
  * @param dest_width 目的のサイズ(幅)
  * @param dest_height 目的のサイズ(高さ)
  * @param src_width 元のサイズ(幅)
  * @param src_height 元のサイズ(高さ)
  * @return
  */
 public static float getFitScale(int dest_width, int dest_height
    		, int src_width, int src_height){
	float ret = 0;
    	
	if(dest_width < dest_height){
		//縦が長い
		if(src_width < src_height){
			//縦が長い
			ret = (float)dest_height / (float)src_height;
    			
			if((src_width * ret) > dest_width){
				//縦に合わせると横がはみ出る
				ret = (float)dest_width / (float)src_width;
			}
		}else{
			//横が長い
			ret = (float)dest_width / (float)src_width;
		}
	}else{
		//横が長い
		if(src_width < src_height){
			//縦が長い
			ret = (float)dest_height / (float)src_height;
		}else{
			//横が長い
			ret = (float)dest_width / (float)src_width;

			if((src_height * ret) > dest_height){
				//横に合わせると縦がはみ出る
				ret = (float)dest_height / (float)src_height;
			}
		}
	}
    	
	return ret;
}