动态位图
本节介绍如何使用动态位图。注意,标准位图会被编译到应用中,因此必须在编译时提供。位图从PNG等格式的文件转换而来,带有大小和格式信息并以内部格式保存。
此外,还可以在运行时间在RAM中创建位图。这被称为动态位图。动态位图的使用与编译到应用中的静态位图相同。这意味着您可以通过图像和按钮等控件使用动态位图。
动态位图配置
在创建动态位图时,需要从位图缓存分配像素存储空间。因此,您必须先配置位图缓存,然后才能创建动态位图。 This is a manual process that cannot be done in TouchGFX Designer or the Generator.
参见关于位图缓存的文章获取配置过程。
必须定义应用中使用的动态位图的最大数量。该最大值与位图缓存地址和大小一起传递到TouchGFX。这里我们配置的是具有最多4张动态位图的位图缓存。您可以在应用中的任何文件中执行。如需只执行一次,文件FrontendApplication.cpp是个不错的选择:
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);
}
如果您使用Windows模拟器,则还必须在此创建位图缓存。在Windows上,可以容易的声明大阵列或使用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
}
使用动态位图示例
为了使用动态位图,我们需要一个控件来显示它。 So insert an Image widget in the view (in code or in TouchGFX Designer):
#include <touchgfx/widgets/Image.hpp>
using namespace touchgfx;
class TemplateView : public View
{
private:
Image image;
}
此控件和动态位图的使用过程分为三步:
- Create the Dynamic Bitmap in the bitmap cache
- Initialize the memory used by the dynamic bitmap
- 将位图分配给控件
The pixel memory returned by dynamicBitmapCreate() (and dynamicBitmapCreateL8() / dynamicBitmapCreateCustom()) is not cleared or initialized in any way. It simply points into the bitmap cache and contains whatever was there before, e.g. leftover data from a previously deleted bitmap, or random contents on system startup.
This is one of the most common mistakes when working with dynamic bitmaps: forgetting to initialize the memory results in a bitmap with garbage pixel data being shown on the display, or even data leaking between what looks like unrelated bitmaps.
Always initialize every pixel before the bitmap is used, either by calling Bitmap::dynamicBitmapClear() right after creating the bitmap, or by immediately writing your own image/pixel data into it (e.g. loading an image from a file, see Loading Images At Runtime).
在setupScreen中创建动态位图。这里我们使用16bpp格式RGB565。如果帧缓冲为24位,则使用RGB888。如需创建透明位图,使用格式ARGB8888。
The address of the pixels in the dynamic bitmap is returned by the dynamicBitmapGetAddress function, but since dynamicBitmapClear() already knows the size and layout of the bitmap, you no longer need to compute the pixel buffer size yourself (which is error prone, since it depends on both format and bitmap dimensions):
#include <touchgfx/Bitmap.hpp>
void TemplateView::setupScreen()
{
BitmapId bmpId;
//Create (16bit) dynamic bitmap of size 100x150
bmpId = Bitmap::dynamicBitmapCreate(100, 150, Bitmap::RGB565);
//Clear all pixels; the memory returned above is *not* initialized
if (bmpId != BITMAP_INVALID)
{
Bitmap::dynamicBitmapClear(bmpId);
}
//Make Image widget show the dynamic bitmap
image.setBitmap(Bitmap(bmpId));
//Position image and add to View
image.setXY(20, 20);
add(image);
...
}
As explained above, the pixel memory you get from the bitmap cache is not cleared automatically, so you must clear or overwrite it yourself before the bitmap is displayed. Bitmap::dynamicBitmapClear() is the recommended way to do this, it zeroes every pixel (and any CLUT/format specific data) of the dynamic bitmap in one call, without you having to know its size or format.
If you want to load your image from a file instead, you can skip the call to dynamicBitmapClear() and replace it with your loader code, since the loader will overwrite every pixel anyway. See the article Loading Images At Runtime.
动态位图操作
动态位图操作全部位于Bitmap类中。
创建动态位图
以下方法用指定的宽度、高度和位图格式创建动态位图。仅当有足够大的未使用存储空间时,才能创建位图。如果未创建位图,该方法返回BITMAP_INVALID。
static BitmapId Bitmap::dynamicBitmapCreate(const uint16_t width, const uint16_t height, BitmapFormat format, ClutFormat clutFormat)
This method (and dynamicBitmapCreateL8() / dynamicBitmapCreateCustom()) does not clear or initialize the pixel data of the new bitmap. The memory simply contains whatever was there before. Call Bitmap::dynamicBitmapClear() right after creating the bitmap, or overwrite every pixel yourself, before using or displaying it.
Clearing a Dynamic Bitmap
Since the pixel data of a newly created dynamic bitmap is uninitialized (see above), it must be cleared or overwritten before use. The following method zeroes all pixel data of a dynamic bitmap:
static void Bitmap::dynamicBitmapClear(BitmapId id)
dynamicBitmapClear() clears the entire bitmap, including any format specific data such as the CLUT (Color LookUp Table) header of an L8 bitmap, and it resets the solid area (see below) back to the same default as when the bitmap was created. Since the method already knows the size and format of the bitmap, you do not need to compute the number of bytes to clear yourself, unlike a manual memset() call on the address returned by dynamicBitmapGetAddress().
If you intend to immediately fill the bitmap with your own pixel data, e.g. by loading an image from a file or drawing into it, you can skip calling dynamicBitmapClear() as long as you are certain that every pixel will be overwritten.
删除动态位图
此方法删除动态位图。
static bool Bitmap::dynamicBitmapDelete(BitmapId id)
获取动态位图中像素的地址
以下方法返回动态位图的地址。文件加载器使用此方法将图像数据复制到位图中。
static uint8_t* dynamicBitmapGetAddress(BitmapId id)
设置动态位图的实心区域
以下方法设置动态位图的实心矩形。
static bool dynamicBitmapSetSolidRect(BitmapId id, const Rect& solidRect)
请阅读Custom Widget一节中关于“实心区域”概念的更多内容。
对于像RGB565和RGB888这样的非透明格式,将实心区域默认设置为整个位图。对于像ARGB8888这样的透明格式,将实心区域设置为空。
Creating a Dynamic Bitmap from memory
It is also possible to create a dynamic bitmap directly from pixel data in flash memory without caching pixel data in RAM. Compared to regular Dynamic Bitmap, cache size does not need to take into account the pixel data:
Note that the pixel address must be 4-byte aligned.
static BitmapId Bitmap::dynamicBitmapCreateExternal(const uint16_t width, const uint16_t height, const void* pixels, BitmapFormat format, uint8_t customSubformat = 0);
竖屏模式的动态位图
TouchGFX能以竖屏模式运行。这适用于显示屏自其原始方向旋转90度后安装的情况。在该模式下使用动态位图时,需要格外小心。
下面是STM32F746的项目示例。它的显示屏宽度为480像素,高度为272像素。帧缓冲具有相同大小。
没有为image1图像控件分配位图。我们将使用它显示动态位图。
在使用竖屏模式时,坐标系沿逆时针方向旋转90度。 (0, 0) 坐标位于“D”附近的左上角。帧缓冲的第一个字节(将屏幕上第一个像素着色),位于右上角。
因此,当以竖屏模式运行时,不旋转帧缓冲。对于动态位图也是如此。但是,要在显示屏上第一行显示的像素必须绘制在帧缓冲的左边缘。
In the function below, we create a dynamic bitmap and make the upper row green, and the right edge red. Since we want a solid white base color rather than just zeroed/black memory, we use memset() directly here instead of Bitmap::dynamicBitmapClear() (which is only useful when zeroed/transparent memory is acceptable, e.g. as a starting point before drawing your own pattern):
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);
}
显示屏显示动态位图:

