## Android Canvas Drawing: Useful Graphics Classes & Operations 🧪

Drawing on an Android Canvas is quite overwhelming, there are many different classes and concepts to understand when drawing something. If you haven’t already read part one of this series make sure to read it here.

In this post, we will be covering some classes that you will find available within the Android Framework which can make your life a bit easier when working with a canvas.

# Rect / RectF ◼️

A rectangle class that stores four values: `top`

, `left`

, `right`

and `bottom`

. These can be used for drawing directly on canvas or just for storing sizes of objects that you want to draw.

The difference between the `Rect`

and `RectF`

class is that the `RectF`

stores float values, where as the `Rect`

class stores integers.

val rect = RectF(100.0f, 200.0f, 300.0f, 400.0f)

With KTX there are many graphics extension functions that you should take advantage of. For instance, there are some for `Rect`

and `RectF`

. One such extension is the destructuring declaration which gets components out of a `Rect`

object:

val rect = RectF(100.0f, 200.0f, 300.0f, 400.0f)

val (left, top, right, bottom) = rect

// left = 100.0f, top = 200.0f, right = 300.0f, bottom = 400.0f

You can do other operations with a `Rect`

class, for instance, you can union two `Rects`

together. This basically just includes the points from both `Rects`

and returns a bigger `Rect`

that contains both `Rects`

inside it. There are also extension functions for this operation, but it is also possible without the extension:

val rect = RectF(100.0f, 200.0f, 300.0f, 400.0f) val otherRect = RectF(50.0f, 400.0f, 150.0f, 500.0f) rect.union(otherRect) // rect = RectF(50.0, 200.0, 300.0, 500.0) // alternatively: val combinedRect = rect + otherRect // or alternatively: val combinedRect = rect or otherRect // combinedRect = RectF(50.0, 200.0, 300.0, 500.0)

There are other operations you can perform on a `Rect`

, such as : `and`

, `xor`

, `or`

.

# Point / PointF 👉🏽

Stores an `x`

and `y`

coordinate which represents a “point” on a canvas. `Point`

stores integer values, whereas `PointF`

stores floating point values.

val point = PointF(200.0f, 300.0f)

If you are using KTX, there are some extension functions built onto the `Point`

and `PointF`

classes that make working with points much easier. For instance, operator overloads which add the ability to `plus`

and `minus`

two points from each other.

val start = PointF(100.0f, 30.0f)

val end = PointF(20.0f, 10.0f)

val difference = start - end

val together = start + end

// together = Point(120.0f, 40.0f)

There also exists destructuring declarations for these classes, so we can easily get out `x`

and `y`

coordinates out easily from the `Point`

class:

val start = PointF(100.0f, 30.0f)

val end = PointF(20.0f, 10.0f)

val (x, y) = start - end

// x = 80.0f y = 20.0f

# Matrix 🔢

A 3 by 3 Matrix that stores information which can be used to transform a canvas. A `Matrix`

can store the following kind of transformation information: scale, skew, rotation, translation.

Below is an example of using a `Matrix`

to transform a Bitmap that is drawn on a Canvas.

To use a Matrix when drawing, you can do the following:

val customMatrix = Matrix() // in onDraw() customMatrix.postRotate(20.0f) canvas.withMatrix(customMatrix) { drawBitmap(bitmap, null, rect, paint) }

The above code will draw a bitmap on a canvas that is rotated at 20 degrees. There are a few other functions on a `Matrix`

that we can use such as scaling, rotating and skewing. The great part about using a `Matrix`

over doing everything yourself manually with Canvas transformations, is that the `Matrix`

holds cumulative information about the transformations that are applied to it.

If you translate the `Matrix`

, rotate, scale and translate again, the end values of the translation would be a bit different than the original values. If you were to do this yourself, you would need to calculate that manually if you were performing normal Canvas translate, scale functions.

## preRotate vs proRotate vs setRotate

You might be wondering what `postRotate`

means, considering the fact that there are other methods such as `setRotate`

