Playing with Android canvas drawVertices
Tags: Mobile App Development, Graphic Design, Android, AndroidDev, Android App Development

If you like some simple graphic drawing in Android Canvas, drawVertices is a pretty nice API to use. It could be used as the fundamental for drawing 3D images as shown below.

Anyway, before getting into that complexity, let’s learn the fundamental of the API.

I was searching for some tutorial explaining it and can’t any one for Android. So do some experiments and learn the API to share below.

Have fun!

Prerequisite

Unlike most of the API in the Canvas , in order to drawVertices to draw something, one need to disabled the Hardware Acceleration first using

setLayerType(View.LAYER_TYPE_SOFTWARE, null)

Why? It is simply because it is one of the unsupported feature in Hardware Acceleration as per documented at the point of writing this blog.

The API

The API have 12 arguments. 😱 Wow!



// THIS IS NEEDED

int vertexCount, // usually verts.size

float[] verts, // THIS IS NEEDED

int vertOffset, // usually 0

float[] texs, // can be nullable

int texOffset, // usually 0

int[] colors, // can be nullable

int colorOffset, // usually 0

short[] indices, // can be nullable

int indexOffset, // usually 0

int indexCount, // can be 0

public void drawVertices ( Canvas.VertexMode mode,int vertexCount, // usually verts.sizefloat[] verts,int vertOffset, // usually 0float[] texs, // can be nullableint texOffset, // usually 0int[] colors, // can be nullableint colorOffset, // usually 0short[] indices, // can be nullableint indexOffset, // usually 0int indexCount, // can be 0 Paint paint) // can be just Paint()

Let’s break it down into 4 different grouping.

1. The vertex datas

// THIS IS NEEDED

int vertexCount, // usually verts.size

float[] verts, // THIS IS NEEDED

int vertOffset, // usually 0 Canvas.VertexMode mode,int vertexCount, // usually verts.sizefloat[] verts,int vertOffset, // usually 0

First of all, is the float[] verts . This basically used to store the coordinates of the verts in ( x1, y1, x2, y2, … )format. e.g. if we want to have 3 coordinates as below,

val verts = floatArrayOf(20f, 10f, 25f, 30f, 40f, 15f)

The vertexCount is just to let the API know how many points you have in your verts that you want the API to consume. In the case of everything, then vertexCount = verts.size .

However in case you have a super long array of verts , and only wanted to use a section of it, you could then use vertOffset to indicate your starting point (which by default is 0 ), and follow by the vertexCount points that you are using from the verts . e.g. you have

val verts = floatArrayOf(0f, 0f, 20f, 10f, 25f, 30f, 40f, 15f, 0f)

And you want to use only 20f, 10f, 25f, 30f, 40f, 15f , then your vertOffset = 1 and vertexCount = 6 .

Okay, enough of the vertex data. Let’s jump into the most important ENUM argument for this API. There are 3 of them.

1. Canvas.VertexMode.TRIANGLES

This is the most simplest one, where given the verts , group each 3 coordinate and draw the triangles of it accordingly. e.g.

Give ( x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6 ) coordinates, when draw using TRIANGLES mode, it will draw 2 triangle that groups 3 coordinates each together, where by the below triangles will be formed

( x1, y1, x2, y2, x3, y3 ) ( x4, y4, x5, y5, x6, y6 )

To make things more interesting, I have created an App and draw some triangles below

Using a different set of ( x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6 ), if you draw on it, it will group all adjacent 3 coordinates into a triangle. Hence some coordinate is reused. Hence the triangle form will be as below

( x1, y1, x2, y2, x3, y3 ) ( x2, y2, x3, y3, x4, y4 ) ( x3, y3, x4, y4, x5, y5 ) ( x4, y4, x5, y5, x6, y6 )

To make things more interesting, I have created an App and draw some triangles below

Using a different set of ( x1, y1, x2, y2, x3, y3, x4, y4, x5, y5, x6, y6 ), if you draw on it, it will be using ( x1, y1 ) as the central fan point, and each subsequent adjacent coordinates will be connect to it to form a triangle. Hence the triangle form will be as below

( x1, y1, x2, y2, x3, y3 ) ( x1, y1, x3, y3, x4, y4 ) ( x1, y1, x4, y4, x5, y5 ) ( x1, y1, x5, y5, x6, y6 )

To make things more interesting, I have created an App and draw some triangles below

You could get the full source code from below

2. The tex shader



int texOffset, // usually 0

paint, // can be just Paint() float[] texs, // can be nullableint texOffset, // usually 0 Paint paint, // can be just Paint()

The texs is basically a set of coordinates (same format as verts ) that could be used to map a given bitmap tiles to the triangles drawn. It is using the paint to perform the needed shading onto the triangle.

In here, I’m using a simple paint with shader set as below

val paint = Paint().apply {

val bitmap = BitmapFactory.decodeResource(

resources, R.drawable.pyramid_small)

val shader = BitmapShader(

bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)

this.shader = shader

}

If the coordinate is identical with verts , then it is mapped as per the original image there.

However, we could perform some translation by changing the texs coordinate. Swapping the second and third coordinate would result in a reflection image as below

Lastly, the texOffset is similar to vertOffset , where one could choose how where one would like to begin in a longer than needed texs coordinate arrays. The length of texs used is the same one argument apply to vertexCount

2. The color arrays

int[] colors, // can be nullable

int colorOffset, // usually 0

This is used to fill the triangle with the colors provided. Each colors in the array will be designated to each coordinate of the vertex.

Note: Technically, the size of the color should be verts.size/2 . However I suspect there’s an internal issue in Android OS handling it as per stackoverflow. We’ll still need to provide arbitrary color to fill up the entire verts.size to avoid the crash.

For 3 coordinates, and we would like to designate RED, GREEN and BLUE to the first 3 coordinates, below would be as below (note the -0x100000 is just dummy color of no effect as per stackoverflow)

val verticesColors = intArrayOf(

Color.RED, Color.GREEN, Color.BLUE,

-0x100000, -0x1000000, -0x1000000)

Using this we’ll get the below image (Note the color got smooth gradient across).

If we blend together with the texs shader, it will be semi transparent

4. The Indices

short[] indices, // can be nullable

int indexOffset, // usually 0

int indexCount, // can be 0

The indices is an array that let you cherry pick the coordinate from the verts so that you could use them at a different sequence than what is put in the verts per your liking.

The nice bit is, the picked coordinate could also be repeated, making new triangle from different combination of triangle.

Assuming I have 4 coordinates ( v0, v1, v2, v3 ), and I would like to draw 2 triangle from it using the basic Canvas.VertexMode.TRIANGLES mode. I could write my indices as below

val indices = shortArrayOf(0, 1, 2, 0, 2, 3)

This would then from the two triangles

( V0, V1, V2 ) ( V0, V2, V3 )

This is a very simplistic example to clarify what indices is for. A lot of fancy operation could be achieved from it when give more thoughts to it.