在适配轻量地图渲染时,我遇到过一类很有迷惑性的现象:路名文字有时接近黑色,有时偏红,重新进入页面后颜色还可能变化。第一反应通常是字体绘制或屏幕像素格式有问题,但沿渲染链继续检查后,真正的异常出现在更早的颜色数据边界。

这次排查最重要的收获,是要把“缓冲区像素格式”和“画笔颜色格式”分开理解。

两种格式描述的不是同一件事

RGB565 描述的是像素缓冲区布局。一个像素占 16 位,红色 5 位、绿色 6 位、蓝色 5 位,没有 Alpha。它适合资源有限的设备,因为同样大小的画布只需要 ARGB8888 一半的内存。

ARGB8888 常用于表示一个完整的标量颜色,通常写作 0xAARRGGBB。Alpha、红、绿、蓝各占 8 位。

两者的区别可以概括为:

对比项 RGB565 ARGB8888
常见用途 位图或屏幕缓冲区 画笔、文字、填充颜色
位数 16 位 32 位
Alpha 8 位
表达方式 R5G6B5 A8R8G8B8

屏幕使用 RGB565,并不代表绘制接口里的 32 位 color 也应该传 RGB565。图形引擎完全可以接收标准 ARGB 画笔颜色,再根据目标缓冲区格式完成混合和量化。

ARGB 画笔颜色
    → 图形引擎绘制
    → RGB565 离屏缓冲区
    → 屏幕

我是怎样定位第一个异常点的

我没有先改文字绘制函数,而是从颜色产生位置开始,沿调用链逐层记录:

  1. 地图能力提供层收到的原始颜色;
  2. MAL 数据转换后的颜色;
  3. 公共渲染管理层拆分出的 A、R、G、B;
  4. 文字绘制接口实际使用的颜色;
  5. 离屏和上屏缓冲区的格式、宽度和 stride。

日志只需要保留颜色数值、结构体大小、字段偏移和缓冲区元数据,不需要输出地图正文或其他业务数据。

连续多次进入同一页面后,我关注到一个关键特征:同一文字颜色的低 16 位保持稳定,高 16 位却会变化。与此同时,颜色字段的偏移和相邻字段都正常。

使用合成数据表示,大致类似:

次数 收到的 32 位数值
第一次 0x1234F800
第二次 0xAB09F800
第三次 0x7F22F800

低 16 位 0xF800 一直相同,高 16 位没有稳定含义。这个特征比“屏幕是 RGB565”更有价值,它说明生产端可能只写入了颜色字段的两个字节,而另外两个字节保留了旧内存数据。

下游仍按 ARGB8888 拆分整个 32 位值,高位便会被解释成 Alpha 和红色分量,于是产生随机的透明度和偏色。

为什么有些环境没有复现

部分写入问题经常具有偶发性。同一段代码在一种环境中看起来正常,不代表字段已经正确初始化。

是否复现可能受到以下因素影响:

  • SDK 或静态库版本不同;
  • 编译优化和栈布局不同;
  • 结构体是否恰好被清零;
  • 内存分配器复用模式不同;
  • 页面进入顺序和回调时机不同。

因此,判断问题不能只依赖“某块设备没有看到异常”,而应检查接口声明、实际写入宽度和跨多次运行的数据稳定性。

修复放在哪一层

公共渲染接口已经约定画笔颜色是 ARGB,公共框架按这个契约处理没有问题。异常数据来自某个具体能力提供方时,修复应放在 MAL 与该实现交界的最窄位置。

如果证据只覆盖文字回调,就只处理文字,不要立即修改所有图元共用的样式转换。否则点、线、面、背景和图标都有可能被错误转换。

修复策略是:

  1. 明确确认低 16 位确实是 RGB565;
  2. 取出 R5、G6、B5;
  3. 分别扩展到 8 位;
  4. Alpha 设置为不透明;
  5. 将标准 ARGB8888 交给公共框架。

几个基础转换结果如下:

RGB565 ARGB8888 颜色
0xF800 0xFFFF0000
0x07E0 0xFF00FF00 绿
0x001F 0xFF0000FF
0xFFFF 0xFFFFFFFF
0x0000 0xFF000000

5 位或 6 位通道扩展到 8 位时,除了左移,还要把高位复制到低位,这样最大值才能达到 255,颜色分布也更均匀。

为什么只清除高位不够

如果只是把 0x1234F800 的高 16 位清零,得到的是 0x0000F800。

这个结果虽然没有随机高位,但按 ARGB8888 解释时,Alpha 为 0,其他通道也不是期望的红色。它只是清除了脏数据,并没有完成 RGB565 到 ARGB8888 的语义转换。

所以排查像素问题时,要区分三件事:

  • 清理未初始化数据;
  • 转换数值中的颜色位宽;
  • 转换整块缓冲区的像素布局。

它们发生在不同边界,不能用一次位运算代替。

不能把转换无条件应用到所有版本

有些地图实现始终会输出完整 ARGB。若适配层无条件取低 16 位再按 RGB565 展开,原本正确的颜色反而会被破坏。

兼容两种行为时,应使用明确的版本、能力或构建配置选择转换方式:

  • 上游输出完整 ARGB:原值透传;
  • 已确认文字只写低 16 位 RGB565:执行归一化;
  • 无法确认:记录必要元数据并继续定位,不能根据高位是否为零猜测。

合法的 ARGB 颜色同样可能包含零高位或特殊 Alpha,启发式判断很容易误伤正常数据。

字节序也要单独确认

0xAARRGGBB 描述的是整数各位的语义。在小端系统内存中,字节排列顺序与十六进制书写顺序并不相同。

分析日志时,我会同时看三类信息:整数值、通过位移得到的 A/R/G/B,以及必要时的内存字节序列。仅凭一行十六进制内存转储,很容易把字节序问题误判为通道顺序问题。

修复后的验证

颜色稳定只是第一步,还需要确认转换后的颜色符合视觉预期。我重点检查:

  • 同一路名多次进入页面后颜色一致;
  • 原始 RGB565 和转换后 ARGB 能一一对应;
  • 文字透明度正确;
  • 点、线、面、背景和图标没有受到影响;
  • 离屏与上屏缓冲区的 mode、stride 和每像素字节数匹配;
  • 页面切换后没有残留旧颜色或失效回调。

总结

这次问题最终不是字体绘制算法,也不是“屏幕必须改成 ARGB8888”,而是颜色生产端与公共画笔契约之间出现了不一致。

排查这类问题时,先区分缓冲区格式和标量颜色,再沿渲染链找到第一个异常点,最后在最窄的适配边界完成归一化。这样既能解决文字偏色,也能避免一次局部修复影响整个地图渲染链。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