盒阴影的绘制方法

2019年08月04日  ·  851 字  ·  阅读

需求

LCUI 现有的阴影绘制方法只适合直角边框,没有考虑到圆角的情况,在添加 LCUI 的圆角绘制功能后,阴影绘制功能也需要重写。

分析

以浏览器的效果作为参考:

Box shadow

从上图可知:

  • 阴影中间的矩形区域是纯色填充
  • 圆角是圆形渐变
  • 四个边则是直线渐变

绘制阴影

为了绘制方便,可以把阴影拆成三部分来绘制:

  1. 内区域,位于中间,只需填充纯色
  2. 边界渐变区域,四个边,填充渐变色
  3. 圆角渐变区域,四个角,结合圆的方程来填充渐变色

Box shadow render rectangles

看上去没什么难度,图中的示例都是圆角半径相同的情况,那像下图中的情况该怎么处理?

Box shadow

按现有的方法来分割绘制区域会像这样:

Box shadow render rectangles

乍一看这些绘制区域分割起来很复杂,简单点,可以先直接给整个阴影区域填充纯色,然后在绘制圆角时把外面的像素清除掉,这样就不用考虑中间区域的分割问题。

剔除内容区域

阴影区域一般是与内容区重叠的,内容区有背景色填充和背景图混合操作,那么,在绘制阴影时如何忽略内容区?有三种做法:

  1. 先绘制阴影,后绘制内容区。如果内容区如果有圆角,填充背景色和背景图时还得跳过圆角外的像素点,挺麻烦的。
  2. 先绘制内容区,后绘制阴影。无需调整内容区绘制方式,在绘制阴影时将阴影作为背景、内容区作为前景进行混合,这样只要内容区域有不透明,就不会填充阴影。但有个问题,如果内容区背景色是透明的话会被填充阴影,需要追加内容区域判定。
  3. 创建一个临时画布,然后在这个画布上绘制阴影,绘制完后清除内容区域内的像素,最后绘制到主画布上。牺牲一点内存来换取开发上的便利,还是值得的。

显然第三种做法是最合适的,其实一开始我也没怎么仔细想第三种做法的实现细节,毕竟搞这么久也懒得搞了,但后来由于阴影和圆角边框的组合渲染效果实在太烂才被迫研究改进方案。

内容区域的圆角部分可以单独清除,但怎么计算剩余的区域?仔细想了下,可以用 LCUI 自带的矩形分割功能来实现,方法如下:

  1. 将内容区域添加至矩形列表
  2. 清除内容区域左上角、右上角、左下角、右下角的像素,并从矩形列表中剔除这四个区域
  3. 遍历矩形列表,清除矩形内的像素

具体实现代码可参考 src/draw/boxshadow.c 文件。

文章版权归作者所有,未经许可不得转载。

问题反馈

对此文章有疑问?你可以点击 这里 反馈