4、图层与合成:硬件图层叠加、Alpha混合优化、合成器设计模式

图层合成这事儿,说白了就是「怎么把好几层画面又快又好地拼成一张图」。很多做嵌入式 GUI 的朋友一开始觉得这有啥难的,不就是把图片叠在一起吗?等你真在 60 帧的刷新率下跑起来,就知道坑有多深了。

我早年做过一个智能手表项目,屏幕分辨率不高,但动画效果要求很花哨。那时候我天真地以为软件逐像素合成就行,结果帧率直接掉到 15 帧,卡得用户想砸表。后来我才意识到——硬件图层叠加才是嵌入式 GUI 的命门。

4.1 硬件图层叠加:让硬件替你干活

现代嵌入式 SoC 里,显示控制器通常都支持多个硬件图层。每个图层有自己的显存区域,可以独立配置颜色格式、透明度、位置和大小。合成工作由硬件完成,CPU 和 GPU 几乎不用参与。

核心思想:能交给硬件做的事,绝不自己动手。硬件图层叠加就是典型的「用硬件资源换性能」。

我习惯把硬件图层分为三类:

  • 背景层:通常是纯色或静态图片,几乎不更新
  • 内容层:显示主要界面内容,比如菜单、列表
  • 覆盖层:弹窗、光标、通知等临时元素

举个例子,一个典型的智能家居控制面板:

图层 内容 更新频率 Alpha
Layer 0 壁纸背景 几乎不变 255
Layer 1 设备列表 中等 255
Layer 2 弹出菜单 高 200
Layer 3 触摸光标 极高 128

你看,光标层单独放一层,更新时只需要刷新这一小块显存,不用重绘整个屏幕。这就是硬件图层的威力。

我的习惯:把最常变化的元素放在最高编号的图层。因为很多硬件合成器是从高到低逐层合成的,高图层变化时,低图层完全不用动。

4.2 Alpha 混合优化:别让透明度拖垮性能

Alpha 混合是性能杀手,这一点我深有体会。曾经有个项目,界面上有大量半透明叠加效果,帧率直接腰斩。后来我仔细分析了 Alpha 混合的计算开销,发现很多地方其实可以优化。

标准 Alpha 混合公式:

// 标准 Alpha 混合
out = src * alpha + dst * (1 - alpha)

// 预乘 Alpha 优化
out = src_pre + dst * (1 - alpha)
// 其中 src_pre = src * alpha 已经提前算好

预乘 Alpha 能省掉一次乘法,别小看这一下,在每帧几百万像素的合成中,省下来的就是帧率。

我总结了几条 Alpha 混合的优化原则:

  • 能不透明就不透明:alpha = 255 时直接拷贝,跳过混合计算
  • 能全透明就跳过:alpha = 0 时直接忽略该图层
  • 预乘 Alpha 是好朋友:提前把颜色乘好透明度
  • 用查表法代替实时计算:对于固定透明度,提前算好混合系数

我曾经踩过的坑:在 STM32 上用软件做 Alpha 混合,每像素用了浮点运算。后来改成定点数 + 查表,速度提升了 4 倍。嵌入式平台上,浮点能不用就不用。

4.3 合成器设计模式:架构决定性能

合成器是 GUI 系统的核心组件,负责管理所有图层的合成策略。我见过太多项目把合成逻辑散落在各处,最后改都改不动。

我个人推荐三种合成器设计模式:

4.3.1 直接合成模式

最简单,也最常用。从底层到顶层逐层合成,每层依次混合。适合图层数量少(2-4层)的场景。

// 直接合成伪代码
void direct_composite(layer_t layers[], int count) {
    framebuffer_t fb = layers[0].buffer;  // 底层直接拷贝
    for (int i = 1; i < count; i++) {
        if (layers[i].alpha == 0) continue;
        if (layers[i].alpha == 255) {
            copy_layer(fb, layers[i]);  // 直接覆盖
        } else {
            blend_layer(fb, layers[i]); // Alpha 混合
        }
    }
}

4.3.2 区域脏矩形模式

只更新变化区域。适合局部更新频繁的场景,比如触摸反馈、光标移动。

  • 维护一个脏矩形列表
  • 只合成脏矩形覆盖的区域
  • 硬件图层支持部分更新时效果最佳

4.3.3 硬件卸载模式

把合成工作完全交给显示控制器。CPU 只负责更新图层内容,不参与合成。

模式 CPU 负载 内存占用 适用场景
直接合成 高 低 图层少、硬件弱
脏矩形 中 中 局部更新多
硬件卸载 极低 高(多缓冲区) 硬件支持图层叠加

我建议:先搞清楚你的硬件支持几个图层,再选模式。很多 MCU 只有 1-2 个硬件图层,那就老老实实用直接合成 + 脏矩形优化。

4.4 合成器架构图

下面这张图是我自己项目里用过的合成器架构,你可以参考一下:

嵌入式 GUI 合成器架构 应用层(UI 组件) 图层管理器 图层创建 | 图层排序 | 脏矩形追踪 | 更新策略 软件合成器 逐像素 Alpha 混合 | 颜色格式转换 硬件合成器 显示控制器 | 硬件图层寄存器 帧缓冲区 → 显示输出 软件合成器适合图层少、硬件不支持叠加的场景;硬件合成器是性能最优解

4.5 实战建议:从硬件出发做设计

说了这么多,最后给几条实在的建议:

  1. 先看芯片手册:搞清楚你的 SoC 支持几个硬件图层、每个图层的颜色格式限制、是否支持独立 Alpha
  2. 图层数量宁少勿多:硬件图层是稀缺资源,能用 2 层解决的别用 3 层
  3. Alpha 混合能省则省:半透明效果看着漂亮,但性能代价很大。我一般只在弹窗和光标上用半透明
  4. 脏矩形一定要做:不管用哪种合成模式,只更新变化区域都是性价比最高的优化
  5. 预乘 Alpha 是标配:所有图片资源在加载时就预乘好,运行时直接混合

记住一句话:图层合成的终极目标,是让 CPU 在每一帧里什么都不用做。所有合成工作,要么由硬件完成,要么提前算好。

嗯,图层与合成这块,说到底就是「怎么把画面拼起来最省力」。你想想看,硬件图层叠加就像搭积木,每块积木都是独立的,拼的时候只动需要动的那块。Alpha 混合优化就是让每块积木的拼接缝更顺滑,但别让顺滑变成负担。合成器设计模式则是决定谁来搭积木——是 CPU 亲自上阵,还是让硬件流水线自动完成。

我在实际项目中,最常用的组合是「硬件图层叠加 + 脏矩形更新 + 预乘 Alpha」。这套组合拳打下来,60 帧基本稳如老狗。当然,具体怎么选,还得看你的硬件平台和业务场景。