Android Rotate and Scale Bitmap Example

I built an Android demo app so I could test my understanding of displaying bitmaps on a canvas. I had done scaling of bitmaps, rotation of bitmaps, and translation from one origin to another, but I had not done more than one of those transformations at a time.

three-at-once-largerThe demo app is shown in the figures above. There are two images in the center of the screen. Each image is scaled to fit within the light blue region. When you press the Rotate button, each of the images is rotated around its center, while maintaining its position in the center of the region on the screen. The Scale button resizes the images. There are three different sizes. Each time you touch Scale, it switches to the next size. The Offset cycles you through four different offsets.

In the app MainActivity, two instances of StarshipView are in the layout. In the onCreate method, each view is assigned a bitmap.

sv.setBitmapFromResource (R.drawable.starship1);
 sv.setScale (1.0f);
 sv.invalidate ();

The onClick method in MainActivity gets called whenever a button is clicked. The code in onClick finds the two views in its layout and sets properties that control the amount of rotation, size of the bitmap, and x and y offsets.

sv.setScale (newScale1);
 sv.setDegrees (degrees1);
 sv.setOffsetX (newOffset1);
 sv.setOffsetY (newOffset1);
 sv.invalidate ();

Inside class StarshipView, in the onDraw method, the bitmap  assigned to the view is written to the canvas. The code is actually very simple, once you get comfortable with using Matrix objects to do the work. Here’s what goes on in the onDraw method of class StarshipView.

First, the Matrix object is set so it will fit the bitmap into the rectangle for the view. For this demo app, I chose some interesting sizes to test this part of the code. The starship image is 512 x 512. It is scaled to fit into the 96 dp area on the left. The star field image on the right is 96 x 96 is displayed in the 120 dp square on the right.

The second step is to translate the view up and left by half the width and half the height. That is done because rotation is around the top left point (the origin) of the view. Rotation follows that step. It is very simple: “matrix.postRotate (rotation)”.

/**
 * Draw the bitmap onto the canvas.
 *
 * The following transformations are done using a Matrix object:
 * (1) the bitmap is scaled to fit within the view;
 * (2) the bitmap is translated up and left half the width and height, to support rotation around the center;
 * (3) the bitmap is rotated N degrees;
 * (4) the bitmap is translated to the specified offset valuess.
 */
@Override public void onDraw(Canvas canvas) {
 if (pBitmap == null) return;
 // Use the same Matrix over and over again to minimize 
 // allocation in onDraw.
 Matrix matrix = mMatrix; 
 matrix.reset ();

 float vw = this.getWidth ();
 float vh = this.getHeight ();
 float hvw = vw / 2;
 float hvh = vh / 2;
 float bw = (float) pBitmap.getWidth ();
 float bh = (float) pBitmap.getHeight ();

 // First scale the bitmap to fit into the view. 
 // Use either scale factor for width and height, 
 // whichever is the smallest.
 float s1x = vw / bw;
 float s1y = vh / bh;
 float s1 = (s1x < s1y) ? s1x : s1y;
 matrix.postScale (s1, s1);

 // Translate the image up and left half the height 
 // and width so rotation (below) is around the center.
 matrix.postTranslate(-hvw, -hvh);

 // Rotate the bitmap the specified number of degrees.
 int rotation = getDegrees ();
 matrix.postRotate(rotation);

 // If the bitmap is to be scaled, do so.
 // Also figure out the x and y offset values, which start 
 // with the values assigned to the view
 // and are adjusted based on the scale.
 float offsetX = getOffsetX (), offsetY = getOffsetY ();
 if (pScale != 1.0f) {
  matrix.postScale (pScale, pScale);

  float sx = (0.0f + pScale) * vw / 2;
  float sy = (0.0f + pScale) * vh / 2;

  offsetX += sx;
  offsetY+= sy;

 } else {
  offsetX += hvw;
  offsetY += hvh;
 }

 // The last translation moves the bitmap to where it has to be to have its top left point be
 // where it should be following the rotation and scaling.
 matrix.postTranslate (offsetX, offsetY);

 // Finally, draw the bitmap using the matrix as a guide.
 canvas.drawBitmap (pBitmap, matrix, null);

}

Once the bitmap is rotated, it needs to have its location translated to the place where it should display in the view. That is specified in the offsetX and offsetY values. So you see one more matrix.postTranslate call in the method.

The final action in the onDraw method is the drawing of the bitmap. Notice that the drawBitmap method uses the Matrix with the various transformations encoded in it.

Source Code

You can download the source code for this demo from  the Wglxy.com website. Click here: download zip file from wglxy.com. The zip is attached at the bottom of that page. After you import the project into Eclipse, it’s a good idea to use the Project – Clean menu item to rebuild the project.

This demo app was compiled with Android 4.4 (API 19). It works in all API levels from API 10 on up.

References

As with many other problems, I found very good advice on StackOverflow. A StackOverflow post on rotating images around the center of the image helped me.

About Bill Lahti

Bill Lahti is an indie game developer. Latest project: an exploration game. Recent game: Hop, Skip, and Thump, a simple abstract strategy game. Other iOS and Android games: Double Star II, Wing Leader, Gomoku League.
This entry was posted in Android and tagged , , , , . Bookmark the permalink.

7 Responses to Android Rotate and Scale Bitmap Example

  1. Dolcevita says:

    thank you for your post blahti! You’re an useful guide in the darkness of Android programming language! 🙂

  2. Pingback: My Android Programming Tutorials By Topic | More Is Not Always Better

  3. preeti says:

    sir i wan to make a simple matrix on canvas can u help me plzzz??????

    • Bill Lahti says:

      This demo here has fairly simple examples of use of matrix. I found that it was best to break things down into separate steps and debug each step individually before combining them.
      What is it that you are trying to do?

  4. kasti tandel says:

    i want to rotate rectangle which is draw by on touch listner .how it possible?

  5. B.Roux says:

    Hi, thank you for this post. I’m having trouble centering my image. Could you please help me?? I posted my code here: http://stackoverflow.com/questions/36350570/android-circle-not-rotating-from-center

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.