This tutorial will cover the last paint type available for filling and/or stroking paths: patterns. Patterns are based on bitmap images, so before to start with the real tutorial, we have to introduce images as they are defined by OpenVG specifications.

Images are rectangular collections of pixels. Image data may be inserted or extracted in a variety of formats with varying bit depths, color spaces, and alpha channel types. Images may be drawn to a drawing surface, used to define paint patterns, or operated on directly by image filter operations.

An image defines a coordinate system in which pixels are indexed using integer coordinates, with each integer corresponding to a distinct pixel. The lower-left pixel has a coordinate of `(0, 0)`

, the x coordinate increases horizontally from left to right, and the y coordinate increases vertically from bottom to top.

The `VGImageFormat`

enumeration defines the set of supported pixel formats and color spaces for images. The letter `A`

denotes an alpha channel, `R`

denotes red, `G`

denotes green, and `B`

denotes blue. `X`

denotes a padding byte that is ignored. `L`

denotes grayscale, and `BW`

denotes (linear) bi-level grayscale (black-and-white), with `0`

representing black and `1`

representing white in either case. A lower-case letter `s`

represents a non-linear, perceptually-uniform color space, as in `sRGB`

and `sL`

; a lower-case letter `l`

represents a linear color space using the `sRGB`

primaries. Formats with a suffix of `_PRE`

store pixel values in premultiplied format.

Format | Bytes per pixel | Bits per pixel |
---|---|---|

`VG_sRGBX_8888` | 4 | 32 |

`VG_sRGBA_8888` | 4 | 32 |

`VG_sRGBA_8888_PRE` | 4 | 32 |

`VG_sRGB_565` | 2 | 16 |

`VG_sRGBA_5551` | 2 | 16 |

`VG_sRGBA_4444` | 2 | 16 |

`VG_sL_8` | 1 | 8 |

`VG_lRGBX_8888` | 4 | 32 |

`VG_lRGBA_8888` | 4 | 32 |

`VG_lRGBA_8888_PRE` | 4 | 32 |

`VG_lL_8` | 1 | 8 |

`VG_A_1` | n/a | 1 |

`VG_A_4` | n/a | 4 |

`VG_A_8` | 1 | 8 |

`VG_BW_1` | n/a | 1 |

Other available byteorder formats (`A/XRGB`

, `BGRA/X`

, `A/XBGR`

) follow the same “bytes/bits per pixel” rules.

Images can be created through the `vgCreateImage`

function, specifying format and dimensions (in pixels). The `vgImageSubData`

function reads pixel values from memory, performs format conversion if necessary, and stores the resulting pixels into a rectangular portion of a given image; it’s the core function used to upload image pixels to the OpenVG backend (as a simplification, it could be though as the OpenVG counterpart of the OpenGL `glTexSubImage2D`

/ `glTextureSubImage2D`

). For a more comprehensive overview, take a look at images section.

Pattern paint defines a rectangular pattern of colors based on the pixel values of an image.

Pattern paints have a reference system that coincides with images: the lower-left pixel has a coordinate of `(0, 0)`

, the x coordinate increases horizontally from left to right, and the y coordinate increases vertically from bottom to top. Such system is then transported to the path coordinates system throught a “paint-to-user” affine matrix (`VG_MATRIX_FILL_PAINT_TO_USER`

/ `VG_MATRIX_STROKE_PAINT_TO_USER`

). Finally the filled/stroked path is moved to the drawing surface system by another affine matrix, the so called “path-user-to-surface” (`VG_MATRIX_PATH_USER_TO_SURFACE`

).

Pattern Matrices |

The tutorial defines a simple procedural image, then creates a pattern paint object and link the image to it (see `genPaints`

function); here’s a simplified code version for a fixed 64x64 pattern image:

```
unsigned int pixels[64 * 64], x, y;
unsigned int colors[16] = {
0xFF6030FF, 0xFFB060FF, 0xFF9090FF, 0xFF30B0FF,
0x60FF30FF, 0xB0FF60FF, 0x90FF90FF, 0x30FFB0FF,
0x6030FFFF, 0xB060FFFF, 0x9090FFFF, 0x30B0FFFF,
0x303030FF, 0x606060FF, 0x909090FF, 0xB0B0B0FF
};
// create pattern image
VGImage image = vgCreateImage(VG_sRGBA_8888_PRE, 64, 64, VG_IMAGE_QUALITY_BETTER);
// generate procedural pixels (chessboard-like)
for (y = 0; y < 64; ++y) {
unsigned int i = y / 16;
for (x = 0; x < 64; ++x) {
unsigned int j = x / 16;
pixels[(y * 64) + x] = colors[(i * 4) + j];
}
}
// upload pixels to the OpenVG backend
vgImageSubData(image, pixels, 64 * sizeof(unsigned int),
VG_sRGBA_8888_PRE, 0, 0, 64, 64);
// create paint object
VGPaint pattern = vgCreatePaint();
// set the paint object to be a pattern paint
vgSetParameteri(pattern, VG_PAINT_TYPE, VG_PAINT_TYPE_PATTERN);
// link the image to the pattern paint object
vgPaintPattern(pattern, image);
```

&nbps; |
---|

64x64 pattern image |

As it can be seen from the code, to enable pattern paints, we use `vgSetParameteri`

to set the paint type to `VG_PAINT_TYPE_PATTERN`

