본문 바로가기
프로그래밍 놀이터/안드로이드, Java

[Android/안드로이드] Bitmap Resize 에 대한 모든 것. 당신도 이제 Bitmap 을 가지고 놀 수 있습니다.

by 돼지왕 왕돼지 2012. 1. 25.
반응형



1. Information


Bitmap Resize?


 네, 안드로이드는 모바일 디바이스죠.
데스크탑의 모니터처럼 큰 화면을 제공하는 것이 아니기 때문에, 원래 큰 이미지를 작게 줄여서 보여주는 작업이 많이 필요합니다.
예를 들어 인터넷에 있는 큰 그림을 안드로이드 브라우저에서 보여줄 때라던지..
고화질로 찍은 사진을 Gallery 에서 본다던지 할 때 Bitmap ( Image ) Resize 는 필수입니다.

이 때 사용하는 class 들이 Bitmap, BitmapFactory class 입니다.



Bitmap? BItmapFactory?


 네, Bitmap 과 BitmapFactory 는 안드로이드에서 제공하는 기본 class 로 이 두 녀석을 통해서
이미지에 대한 왠만한 처리를 다 할 수 있습니다.

Bitmap 은 이미지 자체를 나타내는 class 이면서, 여러가지 function 을 제공합니다.
BitmapFactory 는 여러가지 소스로부터 Bitmap Object 를 생성하는 일을 하는데, static function 으로 구성되어 있습니다.



BitmapFactory의 API 들을 좀 소개해주세요


static Bitmap decodeByteArray( byte[] data, int offset, int length, BitmapFactory.Options opts )
static Bitmap decodeByteArray( byte[] data, int offset, int length )

static Bitmap decodeFile( String pathName ) 
static Bitmap decodeFile( String pathName, BitmapFactory.Options opts )

static Bitmap decodeFileDescriptor( FileDescriptor fd )
static Bitmap decodeFileDescriptor( FileDescriptor fd, Rect outPadding, BitmapFactory.Options opts )

static Bitmap decodeResource( Resources res, int id, BitmapFactory.Options opts )
static Bitmap decodeResource( Resources res, int id )

static Bitmap decodeResourceStream( Resources res, TypedValue value, InputStream is, Rect pad, BitmapFactory.Options opts )
static Bitmap decodeStream( InputStream is )
static Bitmap decodeStream( InputStream is, Rect outPadding, BitmapFactory.Options opts ) 


API 이름들과 parameter들만 봐도 어떻게 쓰는지 아시겠죠?

각각의 API 들을 간단히만 설명하도록 하겠습니다.

BitmapFactory.decodeByteArray() Camera.PictureCallback 으로부터 전달받은 jpeg 사진 데이터를 기반으로 Bitmap 을 만들 때 사용됩니다.
Camera.PictureCallback 에서 Data 가 byte[] 형태로 들어오기 때문입니다.

BitmapFactory.decodeFile() 은 파일을 그대로 읽어오는 데 사용됩니다.

BItmapFactory.decodeResrouce() 는 Resource ( R. ) 으로부터 Bitmap 을 만들어내며,

BitmapFactory.decodeStream() 은 InputStream 으로부터 Bitmap 을 만들어냅니다.



Bitmap class 에도 static function 들이 있는 것 같던데..


 네. Bitmap class 에도 Bitmap 을 생성하는 static function 들이 있습니다.

static Bitmap createBitmap( Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter )
static Bitmap createBitmap( int width, int height, Bitmap.Config config )
static Bitmap createBitmap( Bitmap source, int x, int y, int width, int height )
static Bitmap createBitmap( int[] colors, int offset, int stride, int width, int height, Bitmap.Config config )
static Bitmap createBitmap( Bitmap src )
static Bitmap createBitmap( int[] colors, int width, int height,  Bitmap.Config config )

static Bitmap createScaledBitmap( Bitmap src, int dstWidth, int dstHeight, boolean filter ) 


이것도 이름과 parameter 들을 보면 단박에 어떤 녀석들인지 알 수 있죠?



Resize 에 대한 얘기는 언제 하나요?


이제부터 하겠습니다. :)

BitmapFactory.decodeXXXX function 들을 보면 똑같은 메소드가 2개씩 overloading 되어 있습니다.
같은 이름이지만 signature 가 다르죠. 마지막 parameter 로 BitmapFactory.Options 를 받느냐 안 받느냐의 차이죠.
이 BitmapFactory.Options 가 decode 를 하면서 옵션을 주는 녀석인데, 요놈이 Resize 와 관련이 있습니다.

BitmapFactory.Options 에도 여러가지가 있지만, Resize 쪽에서 꼭 봐야 할 옵션은 inSampleSize 입니다.
이 녀석은 decode 시 얼마만큼 줄여서 decoding 을 할지를 결정하는 옵션입니다.

