Dynamic Bitmaps
This section explains how to use Dynamic Bitmaps. Recall that standard bitmaps are compiled into the application and therefore must be available at compile time. The bitmaps are converted from e.g. PNG files and stored in an internal format together with size and format information.
It is also possible to create a bitmap in RAM at runtime. This is called a dynamic bitmap. A dynamic bitmap can be used just as the static bitmaps that are compiled into the application. This means that you can use a dynamic bitmap with e.g. the Image and Button widgets.
Dynamic Bitmap Configuration
When you create a dynamic bitmap the pixel memory is allocated from the bitmap cache. You must therefore configure a bitmap cache before you can create dynamic bitmaps. This is a manual process that cannot be done in the TouchGFX Designer or the Generator.
See the article on bitmap caching for configuration instructions.
It is required to define the maximum number of Dynamic Bitmaps used in your application. This maximum is passed to TouchGFX together with the bitmap cache address and size. Here we configure a bitmap cache with up to 4 dynamic bitmaps. You can do this in any file in your application. If you need to do it only once, the file FrontendApplication.cpp is a good candidate:
FrontendApplication.cpp (extract)
#include <gui/common/FrontendApplication.hpp>
#include <touchgfx/Bitmap.hpp>
FrontendApplication::FrontendApplication(Model& m, FrontendHeap& heap)
: FrontendApplicationBase(m, heap)
{
// Place cache start address in SDRAM at address 0xC0008000;
uint16_t* const cacheStartAddr = (uint16_t*)0xC0008000;
const uint32_t cacheSize = 0x300000; //3 MB, as example
Bitmap::setCache(cacheStartAddr, cacheSize, 4);
}
If you are using the Windows simulator you must create a bitmap cache here also. On Windows it is easy to declare a big array or use malloc:
FrontendApplication.cpp (extract)
#include <gui/common/FrontendApplication.hpp>
#include <touchgfx/Bitmap.hpp>
FrontendApplication::FrontendApplication(Model& m, FrontendHeap& heap)
: FrontendApplicationBase(m, heap)
{
#ifdef SIMULATOR
const uint32_t cacheSize = 0x300000; //3 MB, as example
uint16_t* const cacheStartAddr = (uint16_t*)malloc(cacheSize);
Bitmap::setCache(cacheStartAddr, cacheSize, 4);
#else
// Place cache start address in SDRAM at address 0xC0008000;
uint16_t* const cacheStartAddr = (uint16_t*)0xC0008000;
const uint32_t cacheSize = 0x300000; //3 MB, as example
Bitmap::setCache(cacheStartAddr, cacheSize, 4);
#endif
}
Using a Dynamic Bitmap Example
To use the dynamic bitmap we need a widget to show it. So insert an Image widget in the view (in code or in the Designer):
#include <touchgfx/widgets/Image.hpp>
using namespace touchgfx;
class TemplateView : public View
{
private:
Image image;
}
Using this widget with a dynamic bitmap is a three step process:
- Create Dynamic bitmap in the bitmap cache
- Clear the memory used by the dynamic bitmap
- Assign the bitmap to a Widget
Create the dynamic bitmap in setupScreen. Here we use the 16bpp format RGB565. If your framebuffer is 24 bit use RGB888. To create a transparent bitmap, use the format ARGB8888.
The address of the pixels in the dynamic bitmap is returned by the dynamicBitmapGetAddress function:
#include <touchgfx/Bitmap.hpp>
void TemplateView::setupScreen()
{
BitmapId bmpId;
//Create (16bit) dynamic bitmap of size 100x150
const int width = 100;
const int height = 150;
bmpId = Bitmap::dynamicBitmapCreate(100, 150, Bitmap::RGB565);
//set all pixels white
if (bmpId != BITMAP_INVALID)
{
memset(Bitmap::dynamicBitmapGetAddress(bmpId), 0xFF, width*height*2);
}
//Make Image widget show the dynamic bitmap
image.setBitmap(Bitmap(bmpId));
//Position image and add to View
image.setXY(20, 20);
add(image);
...
}
The pixel memory you get from the bitmap cache is not cleared, so you are adviced to clear or overwrite it immediately.
If you want to load your image from a file you can replace the call to memset with your loader code. See the article Loading Images At Runtime
Dynamic Bitmap Operations
The dynamic bitmap operations are all placed in the Bitmap class.
Creating a Dynamic Bitmap
The following method creates a dynamic bitmap with the width, height and bitmap format specified. The bitmap is only created if enough unused memory is available. The method returns BITMAP_INVALID if the bitmap was not created.
static BitmapId Bitmap::dynamicBitmapCreate(const uint16_t width, const uint16_t height, BitmapFormat format, ClutFormat clutFormat)
Deleting a Dynamic Bitmap
This method deletes a dynamic bitmap.
static bool Bitmap::dynamicBitmapDelete(BitmapId id)
Get the address of the pixels in a Dynamic Bitmap
The following method returns the address of the dynamic bitmap. This method is used by file loaders to copy image data into the bitmap.
static uint8_t* dynamicBitmapGetAddress(BitmapId id)
Set the solid area of a Dynamic Bitmap
The following method sets the solid rectangle of a dynamic bitmap.
static bool dynamicBitmapSetSolidRect(BitmapId id, const Rect& solidRect)
Read more about the "solid area" concept in the Custom Widgets article.
By default the solid area is set to be the whole bitmap for non-transparent formats like RGB565 and RGB888. It is set to empty for transparent formats like ARGB8888.
Dynamic Bitmap in portrait mode
TouchGFX can run in portrait mode. This is used in situations where the display is mounted rotated 90 degrees from its native orientation. A little extra care is required when using dynamic bitmaps in this mode.
Here is an example project for the STM32F746. It has a display width of 480 pixels and height of 272 pixels. The framebuffer has the same dimensions.
No bitmap has been assigned to the image1 Image widget. We will use it to show a dynamic bitmap.
When using portrait mode the coordinate system is rotated 90 degrees counter clockwise. The (0, 0) coordinate is in the upper left corner near the 'D'. The first byte of the framebuffer (which colors the first pixels on the screen), is in the upper right corner.
The framebuffer is thus not rotated when running in portrait. This is also true for dynamic bitmaps. But the pixels we want to be shown on first line on the display (as mounted) must be drawn on the left edge in the framebuffer.
In the function below, we create a dynamic bitmap and make the upper row green, and the right edge red:
Screen1View.cpp (extract)
void Screen1View::setupScreen()
{
Screen1ViewBase::setupScreen();
BitmapId bmpId;
bmpId = Bitmap::dynamicBitmapCreate(100, 100, Bitmap::RGB565);
if (bmpId != BITMAP_INVALID)
{
//set all pixels white
uint16_t* const bitmapPixels = (uint16_t*)Bitmap::dynamicBitmapGetAddress(bmpId);
memset(bitmapPixels, 0xFF, 100*100*2);
//first 200 pixels red, => two column on the right on display
for (int i = 0; i<200; i++) bitmapPixels[i] = 0xF800;
//first two pixels in all rows green in bitmap => top two rows on display
for (int i = 0; i<100; i++)
{
bitmapPixels[i*100] = 0x07E0;
bitmapPixels[i*100 + 1] = 0x07E0;
}
}
image1.setBitmap(bmpId);
}
The display shows the dynamic bitmap: