Reputation: 536
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.
Upvotes: 6
Views: 1236
Reputation: 1149
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);
}
}
Upvotes: 1
Reputation: 4102
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.
Upvotes: 0
Reputation: 1237
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();
}
Upvotes: 0