Spritesheet programmatically cutting: best practices

Zappescu picture Zappescu · Sep 7, 2011 · Viewed 13.2k times · Source

I have a big spritesheet (3808x1632) composed by 42 frames. I would present an animation with these frames and I use a thread to load a bitmap array with all the frames, with a splash screen waiting for its end. I'm not using a SurfaceView (and a draw function of a canvas), I just load frame by frame in an ImageView in my main layout. My approach is similar to Loading a large number of images from a spritesheet The completion actually takes almost 15 seconds, not acceptable.

I use this kind of function:

for (int i=0; i<TotalFramesTeapotBG; i++) {
            xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG; 
            yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
            mVectorTeapotBG.add(Bitmap.createBitmap(framesBitmapTeapotBG, xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG));
        }

framesBitmapTeapotBG is the big spritesheet. Looking more deeply, I've read in the logcat that the createBitmap function takes a lot of time, maybe because the spritesheet is too big. I found somewhere that I could make a window on the big spritesheet, using the rect function and canvas, creating small bitmaps to be loaded in the array, but it was not really clear. I'm talking about that post: cut the portion of bitmap

My question is: how can I speed the spritesheet cut?

Edit: I'm trying to use this approach but I cannot see the final animation:

    for (int i=0; i<TotalFramesTeapotBG; i++) {
        xStartTeapotBG = (i % framesInRowsTeapotBG) * frameWidthTeapotBG; 
        yStartTeapotBG = (i / framesInRowsTeapotBG) * frameHeightTeapotBG;
        Bitmap bmFrame = Bitmap.createBitmap(frameWidthTeapotBG, frameHeightTeapotBG, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmFrame); 
        Rect src = new Rect(xStartTeapotBG, yStartTeapotBG, frameWidthTeapotBG, frameHeightTeapotBG); 
        Rect dst = new Rect(0, 0, frameWidthTeapotBG, frameHeightTeapotBG);  
        c.drawBitmap(framesBitmapTeapotBG, src, dst, null);         
        mVectorTeapotBG.add(bmFrame);
    }

Probably, the Bitmap bmFrame is not correctly managed.

Answer

Steve Blackwell picture Steve Blackwell · Sep 8, 2011

The short answer is better memory management.

The sprite sheet you're loading is huge, and then you're making a copy of it into a bunch of little bitmaps. Supposing the sprite sheet can't be any smaller, I'd suggest taking one of two approaches:

  1. Use individual bitmaps. This will reduce the memory copies as well as the number of times Dalvik will have to grow the heap. However, these benefits may be limited by the need to load many images off the filesystem instead of just one. This would be the case in a normal computer, but Android systems may get different results since they're run off flash memory.
  2. Blit directly from your sprite sheet. When drawing, just draw straight from sprite sheet using something like Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint). This will reduce your file loads to one large allocation that probably only needs to happen once in the lifetime of your activity.

I think the second option is probably the better of the two since it will be easier on the memory system and be less work for the GC.