Matrix
如下是四种变换对应的控制参数:
Rect
常用的一个“绘画相关的工具类”,常用来描述长方形/正方形,他只有4个属性:
public int left;
public int top;
public int right;
public int bottom;
这4个属性描述着这一个“方块”,但是这有一个知识点需要理清楚,先看这张图
本Rect最左侧到屏幕的左侧的距离是 left
本Rect最上面到屏幕上方的距离是 top
本Rect最右侧到屏幕左侧的距离是 right
本Rect最下面到屏幕上方的距离是 bottom
这四个属性不单单描述了这个长方形4个点的坐标,间接的描述出这个长方形的尺寸:
长 = bottom - top
宽 = right - left
PorterDuff.Mode
PorterDuff.Mode在自定义View时经常用到,但具体使用的效果,往往是通过不断尝试,而后选择某个Mode来实现效果,这里详细解析下。
先说些通俗的概念:
PorterDuff.Mode源码:
在设置时,调用setXfermode,最终调用nSetXfermode方法,继续追踪:
会发现这是个native方法。
能从源码看到的内容并不多,这个方法用于设置图像的过渡模式。
所谓的过渡是指图像的饱和度、颜色值等参数的计算结果的图像表现。
看下Mode的枚举类型定义:
//其中Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色,[...,..]前半部分计算的是结果图像的Alpha通道值,“,”后半部分计算的是结果图像的颜色值。
//效果作用于src源图像区域
private static final Xfermode[] sModes = {
//所绘制不会提交到画布上
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
//显示上层绘制的图像
new PorterDuffXfermode(PorterDuff.Mode.SRC),
//显示下层绘制图像
new PorterDuffXfermode(PorterDuff.Mode.DST),
//正常绘制显示,上下层绘制叠盖
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
//上下层都显示,下层居上显示
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
//取两层绘制交集,显示上层
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
//取两层绘制交集,显示下层
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
//取上层绘制非交集部分,交集部分变成透明
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
//取下层绘制非交集部分,交集部分变成透明
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
//取上层交集部分与下层非交集部分
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
//取下层交集部分与上层非交集部分
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
//去除两图层交集部分
new PorterDuffXfermode(PorterDuff.Mode.XOR),
//取两图层全部区域,交集部分颜色加深
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
//取两图层全部区域,交集部分颜色点亮
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
//取两图层交集部分,颜色叠加
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
//取两图层全部区域,交集部分滤色
new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
//取两图层全部区域,交集部分饱和度相加
new PorterDuffXfermode(PorterDuff.Mode.ADD),
//取两图层全部区域,交集部分叠加
new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
};
上述代码数中注释了该模式的alpha通道和颜色值的计算方式;
alpha_{out} : 最终输出的alpha通道的值
C_{out} : 最终输出的颜色值
alpha_{src} : 原图的alpha通道值
alpha_{dst} : 目标图的alpha通道值
C_{src} : 原图的颜色值
C_{dst} : 目标图的颜色值
知道了这些值的含义,代入计算公式,即可计算出最终该模式下的alpha通道和颜色值,也即是最终显示出来的效果。
接下来看下图像合成效果示意图:
一目了然,可以清晰的看到不同的模式显示的效果,这也是几乎每一篇相关文档中必不可少的图,之前也从未做过验证,偶尔使用的时候,发现效果不太一致,重新尝试其它值,最终找到想要的效果,就算结束,从未真正了解过,为什么使用的时候,与图中的效果并不一致,仔细研究过后,才发现,这里有个坑,需要注意,具体的细节,继续往下看。
在打算写Demo的时候,看到上图的效果,那么我们第一反应,必然是简单画一个圆形,在画一个矩形,而后使用PorterDuff.Mode来实现想要的效果,同时,我也是这么做的:
private fun canvasDrawRect(canvas: Canvas) {
//创建图层
val count = canvas.saveLayer(50f, 50f, width.toFloat() - 50f, height.toFloat() - 50f, mModePaint)
//设置一个灰色背景
canvas.drawColor(Color.GRAY)
//将Paint的颜色设置为红色
mModePaint.color = Color.RED
//使用 canvas.drawCircle 绘制圆形,(以为这就是DST)
canvas.drawCircle(width/2f, height/2f, mCircleRadius, mModePaint) //DST
//Paint设置蓝色
mModePaint.color = Color.BLUE
//设置模式
mModePaint.xfermode = mDuff
//绘制一个蓝色的矩形(SRC)
canvas.drawRect(mRect, mModePaint)
//清除模式
mModePaint.xfermode = null
canvas.restoreToCount(count)
}
为了能够对照鲜明,我是先在画布上绘制一个绿色的底图,而后才绘制上层的灰色背景和红圆蓝方。
完整的图:
下面效果的绿色部分,即为透明部分:
运行后的效果:
对照图像合成效果示意图,不能说一模一样,只能说没啥关系。
为什么会出现这样的问题?
在仔细查看代码后,并未从代码处看到什么问题。
再次查看参考的网上文档,我发现了不同之处:
文档中,圆形与矩形都是通过Bitmap的方式实现的,并未如我写的一般,直接使用canvas.drawCircle和canvas.drawRect,再次看到 图像合成效果示意图 几个字,灵光一闪,难道是,必须是两个bitmap,才能实现和合成效果示意图上一样的效果。
立刻着手切换:
创建了两个Bitmap,并在两个Bitmap上分别绘制了红圆与蓝色矩形。
private fun canvasDrawBitmap(canvas : Canvas) {
val count =
canvas.saveLayer(50f, 50f, width.toFloat() - 50f, height.toFloat() - 50f, mModePaint)
//绘制红圆的bitmap
mCircleBitmap?.let {
canvas.drawBitmap(it, mMatrix, mModePaint)
}
//设置叠加模式
mModePaint.xfermode = mDuff
//绘制蓝色矩形的bitmap
mRectBitmap?.let {
canvas.drawBitmap(it, mMatrix, mModePaint)
}
//清除叠加模式
mModePaint.xfermode = null
canvas.restoreToCount(count)
}
迫不及待,运行!
(手动撒花)
出来的效果与效果示意图一致,长舒口气,看来,问题确实出现在这里,此时再回头看,发现概念里的过渡模式,已经说明,是图像表现,只是,当时未能理解。
这就是踩的最大的一个坑,这也是我在使用时效果总是区别于效果示意图的主要原因。
至此,已经完成想要的效果。
但此时,又想到,当使用canvas.drawCircle 与 canvas.drawRect,为什么会出现如图所示的效果,如果说完全不起作用,可明显还是有变化的。再次回到第一次实现的效果处,仔细思索。
若canvas.drawRect之前绘制的当作DST, 之后绘制的rect为SRC,是否正确?
显然不是:从上图看到,显示红色的圆未与矩形相交的部分,从始至终都是显示出来的,显然,PorterDuff.Mode计算的值,并不包含红色圆未与矩形相交的部分,反之,PorterDuff.Mode 影响的只有矩形部分。
按照这个逻辑,绿色矩形为SRC, 矩形下面灰色的部分与红色圆的部分为DST, 只不过,此时的两张图是完全相交的。 就比如:我们创建两个矩形,一个红色,一个绿色,它俩的大小,位置一模一样,那么出来的效果,是否就是我们一开始使用canvas.drawRect 和 canvas.drawCircle实现之后的效果。
经验证,确实如此,这里就不再贴代码与效果。
所以,使用canvas.drawCircle 和 canvas.drawRect实现的效果完全就是两部分相交时显示的效果,即我们可以看效果图圆与矩形相交部分,
若效果图相交部分是透明,则实现效果:显示的是底图绿色,
若效果图相交部分是蓝色,则实现效果:显示的是蓝色矩形
若效果图相交部分是黄色,则实现效果: 显示的是未绘制蓝色矩形的效果
至此,最开始使用canvs.darwCircle 和 canvas.drawRect 为什么会出现如图的效果,也有了结果。
至此,结束,又解决一个问题!