Question

I am experimenting with SurfaceView. My requirement is to simply render a node (simple drawable) first. Then, render more nodes at a later point in time.

The snippets of my thread's run method & my doDraw method are below. I am just trying to render 2 different drawables in subsequent passes while retaining both. The problem is it wipes away whatever gets written in 1st pass (see comment in code). How to retain the previously drawn object?

    public void run() {
        Canvas canvas;
        while (_running) {
            canvas = null;
            try {
                canvas = _surfaceHolder.lockCanvas(null);
                synchronized (_surfaceHolder) {
                    doDraw(canvas, isUpdate);
                }
            } finally {
                if (canvas != null) {
                    _surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }


    public void doDraw(Canvas canvas, boolean update){
        if(update){
            //goes here 2nd pass & wipes away things done in 1st pass
            //I want to retain whatever was drawn in 1st pass
            Bitmap thumb = BitmapFactory.decodeResource(getResources(),R.drawable.icon);
            //canvas.drawColor(Color.RED);
            canvas.drawBitmap(thumb, 0, 0, null);
        } else{
            //goes here 1st pass
            Bitmap thumb = BitmapFactory.decodeResource(getResources(), R.drawable.thumb);
            //canvas.drawColor(Color.BLACK);
            canvas.drawBitmap(thumb, 300, 300, null);
            this.isUpdate = true;
        }
    }

UPDATE 1: Still does not seem to work. I changed the run code to this passing a non:

     public void run() {
        Canvas canvas;
        while (_running) {
            canvas = null;
            try {
                Rect dty = null;
                if(isUpdate == true){
//--> In 2nd pass, I was hoping that only these co-ordinates will be updated
                    dty = new Rect(0,0,100,100);  
                    canvas = _surfaceHolder.lockCanvas(dty);
                }else{
                    canvas = _surfaceHolder.lockCanvas(null);
                }
                synchronized (_surfaceHolder) {
                    doDraw(canvas, isUpdate);
                }
            } finally {
                if (canvas != null) {
                    _surfaceHolder.unlockCanvasAndPost(canvas);
                }
            }
        }
    }

Later I tried passing 0,0,1,1 to dirty rectangle. Could not get it to work yet...

Was it helpful?

Solution

The SurfaceView is double- or triple-buffered. The previous contents are "preserved" in the sense that the system doesn't go out of its way to clear older buffers, but you can't rely on that behavior.

If you specify a dirty rect, the framework will render whatever you ask, then copy the non-dirty region from the previous buffer on top of the new buffer.

The system is allowed to expand the dirty rectangle -- the Rect you pass to lockCanvas() may be updated. You're required to redraw every pixel inside it.

For a (somewhat eye-searing) example of this in action, see "Simple Canvas in TextureView" in Grafika.

For more details on how the system works, see this article.

OTHER TIPS

I found this interesting note in the Android documentation:

The content of the Surface is never preserved between unlockCanvas() and lockCanvas(), for this reason, every pixel within the Surface area must be written. The only exception to this rule is when a dirty rectangle is specified, in which case, non-dirty pixels will be preserved.

So to do what you are trying to do, it looks like you need to provide a non-null dirty rectangle in your lockCanvas call. Also, this will only work as long as none of your node pixels intersect.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top