最近一版 Gnome 全套更新后,发现窗口截图的阴影效果突然消失了,而且本来就不怎么好看。然而指望 Gnome 官方修复又不知等到何年…… 干脆自己动手改!Talk is cheap, show me the code :P

注:本文所有截屏都是用我自己修改的版本截的 :P

0. 使用方法

Arch Linux 已提交至 AUR,使用 pacaur 或者 yaourt 即可安装,yaourt -S gnome-screenshot-heavy-shadow ,注意替换原来的 gnome-screenshot
按 PrintScreen 键还是原来默认的全屏截图,方便起见可自行设定一个快捷键。进入 Gnome 设置,在键盘选项中翻到最下面,加号新建快捷键,名称随意,执行 Command 填 gnome-screenshot -wbe s,再设定好方便的快捷键即可,截屏图片会自动保存在 home 的图片目录下。Command 命令填 gnome-screenshot -cwbe s 的话把截图保存到剪贴板。
或者点此下载二进制文件直接使用。代码地址在此

1. 原版代码的 Bug

原版的阴影效果其实并不算真正的阴影效果,只是给整个图像做了 alpha 通道的高斯模糊,再加上一个偏移矢量。然而就算是高斯模糊也没有做好,在计算单个像素的 alpha 值时,原版代码用二维高斯分布得到的权值矩阵进行卷积,相加后并没有作进一步处理,最终导致像素的 alpha 溢出 0xFF。然而代码的最后还用了一个 CLAMP,无意之中把溢出的负值归零了,这给我 debug 带来了一些麻烦。Bug 的结果是最终输出的 alpha 全为零 -- 输出截图完全正常,只是完全没有阴影效果。

2. 重新设计算法

做阴影其实不需要用高斯模糊,一维高斯分布曲线足矣。通过 Matlab 可以很方便地展示一个正态分布的数值曲线,如下所示

a. 依样画葫芦

使用像上面这种一半的正态分布,就能作出整个阴影效果啦!首先可以把原本图像的 alpha 值设为最高值,比方说 255( 实测 255 的效果并不好,会导致曲线 sigma 值难以控制,要么 sigma 太小衰减太快阴影半径太短,要么 sigma 太大衰减太慢边界太重,还是多次尝试才能得到合适的 alpha 上限),然后按比例放大正态分布曲线,就得到合适的 alpha 数值序列,接下来把这个一维序列复制到所有边界上,就完成了图像四条边上的阴影效果。

b. 再依样画一个

四个角上的阴影怎么办呢?还是用这半个正态分布曲线就能完成了!把上面的 alpha 数值序列拿过来再用一下,将其中每个点的值作为最高值,再次按比例缩放原来的正态分布曲线,得到 n 列新的 alpha 数值序列,n 列拼起来就得到 n*n 的二维矩阵,用等高图画出来就是这个样子

这里有个小问题,由于我们计算的是整数 alpha ,一次权值缩放并不会有太大误差,但在这基础上的二次缩放误差会被放大,这样算出来的矩阵并不沿对角对称。这时候就需要把误差大的一半用误差小的一半覆盖,否则角上的阴影效果就会出现蜜汁“凸起”或者“凹陷”。

其实这种做法和高斯模糊是等价的,但我们这里只需要作一个角。由于对称性,其他角上只要变换下坐标就能拼接上,而无需重新计算。

c. 再拼成一整块葫芦


最后拼接好的等高图如上,这样就完成了大部分的算法。

3. 效果微调

如果只是拼接出阴影,实际效果还是很丑的,对原图像加上一些偏移才能看得过去。最后完成的代码中,我给图像加上了偏移微调,还给阴影添加了缩放,总算比原来强多了。

4. 其他

  • 重新设计算法的时候参考了维基百科 Normal distribution,顺道补了不少知识。
  • 成品代码地址: cabbagec@github.
  • 对阴影重量不满意的话可适当调节 src/screenshot-shadow.c 头部的 ALPHA_HIGH,我太穷显示器质量略渣,原来的数值可能会出现较大偏差。
  • 完成修改前,我用 Matlab 写了个生成完整 alpha 矩阵的样品代码,能实现相同的效果,这里就不贴上来了,感兴趣可 email 向我索要。