Nicholas
Nicholas

Reputation: 2058

Low framerate writing bitmaps to the canvas

I have a live wallpaper that is running a looped animation by drawing each frame to the canvas. I have one single image, sized to the exact dimensions of the screen. I have a set of 400 frames sized exactly to fit about the bottom third of the screen; this is where the animation occurs. Here is the code that displays them:

public void updateBG() {
    mHandler.removeCallbacks(mUpdateDisplay);
    if (mVisible) {
        mHandler.postDelayed(mUpdateDisplay, 40);
    }

    if (imagesLoaded < totalFrames) {
        ShowLoadingProgress();
    } else {
        SurfaceHolder holder = getSurfaceHolder();
        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
                Paint p = new Paint();
                p.setAntiAlias(true);
                c.drawRect(0, 0, c.getWidth(), c.getHeight(), p);
                Rect destinationRect = new Rect();
                destinationRect.set(0, 0, canvasWidth, canvasHeight);


                if (animStartX > 0 || animEndX > 0 || animStartY > 0 || animEndY > 0) {
                    c.drawBitmap(BitmapFactory.decodeByteArray(bitmapArray[totalFrames], 0, bitmapArray[totalFrames].length), null, destinationRect, p);
                    Rect destinationRectAnim = new Rect();
                    destinationRectAnim.set(animX, animY, animX+animWidth, animY+animHeight);
                    c.drawBitmap(BitmapFactory.decodeByteArray(bitmapArray[bgcycle], 0, bitmapArray[bgcycle].length), null, destinationRectAnim, p);
                } else {
                    c.drawBitmap(BitmapFactory.decodeByteArray(bitmapArray[bgcycle], 0, bitmapArray[bgcycle].length), null, destinationRect, p);
                }

            }
        } finally {
            if (c != null)
                holder.unlockCanvasAndPost(c);
        }
        bgcycle++;
    }
}

This code is running at approximately 10-12 FPS on an Xperia Ion (dual-core 1.5 GHz cpu) and worse on older devices. It was going much better when I saved entire frames at half the screen resolution. It actually got worse when I started using full frames at the screen resolution (which should have eliminated the need to interpolate on each frame). It then got even worse when I started using this code, which writes two bitmaps (presumably because it can't save the top half of the canvas from the last run).

How can I optimize this? I'd rather not save full frames as just saving the animated part for each frame really reduces memory usage. Something feels really wrong here, especially given that this is running with 3GHz of power behind it. I used to watch movies on a 450 MHz Pentium III. Is there something obvious that I'm doing wrong? Is the phone still trying to interpolate the images even though they are sized correctly?

Upvotes: 0

Views: 243

Answers (1)

Romain Guy
Romain Guy

Reputation: 98501

You are decoding your bitmap on every frame, it's expensive and it will generate garbage that will trigger GCs. You are also rendering using software rendering. Here are your solutions, in order:

  • Don't re-decode the bitmaps on every frame
  • Reuse bitmap objects (see BitmapFactory.Options.inBitmap)
  • Use hardware acceleration (render directly into a View instead of a SurfaceView.) Software interpolation is very expensive.

Upvotes: 3

Related Questions