Skip to content

Display Scaling

51hhh edited this page Sep 16, 2026 · 1 revision

显示缩放与重采样

这一页解决一个具体的用户可见问题:「截图选区里很清楚,贴出来是糊的。」

根因:整数缓冲区缩放

GTK3 不支持 wp_fractional_scale_v1。在分数缩放的桌面上:

  • Monitor::scale_factor() 报告的是整数缓冲区缩放,不是真实缩放
  • 实测:两块真实缩放为 1.3333 和 1.5 的屏,都报 2
  • devicePixelRatio 顶替不了,它就是那个整数 2

后果:1.5 倍缩放的桌面上,WebKit 只拿到缓冲区缩放 2。按原物理尺寸显示就要先被平滑放大 4/3,再被合成器缩回 3/4。头一趟就把细节抹平了。

真实缩放的唯一查询入口

screenshot::desktop_scale_at 是本项目唯一对外的「真实缩放」查询。要把图片像素换算成 CSS 像素,必须问合成器,不能用 scale_factor()。

真实缩放与缓冲区缩放都由后端查好,随 payload 下发给前端。

后端解法:反投影预补偿

pin/resample.rs 直接按缓冲区分辨率出图,并预先补偿合成器那一步缩小。

先量出合成器做了什么

合成器的重采样核是脉冲实测出来的:缓冲区里一个孤立白点,在屏上只留下一个 177/255 的点。

177/255 ≈ 0.694 ≈ 0.8333²

即标准双线性。知道了核,就能反过来问:「这张缓冲区图被缩完,等于原图吗?」不等于就把残差加回去。

迭代过程

Lanczos3 预放大作初值
  → 前向双线性模拟合成器缩小
  → 计算残差
  → 回投影加回
  → 共 4 轮

实测 PSNR(屏上实测,不是理论值)

方案 PSNR
默认平滑 30.28 dB
image-rendering: pixelated 33.95 dB
Lanczos 预放大 34.73 dB
反投影预补偿 43.02 dB

实现约束

生产实现以 Q7 定点保留子像素精度。Lanczos 只缓存当前垂直核需要的行,双线性前向与回投影按行运行,不保留多份全图 f32 RGBA。

64 MiB 是每个 RGBA 平面的硬预算,不是进程总内存上限。 最坏工作集还包含 Q7 缓冲(两字节/通道)、i16 残差、解码原图、可选屏显目标和最终 PNG。

这个预算可容纳 4K 原生屏在 150% 真实缩放 / 200% 缓冲区缩放下的 5120×2880 补偿图。超限会明确记录失败并回退原图,不在几何阶段静默跳过。

实测开销

尺寸 耗时(release)
3413×1920 约 614 ms
5120×2880 约 1.28 s

直接测试进程的瞬时总峰值 RSS 354 MiB,空测试壳基线 6.6 MiB,补偿增量约 347 MiB。

补偿一直在后台运行并且全局串行:多张贴图不会叠加工作集或阻塞开窗。关闭窗口后还在排队的补偿会在解码前取消。

赶上首帧的补偿随第一份 payload 下发;没赶上的走事件换图。

复制与保存永远用原图,不用补偿图——补偿只为屏幕显示服务。

前端的两条兜底判据

后端补偿是主路径,前端 react/pin/rendering.ts 只保留两条判据决定 image-rendering:

判据 条件 结果
isPixelExact 显示尺寸 × 真实缩放 == 图片像素数 最近邻
isBufferExact 图片已是缓冲区分辨率 1:1 搬入,任何滤镜都不该介入

判据不能写成 scale == 1

全屏贴图会被内容尺寸缩小,而 scale 仍是 1。那种情况用最近邻反而差 4.5 dB。

判据必须基于「实际像素数是否整数对应」,不是基于某个缩放变量的值。

列表缩略图不走这条路

列表行的 48 CSS px 缩略图用 image crate 的 thumbnail() 快路径,不是 Lanczos,也不做补偿。后端缩到最长边 128 px 并按 id 缓存。

小图上补偿的收益不足以抵消开销——优化要按尺寸分级,不是一套算法用到底。

Clone this wiki locally