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 가 많은 메모리를 소비하고 있다던지, 메모리 관리를 잘못 한 경우라면
쉽게 가용 메모리를 훌쩍 넘어버리게 되고 다음과 같은 에러 메세지를 보기 쉽습니다.
이 녀석은 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
댓글