ColorFilter
ColorFilter就是颜色过滤器,与Paint一起使用,用于修改Paint绘画的每一个像素的颜色。
从ColorFilter类的树结构中,可以清楚的看到它有3个子类:ColorMatrixColorFilter, LightingColorFilter, PorterDuffColorFilter。接下来我们就看这种3个子类,因为自从API 26以后,官方不再推荐使用其构造函数创建实例,而是由子类创建。
ColorMatrixColorFilter
ColorMatrixColorFilter直译过来就是颜色矩阵过滤器。什么是颜色矩阵(ColorMatrix)呢?我们来看一下ColorMatrix这个类,其内有一个数组,其实其保存的是一个4x5颜色矩阵:
而在Android中,每个像素的RGBA值则存储在一个5*1的颜色分量矩阵C中,由颜色分量矩阵C可以控制图像的颜色效果。
M和C两矩阵相乘的结果为:
R’ = a * R + b * G + c * B + d * A + e;
G’ = f * R + g * G + h * B + i * A + j;
B’ = k * R + l * G + m * B + n * A + o;
A’ = p * R + q * G + r * B + s * A + t;
在一张图片中,每一个像素的颜色由R、G、B、A的值来决定其呈现的效果。也就是说,可以通过颜色矩阵修改颜色分量矩阵的值,从而改变每个像素的颜色的呈现效果。
- 第一行决定了ARGB值的R
- 第二行决定了ARGB值的G
- 第三行决定了ARGB值的B
- 第四行决定了ARGB值的A
- 第五列是颜色的偏移量
在ColorMatrix有一个大小为20且不可改变长度的数组,用来存放颜色矩阵。
private final float[] mArray = new float[20];
每当调用reset方法时,ColorMatrix都会重置这个数组:
/**
* [ 1 0 0 0 0 - red
* 0 1 0 0 0 - green
* 0 0 1 0 0 - blue
* 0 0 0 1 0 ] - alpha
*/
public void reset() {
final float[] a = mArray;
Arrays.fill(a, 0);
a[0] = a[6] = a[12] = a[18] = 1;
}
值得注意的是,R、G、B、A的值应在0-255范围内,在创建颜色矩阵时尤其要小心计算后的值是否在0-255范围内。
那么,现在我们自定义View用来显示处理后的图片显示:
class ColorMatrixFilterView : View {
private val mColorArray = floatArrayOf(
1f, 0f, 0f, 0f, 50f,
0f, 1f, 0f, 0f, 50f,
0f, 0f, 1f, 0f, 50f,
0f, 0f, 0f, 1f, 0f)
lateinit var mBitMap: Bitmap
lateinit var mPaintFilter: Paint
lateinit var mColorMatrix: ColorMatrix
constructor(context: Context) : super(context) {
initData()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initData()
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initDatata()
}
private fun initData() {
// 创建画笔
mPaintFilter = Paint()
// 创建BitMap
mBitMap = BitmapFactory.decodeResource(resources, R.mipmap.ic_sea)
// 新建颜色矩阵对象
mColorMatrix = ColorMatrix()
// 设置颜色矩阵的值
mColorMatrix.set(mColorArray)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// 设置画笔颜色过滤器
mPaintFilter.colorFilter = ColorMatrixColorFilter(mColorMatrix)
// 绘制处理后的图片
canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)
}
}
看上面的例子,从本地资源加载图片并创建了一个BitMap,然后创建了一个ColorMatrix对象,将一个4*5的数组设置为颜色矩阵。接下来,创建了一个ColorMatrixColorFilter对象,并传递刚才创建的ColorMatrix对象。然后调用setColorFilter方法将ColorMatrixColorFilter对象传入.
运行后的效果如下:
来修改下,刚才的示例中并没有添加透明度,把透明度的偏移量添加上:
private val mColorArray = floatArrayOf(
1f, 0f, 0f, 0f, 50f,
0f, 1f, 0f, 0f, 50f,
0f, 0f, 1f, 0f, 50f,
0f, 0f, 0f, 1f, 120f)
看下效果
瞬间有股高大尚的感觉,再拍照时,各种复古模样的照片应该也是采用这种方式,改变了图片本身的颜色矩阵…
ColorMatrix常用API
以下示例中,所用到的ColorMatrix对象矩阵为:
val colorArray = floatArrayOf(
1f, 0f, 0f, 0f, 50f,
0f, 1f, 0f, 0f, 50f,
0f, 0f, 1f, 0f, 50f,
0f, 0f, 0f, 1f, 0f)
cm = ColorMatrix(colorArray)
reset():该方法将ColorMatrix对象中的颜色矩阵重置为矩阵恒等式:
[ 1 0 0 0 0 - red vector 0 1 0 0 0 - green vector 0 0 1 0 0 - blue vector 0 0 0 1 0 ] - alpha vector
- setConcat(ColorMatrix matA, ColorMatrix matB):将颜色矩阵matA和matB复合,相当与对图片进行matA矩阵处理再进行矩阵matB处理。
- postContact(ColorMatrix prematrix):若matA.postConcat(postmatrix)等价与 setConcat(postmatrix,matA)
- preConcat(ColorMatrix prematrix):若matA.preConcat(prematrix)等价与 setConcat(matA,prematrix)。
- setScale(float rScale, float gScale, float bScale, float aScale):设置R、G、B、A对应变量值转到对应的倍数:
假如调用setCsrcale(1f, 2f, 0.5f, 1f),所得的结果为:
Log.i("123", "转换前")
Log.i("123", "cm: ${cm.array.asList()}")
Log.i("123", "cmA: ${cmA.array.asList()}")
cm.postConcat(cmA)
Log.i("123", "转换前")
Log.i("123", "cm: ${cm.array.asList()}")
Log.i("123", "cmA: ${cmA.array.asList()}")
cmfv.setColorMatrix(cm)
//Log
转换前:
cm: [1.0, 0.0, 0.0, 0.0, 50.0,
0.0, 1.0, 0.0, 0.0, 50.0,
0.0, 0.0, 1.0, 0.0, 50.0,
0.0, 0.0, 0.0, 1.0, 0.0]
转换后:
cm: [1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 2.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0]
看这个函数的结果,<font color='red'>实际对应设置的是颜色矩阵对角线上的值,而颜色的偏移量均设置为0,这是为毛呢?</font>.我们来看看setScale函数的源码:
![这里写图片描述](https://img-blog.csdn.net/20171011141236883?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSU9fRmllbGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
- setRotate(int axis, float degrees):设置颜色分量旋转:axis为0时,旋转红色;axis为1时,旋转绿色;axis为2时,旋转蓝色.如何对颜色矩阵赋值。
![这里写图片描述](https://img-blog.csdn.net/20171011143211466?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSU9fRmllbGQ=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
值得注意的是,首先是通过调用reset函数将原颜色矩阵初始化。在该类中的很多函数调用,这样处理,可以看该类源码了解。
setSaturation(float Sat):通过改变矩阵的值设置图像的饱和度。参数0映射为对应的灰色图像,则没有改变。
实际效果就是将彩色图片转换为了黑白图片显示了。
LightingColorFilter
LightingColorFilter是一个模拟照明效果的过滤器,有两个目的:一是将原图的颜色过滤,二是将添加色映射到图片上。在其构造函数中接受两个参数:
LightingColorFilter(int mul, int add)
其中,mul用于与源颜色相乘(称为colormultiply),而add用于添加到源颜色上(称为colorAdd),这两个值都是16进制的色彩值0xAARRGGBB。需要注意的是:Alpha通道是原封不动的彩色滤光片。如果给定一个颜色值,可以这样计算出过滤后的颜色:
R' = R * colorMultiply.R + colorAdd.R
G' = G * colorMultiply.G + colorAdd.G
B' = B * colorMultiply.B + colorAdd.B
如果想将图片中的绿色过滤掉,可以修改G通道,将colorMultiply.G和colorAdd.G赋值为00,那么G的值应为00,也就意味着将图片中的绿色过滤掉。由于其他通道是不变的,colorMultiply.R=colorMultiply.B=FF,colorAdd.R=colorAdd.B=0。A通道是可以忽略的不做考虑。所以可以这么做:
// 设置画笔颜色过滤器
mPaintFilter.colorFilter = LightingColorFilter(0xFFFF00FF.toInt(), 0x00000000)
// 绘制处理后的图片
canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)
效果图:
处理后的图片中已经没有绿色,原来绿色的部分变成了蓝色。这只是将原图中的绿色过滤掉了,又想贪心的将红色映射到图片上,那只能改代码:
// 设置画笔颜色过滤器
mPaintFilter.colorFilter = LightingColorFilter(0xFFFF00FF.toInt(), 0x00FF0000)
// 绘制处理后的图片
canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)
效果图:
PorterDuffColorFilter
PorterDuffColorFilter是一种混合模式的色彩过滤器,它就是一种特殊的图形折叠处理模式,用于将单一的颜色和指定的Porter-Duff模式对源像素的色调进行过滤。至于Porter-Duff中所定义的那些模式,后续介绍,先简单做个例子看下效果:
// 设置画笔颜色过滤器
mPaintFilter.colorFilter = PorterDuffColorFilter(0xFFFF0000.toInt(), PorterDuff.Mode.LIGHTEN)
// 绘制处理后的图片
canvas?.drawBitmap(mBitMap, 0f, 0f, mPaintFilter)
效果图:
若想了解更多Paint相关的内容,请跳入: 自定义View系列文章目录
如果觉得我的文章对您有用,请随意点赞、评论。您的支持将鼓励我继续创作!