Using OpenVG API it is possible to draw vector paths, specifying a paint for the fill (i.e.: the bounded inside region defined by its contours) and a paint for the stroke (i.e. the “widening” of path edges using a straight-line pen). The are a total of four possible paint types: plain colors, linear gradients, radial gradients and bitmap patterns. AmanithVG extends such paint types with an additional one: conical gradients (see `VG_MZT_conical_gradient`

extension). This tutorial will introduce linear gradients.

Linear gradients define a scalar-valued gradient function based on two points `(x0, y0)`

and `(x1, y1)`

with the following properties:

it is equal to

`0`

at`(x0, y0)`

it is equal to

`1`

at`(x1, y1)`

it increases linearly along the line from

`(x0, y0)`

to`(x1, y1)`

it is constant along lines perpendicular to the line from

`(x0, y0)`

to`(x1, y1)`

Such scalar value is mapped to colors by *color ramps*. The application defines the non-premultiplied sRGBA color and alpha value associated with each of a number of values, called *stops*. A stop is defined by an offset between `0`

and `1`

and a color value. Color and alpha values at offset values between the stops are defined by means of linear interpolation between color values defined at the nearest stops above and below the given offset value.

The two geometric points `(x0, y0)`

and `(x1, y1)`

that defines a linear gradient are specified in the *paint coordinates system*; according to the OpenVG pipeline, 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`

).

Gradient Matrices |

To enable linear gradient paint, use `vgSetParameteri`

to set the paint type to `VG_PAINT_TYPE_LINEAR_GRADIENT`

. The linear gradient parameters are set using `vgSetParameterfv`

with a `paramType`

argument of `VG_PAINT_LINEAR_GRADIENT`

. The gradient values are supplied as a vector of 4 floats in the order `x0`

, `y0`

, `x1`

, `y1`

.

```
// create a paint object
VGPaint linGradPaint = vgCreatePaint();
VGfloat linGradParams[4] = { x0, y0, x1, y1 };
// set the paint to be a linear gradient
vgSetParameteri(linGradPaint, VG_PAINT_TYPE, VG_PAINT_TYPE_LINEAR_GRADIENT);
// set geometric parameters for the linear gradient (x0, y0) and (x1, y1)
vgSetParameterfv(linGradPaint, VG_PAINT_LINEAR_GRADIENT, 4, linGradParams);
```

Color ramp parameters are set using `vgSetParameter`

.

The `VG_PAINT_COLOR_RAMP_SPREAD_MODE`

parameter controls the spread mode, that is how the given set of stops are repeated or extended in order to define interpolated color values for arbitrary input values outside the `[0; 1]`

range. The are three modes:

`VG_COLOR_RAMP_SPREAD_PAD`

: extend stops`VG_COLOR_RAMP_SPREAD_REPEAT`

: repeat stops`VG_COLOR_RAMP_SPREAD_REFLECT`

: repeat stops in reflected order

Pad | Repeat | Reflect |

The `VG_PAINT_COLOR_RAMP_STOPS`

parameter takes an array of floating-point values giving the offsets and colors of the stops, in order. Each stop is defined by a floating-point offset value and four floating-point values containing the sRGBA color and alpha value associated with each stop, in the form of a non-premultiplied `(R, G, B, α)`

.

```
VGfloat colStops[25] = {
// ofs R G B α
// ----------------------------
0.00f, 0.40f, 0.00f, 0.60f, 1.00f,
0.25f, 0.90f, 0.50f, 0.10f, 1.00f,
0.50f, 0.80f, 0.80f, 0.00f, 1.00f,
0.75f, 0.00f, 0.30f, 0.50f, 1.00f,
1.00f, 0.40f, 0.00f, 0.60f, 1.00f
};
// set color stops
vgSetParameterfv(linGradPaint, VG_PAINT_COLOR_RAMP_STOPS, 25, colStops);
// set gradient spread mode
vgSetParameteri(linGradPaint, VG_PAINT_COLOR_RAMP_SPREAD_MODE,
VG_COLOR_RAMP_SPREAD_REPEAT);
```

In order to simplify both code and understanding, we will set the `VG_MATRIX_FILL_PAINT_TO_USER`

matrix to the identity: so the paint coordinates system will coincide with the path-user one. An additional simplification is given by setting the `VG_MATRIX_PATH_USER_TO_SURFACE`

matrix as a unifrom scale plus a translation only (see ‘`tutorialDraw`

’ function):

```
vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE);
vgLoadIdentity();
vgTranslate(userToSurfaceTranslation[X], userToSurfaceTranslation[Y]);
vgScale(userToSurfaceScale, userToSurfaceScale);
```

So we can implement a simple map from the paint coordinates system to the drawing surface system as follow:

```
// Map a point from the paint coordinates system to the drawing surface system.
// NB: in the general case we should have applied a matrix transformation given by the
// concatenation of VG_MATRIX_FILL_PAINT_TO_USER and VG_MATRIX_PATH_USER_TO_SURFACE.
PaintToSurface(paintPoint) = (paintPoint * userToSurfaceScale) + userToSurfaceTranslation
```

The inverse transformation maps a point from the drawing surface coordinates system to the paint system:

```
// NB: in the general case we should have applied a matrix transformation given by the
// concatenation of inverse(VG_MATRIX_PATH_USER_TO_SURFACE) and
// inverse(VG_MATRIX_FILL_PAINT_TO_USER) matrices.
SurfaceToPaint(surfacePoint) = (surfacePoint - userToSurfaceTranslation) / userToSurfaceScale
```

The tutorial draws a circle path at the center of drawing surface, filled with a linear gradient. 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 linear 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 a rectangular region (i.e. the drawing surface):

```
// calculate "path user to surface" transformation
void userToSurfaceCalc(const int surfaceWidth,
const int surfaceHeight) {
// 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;
}
```

Gradient control points are highlighted by two white spots that can be moved by using mouse/touch. Their position, in paint coordinates system, is kept by two variables, `linGradStart`

and `linGradEnd`

:

```
// linear gradient parameters (x0, y0) and (x1, y1)
float linGradStart[2];
float linGradEnd[2];
```

every time we need to know their coordinates in surface space (e.g. when we want to draw them, within

`tutorialDraw`

function), we use the`PaintToSurface`

mapping (see`gradientParamsGet`

function)every time we need to set a gradient control point in order to match the mouse/touch position (that is expressed in drawing surface space), we use the

`SurfaceToPaint`

mapping (see`gradientParamsSet`

function)

This tutorial also shows the standard way of working with extensions. OpenVG contains a mechanism for applications to access information about the runtime platform: the `vgGetString`

function returns information about the OpenVG implementation, including extension information. In this tutorial we are interested to verify the runtime support of `VG_MZT_color_ramp_interpolation`

extension (see AmanithVG extensions):

```
void extensionsCheck(void) {
// get the space-separated list of supported OpenVG extensions
const char* extensions = (const char*)vgGetString(VG_EXTENSIONS);
// check for the support of VG_MZT_color_ramp_interpolation extension
smoothRampSupported = extensionFind("VG_MZT_color_ramp_interpolation", extensions);
}
```

The `VG_MZT_color_ramp_interpolation`

extension introduces a smooth color ramp interpolation schema, based on the Hermite interpolant coupled with Catmull-Rom tangents calculation. The result is a much smoother transition in colored gradients:

Linear interpolation | Smooth interpolation |