TGX 1.0.3
A tiny 2D/3D graphics library optimized for 32 bits microcontrollers.
Loading...
Searching...
No Matches
Basic concepts.

The TGX library's main class is the tgx::Image class which, as its name suggests, encapsulate an image, that is a rectangular array of pixels of a given color type. The library provides an extensive set of 2D and 3D drawing procedures to operates on image objects.

All methods/classes are defined inside the tgx namespace. To use the library, just include tgx.h:

#include <tgx.h>
using namespace tgx; // optional, so we no not need to prefix all methods by tgx::
Main header file.

Colors types

The library defines several color classes

  • tgx::RGB565 : 16 bits colors: 5 bits red, 6 bits green, 5 bits blue. Aligned as and convertible to uint16_t. Preferred type when working with embedded platforms/MCUs.
  • tgx::RGB24 : 24 bits color: 8 bits red, 8 bits: green, 8 bits blue. No alignement.
  • tgx::RGB32 : 32 bits color: 8 bits red, 8 bits: green, 8 bits blue. 8 bits (pre-multipled) Alpha channel. Aligned as and convertible to uint32_t. Useful for blending.
  • tgx::RGBf : 96 bits floating point color: 32 bits red (float), 32 bits green (float), 32 bits blue (float). Aligned as float, no alpha channel. Used in the 3D API.
  • tgx::RGB64 : 64 bits color: 16 bits red, 16 bits: green, 16 bits blue. 16 bits (pre-multiplied) Alpha channel. Aligned as and convertible to uint64_t.
  • tgx::HSV : 96 bits colors: 16 bits Hue, 16 bits Saturation, 16 bits Values. Partial support only. Slow, use only to convert between color space, not for drawing operations.

Color types are handled just like usual basic type int , char... Any color type can be converted into any other color type.

Examples:

tgx::RGB565 color1; // create a 16 bits color, undefined value
tgx::RGB565 color2(8, 32, 16); // create a 16 bits color and set the color channels: 8 red (5bits), 32 green (6bits), 16 blue (5bits)
tgx::RGB565 color3(0.25f, 0.5f, 0.5f); // the same as color2 but defined with floats in [0.0f,1.0f] (works for any color type, irrespective of the channel color depths)
uint16_t c2 = (uint16_t)color3; // conversion to uint16_t.
tgx::RGB565 color4 = c2; // create 16 bit color from uint16_t.
int8_t R = color4.R; //
int8_t G = color4.G; // access the raw channels values
int8_t B = color4.B; //
tgx::RGB32 color5(color2); // conversion: create a RGB32 color from RGB565, this yields RGB32(64, 128, 128, 255) - the alpha channel is set to fully opaque.
color5.setOpacity(0.5f); // adjust the alpha channel in order to make the color half transparent. Other channels are multiplied accordingly (pre-multiplied alpha).
Color in R8/G8/B8/A8 format.
Definition: Color.h:1176
Color in R5/G6/B5 format.
Definition: Color.h:216
uint16_t G
Green channel (6 bits)
Definition: Color.h:232
uint16_t R
Red channel (5 bits)
Definition: Color.h:233
uint16_t B
Blue channel (5 bits)
Definition: Color.h:231

Predefined colors: Black, White, Blue, Green, Purple, Orange, Cyan, Lime, Salmon, Maroon, Yellow, Magenta, Olive, Teal, Gray, Silver, Navy, Transparent.

Example:

tgx::RGB32_Yellow; // yellow color in RGB32
tgx::RGB565_Green; // green color in RGB565

For additional details on color manipulations, look directly into the color classes definitions in color.h.

Blending and opacity parameter.

Color types tgx::RGB32 and tgx::RGB64 have an alpha channel which is used for alpha-blending during drawing operations.

Note
Colors with an alpha channel are always assumed to have pre-multiplied alpha.

