简体   繁体   中英

Draw on canvas without making previously drawn bitmap cleared

I need to draw bitmap on canvas and then start drawing over it. When I draw other objects on canvas the previously drawn bitmap gets cleared. To avoid the bitmap from getting cleared, I have to draw it in each ondraw() call. There must be some other way to just update over the previously drawn drawing, else it would be really efficient as I may have to draw many bitmaps.

    @Override
protected void onDraw(Canvas mCanvas) {
    for (Pair<Path, Paint> p : paths) {
        mCanvas.drawPath(p.first, p.second);
    }
    if(merge){
        canvas.drawBitmap(bmp, transform, new Paint());
    }

}

So, what would be the most efficient way to draw over previously drawn drawing without losing it.

I tried writing this by implementing the chess pieces using BitmapDrawable , and then only call invalidateDrawable on the piece that was moved.

That worked fine, but when I checked which parts are redrawn, it turned out that everything seems to be redrawn (or at least all the onDraw functions are being called).

Checking the documentation of the invalidate method on the View class again, I found the following:

This method was deprecated in API level 28. The switch to hardware accelerated rendering in API 14 reduced the importance of the dirty rectangle. In API 21 the given rectangle is ignored entirely in favor of an internally-calculated area instead. Because of this, clients are encouraged to just call invalidate().

This suggests to me that maybe there is simply no need to worry about this case. It seems that Android internally figures out what to redraw, and efficiently does so.

Just in case it may help, you can find the working version on my GitHub . Additionally, here is the most relevant part of the App:

public class RedrawTest extends View {
    private class ChessPiece extends BitmapDrawable {
        final int id;
        public ChessPiece(Context context, int _id) {
            super(getResources(), getBitmapFromAsset(context, "chess_piece.png"));
            id = _id;
        }
    }

    final Bitmap chessboard;
    final ChessPiece[] chessPieces;
    final Rect boardSize;
    final Paint paint = new Paint();
    final ViewOverlay overlay;

    final int borderSize = 32;
    final int nChestPieces = 6;

    private static Bitmap getBitmapFromAsset(Context context, String filePath) {
        AssetManager assetManager = context.getAssets();
        InputStream inputStream;
        Bitmap bitmap = null;
        try {
            inputStream = assetManager.open(filePath);
            bitmap = BitmapFactory.decodeStream(inputStream);
        } catch (IOException e) {
            // handle exception
        }
        return bitmap;
    }

    public RedrawTest(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        chessboard = getBitmapFromAsset(context, "chessboard.jpg");
        boardSize = new Rect(
                borderSize, borderSize,
                chessboard.getWidth()-borderSize, chessboard.getHeight()-borderSize
        );
        overlay = getOverlay();
        chessPieces = new ChessPiece[nChestPieces];
        for (int i=0; i<nChestPieces; ++i) {
            chessPieces[i] = new ChessPiece(context, i);
            chessPieces[i].setBounds(getBoardRect(0, i));
            overlay.add(chessPieces[i]);
        }
    }

    public void redraw(int i, int j, int pieceId) {
        chessPieces[pieceId].setBounds(getBoardRect(i, j));
        invalidateDrawable(chessPieces[pieceId]);
    }

    private Rect getBoardRect(int i, int j) {
        return new Rect(
                boardSize.left + j*boardSize.width()/8 + 5,
                boardSize.top + i*boardSize.height()/8 + 5,
                boardSize.left + (j+1)*boardSize.width()/8 - 5,
                boardSize.top + (i+1)*boardSize.height()/8 - 5);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawBitmap(this.chessboard, 0, 0, paint);
    }
}

I found that not using a custom view class and the onDraw method makes it so the canvas doesn't clear and hence I had to manually clear it myself for my purposes.

See below for the code, just remove the canvas.drawColor line as that is where I paint the canvas clear again.

public void doCanvas(){
    //Create our resources
    Bitmap bitmap = Bitmap.createBitmap(mLittleChef.getWidth(), mLittleChef.getHeight(), Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(bitmap);
    final Bitmap chefBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.dish_special);
    final Bitmap starBitmap= BitmapFactory.decodeResource(getResources(),R.drawable.star);

    //Link the canvas to our ImageView
    mLittleChef.setImageBitmap(bitmap);

    ValueAnimator animation= ValueAnimator.ofInt(canvas.getWidth(),0,canvas.getWidth());
    animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int value = (Integer) animation.getAnimatedValue();
            //Clear the canvas
            canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            canvas.drawBitmap(chefBitmap, 0, 0, null);
            canvas.save();
            canvas.translate(value,0);
            canvas.drawBitmap(starBitmap, 0, 0, null);
            canvas.restore();
            //Need to manually call invalidate to redraw the view
            mLittleChef.invalidate();
        }
    });
    animation.addListener(new AnimatorListenerAdapter(){
        @Override
        public void onAnimationEnd(Animator animation) {
            simpleLock= false;
        }
    });
    animation.setInterpolator(new LinearInterpolator());
    animation.setDuration(mShortAnimationDuration);
    animation.start();
}

In pixi.js there is options.clearBeforeRender ( https://pixijs.download/dev/docs/PIXI.CanvasRenderer.html ) and pixi.js probably using https://www.w3schools.com/tags/canvas_clearrect.asp .

Your question already been asked (but 7y ago, so maybe outdated), and they did as you do How to save objects previously drawn to the Canvas on a redraw? .

You may want to profile, to make sure it is a performance issue, maybe the way you do it doesn't look efficient, but is efficient, if you see what I mean.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM