开发者

Android: Canvas.DrawBitmap VS Drawable.Draw - Huge performance boost

I just found out something and I was wondering about how and why. I'm developing a small arcade game for Android. I decided to ignore OpenGL and use the standard SurfaceView and Drawables to do it, since it's suppose to be light (10 sprites or so). I have drawables that I load, and I use the method Draw and passing them my canvas. This how every sprite is drawn to the screen. Well it turns out that drawing 4-5 big sprites (200X400 or so) takes a long time on less-than-brand-new phone models. Long enough to make my game unplayable. We're talking about 50-60 milliseconds to draw a single frame using this method. And I really don't do anything there apart from drawing, nowhere I can cut costs. So I decided to try and use Bitmaps instead. Here, however, I need to pre-set the size, since there's no 'setBounds' method in a bitmap. No prob, I resize them to fit my current screen on load, problem solved.

OK. So I got bitmaps. I use Canvas.DrawBitmap now to draw. I bench the new draw method.. and I get a whooping 400% performance boost! Instead of 50-60ms, the entire draw loop now takes 8-12ms. What the hell?? To rule it out, I timed the setBounds too, it takes <1ms so it's not to blame. It's the actual Drawable.Draw that slows things down.

For me this is great news, s开发者_Python百科ince I really didn't want to learn OpenGL to make my game playable, but I can't stop wondering about it - Is it fine? are there problems with my method? Why isn't it mentioned anywhere?


The SurfaceView of your Canvas is meant to be used when you should iterate constantly and Drawable is not for that purpose.


Canvas.drawBitmap is doing a lot less work than Drawable.draw so it is faster.

Drawable.draw

Since Drawable is an abstract class, let's look at BitmapDrawable:

BitmapDrawable.draw(canvas)

public void draw(Canvas canvas) {
    final Bitmap bitmap = mBitmapState.mBitmap;
    if (bitmap == null) {
        return;
    }
    final BitmapState state = mBitmapState;
    final Paint paint = state.mPaint;
    if (state.mRebuildShader) {
        final Shader.TileMode tmx = state.mTileModeX;
        final Shader.TileMode tmy = state.mTileModeY;
        if (tmx == null && tmy == null) {
            paint.setShader(null);
        } else {
            paint.setShader(new BitmapShader(bitmap,
                    tmx == null ? Shader.TileMode.CLAMP : tmx,
                    tmy == null ? Shader.TileMode.CLAMP : tmy));
        }
        state.mRebuildShader = false;
    }
    final int restoreAlpha;
    if (state.mBaseAlpha != 1.0f) {
        final Paint p = getPaint();
        restoreAlpha = p.getAlpha();
        p.setAlpha((int) (restoreAlpha * state.mBaseAlpha + 0.5f));
    } else {
        restoreAlpha = -1;
    }
    final boolean clearColorFilter;
    if (mTintFilter != null && paint.getColorFilter() == null) {
        paint.setColorFilter(mTintFilter);
        clearColorFilter = true;
    } else {
        clearColorFilter = false;
    }
    updateDstRectAndInsetsIfDirty();
    final Shader shader = paint.getShader();
    final boolean needMirroring = needMirroring();
    if (shader == null) {
        if (needMirroring) {
            canvas.save();
            // Mirror the bitmap
            canvas.translate(mDstRect.right - mDstRect.left, 0);
            canvas.scale(-1.0f, 1.0f);
        }
        canvas.drawBitmap(bitmap, null, mDstRect, paint);
        if (needMirroring) {
            canvas.restore();
        }
    } else {
        updateShaderMatrix(bitmap, paint, shader, needMirroring);
        canvas.drawRect(mDstRect, paint);
    }
    if (clearColorFilter) {
        paint.setColorFilter(null);
    }
    if (restoreAlpha >= 0) {
        paint.setAlpha(restoreAlpha);
    }
}

You can see that it even calls canvas.drawBitmap internally.

Canvas.drawBitmap

Compare that to Canvas.drawBitmap. It is much shorter.

Canvas.drawBitmap

public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) {
    throwIfCannotDraw(bitmap);
    native_drawBitmap(mNativeCanvasWrapper, bitmap, left, top,
            paint != null ? paint.getNativeInstance() : 0, mDensity, mScreenDensity, bitmap.mDensity);
}

There are a few different drawBitmap methods but all of them are shorter than the Drawable.draw method. Watch out for traps like this to keep your bitmap drawing fast.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