Other color type do not have an alpha channel but the library still support simple blending operations through the (optional) opacity parameter added to all drawing primitives. This parameter ranges in [0.0f, 1.0f] with

  • opacity = 1.0f: fully opaque drawing. This is the default value when the parameter is omited.
  • opacity = 0.0f: fully transparent drawing (so this draws nothing).

Example:

// assume below that `im` is a tgx::Image<tgx::RGB565> object
// the method Image::drawLine has signature drawLine(iVec2 P1, iVec2 P2, color_t color, float opacity).
im.drawLine({10, 20}, {30, 40}, tgx::RGB565_Red); // draw a fully opaque red line from (10,20) to (30, 40).
im.drawLine({10, 20}, {30, 40}, tgx::RGB565_Red, 0.5f); // here opacity=0.5f so we draw a half-transparent red line instead.
const RGB565 RGB565_Red
Color red in RGB565 format.

Image class, memory layout and sub-images

An image object is a lightweight structure (only 16 bytes in memory) similar to a Numpy view in Python. The object records:

  • the dimension of the image (lx, ly).
  • a pointer to the memory buffer buffer[] of pixel colors of type color_t (the template color type of the image).
  • a stride value that is the number of pixels per image row (which may be larger than ly when working with sub-images).

In particular, the image object does not contain the pixels buffer itself but only a pointer to its memory location. As a general rule, the TGX library never perform any dynamic memory allocation.

Several images can reference the same memory buffer (or a part of it). Creating images/sub-images that share the same pixels buffer is useful to restrict drawing operations to a given rectangular region.

The position of a pixel inside an image is given in row major order:

Pixel(i, j) = buffer[i + stride*j]

for 0 ≤ i < lx and 0 ≤ j < ly.

Example:

tgx::RGB32 buff[320*240]; // memory buffer for a 320x240 image in RGB32 color format.
tgx::Image<tgx::RGB32> im0; // create an empty (invalid) image of type RGB32.
im0.set(buff, 320,240) // set the image pixel buffer and dimension (default stride = ly).
tgx::Image<tgx::RGB32> im1(buff, 320,240); // construct and set the image parameter at the same time (same as im0)
tgx::Image<tgx::RGB32> im2 = im({20, 50, 100, 200}); // create a sub-image restricted ot the region [20,50]x[100,200] of im2.
im0.lx(); // width of im1, here it is 320;
im2.ly(); // height of im2, here it is 101 = 200 - 100 + 1
im0.data(); // return the adress of the buffer (here = buff)
im2.data(); // return the adress of the buffer (here = buff + 20 + 100*stride)
im0.stride(); // im1 stride, (here = 320)
im2.stride(); // im2 stride, (here = 320 same as im1)
Image class [MAIN CLASS FOR THE 2D API].
Definition: Image.h:143
TGX_INLINE int stride() const
Return the image stride.
Definition: Image.h:386
void set(T *buffer, int lx, int ly, int stride=DEFAULT_STRIDE)
Set/update the image parameters.
TGX_INLINE const color_t * data() const
Return a pointer to the pixel buffer.
Definition: Image.h:412
TGX_INLINE int lx() const
Return the image width.
Definition: Image.h:362
TGX_INLINE int ly() const
Return the image height.
Definition: Image.h:378

See also: Image::set(), Image::crop(), Image::getCrop(), Image::operator(), Image::isValid(), Image::setInvalid(), Image::imageBox().

Note
Creating image/sub-image is very fast and "cost free" so one should not refrain from creating Image/sub-images whenever needed ! It is the most efficient solution to clip/restrict any drawing operation to a given (rectangular) region of an image.

Vectors and Boxes

The tgx library defines classes for 2D vectors and boxes:

  • tgx::iVec2 : integer-valued 2D vector. Use for pixel location in images
  • tgx::fVec2 : floating point-valued 2D vector. Used for pixel location in images when using sub-pixel precision
  • tgx::iBox2 : integer-valued 2D box. Used to represent a rectangular region inside an image
  • tgx::fBox2 : floating point-valued 2D box. represent a rectangular region inside an image when using sub-pixel precision
