跳转到主要内容

图像压缩

TouchGFX从4.22版开始支持图像压缩。 4.22版到4.23版仅支持L8图像压缩。 4.24版开始,支持RGB565、RGB888和ARGB8888图像格式压缩。

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

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

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

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

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

请注意,许多STM32微控制器中的图形加速器DMA2D和GPU2D(ChromART and NeoChrom GPU)不能直接绘制压缩图像。 压缩图像采用软、硬件混合渲染绘制,即:压缩数据由软件按块解压缩,再适时将这些数据块委托给DMA2D。

许多应用中,不建议压缩所有图像,而是只压缩那些性能不受影响且与减少闪存相关的图像。 另请参阅以下关于解压缩到位图缓存的部分,以获得更低的存储要求和良好的性能。

L8压缩

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

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

L8-ARGB8888

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

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

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

3个算法#{three-algorithms}

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

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

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

RGB压缩

如前所述,TouchGFX 4.24支持RGB565、RGB888和ARGB8888图像格式的压缩。 包含超过256种纯色的图像不能以紧凑L8图像格式存储,而必须以上述格式之一存储。 RGB565、RGB888或ARGB8888图像压缩采用16位、24位或32位像素直接压缩。

以下图为例。 其背景较上文示例更为复杂且丰富,拥有超过256种纯色。 因此,不能以L8存储,而必须采用ARGB8888格式存储,因其具有透明像素(位于角落)。

ARGB8888

图像为240 x 240像素。 那么,像素数据的大小为240 x 240 x 4=230.400字节,因为我们用4个字节以ARGB8888图像格式存储每个像素。

压缩图像会将大小降至32.347字节。 压缩图像的大小要小得多,只有原始图像的14%。 压缩允许我们在同一个Flash空间中拥有多个复杂的背景,或大量减少Flash需求。 压缩还能使限制Flash使用率的设备采用比L8格式更复杂、更丰富的图形。

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

Caution
某些情况下,对RGB图像应用任何抖动算法都会损害RGB压缩的效果。

两种算法

TouchGFX使用两种略有差别的压缩算法,根据要压缩的图像格式自动选择。 算法包括:

  • QOI使用Quite OK图像格式压缩算法的变体对RGB888和ARGB8888像素进行编码。
  • QOI565使用为16位像素值定制的QOI变体对RGB565像素进行编码。

RGB压缩算法的两种变体均针对快速解压缩速度进行了优化,以限制渲染压缩RGB图像时的运行时性能损失。

使用压缩图像

启用图像压缩功能

图像压缩需要目标应用程序中的额外代码。 为了避免较高的空间需求,某些平台可选择压缩代码。 您可以为项目启用该功能。

单击设计器左侧的“配置”,然后单击“框架功能”

启用图像压缩

图像压缩功能在底部显示。 您可以启用您需要的特定功能集。

目标具有不同的框架功能选项,某些情况下,目标没有可选功能,如下所示:

无可选框架功能

这种情况下,所有功能始终启用。

Caution
请注意,如果您使用未启用的功能,设计器不会显示错误或警告。 如使用图像压缩,但未启用该功能,则不会绘制图像。

L8压缩

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

配置L8-ARGB888图像压缩

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

选择控件的压缩图像

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

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

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

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

RGB压缩

在TouchGFX设计器中RGB565、RGB888或ARGB8888图像的压缩过程与L8压缩相同。

配置L8-ARGB888图像压缩

如选择“是”,图像转换器将使用与图像格式匹配的算法压缩图像。

与L8压缩一样,我们通常可以为控件选择图像,并在代码中引用位图。

压缩等级

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

我们在上文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, ....

第一行注释的末尾显示了压缩算法。 这里,我们看到图像转换器已经选择了LZW9算法。 第5行的末尾显示,我们现有5735字节的像素数据,而原来是33856字节。 结果压缩到16.9%(压缩级别越小越好)。 注意! 压缩率不包括颜色表。

