Skip to content

Android.graphics.Bitmap

안드로이드 비트맵에 대한 설명.

메모리가 넘치거나 새기 쉬운 비트맵 디코딩

네트워크로 이미지를 잘 읽어왔어도 메모리를 다루면서 빠지기 쉬운 함정이 많다.

비트맵의 크기가 클 때는 Out of Memory 에러가 발생하지 않도록 유의해야 한다. 이미지 파일은 디코딩을 거쳐야 화면에 출력된다. 비트맵이 차지하는 메모리의 용량은 이미지의 크기에 비례한다. Galaxy Nexus에서 촬영한 이미지의 크기는 2592x1936픽셀인데, 이는 비트맵으로는 약 19MB(2592 x 1936 x 4바이트)이다.[1] 가용 메모리가 16MB인 기기에서 이 비트맵을 메모리에 올리면 Out of Memory 에러가 발생한다. 따라서 작은 크기로 변환하거나 품질을 낮추어서 디코딩해야 한다. Android는 android.graphics.BitmapFactory.Options 클래스로 그런 기능을 제공한다.

Android Bitmap Warning

Android 버전에 따라서는 비트맵 자원을 명시적으로 해제해야 한다. Android 3.1(HONEYCOMB_MR1) 이상은 비트맵을 VM의 힙 메모리에 저장하지만, Android 2.3(Gingerbread) 이하 버전은 비트맵을 네이티브 힙에 저장한다. 예전 버전에서 비트맵은 CG(garbage collection)의 대상이 되지 않아 Bitmap 객체의 recycle() 메서드를 호출하여 직접 메모리를 해제해야 한다. 아니면 BitmapFactory.Options의 inPurgeable 플래그를 true로 설정한다.

Rounded corner bitmaps on Android

public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
    Canvas canvas = new Canvas(output);

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
    final RectF rectF = new RectF(rect);
    final float roundPx = 12;

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawRoundRect(rectF, roundPx, roundPx, paint);

    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);

    return output;
}

Masking(crop) image

Finally got the solution while changing mask image and using of Xfermode with Bitmap

ImageView mImageView= (ImageView)findViewById(R.id.imageview_id);
Bitmap original = BitmapFactory.decodeResource(getResources(),R.drawable.content_image);
Bitmap mask = BitmapFactory.decodeResource(getResources(),R.drawable.mask);
Bitmap result = Bitmap.createBitmap(mask.getWidth(), mask.getHeight(), Config.ARGB_8888);
Canvas mCanvas = new Canvas(result);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mCanvas.drawBitmap(original, 0, 0, null);
mCanvas.drawBitmap(mask, 0, 0, paint);
paint.setXfermode(null);
mImageView.setImageBitmap(result);
mImageView.setScaleType(ScaleType.CENTER);
mImageView.setBackgroundResource(R.drawable.background_frame);

두 개의 비트맵 이미지를 병합하는 방법

/** 두 비트맵을 머지 한다.
  * @param src 원래의  비트맵
  * @param mergeBitmap 머지할 비트맵
  * @return 병합한 비트맵
  */
public static Bitmap mergeBitmap(Bitmap src, Bitmap mergeBitmap) {
    int width = src.getWidth();
    int height = src.getHeight();

    Bitmap mergeBitmap2 = Bitmap.createScaledBitmap(mergeBitmap, width, height, true);

    for (int x=0; x<width; x++) {
        for (int y=0; y<height; y++) {
            int maskPixel = mergeBitmap2.getPixel(x, y);
            int tilePixel = src.getPixel(x, y);

            int mergedPixel = Color.argb(255, Color.red(tilePixel) | Color.red(maskPixel), 
                Color.green(tilePixel) | Color.green(maskPixel), Color.blue(tilePixel) | Color.blue(maskPixel));

            src.setPixel(x, y, mergedPixel);
        }
    }

    return src;
}

Drawable to Bitmap 변환방법

BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.icon);
Bitmap bitmap = drawable.getBitmap();

두 개의 비트맵 이미지를 합성하는 방법

private Bitmap mergeBitmap(Bitmap src1, Bitmap src2, int distanceLeft, int distanceTop) {
    Bitmap overlay = Bitmap.createBitmap(src1.getWidth() + distanceLeft, src1.getHeight() + distanceTop, src1.getConfig());
    Canvas canvas = new Canvas(overlay);
    canvas.drawBitmap(src1, distanceLeft, distanceTop, null);
    canvas.drawBitmap(src2, 0, 0, null);
    return overlay;
}

비트맵 이미지를 저장하는 방법

우선, 아래와 같이 권한을 설정한다.

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

소스코드는 아래와 같다.

imgview = (ImageView)findViewById(R.id.imageView1);
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.comp1_16);
imgview.setImageBitmap(bm);
try {
    File file = new File("test.png");
    FileOutputStream fos = new FileOutputStream(file);
    bm.compress(CompressFormat.PNG, 100 , fos);
    fos.flush();
    fos.close();
    Toast.makeText(this, "file ok", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
    Toast.makeText(this, "file error", Toast.LENGTH_SHORT).show();
}

저장된 Bitmap 불러오기

저장할때는 파일 이름만 있어도 알아서 App의 Data폴더에 저장되지만 불러올때는 전체 패스를 다 적어줘야한다.

try {
    imgview = (ImageView)findViewById(R.id.imageView1);
    String imgpath = "data/data/com.test.SDCard_Ani/files/test.png";
    Bitmap bm = BitmapFactory.decodeFile(imgpath);
    imgview.setImageBitmap(bm);
    Toast.makeText(getApplicationContext(), "load ok", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
    Toast.makeText(getApplicationContext(), "load error", Toast.LENGTH_SHORT).show();
}

비트맵 회전

Bitmap이나 Drawable을 회전 할 수 있는 샘플코드는 아래와 같다.

public static Bitmap rotate(Bitmap b, int degrees) {
    if ( degrees != 0 && b != null ) {
        Matrix m = new Matrix();
        m.setRotate( degrees, (float) b.getWidth() / 2, (float) b.getHeight() / 2 );
        try {
            Bitmap b2 = Bitmap.createBitmap( b, 0, 0, b.getWidth(), b.getHeight(), m, true );
            if (b != b2) {
                b.recycle();
                b = b2;
            }
        } catch (OutOfMemoryError ex) {
            // We have no memory to rotate. Return the original bitmap.
        }
    }
    return b;
}

비트맵 크기 변환 방법

안드로이드에서 비트맵 리사이징 하는 방법은 아래와 같다.

BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.movie_opening_1);
Bitmap movie_opening_bitmap = Bitmap.createScaledBitmap(drawable.getBitmap(), destWidth, destHeight, true);

See also

Favorite site

References


  1. Hello_world_»_Android_App_memory_optimization.pdf