Note
3D variants for vectros and boxes are also available: tgx::iVec3, tgx::fVec3, tgx::iBox3, tgx::fBox3 c.f. the 3D API tutorials for details.
Warning
Pixel adressing varies slightly when using integer valued coordinates vs floating point valued coordinates.
  • Integer valued cordinates: iVec2(i,j) represents the location of the pixel (i,j) in the image. The whole image corresponds to the (integer valued) box iBox2(0, lx - 1, 0, ly - 1).
  • Floating point valued coordinates: fVec2(i,j) represents the center of pixel (i,j) in the image. The pixel itself is a unit lenght square box centered around this location i.e. represented by fBox2( i-0.5f, i+0.5f, j-0.5f, j+0.5f). The whole image corresponds to the (floating point valued) box fBox2( -0.5f, lx - 0.5f, -0.5f, ly - 0.5f).

Vectors and boxes support all the usual operations: arithmetics (addition, dot product..), copy, type conversion...

Most drawing methods take vectors and boxes as input parameters instead of scalars. It is recommended to use initializer lists makes the code more readable. For exapple, just write {10, 20} instead of tgx::iVec2(10, 20).

Example:

tgx::iVec2 v1(10, 30); // integer valued vector (10,30)
tgx::fVec2 v2 = v1; // conversion: floating point valued vector (10.0f, 30.0f)
float x = 2.0f;
tgx::fVec2 v3 = { x, 7 }; // construct the vector from an initializer list. vector (2.0f, 7.0f)
(v2 + v3) * 3.0f; // usual maths operations are available...
v3.x = 4; // direct access to coordinate values v3 is now (2.0f, 4.0f)
tgx::iBox2 B(40, 80, 20, 30); // format (minx, maxx, miny, maxy) -> integer valued box [40,80]x[20,30]
// assume below that `im` is a tgx::Image<tgx::RGB32> object
// method `Image::drawCircle(iVec2 center, int r, color_t color, float opacity)` takes integer value parameters.
im.drawCircle(v1, 12, tgx::RGB32_Red); // draw a red circle centered at v1 = (10, 30) of radius 12.
// method `Image::drawCircleAA(fVec2 center, float r, color_t color, float opacity)` takes floating point valued parameters for sub-pixel precision:
im.drawCircleAA({50.0f, 60.0f} , 11.5f, tgx::RGB32_Red); // draw a blue circle centered at (50.0f, 60.0f) of radius 11.5f. Use an initalizer list to define the center.
// method `Image::fillRect(const iBox2 & B, color_t color, float opacity)` takes an integer valued box as input parameter.
im.fillRect(B, tgx::RGB32_Black); // draw a filled black box [40, 80]x[20,30]
im.fillRect({50, 60 , 70, 80}, tgx::RGB32_Black); // using an initializer list: draw a filled black box [50, 60]x[70,80]
const RGB32 RGB32_Black
Color black in RGB32 format.
const RGB32 RGB32_Red
Color red in RGB32 format.
Generic 2D Box [specializations iBox2 , fBox2, dBox2].
Definition: Box2.h:151
Generic 2D vector [specializations iVec2, fVec2, dVec2].
Definition: Vec2.h:64
T x
'x' coordinate (first dimension)
Definition: Vec2.h:72

Storing images in .cpp files

The memory buffer an image points to may reside in flash/ROM and it if possible to define 'const' images directly in .cpp files for easy inclusion in a project.

For example, the 10x10 image

smiley_icon

Can be stored as:

