| TGX 1.0.4 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::
tgx.h
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).
tgx::RGB32
Color in R8/G8/B8/A8 format.
Definition Color.h:1176
tgx::RGB565
Color in R5/G6/B5 format.
Definition Color.h:216
tgx::RGB565::G
uint16_t G
Green channel (6 bits)
Definition Color.h:232
tgx::RGB565::R
uint16_t R
Red channel (5 bits)
Definition Color.h:233
tgx::RGB565::B
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
tgx::RGB32_Yellow
const RGB32 RGB32_Yellow
Color yellow in RGB32 format.
tgx::RGB565_Green
const RGB565 RGB565_Green
Color green in RGB565 format.

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.
tgx::RGB565_Red
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:

(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)
tgx::Image
Image class [MAIN CLASS FOR THE 2D API].
Definition Image.h:143
tgx::Image::stride
TGX_INLINE int stride() const
Return the image stride.
Definition Image.h:386
tgx::Image::set
void set(T *buffer, int lx, int ly, int stride=DEFAULT_STRIDE)
Set/update the image parameters.
tgx::Image::data
TGX_INLINE const color_t * data() const
Return a pointer to the pixel buffer.
Definition Image.h:412
tgx::Image::lx
TGX_INLINE int lx() const
Return the image width.
Definition Image.h:362
tgx::Image::ly
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]
tgx::iBox2
Box2< int > iBox2
Integer-valued 2D box.
Definition Box2.h:45
tgx::RGB32_Black
const RGB32 RGB32_Black
Color black in RGB32 format.
tgx::RGB32_Red
const RGB32 RGB32_Red
Color red in RGB32 format.
tgx::fVec2
Vec2< float > fVec2
Floating point valued 2D vector with single (float) precision.
Definition Vec2.h:49
tgx::iVec2
Vec2< int > iVec2
Integer-valued 2D vector.
Definition Vec2.h:47
tgx::Vec2::x
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

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:

- Method Image<color_t>::copyFrom(src_im, opacity) can be used to create deep copy of images and it can also change the color type and resize them (using bilinear interpolation).
- Method Image<color_t>::convert<color_dst>() can be used to change the color type of an image "in place".

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
tgx::Image<tgx::RGB565> im3 = im1.convert<tgx::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 !
tgx::Image::convert
Image< color_dst > convert()
Convert this image to another type.
tgx::Image::copyFrom
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.
tgx::RGB24
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.

- Details for 2D drawing are provided in the 2D API tutorial
- Details for 3D drawing are provided in the 3D API tutorial