【问题标题】:SurfaceView.lockCanvas() does not properly clear the bitmap bufferSurfaceView.lockCanvas() 未正确清除位图缓冲区
【发布时间】:2012-08-10 23:56:32
【问题描述】:

我创建了一个扩展 SurfaceView 的类,以便循环一系列 ARGB 位图。 这主要是有效的,除了底层位图的状态(通常但不总是)为每个新帧保留。

换句话说,如果我显示的第一帧是不透明的,而随后的帧是透明的,那么在绘制新帧时不会清除原始帧中的不透明像素。

这种行为让我很困惑,因为 SurfaceHolder.lockCanvas() 的文档特别指出:

“unlockCanvas() 和 lockCanvas() 之间永远不会保留 Surface 的内容,因此,必须写入 Surface 区域内的每个像素。”

如果我只是有一个纯色背景,然后调用 canvas.drawARGB(255,0,0,0) 成功将其清除为黑色...但我想要一个透明背景,我无法清除它为透明色,因为 canvas.drawARGB(0,0,0,0) 没有效果。

import java.util.ArrayList;
import java.util.Random;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/*
 * Accepts a sequence of Bitmap buffers and cycles through them.
 */

class AnimatedBufferView extends SurfaceView implements Runnable
{
    Thread thread = null;
    SurfaceHolder surfaceHolder;
    volatile boolean running = false;

    ArrayList<Bitmap> frames;
    int curIndex = 0;

    public AnimatedBufferView(ArrayList<Bitmap> _frames, Context context) 
    {
        super(context);
        surfaceHolder = getHolder();
        frames = _frames;
    }

    public void onResume(){
        running = true;
        thread = new Thread(this);
        thread.start();
    }

    public void onPause(){
        boolean retry = true;
        running = false;
        while(retry){
            try {
                thread.join();
                retry = false;
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() 
    {
        // TODO Auto-generated method stub
        while(running)
        {
            if(surfaceHolder.getSurface().isValid())
            {               
                Canvas canvas = surfaceHolder.lockCanvas();

                //clear the buffer?
                //canvas.drawARGB(255, 0, 0, 0);

                //display the saved frame-buffer..
                Matrix identity = new Matrix();
                Bitmap frame = frames.get(curIndex);
                canvas.drawBitmap(frame, identity, null);


                surfaceHolder.unlockCanvasAndPost(canvas);

                curIndex = (curIndex + 1) % frames.size();

                try {
                    thread.sleep( 100 );
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

【问题讨论】:

    标签: android surfaceview


    【解决方案1】:

    好的,发现问题在于默认的 Porter-Duff 绘图模式,导致无法绘制透明颜色。只需要改变模式。即,

    Canvas canvas surfaceView.lockCanvas();
    canvas.drawColor(0, Mode.CLEAR);
    

    【讨论】:

      【解决方案2】:

      这个让我困惑了一段时间。基本上,您将不得不每帧重绘整个屏幕。这是因为当您锁定画布时,您得到的是一个落后两帧的画布,而不是发布的最后一帧。这是由于帧被缓冲的方式。只有两个缓冲区,一个是当前显示的,一个是在当前缓冲区之前发布的。当您锁定画布时,您会得到一个当前未显示的画布(这意味着您会得到一个画布,它是一个框架)。

      这就是为什么您可能会听到人们将缓冲区称为交换缓冲区的原因,因为当前显示屏幕会与您绘制所有内容的屏幕交换。这意味着您永远不会真正取回最新的缓冲区。

      希望这对你有某种意义。

      【讨论】:

      • 感谢 Jay,但我没有关注...我已经在尝试让表面视图在每一帧上重绘其内容。问题是 lockCanvas 返回的缓冲区不是空的,在开始绘制新帧之前我无法清除它。我只能用不透明的颜色填充缓冲区,但这不起作用,因为缓冲区应该有透明背景。
      • 不回答这个问题,但非常有趣的信息!
      • 有道理,但为什么不能选择只使用单个渲染纹理呢?例如,带有画笔的绘画应用......
      • @Andi Jay,我迟到了 9 年,但我想知道你是否有你解释的行为的来源。根据我正在经历的行为,这是有道理的
      【解决方案3】:

      谷歌文档说的是对的……因为你重新锁定了表面的像素,并且有两个缓冲区,因为 android 根据绘图调用的速度使用三重或双缓冲,它可以在一帧一个缓冲区上绘制无论您在绘图调用之前和下一次填充它的任何内容,它都可以使用另一个内容绘制另一个缓冲区,因此您可以锁定由来自另一个缓冲区的数据构成的表面......

      所以最好在绘制任何内容之前覆盖所有内容,例如使用 proter duff 模式和清晰颜色或使用 canvas.drawColor(Color.BLACK); 仅使用黑色

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-27
        • 1970-01-01
        • 2017-01-26
        • 2012-10-12
        • 1970-01-01
        相关资源
        最近更新 更多