inSampleSize 의 값은 다음과 같이 해석할 수 있습니다.
만약 inSampleSize = 4 라면. 4개의 픽셀을 한 픽셀로 쳐서 decode 해라.. 즉 1/4배 크기로 만들라는 이야기지요.

inSampleSize 옵션은 1보다 작은 값일때는 무조건 1로 ceil 되어 세팅됩니다. 1/1 = 1 배로 sample 하라는 의미이죠.
1보다 큰 값 값이 들어가면 1/N 배로 sample 하게 됩니다.
Tip 은.. decode 로직상 2의 지수의 값이 들어갈 때 가장 빠른 decode 속도를 보여준다고 합니다.
2, 4, 8, 16 ... 이런 숫자 말입니다.

2의 지수형태의 숫자가 아닐 때, 예를 들어 1/6 로 sampling 하고 싶을 때는 속도문제 때문에
inSampleSize = 6 을 주는 것보다, inSampleSize = 4 를 option 으로 주어 decoding 을 한 후.
Bitmap.createScaledBitmap() 메소드를 이용하여 한번 더 줄이는 것을 추천드립니다.



예를 한번 들어주세요~


 당연하지요. 예제만큼 빠른 학습도 없죠?

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 4;
Bitmap src = BitmapFactory.decodeFile( "/sdcard/image.jpg", options );
Bitmap resized = Bitmap.createScaledBitmap( src, dstWidth, dstHeight, true ); 


이 예는 약 4 와 8 사이의 sampling 일 경우 createScaledBitmap() 도 사용하는 예제입니다.
먼저 inSampleSize = 4 로 decoding 한 후, createScaledBitmap() 을 통해서 원하는 사이즈로 바꾸어 줍니다.



근데 듣다보니.. Resize 를 안 하면 안 되는거야?? 왜 귀찮게 Resize 를 해?


 단호히 "안됩니다!!".
Android 는 Mobile device 로 메모리 관리가 매우 중요한 이슈입니다.
이미지를 Resize 하지 않고 decode 하게 되면 큰 파일들에 대하여 OutOfMemoryError 가 발생하게 됩니다.
요즘은 물론 메모리가 1GB 가 넘어가는 상황이라 이전에 비해 이 OutOfMemoryError 가 발생하는 상황이 줄어들고 있지만,
브라우저나 갤러리 앱처럼 여러 이미지를 동시에 다루는 경우라면 더더욱 OutOfMemoryError 가 발생하기 쉽습니다.






자, 얼마나 많은 메모리를 사용하는지 알아보겠습니다.
제 단말 ( HTC Desire HD ) 의 Camera 로 촬영을 하면 기본적으로 2048 X 1216 의 해상도로 이미지가 저장이 됩니다.
이 데이터를 decode 하려면.. 

2048 * 1216 * 4byte = 9.5MB 의 메모리가 필요합니다.

사진 한장 디코드하는데 9.5MB 가 필요하다면.. 100장을 디코드하면 950MB 약 1G가 다 소진됩니다.
저사양 단말이라든지, 다른 process 가 많은 메모리를 소비하고 있다던지, 메모리 관리를 잘못 한 경우라면
쉽게 가용 메모리를 훌쩍 넘어버리게 되고 다음과 같은 에러 메세지를 보기 쉽습니다.


java.lang.OutOfMemoryError: bitmap size exceeds VM budget.


이 녀석은 Exception 이 아닌 Error 로서, 사용하고 있는 Memory를 제대로 다 해제하지 못한다면
앱을 무조건 닫아서 메모리를 확보해야 합니다.


[OutOfMemoryError 관련하여 GC 이야기 링크]



따라서 이 OutOfMemoryError 를 만나지 않으려면
반드시 원본 사이즈가 필요한 경우가 아니라면 SampleSize Option 을 주어 decoding 하는 습관을 길러야 하겠습니다.


[inSampleSize 에 대한 참고사항]


[Resize에 대한 숨겨진 스킬들]





2. Summary


- Android 는 Mobile Device 로 화면이 데스크탑 화면처럼 크지 않아서 Resize 를 해야 하는 경우가 많이 발생한다.

- Resize 와 Decoding은 Bitmap class 와 BitmapFactory class 를 활용하면 쉽다.

- Resize 는 BitmapFactory class 의 decode 함수의 parameter 로 BitmapFactory.Options 의 inSampleSize 값을 주어 할 수도 있고, Bitmap class 의 createScaledBitmap() 함수를 사용하여 할 수 있습니다.

- Resize 를 하지 않고 decode 를 할 경우 OutOfMemoryError 가 발생하기 쉽습니다.

- Resize 에 대한 좋은 예제들은 android 기본 앱에서 찾아 볼 수 있습니다.




3. References


- http://developer.android.com/reference/android/graphics/Bitmap.html 
   Android Developer Doc. Bitmap

http://developer.android.com/reference/android/graphics/BitmapFactory.html 
   Android Developer Doc. BitmapFactory







반응형

댓글