跳转到主要内容

创建动态L8图像

本节介绍动态L8图像的使用,特别是如何创建调色板。

通读此处的动态位图L8图像格式

动态L8图像

动态L8图像的创建与任何其他动态位图一样,区别在于还必须指定要为位图其创建的调色板类型。

TouchGFX支持3种调色板:

调色板说明
CLUT_FORMAT_L8_ARGB888832位,红、绿、蓝色和像素阿尔法通道各8位
CLUT_FORMAT_L8_RGB88824位,红、绿和蓝色各8位,无像素阿尔法通道
CLUT_FORMAT_L8_RGB56516位,红、绿和蓝色分别为5位、6位和5位,无像素阿尔法通道

用24位调色板创建动态L8图像

下面我们用24位调色板创建一幅100x100像素L8位图:

Screen1View.cpp
BitmapId dynamicBitmap1 = Bitmap::dynamicBitmapCreateL8(100, 100, Bitmap::CLUT_FORMAT_L8_RGB888, 256);

该句实现了在位图缓存中分配了100x100像素L8位图和24位调色板。 该调色板可以为动态位图提供的256种颜色,但为了减少存储空间的浪费,可以调整为需要的数量。

访问调色板

像素(前32位对齐地址)后面依次是包含L8调色板格式(以小端模式保存)的2个字节、包含调色板中条目数量的2个字节(以小端模式保存)然后是实际调色板颜色。 这意味着调色板颜色数据位于像素数据后面4个字节之后(32位对齐)。

我们可以获取指向(目前为止未初始化)调色板的指针,如下:

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);

动态L8位图示例

下面我们来看一个创建动态L8位图和操作调色板的完整示例。 通常不会对一般应用进行调色板操作。 该示例旨在描述如何访问和生成调色板。

首先,使用TouchGFX Designer,创建一个屏幕,并在背景上插入一个白色方框和一幅图像,例如在(x=140, y=8)的位置:

创建屏幕

现在生成代码并打开Screen1View.cpp文件。 我们必须在setupScreen中插入代码,进行位图缓存初始化并创建动态位图。

我们先创建一幅200*256像素的位图。 记住,L8位图中的每个像素为一个字节。 我们对图像的每一行使用不同颜色。 第一行为0号色,最后一行为255号色。

然后,我们初始化调色板中的颜色。 计算出调色板的起始地址,并设置256种颜色的RGB值。 这里,我们创建的颜色从绿色渐变为红色并再次渐变回绿色。

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)));
}
}
}

我们得到了一个这样的屏幕:

显示L8图像

操作调色板

由于我们可以访问动态L8位图使用的调色板,因此可以轻松地操作它。

我们将调色板颜色循环改变,每次向下循环一个颜色值,并强制每帧重新绘制图像:

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();
}

这会造成动态向上移动动态位图中的颜色:

L8调色板循环色