This tutorial will introduce a new type of gradient paint type, not covered by OpenVG specifications: conical gradients. In order to extend the OpenVG API and add the support for conical gradients, new values for existing parameter types have been defined (see vgext.h), anyway the OpenVG pipeline behaviour remains the same. So lets start.

Conical gradients define a scalar-valued gradient function based on a gradient center point (cx, cy), a target point (tx, ty) and the number or repeats. The scalar function has the following properties:

• it is equal to 0 along the oriented direction (cx, cy) - (fx, fy) (i.e. from center towards target)

• proceding counterclockwise, it is equal to the normalized angle (respect to the center-target direction) multiplied by the number of repeats

Such scalar value is mapped to colors by color ramps, exactly as it happens for linear (have a look at it for more details) and radial gradients (have a look at it for more details).

Geometric properties (center and target) defining a conical 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).

To enable conical gradient paint, use vgSetParameteri to set the paint type to VG_PAINT_TYPE_CONICAL_GRADIENT_MZT. The conical gradient parameters are set using vgSetParameterfv with a paramType argument of VG_PAINT_CONICAL_GRADIENT_MZT. The gradient values are supplied as a vector of 5 floats in the order cx, cy, tx, ty, repeats.

// create a paint object
VGfloat conGradParams[5] = { cx, cy, tx, ty, repeats };
// set the paint to be a conical gradient
// set geometric parameters for the conical gradient (cx, cy), (tx, ty), repeats


Color ramps behaviour does not change for conical gradients, it’s the same as specified for linear and radial gradients: VG_PAINT_COLOR_RAMP_STOPS parameter takes an array of floating-point values giving the offsets and colors of the stops, and all the three spread modes are supported too (VG_COLOR_RAMP_SPREAD_PAD, VG_COLOR_RAMP_SPREAD_REPEAT, VG_COLOR_RAMP_SPREAD_REFLECT).

In the pictures below you can see the visual meaning of conical gradient repeats.

No repeatsTwo repeatsThree repeats

## The tutorial code

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);
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).
SurfaceToPaint(surfacePoint) = (surfacePoint - userToSurfaceTranslation) / userToSurfaceScale


Conical gradient repeats represent just a dimensionless number, so it remains the same regardless of the reference system.

The tutorial draws a circle path at the center of drawing surface, filled with a radial 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 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:

// 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 (i.e. center and focus) are highlighted by two white spots that can be moved by using mouse/touch. Such geometric parameters are stored, in paint coordinates system, by three variables:

// conical gradient parameters: center, target and repeats

• 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 PaintToSurfacemapping (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)
The VG_MZT_color_ramp_interpolation extension, that we have seen in the previous tutorials, is supported by conical grandients too.