Archive for September, 2009

Canvas.save, Canvas.restore; Camera.save, Camera.restore

September 30, 2009

Android graphics package contains android.graphics.Canvas and android.graphics.Camera classes, both of which allow you to manipulate the “Modelview” transformation matrix, with the latter manipulating your drawing in “3D”.

Basically the ability to manipulate complex, hierarchical transformation is achieved by an(?) internal matrix stack with the top matrix being the current one in effect and the one that you are manipulating. All the transformations you are making on the current matrix are cumulative; so if you were just to manipulate the current matrix, it’s going to be a big pain to draw something like a car or a human figure which conceptually is assembled by different parts.  Canvas.save()/Camera.save() is to push a matrix into the stack; the result is the transformation you’ve made is saved into the stack.  Canvas.restore()/Camera.restore() is to discard the current transformation and go back to the last saved one and continue on it. So if you want to continue with some transformation you made a while ago, you should first save/push it into the stack first so that you can go back to it by restoring/popping it out from the stack. See the following pseudo code for how we go about drawing a drag race car with huge wheels:

//setup/clean up stuff
canvas.save(); // remember where we are since we’d like to go back to this original transformation later;
// move the front wheel to front left
// make it twice as big as the normal wheel
// draw front-left wheel in normal size
canvas.restore(); // go back to the identity matrix;
// move the front wheel to front right
// make it twice as big as the normal wheel
// draw front-right wheel in normal size
canvas.restore();
//… draw body
canvas.restore();
//… and other wheels…

The pattern is

  • If you want to go back to the current state in terms of transformation, you should remember where you are by calling Canvas.save() or Camera.save());
  • Transform the matrix (via Canvas or Camera methods such as Canvas.translate() and Camera.rotateY();
  • Do drawing;
  • Go back to the transformation state you remember in step 1 by calling Canvas.restore()/Camera.restore() if you want to continue from the transformation you left in step 1;
  • In theory, if you only have one matrix transformation saved in the stack, you can simply push it out by calling Canvas.restore()/Camera.restore() again and again. But in the real world (at least applying for cupcake and donut),  I’ve found I have to keep “saving” before each new transformation and “restoring” after it, even if I was “saving”/”restoring” an identity matrix. So this means you would normally call Canvas.save()/Camera.save() each time before a new transformation;
  • Transform the matrix;
  • Do drawing;
  • Canvas.restore()/Camera.restore();

To summarize (applying to android.graphics.Camera as well), you repeatedly do the following cycles:

  1. canvas.save();
  2. transform matrix;
  3. draw;
  4. canvas.restore();

Note that in android, #2 transformation should be coded before #3 drawing to make the drawing stick to the transformation. If you reverse 2/3, your drawing won’t be affected by the transformations that come after it. This is kinda “counter-intuitive” at least for me since you have always need to set your canvas in position before you paint but not the other way around.

Check out the “Red book” for “Manipulating the Matrix Stacks” to get a better understanding. And here is a sample code showing you how I draw the awesome Batmobile (Spiderman’s car) crawling on a skyscraper (use your imagination for the wall and other details as I am still polishing my drawing skill. Please hold on for the next episode!):

package com.mh.android.test;

import android.app.Activity;
import android.content.Context;
import android.graphics.Camera;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;

public class HelloAndroidAgain extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MyTestView(this));
}

private static class MyTestView extends View {

public MyTestView(Context context) {
super(context);
setMinimumWidth(200);
setMinimumHeight(200);
//camera = new Camera();
}
private Camera camera = new Camera();
@Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);

camera.save();
camera.rotateY(60f);
camera.applyToCanvas(canvas);

canvas.drawColor(Color.DKGRAY);
Paint paint = new Paint();
paint.setTextSize(24);

//front left wheel
canvas.save();
canvas.translate(50f, 50f);//xCtr, yCtr
paint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 30, paint);
paint.setColor(Color.WHITE);
canvas.drawText(“FL”, -10, 10, paint);//string
canvas.restore();

//front right wheel
canvas.save();
canvas.translate(150f, 50f);
paint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 30, paint);
paint.setColor(Color.WHITE);
canvas.drawText(“FR”, -10, 10, paint);
canvas.restore();

//rear left wheel
canvas.save();
canvas.translate(50f, 150f);
paint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 30, paint);
paint.setColor(Color.WHITE);
canvas.drawText(“BL”, -10, 10, paint);
canvas.restore();

//rear right wheel
canvas.save();
canvas.translate(150f, 150f);
paint.setColor(Color.GREEN);
canvas.drawCircle(0, 0, 30, paint);
paint.setColor(Color.WHITE);
canvas.drawText(“BR”, -10, 10, paint);
canvas.restore();

//body
canvas.save();
canvas.translate(100f, 100f);
paint.setColor(Color.RED);
canvas.drawRect(-50, -50, 50, 50, paint);
canvas.restore();

camera.restore();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}

}
}

Advertisements

Uninstall Adobe AIR application in Linux/Ubuntu

September 11, 2009

I am talking about AIR 1.5.2 installer and Ubuntu 9.04 (cat /etc/lsb-release to find out your Ubuntu version).

Simple run the installation package file (*.air, which is basically an zip file) again. If you don’t know where it is (and presumably still in your machine), go to “Applications->Accessories->Adobe AIR Application Installer”, which opens the most recently installed AIR package (*.air), then click “Uninstall”.

Multiple offline devices attached

September 10, 2009

This was driving me crazy this morning as I was debugging using G1 phone on Eclipse. Basically the Android AVD Manager sees multiple offline devices attached, although they are all the same device (sometimes it sees one online device sometimes none online device); despite that “adb devices” reports the correct info about device attached.

So basically Mr. Mark Murphy’s suggestion solved my problem:

1. Unplug all devices from computer and shut down all instances of emulators;

2. Run “adb kill-server“;

3. Run “adb devices” and you should see an empty list of devices;

4. Plug back in your device or re-lauch emulator. Now hopefully AVD Manager should now act normally.