跳转到主要内容

图像压缩

TouchGFX从4.22版开始支持图像压缩。 4.22版仅支持L8图像压缩。

图像压缩是降低图像存储需求的过程。 如果使用较小的闪存,缩减项目中的图像存储空间可以降低成本。 缩减还意味着可以在项目中使用更多的图像,从而获得更丰富的UI。

图像压缩通常有两种风格:无损或有损。 有损图像压缩通过去除图像的微小细节来实现。 这样通常会得到最大的缩减,但原始图像无法准确再现。 无损压缩总是无任何差异地再现原始图像。 无损压缩的尺寸缩减通常较小。

对于图形,通常要求UI元素完全按照其设计来绘制。 因此,TouchGFX只支持无损压缩。

图像压缩的优点是存储空间减少,但也有缺点,因为图像在绘制到帧缓存时必须解压缩。 与绘制未压缩的图像相比,这种解压缩在许多情况下需要CPU做更多的工作。 结果可能是性能下降。

这意味着必须将闪存减少带来的优势与CPU使用率增加带来的劣势进行比较。

Be aware that the graphics accelerators DMA2D and GPU2D (ChromART and NeoChrom GPU) present in many STM32 micro controllers cannot draw a compressed image directly. Compressed images are always drawn using software rendering.

许多应用中,不建议压缩所有图像,而是只压缩那些性能不受影响且与减少闪存相关的图像。

L8压缩

如上所述,TouchGFX 4.22支持L8图像压缩。 回想一下,L8位图格式仅适用于高达256色的图像。 每个像素只是一个8位数字,表示与图像一起存储的颜色表中的一种颜色。 L8压缩仅压缩像素数。 颜色表保持不变。

以下图为例。 其在计量应用中用作背景。

L8-ARGB8888

图像为184 x 184像素。 因此,像素数据的大小为184×184=33,856字节。

如果我们压缩图像,像素数据将减少到5,735字节。 包括颜色表在内的图像数据总尺寸小于原始图像的20%。 因此,压缩可以让我们在同一大小的闪存空间中拥有5个不同的背景,或者将闪存需求减少28,121字节。

压缩的L8图像像普通的未压缩位图一样使用。 例如,您可以使用图像控件显示图像,而无需在TouchGFX Designer或代码中对项目进行任何修改。 从而轻松地使用压缩图像。

也有一些限制。 压缩图像不能与缩放或旋转图像的控件或画布控件一起使用。 参见以下列表。

Caution
压缩图像不能与所有控件一起使用。 设计器会为您提供一个警告图标。

将压缩图像用于不兼容的控件。

三种算法

TouchGFX采用三种不同的压缩算法。 图像转换器选择提供最佳压缩的算法,除非用户在配置中强制使用特定算法。 算法包括:

  • L4,对每个像素进行4比特编码。 仅适用于最多16种颜色的图像。
  • RLE,像素的游程编码。 仅适用于最多64种颜色的图像。
  • LZW9,基于词典的编码。 适用于所有L8图像。

RLE算法的解压缩速度比LZW9快得多,因此如果LZW9仅压缩图像,转换器将选择RLE稍微好一点。

使用压缩图像

压缩图像和普通图像用法相同。 您可以在TouchGFX 设计器中将控件配置为使用位图,也可以在代码中分配位图。

配置L8-ARGB888图像压缩

唯一需要的配置是将“压缩”值设置为“自动”。 然后,图像转换器将自动选择最合适的压缩,或者如果图像不可压缩则不选择。

选择控件的压缩图像

现在,我们可以正常选择控件的图像。 与未压缩的图像相比,没有什么区别。

使用代码中的图像也没有区别。 通常用压缩位图的BitmapID来进行引用:

    image1.setXY(148, 148);
image1.setBitmap(touchgfx::Bitmap(BITMAP_GAUGE_BACKGROUND_ID));

在此阅读有关压缩L8图像的更多信息

压缩等级

图像转换器选择的压缩算法会写入生成的文件中。 在这里我们还可以找到压缩等级。

我们在上文使用的图像生成到文件generated/images/src/image_gauge_background.cpp中。 此文件的标题为:

image_gauge_background.cpp (extract)
// 4.22 D0 AN R0 FL8_ARGB8888 U888 N0 SExtFlashSection EExtFlashSection CL8_LZW9

LOCATION_PRAGMA("ExtFlashSection")
KEEP extern const unsigned char image_gauge_background[] LOCATION_ATTRIBUTE("ExtFlashSection") = {
// 184x184 L8_ARGB8888 pixels. 压缩[输出/输入x 100]:5735/33856 x 100 = 16.9% 0x00, 0x26, 0x50, 0xa8, 0x60, 0xe1, 0x02, 0x86, 0x0c, 0x1a, 0x36, 0x70, ....

The end of the comment in the first line shows the compression level. Here we see that the Image Converter has selected the LZW9 algorithm. 第5行的末尾显示,我们现有5735字节的像素数据,而原来是33856字节。 Resulting in a compression to 16.9% (the smaller compression level the better). Note! The compression doesn't count the color table.

将图像解压缩到位图缓存

大多数情况下,绘制压缩图像的性能比绘制未压缩图像差。 此外,如引言中所述,STM32微控制器(DMA2D和GPU2D)中的图形加速器无法绘制压缩图像。 因此,压缩图像始终由软件绘制,从而导致性能降低和cpu负载增加。

出于这些原因,TouchGFX还包含在运行时将压缩图像解压缩到RAM中位图缓存的功能。

当图像被解压缩到RAM时,绘制性能类似于使用未压缩的图像,加速器可以绘制图像。

要解压缩,我们首先需要设置位图缓存。 在此阅读有关使用位图缓存的更多信息。

设置位图缓存后,我们可以使用函数Bitmap::decompress来解压缩图像。 完整代码如下所示:

// Define an array for the bitmap cache
uint16_t cache[20*1024]; //40 KB cache
// Define an array for the decompression temporary buffer
uint16_t lzwBuffer[1024];

void TemplateView::setupScreen()
{
...
Bitmap::setCache(cache, sizeof(cache));
bool r = Bitmap::decompress(BITMAP_GAUGE_BACKGROUND_ID, lzwBuffer);
image1.setXY(148, 148);
image1.setBitmap(touchgfx::Bitmap(BITMAP_GAUGE_BACKGROUND_ID));
}

位图缓存必须足够大才能容纳未压缩的图像。 我们的图像是184 x 184像素加包含207 ARGB888颜色的颜色表。 因此,总大小为34.688字节。

在LZW9压缩图像的解压缩期,解压缩器使用2048字节的缓冲区(用于构建字典)。 解压缩后不再需要缓冲区,可以重新用于其他目的。 RLE和L4压缩图像的解压缩不需要缓冲区。

当不再使用解压缩图像时,可以使用Bitmap::cacheRemoveBitmap将其从位图缓存中移除。

限制

压缩图像不能与缩放或旋转图像的控件或填充区域的控件一起使用。 此类限制出于性能原因而制定。 TouchGFX 设计器不允许您选择压缩图像。

如果你想在这些控件中使用一个特定的L8图像,我们建议不要对图像进行压缩。 或者,您可以在运行时解压缩图像。

不支持压缩图像的控件有:

  • TextureMapper
  • 可缩放图像
  • 指针和弧形仪表
  • 用于填充图形下方区域的静态和动态图表
  • 指针模拟时钟
  • 圆、线和形状
  • 圆形和线形进度条