3.2. VG Lite Library
3.2.1. Vector Graphics Description
Vector graphics are objects that are defined by:
- instructions (such as
line_to
orcurve_to
), - coordinates (parameters of the instructions),
- styles (plain color with or without transparency, gradients, pattern),
Vector graphics have the advantage over raster graphics in that they can be transformed without loss of quality.
Vector graphics are commonly used in vector fonts (i.e., TrueType) and SVG.
They are intrinsically different from the more common raster graphics file formats such as BMP, JPEG, or PNG.
They allow rendering complex objects with a constant amount of data.
3.2.2. Aim of the Library
The VG Lite foundation library provides access to the VG Lite functionalities of the platform. It has been designed to be as close as possible to the native VGLite library provided in the MIMXRT595-EVK SDK.
Note
The library does not respect the Java class naming convention (camel case): it uses the same naming convention as C library vglite
. The functions have the same prototype as the vg_lite.h
functions.
3.2.3. Importing the Library
The library can be retrieved by adding the following dependency to your module.ivy
:
<dependency org="com.microej.api" name="vivante-vglite" rev="0.6.0" conf="provided->*" />
Note
The VGLite library is a foundation library. The platform provides it.
3.2.4. The Java API
The library is made of several units:
vg_lite
: Implements the supported APIs ofvg_lite.h
:vg_lite_clear
: Fills a (partial) buffer with a specified color.vg_lite_draw_path
: Draws a path to a MicroUI GraphicsContext.vg_lite_draw_gradient
: Fills a path with a gradient.
vg_lite_path
: This class represents avg_lite_path
structure. It also provides the necessary method to manipulate the data. The class holds a handle that is used to manage the structure natively.vg_lite_matrix
: This class represents avg_lite_matrix
structure. It also provides the necessary method to manipulate the data. The class holds a handle that is used to manage the structure natively. The class also provides the follwing APIs that are mapped on native matrix transformation functions:vg_lite_identity
: To set a matrix toidentity
.vg_lite_translate
: Translates a matrix.vg_lite_scale
: Scales a matrix.vg_lite_rotate
: Rotates a matrix.vg_lite_perspective
: Updates a matrix to apply perspective transformations.
Note
vg_lite_matrix
and vg_lite_gradient
are closable classes.
As they are doing dynamic allocation, it is essential to close them before they are dereferenced to provide memory leaks.
3.2.5. Matrix Transformation
Matrix operations should be executed in the reverse order of the expected result.
More information about matrix operations can be found at http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ (For 2D Graphics, 3x3 matrices are used instead of 4x4 for 3D graphics).
For instance, consider the following code:
public static void draw(GraphicsContext gc, vg_lite_path path, int x, int y, float angle) {
// Update transformation matrix
vg_lite_matrix matrix = new vg_lite_matrix();
vg_lite_matrix.vg_lite_translate(x, y, matrix); // 1. Translate
vg_lite_matrix.vg_lite_rotate(angle, matrix); // 2. Rotate
// Draw path
vg_lite.vg_lite_draw_path(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, matrix, vg_lite.VG_LITE_BLEND_NONE,
0xff000000 | Colors.GREEN);
// Close matrix
matrix.close();
}
The rendered object will be first translated by (x, y)
and then rotated by angle
(in degrees).
3.2.6. Vector Paths
A path is an array containing instructions and coordinates.
Consider the following code:
public class DrawPath {
private static final byte[] RAW_PATH_DATA = new byte[] { 2, (byte) -100, (byte) -100, // move to -100, -100
4, (byte) 100, (byte) -100, // line to 100, -100
4, (byte) 100, (byte) 100, // line to 100, 100
0 // close path
};
public static void main(String[] args) throws InterruptedException {
// Start application
MicroUI.start();
Display display = Display.getDisplay();
GraphicsContext gc = display.getGraphicsContext();
// Create path
vg_lite_path path = new vg_lite_path(-100, -100, 100, 100, vg_lite_path.VG_LITE_HIGH, vg_lite_path.VG_LITE_S8,
RAW_PATH_DATA);
// Paint background
gc.setColor(Colors.WHITE);
Painter.fillRectangle(gc, 0, 0, gc.getWidth(), gc.getHeight());
// Update transformation matrix
vg_lite_matrix matrix = new vg_lite_matrix();
vg_lite_matrix.vg_lite_translate(gc.getWidth() / 2, gc.getHeight() / 2, matrix);
// Draw path
vg_lite.vg_lite_draw_path(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, matrix, vg_lite.VG_LITE_BLEND_NONE,
0xff000000 | Colors.BLUE);
// Flush content on display
display.flush();
// Ensure flush is completed before rendering next drawing
display.waitFlushCompleted();
// Stop application
Thread.sleep(2000);
matrix.close();
MicroUI.stop();
}
}
A blue right triangle will be rendered at coordinates (x, y)
.
Note
This example shows how to create raw path data.
VGLite utils library provides helper utilities to draw paths.
3.2.7. Gradients
This library supports linear gradients.
On the GCNanoLiteV, Gradients are images of width 256 and height of 1 pixel, which are expanded to fill the path. An image contains smooth transitions between several color ramps. The stops define the position of each ramp. As it is a 256 width image (from 0 to 255), to position a color at 25%, the value 63 shall be used (255 / 4).
Transformation matrices like any graphic object can then transform the gradient.
Consider the following code:
public class DrawGradient {
private static final int[] RAMPS = { 0xFFFF0000, 0xFF00FF00, 0xFF0000FF };
private static final int[] STOPS = { 0, 127, 255 };
private static final byte[] RAW_PATH_DATA = new byte[] { 2, (byte) -100, (byte) -100, // move to -100, -100
4, (byte) 100, (byte) -100, // line to 100, -100
4, (byte) 100, (byte) 100, // line to 100, 100
0 // close path
};
private static vg_lite_matrix pathMatrix;
private static vg_lite_gradient gradient;
private static int angle;
private static boolean animating;
public static void main(String[] args) throws InterruptedException {
// Start application
MicroUI.start();
Display display = Display.getDisplay();
GraphicsContext gc = display.getGraphicsContext();
// Create path
vg_lite_path path = new vg_lite_path(-100, -100, 100, 100, vg_lite_path.VG_LITE_HIGH, vg_lite_path.VG_LITE_S8,
RAW_PATH_DATA);
pathMatrix = new vg_lite_matrix();
gradient = new vg_lite_gradient();
animating = true;
int x = gc.getWidth() / 2;
int y = gc.getHeight() / 2;
while (animating) {
// Paint background
gc.setColor(Colors.WHITE);
Painter.fillRectangle(gc, 0, 0, gc.getWidth(), gc.getHeight());
// Animate: update matrix
animate(x, y);
// draw path
vg_lite.vg_lite_draw_gradient(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, pathMatrix, gradient,
vg_lite.VG_LITE_BLEND_NONE);
// Flush content on display
display.flush();
// Ensure flush is completed before rendering next drawing
display.waitFlushCompleted();
}
// Stop application
pathMatrix.close();
gradient.close();
MicroUI.stop();
}
private static void animate(int x, int y) {
// Update transformation matrix
vg_lite_matrix.vg_lite_identity(pathMatrix);
vg_lite_matrix.vg_lite_translate(x, y, pathMatrix); // 1. Translate
vg_lite_matrix.vg_lite_rotate(angle++, pathMatrix); // 2. Rotate
vg_lite_gradient.vg_lite_init_grad(gradient); // Initialization of the gradient object
vg_lite_gradient.vg_lite_set_grad(gradient, RAMPS.length, RAMPS, STOPS); // Set gradients parameters
vg_lite_gradient.vg_lite_update_grad(gradient); // Create the 256x1 image
vg_lite_matrix gradientMatrix = gradient.getMatrix();
vg_lite_matrix.vg_lite_identity(gradientMatrix);
vg_lite_matrix.vg_lite_translate(x - 100, y - 100, gradientMatrix); // 2. Translate
vg_lite_matrix.vg_lite_scale((float) 200 / 256, (float) 200 / 256, gradientMatrix); // 1. Scale
}
}
A rotating right triangle with a static gradient will be rendered at coordinates (x, y)
.
3.2.8. Clipping
Clipping is managed throw the MicrouiGraphicsContext.setClip
API.
Update the render
method by the following code:
[...]
// Animate: update matrix
animate(x, y);
// Set clip
gc.setClip(x - 100, y - 100, 200, 200);
// Draw path
vg_lite.vg_lite_draw_gradient(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, pathMatrix, gradient,
vg_lite.VG_LITE_BLEND_NONE);
[...]
The triangle is now clipped during rendering.
3.3. VG Lite util Library
MicroEJ also provides an addon library to help drawing Vector Graphics in the java source code.
3.3.1. Importing the Library
The library can be retrieved by adding the following dependency to your module.ivy
:
<dependency org="com.microej.vivante" name="utils" rev="0.7.0" />
3.3.2. The Java API
The com.microej.vivante.utils
library contains the classes PathBufferS8
, PathBufferS16
, PathBufferS32
and PathBufferFP32
.
Each of these classes allows generating paths in formats supported by the i.MX RT595 GPU quickly.
A buffer is wrapped by the instantiated objects that will then be used to create a vg_lite_path
object. This buffer is updated by the drawing methods using the appropriate format.
Each class provides the following methods:
move
: moves the cursor to the given absolute position,moveRel
: moves the cursor to the given relative position,line
: draws a line from the cursor position to the given absolute position and update the cursor,lineRel
: draws a line from the cursor position to the given relative position and update the cursor,quad
: draws a quadratic Bezier curve from the cursor position to the given absolute position and update the cursor,quadRel
: draws a quadratic Bezier curve from the cursor position to the given relative position and update the cursor,cubic
: draws a cubic Bezier curve from the cursor position to the given absolute position and update the cursor,cubicRel
: draws a cubic Bezier curve from the cursor position to the given relative position and update the cursor,close
: closes the current path,toVGLitePath
: exports the current path to a newvg_lite_path
object,updateVGLitePath
: updates an existingvg_lite_path
object with the current path.
Consider the following code:
public class DrawRedBall {
private static final float TANGENT = 0.5522847498307933F; // Magic number to approximate circle arcs from Bezier
public static void main(String[] args) throws InterruptedException {
// Start application
MicroUI.start();
Display display = Display.getDisplay();
GraphicsContext gc = display.getGraphicsContext();
// Create path: approximation of a circle using Bezier cubic curves
PathBufferFP32 pb = new PathBufferFP32();
pb.move(0, -10);
pb.cubicRel(10 * TANGENT, 0, 10, 10 - 10 * TANGENT, 10, 10);
pb.cubicRel(0, 10 * TANGENT, -10 + 10 * TANGENT, 10, -10, 10);
pb.cubicRel(-10 * TANGENT, 0, -10, -10 + 10 * TANGENT, -10, -10);
pb.cubicRel(0, -10 * TANGENT, 10 - 10 * TANGENT, -10, 10, -10);
vg_lite_path path = pb.toVGLitePath(-10, -10, 10, 10, vg_lite_path.VG_LITE_UPPER);
// Paint background
gc.setColor(Colors.WHITE);
Painter.fillRectangle(gc, 0, 0, gc.getWidth(), gc.getHeight());
// Update transformation matrix
vg_lite_matrix matrix = new vg_lite_matrix();
vg_lite_matrix.vg_lite_identity(matrix);
vg_lite_matrix.vg_lite_translate(gc.getWidth() / 2, gc.getHeight() / 2, matrix); // 1. Translate
vg_lite_matrix.vg_lite_scale(5, 5, matrix); // 2. Scale
// Draw path
vg_lite.vg_lite_draw_path(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, matrix, vg_lite.VG_LITE_BLEND_NONE,
0xff000000 | Colors.RED);
// Flush content on display
display.flush();
// Ensure flush is completed before rendering next drawing
display.waitFlushCompleted();
// Stop application
Thread.sleep(2000);
matrix.close();
MicroUI.stop();
}
}
A red circle is rendered.
Note
The GPU does not support circle arc directly (see MicroUI Drawings). It is however possible to approximate circles with Bezier curves. See https://en.wikipedia.org/wiki/Composite_B%C3%A9zier_curve#Approximating_circular_arcs.
3.3.3. Going Further
One of the significant advantages of vector graphics is that animation of objects can be easy thanks to the matrix transformations without changing the original resource.
Update the following code in the DrawRedBall
class:
public class DrawRedBall2 {
private static final float TANGENT = 0.5522847498307933F; // Magic number to approximate circle arcs from Bezier
private static final int MAX_X_SCALE = 7;
private static final int MIN_X_SCALE = 4;
private static final int MAX_Y_OFFSET = 100;
private static final int Y_STEP = 5;
private static final float SCALE_STEP = 1F / 3;
private static final int STATE_DOWN = 0;
private static final int STATE_BOUNCE_DOWN = 1;
private static final int STATE_BOUNCE_UP = 2;
private static final int STATE_UP = 3;
private static vg_lite_matrix matrix;
private static float xScale = 5;
private static float yScale = 5;
private static int state = STATE_DOWN;
private static int yOffset = 0;
private static boolean animating;
public static void main(String[] args) {
// Start application
MicroUI.start();
Display display = Display.getDisplay();
GraphicsContext gc = display.getGraphicsContext();
// Create path: approximation of a circle using Bezier cubic curves
PathBufferFP32 pb = new PathBufferFP32();
pb.move(0, -10);
pb.cubicRel(10 * TANGENT, 0, 10, 10 - 10 * TANGENT, 10, 10);
pb.cubicRel(0, 10 * TANGENT, -10 + 10 * TANGENT, 10, -10, 10);
pb.cubicRel(-10 * TANGENT, 0, -10, -10 + 10 * TANGENT, -10, -10);
pb.cubicRel(0, -10 * TANGENT, 10 - 10 * TANGENT, -10, 10, -10);
vg_lite_path path = pb.toVGLitePath(-10, -10, 10, 10, vg_lite_path.VG_LITE_UPPER);
matrix = new vg_lite_matrix();
animating = true;
int x = gc.getWidth() / 2;
int y = gc.getHeight() / 2;
while (animating) {
// Paint background
gc.setColor(Colors.WHITE);
Painter.fillRectangle(gc, 0, 0, gc.getWidth(), gc.getHeight());
// Animate: update matrix
animate(x, y);
// draw path
vg_lite.vg_lite_draw_path(gc, path, vg_lite.VG_LITE_FILL_EVEN_ODD, matrix, vg_lite.VG_LITE_BLEND_NONE,
0xff000000 | Colors.RED);
// Flush content on display
display.flush();
// Ensure flush is completed before rendering next drawing
display.waitFlushCompleted();
}
// Stop application
matrix.close();
MicroUI.stop();
}
private static void animate(int x, int y) {
switch (state) {
default:
case STATE_DOWN:
if (yOffset < MAX_Y_OFFSET) {
yOffset += Y_STEP;
} else {
state = STATE_BOUNCE_DOWN;
}
break;
case STATE_BOUNCE_DOWN:
if (xScale < MAX_X_SCALE) {
xScale += SCALE_STEP;
yScale -= SCALE_STEP;
yOffset += Y_STEP;
} else {
state = STATE_BOUNCE_UP;
}
break;
case STATE_BOUNCE_UP:
if (xScale > MIN_X_SCALE) {
xScale -= SCALE_STEP;
yScale += SCALE_STEP;
yOffset -= Y_STEP;
} else {
state = STATE_UP;
}
break;
case STATE_UP:
if (yOffset > -MAX_Y_OFFSET) {
yOffset -= Y_STEP;
} else {
state = 0;
}
break;
}
// Update transformation matrix
vg_lite_matrix.vg_lite_identity(matrix);
vg_lite_matrix.vg_lite_translate(x, y + yOffset, matrix); // 1. Translate
vg_lite_matrix.vg_lite_scale(xScale, yScale, matrix); // 2. Scale
}
}
A ball bouncing up and down is rendered.
3.3.4. Limitations
The VGLite foundation library has the following limitations:
Rendering is not pixel accurate
The front-panel cannot render the following blending algorithms:
- VG_LITE_BLEND_SCREEN
- VG_LITE_BLEND_MULTIPLY
- VG_LITE_BLEND_ADDITIVE
To keep the front-panel operational when using these blending modes, VG_LITE_BLEND_SRC_OVER is used instead and a warning is displayed in the console.
The matrix transformation
vg_lite_perspective
is not available yet.