Tuesday, October 28, 2008

Android Clipping

Today is a good day, I finally solved my simple clipping issue. While the issue was simple I had been neglecting it for some unknown reason. Well this afternoon I decided today was the day to face it. Often for sprites you will have an animation strip containing multiple images, however you only want to display a single frame not the whole image. Clipping allows you to specify a particular region on the screen to draw too, anything outside the region will not be shown to the user.

I needed clipping to behave just like J2ME. In J2ME you can continually modify the clip as many times as you like. The code below will draw 2 rectangles on a white background.

public void paint (Graphics g) {
g.setClip(0, 0, canvasWidth, canvasHeight);
g.setColor(WHITE);
g.fillRect(0,0, canvasWidth, canvasHeight);

g.setClip(0, 0, 100, 100);
g.setColor(MAGENTA);
g.fillRect(0, 0, 100, 100);

g.setClip(100, 100, 50, 50);
g.setColor(GREEN);
g.fillRect(100, 100, 50, 50);
}


My first attempt in Android many moons ago (actually over a year ago) involved me being naive and not fully reading the docs. I was continually saving the current clip and restoring it everytime I wanted to modify the clip. Here is some sample code.

public void onDraw (Canvas canvas) {
//jmt: save our current clip
canvas.save(Canvas.CLIP_SAVE_FLAG);
//jmt: clear the screen
canvas.clipRect(0, 0, mCanvasWidth, mCanvasHeight);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, mCanvasWidth, mCanvasHeight, mPaint);
//jmt restore clip to previous state
canvas.restore();

//jmt: save our current clip
canvas.save(Canvas.CLIP_SAVE_FLAG);
//jmt: draw our animation
canvas.clipRect(x, y, x + FRAME_WIDTH, y + FRAME_HEIGHT);
canvas.drawBitmap(mAnimation, x - (mCurretFrame * FRAME_WIDTH) , y, mPaint);
//jmt: restore clip to previous state
canvas.restore();
}

Needless to say on more complex games this would soon become an issue. Fast forward to today where I knew there had to be a better method. So I started reading the documentation and suprise suprise (well it wasn't really a surprise) I found a better way of handling clipping. Let me introduce you to Region.OP, this marvel allows me to modify the clip in the same manner as J2ME and thus save a lot of work. The previous code snippet now looks like this

public void onDraw (Canvas canvas) {
//jmt: clear the screen
canvas.clipRect(0, 0, mCanvasWidth, mCanvasHeight, Region.Op.REPLACE);
mPaint.setColor(Color.WHITE);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, mCanvasWidth, mCanvasHeight, mPaint);

canvas.clipRect(x, y, x + FRAME_WIDTH, y + FRAME_HEIGHT, Region.Op.REPLACE);
canvas.drawBitmap(mAnimation, x - (mCurretFrame * FRAME_WIDTH) , y, mPaint);
}

Now all my animations are displayed correctly and I have less code.

9 comments:

Anonymous said...

Thank you, I hope to resolve my problem with clipping issues, :)

Kermit said...

Great tip! thanks. is this also faster?

Anonymous said...

thank you! it helps me a lot.
until next time.
god bless and good luck!

Anonymous said...

thank you very much..
i like to ask how to clip image..
i tried to use your code but only a small rectangle is draw,,
can you teach me how to clip image.

XdebugX said...

Thanks alot! Had the same issue with clipping your code example helped.

Anonymous said...

Thank you! When coming from J2SE, J2ME the android way of doing things is not very intuitive - your blog helps a lot.

Anonymous said...

I discounted using clipping as I figured a drawBitmap call with the same information would be equivalent speed-wise. Boy was I wrong.

I tried double buffering, get/setPixels, nothing seemed to give any kind of improvement.

Finally I tried just setting the clip region before performing the same draw call. Thanks to this post I've got a scrolling map made of arbitrary tiles blitzing along at 60fps. Cheers!

Anonymous said...

Very good! Thanks. You saved me a lot of time! Fell like if I would have bought you a crates of beer!

Unknown said...

6 years after this post still helps people!
Thank you!