压缩失败

某些情况下,图像转换器在压缩图像时会出现警告或错误。 这是因为图像与所选算法不兼容,或者压缩图像的大小未低于原始图像的90%。 这种情况仅在极少数的巧合下才会发生。 例如,一个5 x 5的小图像使用25种不同的色彩。 由于图像中无重复或冗余,所以压缩不可能减少数据。

L8图像

对于L8图像,其中的原因是选择了与给定图像不匹配的特定算法。 例如,当图像包含17种或更多颜色,并且选择了L4压缩,则不能使用所选算法来压缩图像。 生成代码时,图像转换器将打印错误消息,设计器将显示错误消息:

特定算法的压缩误差

解决该问题的方法是使用另一种算法。 首选方式是为L8图像选择“自动”。 然后,图像转换器将尝试所有算法,并选择适用的最佳算法。

某些罕见情况下,图像不会压缩到原始大小的90%以下。 此时,图像转换器不会压缩图像并发出警告文本,如下所示:

压缩警告

不压缩的原因是节省的Flash大小不会对渲染性能有所改善。 可以不使用“自动”,而是选择一种算法(L4、RLE或LZW)来强制压缩。

警告消息仅为提供信息。 代码生成仍在继续,项目将按预期进行。

RGB图像

对于RGB图像格式,您不能选择“自动”,因为只有一种压缩算法。 如果您选择“是”,且图像无法压缩到原始大小的90%以下,则图像转换器将生成错误:

RGB图像压缩错误

解决方案是针对图像压缩选择“无”。

将图像解压缩到位图缓存

大多数情况下,绘制压缩图像的性能比绘制未压缩图像差。 此外,如引言中所述,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)); // Register the bitmap cache
bool r = Bitmap::decompress(BITMAP_GAUGE_BACKGROUND_ID, lzwBuffer); // Decompress the bitmap
image1.setBitmap(touchgfx::Bitmap(BITMAP_GAUGE_BACKGROUND_ID)); // Use the bitmap as normal
image1.setXY(148, 148); // Position Image widget
}

此例中,我们想将上文L8压缩示例中的184 x 184图像解压缩到位图缓存中。 位图缓存必须足够大才能容纳未压缩的图像。 184 x 184像素加上包含207种ARGB8888色的颜色表。 因此,总大小为34,688字节。

此例中,我们使用LZW9算法。 在LZW9压缩图像的解压缩期,解压缩器使用2048字节的缓冲区(用于构建字典)。 解压缩后不再需要缓冲区,可以重新用于其他目的。 L4、RLE、QOI或QOI565压缩图像的解压缩不需要缓冲区。

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

限制程序大小

如果使用位图缓存解压缩,则可通过一些选项来限制程序的大小。 如上所述,有两种图像压缩;L8和RGB。 使用Bitmap::decompress时,您的程序将包含解压缩L8和RGB图像的代码。 如果只使用RGB图像压缩,则可用专用方法将RGB图像解压缩到位图缓存中,即Bitma::depressRGB,这样您的程序将只包含解压缩RGB图像所需的代码。 如果只使用L8压缩,也同样适用,这里的方法称为Bitmap::decompressL8。 参见以下示例。

void TemplateView::setupScreen()
{
...
// Decompress the bitmap (RGB using QOI)
bool r = Bitmap::decompressRGB(BITMAP_GAUGE_BACKGROUND_ID);
...
}
void TemplateView::setupScreen()
{
...
// Decompress the bitmap (L8 using RLE, no buffer required)
bool r = Bitmap::decompressL8(BITMAP_GAUGE_BACKGROUND_ID);
...
}

限制

处理压缩图像时存在一些限制。 压缩图像不能与缩放或旋转图像的控件、填充区域的控件或画布控件一起使用。

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

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

此类限制出于性能原因而制定。 TouchGFX 设计器不允许您选择压缩图像。

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

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

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