. The `vgPaintPattern`

function, instead, replaces any previous pattern image defined on the given paint object with a new pattern image.

Patterns may be extended (tiled) using one of four possible tiling modes, defined by the `VGTilingMode`

enumeration:

- the
`VG_TILE_FILL`

condition specifies that pixels outside the bounds of the source image should be taken as the color`VG_TILE_FILL_COLOR`

. Such color is expressed as a non-premultiplied`sRGBA`

color and alpha value. Values outside the`[0, 1]`

range are interpreted as the nearest endpoint of the range.

Tile fill |

- the
`VG_TILE_PAD`

condition specifies that pixels outside the bounds of the source image should be taken as having the same color as the closest edge pixel of the source image; that is, a pixel`(x, y)`

has the same value as the image pixel`(max(0, min(x, width – 1))`

,`max(0, min(y, height – 1)))`

.

Tile pad |

- the
`VG_TILE_REPEAT`

condition specifies that the source image should be repeated indefinitely in all directions; that is, a pixel`(x, y)`

has the same value as the image pixel (`x mod width`

,`y mod height`

) where the operator`(a mod b)`

returns a value between`0`

and`(b – 1)`

such that`a = (k * b) + (a mod b)`

for some integer`k`

. For example, a such module operator coincides with the standard C/C++/Java`%`

operator.

Tile repeat |

the

`VG_TILE_REFLECT`

condition specifies that the source image should be reflected indefinitely in all directions. That is, a pixel`(x, y)`

has the same value as the image pixel`(u, v)`

where:`u = (x mod width)`

if`floor(x/width)`

is even;`(width – 1 – (x mod width))`

otherwise`v = (y mod height)`

if`floor(y/height)`

is even;`(height – 1 – (y mod height))`

otherwise

Tile reflect |

The pattern tiling mode is set using `vgSetParameteri`

with a `paramType`

argument of `VG_PAINT_PATTERN_TILING_MODE`

.

```
VGfloat tileColor[4] = { 0.1f, 0.6f, 0.3f, 1.0f };
// set tiling mode
vgSetParameteri(pattern, VG_PAINT_PATTERN_TILING_MODE, VG_TILE_FILL);
vgSetfv(VG_TILE_FILL_COLOR, 4, tileColor);
```

The tutorial draws a circle path at the center of drawing surface, filled with a colored pattern. The path is created in object space with center at `(0, 0)`

and a radius of `1`

(see `genPaths`

function):

```
// create the circle that will be filled by the radial gradient paint
filledCircle = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F,
1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
vguEllipse(filledCircle, 0.0f, 0.0f, 2.0f, 2.0f);
```

By having defined the path in this tricky way, it’s really easy to scale and center it in order to fit the drawing surface:

```
// find the minimum dimension between surface width and height, then halve it
int halfDim = (surfaceWidth < surfaceHeight) ? (surfaceWidth / 2) : (surfaceHeight / 2);
// calculate scale factor in order to cover 90% of it
userToSurfaceScale = halfDim * 0.9f;
// translate to the surface center
userToSurfaceTranslation[X] = surfaceWidth / 2;
userToSurfaceTranslation[Y] = surfaceHeight / 2;
// "user to surface" transformation (a uniform scale plus a translation),
// upload the matrix to the OpenVG backend
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();
vgTranslate(userToSurfaceTranslation[X], userToSurfaceTranslation[Y]);
vgScale(userToSurfaceScale, userToSurfaceScale);
```

The user can set the pattern position and orientation through two control points, called `center`

and `target`

(defined in surface space). Such control points are highlighted by two white spots that can be moved by using mouse/touch.

In order to accomplish the task to map the pattern image to the desired bounds, we have to review some details (see `setMatrices`

function):

we define the direction vector (i.e. the baseline where we want to lay the image) in surface space as:

`direction = target - center`

the rotation factor is given by the relative slope between the two control points:

`rotation = arctan(direction[Y] / direction[X])`

the scale factor is given by the distance between the two control points divided by the image size (e.g. 64 in the example above):

`scale = length(direction) / imageSize`

the translation factor is given by the

`center`

point

```
// calculate pattern direction
float direction[] = {
patternTarget[X] - patternCenter[X],
patternTarget[Y] - patternCenter[Y]
};
// calculate scale factor
float dist = hypot(direction[X], direction[Y]);
float scale = dist / patternImageSize;
// calculate rotation factor
float rotation = atan2(direction[Y], direction[X]);
// "paint to user" transformation, upload the matrix to the OpenVG backend
vgSeti(VG_MATRIX_MODE, VG_MATRIX_FILL_PAINT_TO_USER);
vgLoadIdentity();
vgTranslate(patternCenter[X], patternCenter[Y]);
vgScale(scale, scale);
// OpenVG rotations must be expressed in degrees
vgRotate(rotation * 57.2957f);
```

The code represents the mapping between the pattern coordinates system and the drawing surface system (where control points live). But we know that, in between, there is the “user-to-surface” transformation too, which unfortunately interferes with the desired mapping. So, by setting the `VG_MATRIX_FILL_PAINT_TO_USER`

matrix, we must take care to nullify the effect of `VG_MATRIX_PATH_USER_TO_SURFACE`

matrix by appending its inverse transformation (i.e. essentially dividing `scale`

and `patternCenter`

values by `userToSurfaceScale`

; see `setMatrices`

function for the code details).