63

我想更好地了解 Android (2D) Canvas 绘图管道的组件如何组合在一起。

例如,XferModeShaderMaskFilterColorFilter是如何交互的?这些类的参考文档非常少,CanvasPaint的文档并没有真正添加任何有用的解释。

我也不完全清楚具有固有颜色的绘图操作(例如:drawBitmap,与“矢量”原语如drawRect)如何适应所有这些 - 他们总是忽略Paint's 颜色并使用它们的固有颜色代替吗?

我也很惊讶一个人可以做这样的事情:

Paint eraser = new Paint();
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rectF, eraser);

这会擦除一个椭圆。在我注意到这一点之前,我的心智模型是绘制到画布(概念上)绘制到单独的“层”,然后使用 Paint 的传输模式与 Canvas 的位图组合该层。如果它像那样简单,那么上面的代码将擦除整个位图(在剪辑区域内),因为 CLEAR总是将颜色(和 alpha)设置为 0,而不考虑源的 alpha。所以这意味着有一种额外的掩蔽将擦除限制为椭圆形。

我确实找到了API 演示,但每个演示都“在真空中”工作,并且没有显示它关注的东西(例如:XferModes)如何与其他东西(例如:ColorFilters)交互。

通过足够的时间和精力,我可以凭经验弄清楚这些部分是如何关联的,或者去破译来源,但我希望其他人已经解决了这个问题,或者更好的是有一些关于管道/绘图模型的实际文档我错过了。

这个问题的灵感来自于看到另一个 SO question 的答案中的代码。

更新

在四处寻找一些文档时,我突然想到,由于我在这里感兴趣的很多东西似乎是在skia之上的一个非常薄的单板,也许有一些 skia 文档会有所帮助。我能找到的最好的东西是文档,SkPaint其中说:

有 6 种类型的效果可以分配给绘画:

  • SkPathEffect - 在生成 Alpha 蒙版之前修改几何体(路径)(例如虚线)
  • SkRasterizer - 组成自定义遮罩层(例如阴影)
  • SkMaskFilter - 在着色和绘制之前修改 alpha 蒙版(例如模糊、浮雕)
  • SkShader - 例如渐变(线性、径向、扫描)、位图图案(钳位、重复、镜像)
  • SkColorFilter - 在应用 xfermode 之前修改源颜色(例如颜色矩阵)
  • SkXfermode - 例如 porter-duff 传输模式、混合模式

它没有明确说明,但我猜这里的效果顺序是它们出现在管道中的顺序。

4

3 回答 3

50

就像 Romain Guy 所说,“这个问题在 StackOverflow 上很难回答”。实际上没有任何完整的文档,完整的文档包含在这里会有点大。

我最终阅读了源代码并做了一堆实验。我一路上记笔记,最后把它们变成了一个文件,你可以在这里看到:

以及这张图:

在此处输入图像描述

显然,这是“非官方的”,因此适用正常的警告。

基于上述,以下是一些“子问题”的答案:

我也不完全清楚具有固有颜色的绘图操作(例如:drawBitmap,与“矢量”原语如drawRect)如何适应所有这些 - 他们总是忽略Paint's 颜色并使用它们的固有颜色代替吗?

“源颜色”来自Shader. drawBitmap如果使用了非,则inShader临时替换为 a 。在其他情况下,如果没有指定仅生成纯色的 a ,则使用 ' 颜色。BitmapShaderALPHA_8 BitmapShaderShaderPaint

我也很惊讶一个人可以做这样的事情:

Paint eraser = new Paint();
eraser.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawOval(rectF, eraser);

这会擦除一个椭圆。在我注意到这一点之前,我的心智模型是绘制到画布(概念上)绘制到单独的“层”,然后使用 Paint 的传输模式与 Canvas 的位图组合该层。如果它像那样简单,那么上面的代码将擦除整个位图(在剪辑区域内),因为 CLEAR 总是将颜色(和 alpha)设置为 0,而不考虑源的 alpha。所以这意味着有一种额外的掩蔽将擦除限制为椭圆形。

XferMode适用于“源颜色”(来自)Shader和“目标颜色”(来自Canvas's Bitmap)。然后使用在光栅化中计算的蒙版将结果与目的地混合。有关详细信息,请参阅上述文档中的传输阶段。

于 2011-05-09T21:35:06.773 回答
12

这个问题在 StackOverflow 上很难回答。然而,在我开始之前,请注意形状(例如 drawRect())没有固有颜色。颜色信息始终来自 Paint 对象。

这会擦除一个椭圆。在我注意到这一点之前,我的心智模型是绘制到画布(概念上)绘制到单独的“层”,然后该层使用画布的传输模式与画布的位图组合。如果它像那样简单,那么上面的代码将擦除整个位图(在剪辑区域内),因为 CLEAR 总是将颜色(和 alpha)设置为 0,而不管源的 alpha。所以这意味着有一种额外的掩蔽将擦除限制为椭圆形。

你的模型有点不对劲。椭圆不会被绘制到单独的图层中(除非您调用 Canvas.saveLayer()),它会直接绘制到 Canvas 的支持位图上。Paint 的传输模式应用于基元绘制的每个像素。在这种情况下,只有椭圆的光栅化结果会影响位图。没有特殊的蒙版,椭圆本身就是蒙版。

无论如何,这是管道的简化视图:

  1. 基元(矩形、椭圆形、路径等)
  2. 路径效应
  3. 光栅化
  4. 蒙版过滤器
  5. 颜色/着色器/滤色器
  6. Xfermode

(我刚刚看到您的更新,是的,您发现的内容按顺序描述了管道的各个阶段。)

当使用层 (Canvas.saveLayer()) 时,管道会变得稍微复杂一些,因为管道会加倍。您首先通过管道在离屏位图(层)内渲染图元,然后通过管道将离屏位图应用于画布。

于 2011-05-01T18:08:11.170 回答
0

SkPathEffect - 在生成 Alpha 蒙版(例如虚线)之前修改几何体(路径) SkRasterizer - 组成自定义蒙版层(例如阴影) SkMaskFilter - 在着色和绘制之前修改 Alpha 蒙版(例如模糊) SkShader - 例如渐变(线性、径向、扫描)、位图模式(钳位、重复、镜像) SkColorFilter - 在应用 xfermode 之前修改源颜色(例如颜色矩阵) SkXfermode - 例如 porter-duff 传输模式、混合模式

http://imgur.com/0X5Yqod

于 2017-07-13T14:50:16.497 回答