更好的帧速率绘制画布中的位图?

时间:2011-05-29 03:49:33

标签: android opengl-es bitmap live-wallpaper

我正在动态壁纸拍摄动画。这是代码。它几乎遵循CubeWallpaper演示:

    void drawFrame() {
        final SurfaceHolder holder = getSurfaceHolder();
    final BufferedInputStream buf;
    final Bitmap bitmap, rbitmap;

        Canvas c = null;
        try {
            c = holder.lockCanvas();
            if (c != null) {
        try {
        buf = new 
            BufferedInputStream(assets.
                    open(folder+"/"
                         +imageList[ilen++])
                    );
        bitmap = BitmapFactory.
            decodeStream(buf);
        rbitmap = Bitmap.createBitmap
            (bitmap,
             0,0,imageWidth,imageHeight,
             transMatrix,false);
        c.drawBitmap(rbitmap,
                 paddingX,
                 paddingY,
                 null);
        if ( ilen >= imageCount ) ilen=0;
        }
        catch (Exception e) { e.printStackTrace(); }
            }
        } finally {
            if (c != null) holder.unlockCanvasAndPost(c);
        }

        // Reschedule the next redraw
        mHandler.removeCallbacks(mDrawCube);
        if (mVisible) {
            mHandler.postDelayed(mDrawCube, fps);
        }
    }

其中“transMatrix”是之前预定义的缩放和旋转矩阵。

它应该以30fps渲染,但当然不会那样做。我最初的猜测是BufferedInputStream是一个因素。我应该缓存其中的一些,因为我和Bitmaps一起使用。但任何其他想法?我是否必须使用OpenGL黑客进行动态壁纸?

2 个答案:

答案 0 :(得分:4)

是的,BufferedInputStreamBitmapFactory确实不应该在drawFrame()。你在每一帧上加载和创建资源,这是一个巨大的浪费。就像你说的那样,事先尽可能多地缓存,如果你发现在绘图过程中需要加载更多,请使用一个单独的线程来做到这一点,这样就不会减慢绘图速度。

答案 1 :(得分:3)

我遇到了同样的问题:在动态壁纸的背景下慢画布渲染。

我同意其他人说你在渲染时不应该做任何重的cpu / io特别是在UI线程上加载图像。

但是还有一件事需要注意。您在渲染帧后请求重绘(mHandler.postDelayed(...))。如果你想要30 fps并因此你要求重绘(1000/30)33ms那么它不会导致每秒30帧。为什么呢?

让我们假设将所有东西渲染到画布需要28ms。完成后,您要求在33毫秒后重绘。也就是说,重绘之间的时间间隔为61毫秒,相当于每秒16帧。

您有两种方法可以解决它:

1)将mHandler.postDelayed(...)代码放到drawFrame(...)方法的开头。这似乎没问题,但它有一些缺点:如果你的实际FPS非常接近实际设备上的最大可能FPS - 换句话说UI线程一直忙着你的画布渲染 - 那么就没有时间了UI线程做其他事情。它并不一定意味着你的LWP或主屏幕会滞后但你(你的LWP)可能会开始错过一些触摸事件(就像我的LWP那样)。

2)更好的解决方案是在创建曲面时启动一个单独的线程,并将其引用传递给SurfaceHolder。在这个单独的线程中进行渲染。此线程中的render方法如下所示:

private static final int DESIRED_FPS = 25;
private static final int DESIRED_PERIOD_BETWEEN_FRAMES_MS = (int) (1000.0 / DESIRED_FPS + 0.5);



@Override
public void run() {
    while (mRunning) {
        long beforeRenderMs = SystemClock.currentThreadTimeMillis();            
        // do actual canvas rendering
        long afterRenderMs = SystemClock.currentThreadTimeMillis();
        long renderLengthMs = afterRenderMs - beforeRenderMs;
        sleep(Math.max(DESIRED_PERIOD_BETWEEN_FRAMES_MS - renderLengthMs, 0));
    }
}