OpenVG specifications state that images, as opposed to paths, can be drawn using a perspective image-user-to-surface transformation. The vgDrawImage
function uses a 3x3
projective (or perspective) transformation matrix (representing the image-user-to-surface transformation) that maps user coordinates to surface coordinates.
Lets see what happens.
A projective matrix consists of the following 9 entries:
The projective transformation maps a point (x, y)
into the point:
The concatenation of two projective transformations is a projective transformation, whose matrix form is the product of the matrices of the original transformations. Both affine and projective transformations map straight lines to straight lines. However, affine transformations map evenly spaced points along a source line to evenly spaced points in the destination, whereas projective transformations allow the distance between points to vary due to the effect of division by the denominator.
Although OpenVG does not provide support for three-dimensional coordinates, proper setting of the w
matrix entries can simulate the effect of placement of images in three dimensions, as well as other warping effects.
OpenVG (VGU) provides three utility functions to compute 3x3 projective transform matrices. The first two compute the transformation from an arbitrary quadrilateral onto the unit square, and vice versa. The third computes the transformation from an arbitrary quadrilateral to an arbitrary quadrilateral. The output transformation is stored into matrix
argument as 9
elements, in the order sx
, shy
, w0
, shx
, sy
, w1
, tx
, ty
, w2
:
// sets the entries of the given matrix to a projective transformation that maps points:
// (sx0, sy0) to (0, 0)
// (sx1, sy1) to (1, 0)
// (sx2, sy2) to (0, 1)
// (sx3, sy3) to (1, 1)
VGUErrorCode vguComputeWarpQuadToSquare(VGfloat sx0, VGfloat sy0,
VGfloat sx1, VGfloat sy1,
VGfloat sx2, VGfloat sy2,
VGfloat sx3, VGfloat sy3,
VGfloat* matrix);
vguComputeWarpQuadToSquare |
// sets the entries of the given matrix to a projective transformation that maps points:
// (0, 0) to (dx0, dy0)
// (1, 0) to (dx1, dy1)
// (0, 1) to (dx2, dy2)
// (1, 1) to (dx3, dy3)
VGUErrorCode vguComputeWarpSquareToQuad(VGfloat dx0, VGfloat dy0,
VGfloat dx1, VGfloat dy1,
VGfloat dx2, VGfloat dy2,
VGfloat dx3, VGfloat dy3,
VGfloat* matrix);
vguComputeWarpSquareToQuad |
// sets the entries of the given matrix to a projective transformation that maps points:
// (sx0, sy0) to (dx0, dy0)
// (sx1, sy1) to (dx1, dy1)
// (sx2, sy2) to (dx2, dy2)
// (sx3, sy3) to (dx3, dy3)
VGUErrorCode vguComputeWarpQuadToQuad(VGfloat dx0, VGfloat dy0,
VGfloat dx1, VGfloat dy1,
VGfloat dx2, VGfloat dy2,
VGfloat dx3, VGfloat dy3,
VGfloat sx0, VGfloat sy0,
VGfloat sx1, VGfloat sy1,
VGfloat sx2, VGfloat sy2,
VGfloat sx3, VGfloat sy3,
VGfloat* matrix);
vguComputeWarpQuadToQuad |
In all cases, if there is no projective mapping that satisfies the given constraints, or the mapping would be degenerate (i.e., non-invertible), VGU_BAD_WARP_ERROR
is returned and the output matrix is unchanged.
At initialization time, and every time the drawing surface changes size (i.e. due to a window resize / device orientation turn), an image (representing a sporty girl) is generated by drawing several paths with different colors, then pixels are copied from the drawing surface to the VGImage
object using the vgGetPixels
function. Paths and colors data, defining the vector girl, is stored in a separate file (see girl_data.c
/ GirlData.java
).
The sporty girl image, generated from vector paths |
The tutorial code maps the image outline to a custom quadrilateral, using the vguComputeWarpQuadToQuad
function. The quadrilateral is defined by four control points, that can be moved by mouse / touch.
VGImage girlImage;
VGfloat warpMatrix[9];
ControlPoint controlPoints[4];
// calculate warp matrix
vguComputeWarpQuadToQuad(// destination quadrilateral
controlPoints[0].x, controlPoints[0].y,
controlPoints[1].x, controlPoints[1].y,
controlPoints[2].x, controlPoints[2].y,
controlPoints[3].x, controlPoints[3].y,
// source image bounds
0.0f, 0.0f,
imageWidth, 0.0f,
imageWidth, imageHeight,
0.0f, imageHeight,
// the output matrix
warpMatrix);
// upload the warp matrix to the OpenVG backend
vgSeti(VG_MATRIX_MODE, VG_MATRIX_IMAGE_USER_TO_SURFACE);
vgLoadIdentity();
vgLoadMatrix(warpMatrix);
// draw girl image
vgDrawImage(girlImage);
Warp example 1 | Warp example 2 |