and `preRotate`

on a `Matrix`

. These three methods all do slightly different things:

`setRotate`

— Completely **resets** the current `Matrix`

and applies the rotation, thus losing any information that you may already have stored in your `Matrix`

.

`preRotate`

— The rotation will be applied **before** whatever the current`Matrix`

contains.

`postRotate`

— The rotation will be applied **after** whatever the current`Matrix`

contains.

## Perspective Drawing with Matrix

A `Matrix`

object can also provide us with the ability to perform perspective drawing, which is not possible to achieve with just standard Canvas transformation APIs. The function that allows perspective drawing or skewing of a canvas is `Matrix#setPolyToPoly()`

. This method does sound a bit confusing at first, but once you wrap your head around how it works, it is not so bad!

Here is an example bitmap that has been “skewed” using the `setPolyToPoly`

method.

The `setPolyToPoly`

method takes input (`src`

) “points”, and maps them to the specified output (`dst`

) “points”. I say “points” because they aren’t real point objects as we’ve just explored earlier in this post, but they are rather just values in a float array, which can be quite confusing.

You can see in the `src`

array below, the first two values are representing the top left point of the image, the second two values represent the top right point and so on. These points can be in any order, but they must match with the corresponding point that you want it to map to, in the `dst`

array.

val src = floatArrayOf( 0f, 0f, // top left point width, 0f, // top right point width, height, // bottom right point 0f, height // bottom left point ) val dst = floatArrayOf( 50f, -200f, // top left point width, -200f, // top right point width, height +200f, // bottom right point 0f, height // bottom left point ) val pointCount = 4 // number of points // the second and fourth arguments represent the index in the src and dest arrays from which the points should start being mapped from newMatrix.setPolyToPoly(src, 0, dst, 0, pointCount) canvas.withMatrix(newMatrix) { drawBitmap(bitmap, null, rect, paint) }

In this example, the bottom right point, will be mapped from the point `[width, height] `

to the point `[width, height +200f]`

.

So you can see from the above example that a `Matrix`

can do some pretty powerful and interesting stuff.

## Tip: Use the Matrix class to work between different coordinate systems

If you have two different coordinate systems that you are dealing with on a single view, then leveraging a `Matrix`

class can help you map between the two.

For instance, if you get a touch event from Android that is measured in the height and width of the size of the screen, but you would like to know what that point would be inside the image you are drawing on screen, within that coordinate system (ie the coordinate system of the image), you can use a `Matrix`

to map between these two systems.

In order to get the point mapped inside the image drawn on screen, we can use the `Matrix#mapPoints()`

method:

fun mapPoint(point: PointF): PointF {

computeMatrix.reset() // apply the same transformations on the matrix that are applied to the Image

computeMatrix.postTranslate(20f, 20f)

computeMatrix.postRotate(20f, x, y) // create float array with the points we want to map

val arrayPoint = floatArrayOf(point.x, point.y) // use the map points function to apply the same transformations that the matrix has, onto the input array of coordinates

computeMatrix.mapPoints(arrayPoint) // get the points out from the array, these will now be transformed by the matrix.

return PointF(arrayPoint[0], arrayPoint[1])

}

In the above example, the input point would be the touch event from Android, and the translation and rotation that we apply on the `computeMatrix`

is the same translation and rotation we applied on the image when it was drawn. Then we create a float array which contains the original `x`

and `y`

point. We then call the `mapPoints`

method with this array. It’ll then transform the values in place and when we query the array for the first and second values it’ll be the mapped coordinate, which is the point inside the image view.

# Summary 👨🏾🎨

You can see that the Android Graphics APIs contain loads of useful classes that you can leverage to do a lot of the calculations and mathematics for you. From `Points`

to `Rects`

to more complex calculation classes like `Matrix`

classes, we can see that there are many things we can use to help us draw graphics on the screen! Make sure to include KTX to have an even smoother experience when working with Android Graphics classes.

Have any questions or comments? Feel free to reach out and say hi to me on Twitter.