Creating Dynamic L8 Images
This section explains the use of dynamic L8 images and especially how to create the palette.
Read in general about Dynamic Bitmaps here and about the L8 image format here.
Dynamic L8 Images
Dynamic L8 bitmaps are created like any other dynamic bitmap, except that we also have to specify the type of palette to create for the bitmap.
TouchGFX supports 3 types of palettes:
Palette | Description |
---|---|
CLUT_FORMAT_L8_ARGB8888 | 32-bit, 8 bits for each of red, green, blue and per pixel alpha channel |
CLUT_FORMAT_L8_RGB888 | 24-bit, 8 bits for each of red, green and blue, no per pixel alpha channel |
CLUT_FORMAT_L8_RGB565 | 16-bit, 5 bits for red, 6 bits for green, 5 bits for blue, no per pixel alpha channel |
Creating a Dynamic L8 image with 24-bit palette
Here we create a 100x100 pixels L8 bitmap with a 24-bit palette:
Screen1View.cpp
BitmapId dynamicBitmap1 = Bitmap::dynamicBitmapCreateL8(100, 100, Bitmap::CLUT_FORMAT_L8_RGB888, 256);
This call allocated a 100x100 L8 bitmap and a 24-bit palette in the bitmap cache. The palette holds 256 colors for dynamic bitmaps, but this number can be adjusted to the desired size to reduce memory waste.
Accessing the palette
Right after the pixels (on the first 32-bit aligned address) are 2 bytes containing the L8 palette format (stored little endian), followed by 2 bytes containing the number of entries in the palette (stored little endian), followed by the actual palette colors. This means the palette colors are located 4 bytes after the pixels (32-bit aligned).
We can get a pointer to the (so far uninitialized) palette like this:
Screen1View.cpp
//Get a pointer to the bitmap data (pixels and palette)
uint8_t* data = Bitmap::dynamicBitmapGetAddress(dynamicBitmap1);
//1 byte pr pixel, round up to 32-bit
uint32_t byteSize = 100*100;
byteSize = ((byteSize + 3) & ~3);
//Palette size is saved in byte 2 and 3
int palSize = (data[3] << 8) | data[2];
//Palette starts four bytes after the pixels
uint8_t* pal = (data + byteSize + 4);
Dynamic L8 Bitmap Example
We will now go through an example of creating a dynamic L8 bitmap and manipulating the palette. Manipulating the palette if not a typical thing to do for a general application. The example serves the purpose of illustrating how to access and generate a palette.
First we create a Screen in TouchGFXDesigner and insert a white Box in the background and an Image at e.g. x=140, y=8:
Now generate the code and open the Screen1View.cpp file. We must insert code in setupScreen to initialize the bitmap cache and create a dynamic bitmap.
We create a bitmap of 200*256 pixels. Remember each pixel in an L8 bitmap is one byte. We color each row of the image with a different color. First row has color 0, last row has color 255.
Then we initialize the colors in the palette. We calculate the start address of the palette and set the RGB values of the 256 colors. Here we create colors that go from green to red and back to green again.
Screen1View.cpp
#ifdef SIMULATOR
uint32_t cacheBuffer[320*1024/4]; //simulate PSRAM
uint16_t* psram = (uint16_t*)cacheBuffer;
#else
uint16_t* psram = (uint16_t*)(0xd0000000 + 480*272*2*2); //Address after two 16bit framebuffers
#endif
Screen1View::Screen1View()
{
}
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();
//Create one dynamic bitmap
Bitmap::setCache(psram, 320*1024, 1); //320Kb cache
const uint16_t palSize = 256;
BitmapId dynamicBitmap1 = Bitmap::dynamicBitmapCreateL8(200, 256, Bitmap::CLUT_FORMAT_L8_RGB888, palSize);
imageDynamic.setBitmap(Bitmap(dynamicBitmap1));
if (dynamicBitmap1 == BITMAP_INVALID)
{
touchgfx_printf("Unable to create dynamic bitmap\n");
}
else
{
uint8_t* data = Bitmap::dynamicBitmapGetAddress(dynamicBitmap1);
uint8_t* pixel = data;
//make colored rows
for (int y = 0; y<256; y++)
{
for (int x = 0; x<200; x++)
{
*pixel++ = y;
}
}
uint32_t byteSize = 200*256;
byteSize = ((byteSize + 3) & ~3);
//Palette starts four bytes after the pixels
uint8_t* pal = (data + byteSize + 4);
//Make palette with 256 colors from green to red to green
for (int i = 0; i < palSize; i++)
{
//BGR
pal[i * 3 + 0] = 0x00;
pal[i * 3 + 1] = 127 * (1 + cosf(i * 6.28f / (palSize - 1)));
pal[i * 3 + 2] = 255 * (sinf(i * 3.14f / (palSize - 1)));
}
}
}
This gives us a Screen that looks like this:
Manipulating the Palette
Since we have access to the palette used for the dynamic L8 bitmap, we can easily manipulate it.
Here we cycle the colors one index down and force a redraw of the image in every frame:
Screen1View.cpp
...
void Screen1View::handleTickEvent()
{
//get palette address
uint8_t* data = Bitmap::dynamicBitmapGetAddress(imageDynamic.getBitmap());
uint32_t byteSize = 200*256;
byteSize = ((byteSize + 3) & ~3);
const int palSize = (data[3] << 8) | data[2];
uint8_t* pal = (data + byteSize + 4);
//Cycle palette, copy color 0
int8_t blue = pal[0], green = pal[1], red = pal[2];
//Move palette down, 1->0, 2->1, ...
for (int i = 3; i < palSize * 3; i++)
{
pal[i - 3] = pal[i];
}
//Insert color 0 as color 255
pal[(palSize - 1) * 3 + 0] = blue;
pal[(palSize - 1) * 3 + 1] = green;
pal[(palSize - 1) * 3 + 2] = red;
//Force redraw by invalidating
imageDynamic.invalidate();
}
This will move colors in the dynamic bitmap "upwards":