【问题标题】:Better frame rate drawing bitmaps in a Canvas?更好的帧率在画布中绘制位图?
【发布时间】:2011-05-29 03:49:32
【问题描述】:

我正在为动态壁纸中的动画拍摄。这是代码。它几乎遵循 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 是一个因素。当我与位图一起使用时,我可能应该缓存其中的一些。但还有其他想法吗?我是否必须将 OpenGL hack 用于动态壁纸?

【问题讨论】:

    标签: android opengl-es bitmap live-wallpaper


    【解决方案1】:

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

    【讨论】:

    • Geobits 是正确的。更重要的是,您编写的代码会引发大量垃圾收集,进一步减慢速度。
    • OK 做一个单独的线程进行缓存,但我仍然没有加快速度。这是我的解决方案的概要: 1. 将大约 1 秒的动画预加载到位图数组中, 2. 绘制每个位图。 3. 在我绘制时将每个单元格标记为“空”。 4. 一个单独的线程用下一个动画“重新填充”空单元格。
    • 对不起...这个评论框不喜欢“输入”,所以我需要重新输入我的评论:好的,做一个单独的线程进行缓存,我仍然没有加快速度。这是我的解决方案的概要: 1. 将大约 1 秒的动画预加载到位图数组中, 2. 绘制每个位图。 3. 在我绘制时将每个单元格标记为“空”。 4. 一个单独的线程用下一个动画“重新填充”空单元格。问题是即使使用线程,位图加载也总是很慢,因此您最终会遇到瓶颈。我还尝试一次加载一个“块”位图和同样的问题。我需要使用opengl吗?
    • 不,openGl 根本不会加速 bitmpap 加载,相信我。事实上,在播放过程中加载、创建和销毁这么多资源永远不会很快。这就是为什么大多数具有大量图形的游戏在开始时加载时间很长,因此不会减慢游戏速度。您要加载多少张图片(以文件大小计)?
    • 这就是我所担心的——位图加载是问题所在。大约 150 个位图(5 秒动画),这对于以原始分辨率预加载来说太多了。
    【解决方案2】:

    我遇到了同样的问题:动态壁纸环境下的画布渲染速度很慢。

    我同意其他人的说法,即在渲染时不应该做任何 cpu/io 繁重的工作,例如加载图像,尤其是在 UI 线程上。

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

    假设将所有内容渲染到画布上需要 28 毫秒。完成后,您在 33 毫秒后请求重绘。也就是说,重绘之间的周期是 61 毫秒,相当于每秒 16 帧。

    你有两种选择来解决它:

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

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

    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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-06
      • 1970-01-01
      • 2012-11-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多