2

I have a transparent sprite consists of 10 images which each image has size of 1,000x1,000 px (that means the sprite has size of 10,000x1,000 px). The reason I made quite big resolution because actually I have a full screen animation application and I just want to make sure the image still has a good quality on a big screen phones.

So I have class Sprite.java

public class Sprite {
    private static final int BMP_ROWS =1;
    private static final int BMP_COLUMNS = 10;
    private int x= 0;
    private int y = 0;
    private GameView gameView;
    private Bitmap bmp;
    private int currentFrame =0;
    private int width;
    private int height;

    public Sprite(GameView gameView, Bitmap bmp){
        this.gameView = gameView;
        this.bmp = bmp;
        this.width = bmp.getWidth() / BMP_COLUMNS;
        this.height = bmp.getHeight() / BMP_ROWS;
    }

    private void update(){
        currentFrame = ++currentFrame % BMP_COLUMNS;
    }

    public void onDraw(Canvas canvas){
        update();
        int srcX = currentFrame*width;
        int srcY = 0 * height;
        Rect src = new Rect(srcX, srcY, srcX+width, srcY+height);
        Rect dst = new Rect(x, y, x+width, y+height);
        canvas.drawBitmap(bmp, src, dst, null); 
    }
}

and this is the code of GameView.java

public class GameView extends SurfaceView {
    private Bitmap bmp;
    private SurfaceHolder holder;
    private GameLoopThread gameLoopThread;
    private Sprite sprite;

    public GameView(Context context) {
        super(context);
        gameLoopThread = new GameLoopThread(this);
        holder = getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {

            @Override
            public void surfaceDestroyed(SurfaceHolder holder) {
                boolean retry = true;
                gameLoopThread.setRunning(false);
                while(retry){
                    try{
                        gameLoopThread.join();
                        retry = false;
                    } catch(InterruptedException e){}
                }
            }

            @Override
            public void surfaceCreated(SurfaceHolder holder) {
                gameLoopThread.setRunning(true);
                gameLoopThread.start();
            }

            @Override
            public void surfaceChanged(SurfaceHolder holder, int format, int width,
                    int height) {
            }
        });
        bmp = BitmapFactory.decodeResource(getResources(), R.drawable.elephant);
        sprite = new Sprite(this, bmp);
    }
    @SuppressLint("WrongCall")
    @Override
    protected void onDraw(Canvas canvas){
        canvas.drawColor(Color.WHITE);
        sprite.onDraw(canvas);
    }
}

However, when I run it on the emulator, I got error

08-19 11:14:21.103: E/AndroidRuntime(385): FATAL EXCEPTION: main
08-19 11:14:21.103: E/AndroidRuntime(385): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:460)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:359)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:385)
08-19 11:14:21.103: E/AndroidRuntime(385):  at binus.killthemall.GameView.<init>(GameView.java:49)
08-19 11:14:21.103: E/AndroidRuntime(385):  at binus.killthemall.MainActivity.onCreate(MainActivity.java:15)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.ActivityThread.access$1500(ActivityThread.java:117)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.os.Handler.dispatchMessage(Handler.java:99)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.os.Looper.loop(Looper.java:123)
08-19 11:14:21.103: E/AndroidRuntime(385):  at android.app.ActivityThread.main(ActivityThread.java:3683)
08-19 11:14:21.103: E/AndroidRuntime(385):  at java.lang.reflect.Method.invokeNative(Native Method)
08-19 11:14:21.103: E/AndroidRuntime(385):  at java.lang.reflect.Method.invoke(Method.java:507)
08-19 11:14:21.103: E/AndroidRuntime(385):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
08-19 11:14:21.103: E/AndroidRuntime(385):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
08-19 11:14:21.103: E/AndroidRuntime(385):  at dalvik.system.NativeStart.main(Native Method)

I don't want to scale my image into smaller size. So, what's wrong and what should I do?

Kara
  • 6,115
  • 16
  • 50
  • 57
noobprogrammer
  • 1,140
  • 7
  • 22
  • 35
  • wanna display 10 images at a time? – Exceptional Aug 19 '13 at 05:04
  • http://stackoverflow.com/questions/8417034/how-to-make-bitmap-compress-without-change-the-bitmap-size – Exceptional Aug 19 '13 at 05:06
  • @Exeptional I'm making an animation using .png sprite that consists of 10 images, let's say every image changes every 1 second. Actually like this http://edumatica.ing.ula.ve/teleclases/tecnomatica/Animatica/Teleclase/Formacion/Sprites/Spriteca/Sprites%201.gif – noobprogrammer Aug 19 '13 at 05:16
  • @Exeptional 1000x1000 pixels – noobprogrammer Aug 19 '13 at 05:48
  • @noobprogrammer : I have already answer a similar question here. It might be helpful to you. http://stackoverflow.com/questions/18255572/android-bitmap-cache-takes-a-lot-of-memory/18255693#18255693 – arshu Aug 19 '13 at 06:26
  • hey, that's the problem.. try to reduce it.. else it will make prob.. – Exceptional Aug 19 '13 at 06:26

4 Answers4

0

Your bitmap size is 80 MBytes. It's really heavy and it's normal that android kills your app with such memory requests. You need to split it somehow, or use Bitmap Options to get image that you actually need.

Maxim Efimov
  • 2,747
  • 1
  • 19
  • 25
0

An app tends to get about 40ish MB of memory on Android (it varies based on device)- and that includes the size of all resources. So half of that is already gone. You really don't have a lot of memory to work with. You'll be able to load 1 image of that size on most devices. You won't be able to have 2, and if you try to page 1 in and the original out you'll end up spinning up the GC constantly. This approach won't work with images of this size on Android.

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • So, is there any solution to this problem? – noobprogrammer Aug 19 '13 at 06:03
  • Use smaller images and upscale. Or only hold 1-2 frames in memory at a time and accept the thrashing, possibly aided by an LRU cache- but you're going to have trouble doing this with images of this size, as you'd be leaving very little room for the entire rest of your app. You ma also want to break it up by screen size- smaller sizes tend to be lower spec devices, so they'll have less memory to play with. – Gabe Sechan Aug 19 '13 at 06:42
0

For decoding bitmap use this method and -See this also -Out of memory while creating bitmaps on device

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);
}


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) {

// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
    final int widthRatio = Math.round((float) width / (float) reqWidth);

// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
 }

 return inSampleSize;
}
Community
  • 1
  • 1
T_V
  • 17,440
  • 6
  • 36
  • 48
0
  1. Show a scaled image normally.

  2. Decode and show a portion of bitmap using BitmapRegionDecoder, when zoomed in.

  3. Use a disc Cache to store bitmaps.

S.D.
  • 29,290
  • 3
  • 79
  • 130