Android,将位图绘制到画布的最快方法

14

请问在canvas上绘制位图的最快方法是什么?

我目前有一张位图(和用于绘制的canvas),我使用它来双缓冲绘图调用,然后当我绘制到canvas上时通过应用1像素的canvas平移产生滚动效果。这样做会将帧速率从60+ FPS降至约40个,性能下降很明显。目前我没有使用SurfaceView或GLSurfaceView,只是想知道是否还有其他可以提高速度的方法。下面是onDraw()代码:

@Override
    public void onDraw(Canvas canvas)
    {
        //update fps text
        mFpsTracker.frameTouch();

        if(mBufferedBitmap == null)
        {
            mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444);
            mBufferedCanvas = new Canvas(mBufferedBitmap);
        }

        paint.setColor(Color.BLUE);
        mBufferedCanvas.drawLine(0, getHeight(), getWidth(), getHeight(), paint);
        mBufferedCanvas.translate(0, -1);


    canvas.drawBitmap(mBufferedBitmap, 0, 0, null); 

    //draw fps
    mTextPaint.setColor(Color.WHITE);
    canvas.drawText(mFpsTracker.getFPSString(), 40, 40, mTextPaint);


    invalidate();       
}

2
如果有人感兴趣,使用SurfaceView可以将此代码的帧速率提高约10-15FPS。 - Dori
我感到困惑,SurfaceView 到底有什么神奇的地方可以提高性能?或者你是在不同的方式下使用 SurfaceView -- 例如,我期望在大多数设备上使用 RGB_565 可以更快地绘制。 - Qwertie
1
在 SurfaceView 中,绘制是在 UI 线程之外完成的。http://developer.android.com/reference/android/view/SurfaceView.html - Dori
更正一下,可以在不同的线程上执行。SurfaceView 不需要启动新线程。即使需要,引入新线程也不能自动保证给定任务的更好性能。 - Qwertie
你也在onDraw()方法中调用了invalidate() - TheRealChx101
@Qwertie - 不是说这能保证更好的性能 - 只是报告观察到的性能差异。@chx101 - 这是很多年前的事情了,但据我所知,在这里不使用invalidate()意味着下一帧不会调用onDraw() - Dori
5个回答

6
请参阅Romain Guy的博客文章this
视频版本在此here
不要再使用ARGB_4444了,它已经过时了。每个像素只分配了每个通道4位(因此得名)。 ARBG_8888提供了16777216种颜色,而ARBG_4444仅提供4096种颜色,但每个像素使用4个字节而不是2个字节。
在Gingerbread中,Android将ARGB_8888作为Surface的标准格式,并增加了每个进程的内存分配量。

Window和(假设您使用简化的SurfaceViewRGBA_8888设置为格式更加高效。这可以避免明显较慢的格式更改。

其他提示包括:

  • 限制alpha合成,因为这需要从Skia进行比较昂贵的混合。
  • 请求Bitmap 选项,优先选择ARGB_8888 配置并禁用抖动。
  • 如果可能,删除窗口背景
  • 启用硬件加速,但要注意不支持的操作

在2.1设备上,我能够以50 fps在屏幕上绘制至少300个位图。


+1 谢谢。你的位图大小是整个画布的大小吗?就像我上面所示的那样? - Dori
不,我将代码拆分为可重用的部分,但从速度上来说不应该有太大影响。一般来说,经验法则是你应该能够以60帧每秒绘制屏幕上2.5倍数量的像素。 - Mark
我想,如果不知道尺寸或设备,很难量化性能的差异,但是我很感谢你的帖子和建议。你提到的经验法则是从哪里来的呢? - Dori
虽然声称60帧的说法实际上来自于这里,但由于文章中存在事实错误和糟糕的写作,其案例并没有得到充分证实。请参考此处:http://developer.android.com/guide/topics/graphics/hardware-accel.html#tips - Mark

2
我和你有一样的问题,请告诉我你发现了什么新的东西。
到目前为止,这是我找到的:
  1. 对于 Android 版本 > 3,最好不要使用双缓冲,因为你会得到硬件加速(需要在清单中设置为 true)
  2. 设置 paint.setDither(true),它将在任何具有不同颜色的设备上更好地工作,而 ARGB_4444 是大多数设备的颜色。点击此处了解更多信息

1
嘿,你确定将抖动设置为true更快吗?文档上说“一般情况下不使用抖动更快”。 - Dori
我确定,在超过7个设备上进行测试。这不是很大的差异,但它有点帮助,图片看起来更好。请检查我回答中添加的链接。 - Ilya Gazman

1
在onSizeChange中,您可以根据画布大小调整或创建位图,然后帧绘制速度将快得多,约为60fps。但是,在某些Android设备上,使用自定义视图在无限循环中会变慢并变得不稳定,因此我不建议这样做。相反,最好使用SurfaceView。
请查看此示例:如何在画布内使用动画框架?

+1 感谢提供链接。如果我一开始使用了错误大小的位图,那么只有在调用 onSizeChanged 后绘图才会更快吗? - Dori
这是因为Android设备屏幕尺寸的差异很大,加上Honeycomb 3.x没有使用全屏,因为它在底部有一个面板,所以onSizeChanged可以告诉画布的真实大小,从而可以相应地调整其他图像的大小,但与速度无关,你是正确的。 - Lumis
在你的情况下,我不确定位图有多大,但一个快速的方法是不翻译位图的画布,而只是在不同的X,Y坐标上绘制位图,或者使用canvas.drawBitmap(bitmap, fromRect, toRect, paint) 并像第二个例子中那样移动矩形。 - Lumis

0

我认为你可以从画布中获得良好的性能.. 但这需要大量的工作..

如果你从一个良好的性能图形库开始,即使你犯了很多错误,你可能仍然会得到良好的性能 :) 哈哈

目前有一个最快绘图库的竞赛正在进行... libgdx 目前处于领先地位...

http://code.google.com/p/libgdx/wiki/SimpleApp#Project_Setup


0

你需要在某个地方创建位图(例如 onCreate 或其他地方(构造函数会更好),因为当你做滚动效果时,你会不断地创建新的位图。所以只需要在构造函数中创建它,然后使用这个位图。

对于我有类似问题时,这是一个很好的解决方案。 尝试在其他地方创建这个mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444);,在没有 invalidate 的地方。 希望能帮到你。


我正在ondraw()调用中懒加载我的位图,这样它只会被创建一次,然后被重复使用...我应该查看translate以查看是否有任何奇怪的事情发生,或者在ddms下运行以查看是否有任何隐藏的对象创建。 - Dori

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接