// macro to make the array more readable/compact.
#define C(val) tgx::RGB565((uint16_t)val)
// image data, the PROGMEM keyword is to insure that the image is store in FLASH memory.
const tgx::RGB565 smiley_data[] PROGMEM = {
C(0xffff), C(0xffff), C(0xf714), C(0xe609), C(0xdd40), C(0xdd40), C(0xe609), C(0xf714), C(0xffff), C(0xffff),
C(0xffff), C(0xee8f), C(0xddc6), C(0xeef1), C(0xfffb), C(0xfffb), C(0xeef1), C(0xddc6), C(0xee8f), C(0xffff),
C(0xeef4), C(0xdda5), C(0xf775), C(0xffd8), C(0xffd8), C(0xffd8), C(0xffd8), C(0xf775), C(0xdda5), C(0xeef4),
C(0xd5a9), C(0xee8d), C(0xff94), C(0x9b20), C(0xff94), C(0xff94), C(0x9b20), C(0xff94), C(0xee8d), C(0xd5a9),
C(0xc4a0), C(0xff50), C(0xff50), C(0xff50), C(0xff50), C(0xff50), C(0xff50), C(0xff50), C(0xff50), C(0xc4a0),
C(0xbc60), C(0xff0b), C(0xff0b), C(0xff0b), C(0xff0b), C(0xff0b), C(0xff0b), C(0xff0b), C(0xff0b), C(0xbc60),
C(0xc4e8), C(0xe5c4), C(0xfea6), C(0xaba0), C(0xfea6), C(0xfea6), C(0xaba0), C(0xfea6), C(0xe5c4), C(0xc4e8),
C(0xd612), C(0xc481), C(0xf622), C(0xdd40), C(0x9b20), C(0x9b20), C(0xdd40), C(0xf622), C(0xc481), C(0xd612),
C(0xf79e), C(0xc54d), C(0xbc60), C(0xdd60), C(0xfe40), C(0xfe40), C(0xdd60), C(0xbc60), C(0xc54d), C(0xf79e),
C(0xffff), C(0xef7d), C(0xcdd2), C(0xb4a8), C(0xaba0), C(0xaba0), C(0xb4a8), C(0xcdd2), C(0xef7d), C(0xffff) };
// image object, 10x10 image with smiley_data as pixel buffer.
const tgx::Image<tgx::RGB565> smiley(smiley_data, 10, 10);

The /tools/ subdirectory of the library contains the Python script image_converter.py which converts an image from a classical format (jpeg/png/bmp...) into a .cpp file.

Note
The TGX library also makes it easy to directly load PNG, JPEG and GIF images (which may be stored in RAM/FLASH or on another media such as a SD card) by using bindings with external libraries. See section TGX extensions via external librairies for more details.

Copying/converting images to different color types.

Recall that a tgx::Image object is just a view into a buffer of pixels of a given color type. Thus, copies of image object are shallow (they share the same image buffer / same color type).

The library provides methods the create "deep" copies and convert images to different color types:

tgx::RGB32 buf1[100*100]; // buffer for a 100x100 image in RGB32 color format
tgx::Image<tgx::RGB32> im1(buf1, 100, 100); // image that encapsulates the pixel buffer buf1
// ... draw some things on im...
tgx::RGB24 buf2[50 * 50]; // buffer for a 50x50 image in RGB24 color format
tgx::Image<tgx::RGB24> im2(buf2, 50, 50); // image that encapsulates the pixel buffer buf2
// copy the content of im1/buf1 into im2/buf2, resize and perform color conversion automatically !
im2.copyFrom(im1);
// we can directly change the color type of im1 in place from RGB32 to RGB565
// ok, now im3 has the same content as im1 had, but in RGB565 color format.
// Beware that im1 now contains 'garbage' (seen as RGB32 data) but writing
// on im1 will change the content of im3 as they share the same buffer !
Image< color_dst > convert()
Convert this image to another type.
void copyFrom(const Image< src_color_t > &src_im, float opacity=TGX_DEFAULT_NO_BLENDING)
Copy (or blend) the src image onto the destination image with resizing and color conversion.
Color in R8/G8/B8 format.
Definition: Color.h:681
Note
See also the methods for blitting sprites which may be used for copy and color type conversion.

Drawing things on an images...

Here comes the good part ! Now that can can create and manipulate images, we obviously want to draw thing on them !

TGX defines many primitives to draw shapes (such as lines, rectangle, circles, bezier curves), to blit sprites or write text... As well as a simple (but full-featured!) 3D engine.