Tistory View

Android Develop/helper

안드로이드 Bitmap 로드하기

God Dangchy What should I do? 2019. 9. 8. 21:15

안드로이드에서 지원하는 이미지 포맷

안드로이드 기본 디코더에서 지원하는 이미지 포맷은 현재 BMP, GIF, JPG, PNG, WebP, HEIF이며

WebP는 Android 4.0이상부터 WebP중 일부만 지원하고 무손실,투명은 4.2.1이상에서 지원한다. HEIF는 8.0부터 지원한다.

 

실제 프로그래밍할 경우는 읽을 수 있는 포맷만을 읽어들이기 때문에, 지원사항은 그리 중요하지 않다. 단지 이미지의 로딩에 성공/실패만이 필요할 따름이다. 만약 낮은 버전의 기기에서 WebP나 HEIF를 지원하고 싶다면 NDK를 이용하여 Decoder를 컴파일해서 앱에 넣어야 한다.

 

 

비트맵 로드하기

안드로이드에서는 이미지 파일/메모리등에서 바로 Bitmap을 생성할 수 있다.

Bitmap BitmapFactory.decodeFile(String pathName, BitmapFactory.Options opts);
Bitmap BitmapFactory.decodeByteArray(byte[] data, int offset, int length, BitmapFactory.Options opts)

또한 Resources와 InputStream과 FileDescriptor에서도 비트맵을 로드할 수 있다.

 

다음의 예는 파일에서 비트맵을 로드하는 예제이다.

try {

    Bitmap bmp = BitmapFactory.decodeFile("파일패스" );

    if ( bmp != null ) {
        m_ivShowImage.setImageBitmap(bmp); // m_ivShowImage : ImageView
    }
}
catch( Exception e ) {
    e.printStackTrace();
}

 

Asset에서 비트맵 로드하기

앱에서 이미지파일을 Asset에 넣어두는 경우가 많다. 실제 외부에서 다운로드하는 파일이 아닐 경우, Asset의 이미지를 넣고, 바로 가져와 사용할 경우가 많기에 다음과 같은 코드가 자주 사용된다.

Bitmap              bmp = null;
InputStream         is  = null;

AssetManager am = this.getAssets();

try {

    is = am.open( assetPath, AssetManager.ACCESS_UNKNOWN );
    if( is == null ) {
        throw new Exception("fail to open asset");
    }


    bmp = BitmapFactory.decodeStream( is, null, null );
    //bmp = BitmapFactory.decodeFileDescriptor( fd );
    if( bmp == null ) {
        throw new Exception("fail to load bitmap from InputStream");
    }

    m_ivShowImage.setImageBitmap(bmp);

    is.close();
    is = null;
}
catch( Exception e )
{
    Log.d( TAG, e.getMessage() );
    e.printStackTrace();
}
finally
{
    try {
        if( is != null ) {
            is.close();
        }
    }
    catch( Exception e ) { }
}

 

 

OOM을 피해야 한다.

Android에서 Java로 Bitmap을 다룰 경우 OOM(Out-Of-Memory)를 늘 신경쓰며 작업을 해야한다. 아무리 기기에 램이 많아도 Java로 프로그래밍을 할 경우, 한 앱이 사용할 수있는 메모리의 크기는 제한되어있기 때문에, 조금이라도 큰 Bitmap을 처리할 경우 OOM을 피할 수는 없다. 그래서 Bitmap을 처리할 때, 필요한 최소 사이즈로 불러와야 하며, 사용이 끝난 Bitmap은 즉시 즉시 Recycle을 해줘야 한다.

 

 

이 OOM을 피하게위해 이미지를 필요한 사이즈로 불러와야 하는 데, 그 논리는 필요한 이미지크기보다 같거나 큰 경우중 가장 작은 이미지로 불러오는 것으로, 이미지의 크기를 1/4씩 줄이기 때문에 필요한 크기보다 작은 것을 사용할 경우 화질이 치명적으로 떨어지는 문제가 있기에 이 방식을 쓴다.

 

이 코드는 구글에서 제공해 주고 있다.

 

public static 
int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight)
{
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }
    return inSampleSize;
}

public static
Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

사용법

imageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

예제는 Resource에서 Bitmap을 가져오지만, 이 부분을 다른 것으로 바꾸면 된다.

 

 

이 코드는 내부의 이미지 처리작업 중에서 실제 픽셀을 찍는 크기가 원본보다 작기 때문에(원본이 클경우만) 대부분의 경우 속도 또한 빨라진다. 특히 이 코드는 JPG파일에서 폭발적인 성능향상이 있는 데, 이는 대부분의 JPG파일의 저장방식이 8x8픽셀 단위로 저장되어있어, 필요없는 부분은 복잡한 디코딩작업을 아예하지 않기 때문이다. JPG파일이 대부분인 현시점에서는 아주 좋은 방식이다.

 

 

 

엣셋폴더를 만드는 법은 이 글을 참조

안드로이드 Canvas/Paint/TypeFace 외부 서체/폰트 이용하기

 

안드로이드 Canvas/Paint/TypeFace 외부 서체/폰트 이용하기

안드로이드의 Paint는 기본적으로 외부폰트파일을 이용할 수가 있다. 앱에 폰트파일을 포함하여 이를 불러올 수 있게 이미 함수를 지원해준다. 이 함수는 Typeface class내에 있다. 함수의 원형은 다음과 같다. pu..

jamssoft.tistory.com

 

Replies
Reply Write