一、setColorFilter
public ColorFilter setColorFilter(ColorFilter filter)
在前面我们学习过Paint的setColor方法,这里又多了一个setColorFilter颜色过滤,看到这里你肯定会疑惑,你说你一个“笔”有颜色还算正常,你还能过滤颜色?
那么过滤颜色是什么意思呢?其实就像我们现实中的筛子,过滤掉不要的杂质,得到想要的东西,这里的意思就是把颜色“过滤”一遍,得到我们想要的色彩.
仔细观察Paint的这个方法,它需要一个ColorFilter 对象,而且还会返回一个ColorFilter 对象,ColorFilter 翻译为中文就是颜色过滤器,也就是筛子,在绘图软件中被称为颜色滤镜.
我们再来看ColorFilter 的源码:
public class ColorFilter {
public long native_instance;
@Override
protected void finalize() throws Throwable {
try {
super.finalize();
} finally {
destroyFilter(native_instance);
native_instance = 0;
}
}
static native void destroyFilter(long native_instance);
}
发现ColorFilter 源码里面基本啥都没有,这时候我们应该想到:ColorFilter 肯定有子类.
不错,ColorFilter 有3个子类:
那么接下来就是解析这3个子类了.
二、ColorMatrixColorFilter
这个类翻译为中文就是色彩矩阵颜色过滤器,我们来看一下这个类的成员变量、构造方法:
public class ColorMatrixColorFilter extends ColorFilter {
private final ColorMatrix mMatrix = new ColorMatrix();
public ColorMatrixColorFilter(ColorMatrix matrix) {
mMatrix.set(matrix); //★
......
}
public ColorMatrixColorFilter(float[] array) {
if (array.length < 20) {
throw new ArrayIndexOutOfBoundsException();
}
mMatrix.set(array); //★
......
}
......
这个类也很简单,初始化的时候需要一个ColorMatrix 对象或者一个色彩矩阵数组,这两者最终都是设置给了其成员变量mMatrix ,所以我们需要关心的就是ColorMatrix (色彩矩阵)这个类了.
1、ColorMatrix
在Android中:一个色彩信息包含R、G、B、Alpha信息,而修改色彩信息,需要使用ColorMatrix:
public class ColorMatrix {
private final float[] mArray = new float[20];
public ColorMatrix() {
......
}
public ColorMatrix(float[] src) {
......
}
public ColorMatrix(ColorMatrix src) {
......
}
......
}
在ColorMatrix 中定义了一个4x5的float[]类型的矩阵用来表示、修改颜色信息:
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
R, 0, 0, 0, 0,
0, G, 0, 0, 0,
0, 0, B, 0, 0,
0, 0, 0, A, 0
});
其中:
一共四行,分别代表:R、G、B、A,为1时表示保持原色彩的R、G、B、A值.
每一行最后一列我称之为:增加量,比如:
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 88,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
});
在原有的色彩R色上增加88的量,使之更偏向红色.
接下来我们通过实例来看看颜色矩阵的用法.
2、例(保持蓝色通道、透明度通道的颜色矩阵)
private void gogogo(Canvas canvas) {
mPaint.setAntiAlias(true);
mPaint.setARGB(255, 255, 255, 255);
canvas.drawCircle(100, 100, 50, mPaint);
canvas.translate(0, 200);
ColorMatrix colorMatrix = new ColorMatrix(new float[]{//保持原色彩B、A信息的颜色矩阵
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
});
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);//色彩矩阵颜色过滤器
mPaint.setColorFilter(filter);//为画笔设置颜色过滤器
canvas.drawCircle(100, 100, 50, mPaint);
}
红绿蓝混合后为白色,所以一开始画笔是白色的,第一个圆是白色的,然后通过setColorFilter设置颜色过滤器,过滤器中仅保存B、A信息,所以第二个圆是蓝色的.
3、颜色过滤器原理
最终的颜色其是通过矩阵相乘得到的,计算方式:
可能有的人会说,这到底有什么用?其实这个颜色矩阵在相机应用中使用最多:图片是由一个个像素组成的,一张图片可以有几十万像素,而每个像素都有对应的色彩数组,我们可以通过颜色矩阵ColorMatrix来转换每个像素的色彩信息,得到经过色彩处理的图片,那些美颜相机就是基于此原理做出来的.
4、常见颜色矩阵
(1)单通道
public class XView extends View {
private Paint mPaint;
private Bitmap bitmap;
public XView(Context context) {
this(context, null);
}
public XView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.lks);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
gogogo(canvas);
}
private void gogogo(Canvas canvas) {
mPaint.setAntiAlias(true);
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 600, 350), mPaint);//原图
canvas.translate(0, 350);//画布向下平移
// 色彩矩阵
ColorMatrix colorMatrix = new ColorMatrix(new float[]{//红色通道
1, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0
});
// ColorMatrix colorMatrix = new ColorMatrix(new float[]{//绿色通道
// 0, 0, 0, 0, 0,
// 0, 1, 0, 0, 0,
// 0, 0, 0, 0, 0,
// 0, 0, 0, 1, 0
// });
// ColorMatrix colorMatrix = new ColorMatrix(new float[]{//蓝色通道
// 0, 0, 0, 0, 0,
// 0, 0, 0, 0, 0,
// 0, 0, 1, 0, 0,
// 0, 0, 0, 1, 0
// });
mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));//设置颜色过滤器
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 600, 350), mPaint);
}
}
(2)某个通道颜色加深
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 66,
0, 0, 0, 1, 0
});
(3)色彩缩放(颜色变暗、变亮)
ColorMatrix colorMatrix = new ColorMatrix(new float[]{//变暗
0.5f, 0, 0, 0, 0,
0, 0.5f, 0, 0, 0,
0, 0, 0.5f, 0, 0,
0, 0, 0, 1, 0
});
ColorMatrix colorMatrix = new ColorMatrix(new float[]{//变亮
1.5f, 0, 0, 0, 0,
0, 1.5f, 0, 0, 0,
0, 0, 1.5f, 0, 0,
0, 0, 0, 1, 0
});
(4)色彩反转
ColorMatrix colorMatrix = new ColorMatrix(new float[]{
-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0
});
5、颜色矩阵ColorMatrix 的方法
public void set(ColorMatrix src)
public void set(float[] src)
搭配无参构造使用
public void reset()
重置色彩矩阵,就是把colorMatrix恢复如下:
[ 1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 ]
直接使用矩阵调节色彩,这对于很多不熟悉矩阵的人来说很困难.
ColorMatrix 也提供了一些方法用来调节色彩.
public void setScale(float rScale, float gScale, float bScale, float aScale)
色彩缩放:效果和上面例子中一样,rScale、gScale、bScale、aScale分别是R、G、B、A的缩放比例.
public void setSaturation(float sat) 调节饱和度
还有一些色彩旋转、色彩矩阵相乘方法,有兴趣的可以去了解一下.
三、LightingColorFilter
光照颜色过滤器
public LightingColorFilter(int mul, int add)
mul:色彩倍增
add:色彩增加
mul、add的取值都是0xRRGGBB,对应RGB颜色值
注意:LightingColorFilter只对RGB色值起作用,对透明度不起作用
源码中给出了它的计算方式:
给定一个色彩R、G、B值,最终色彩值为:
* R' = R * colorMultiply.R + colorAdd.R
* G' = G * colorMultiply.G + colorAdd.G
* B' = B * colorMultiply.B + colorAdd.B
1、mul 光照效果
LightingColorFilterw为什么叫光照颜色过滤器,其实就是因为第一个参数mul.
下面我们通过实例查看,还是在XView的gogogo方法里面做更改:
private void gogogo(Canvas canvas) {
mPaint.setAntiAlias(true);
mPaint.setColorFilter(new LightingColorFilter(Color.WHITE, 0));//白光照射,无变化
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
canvas.translate(0, 140);
mPaint.setColorFilter(new LightingColorFilter(Color.RED, 0));//红光照射
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
canvas.translate(0, 140);
mPaint.setColorFilter(new LightingColorFilter(Color.GREEN, 0));//绿光照射
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
canvas.translate(0, 140);
mPaint.setColorFilter(new LightingColorFilter(Color.BLUE, 0));//蓝光照射
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
canvas.translate(0, 140);
mPaint.setColorFilter(new LightingColorFilter(Color.GRAY, 0));//灰光照射
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
canvas.translate(0, 140);
mPaint.setColorFilter(new LightingColorFilter(Color.YELLOW, 0));//黄光照射
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 240, 140), mPaint);
}
可以发现,在不同的光照下得到的图片不一样,所以这个类叫: 光照颜色过滤器,上面给出的计算公式只是为了让大家更好理解这个类,实际上 这个类设计的本意不是为了让我们去进行复杂的计算,而是显示光照效果.
2、add 色彩增强效果
add的作用是增强某个通道的颜色值,例:
private void gogogo(Canvas canvas) {
mPaint.setAntiAlias(true);
mPaint.setColorFilter(new LightingColorFilter(Color.WHITE, 0));//白光照射,无变化
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 600, 350), mPaint);
canvas.translate(0, 350);
mPaint.setColorFilter(new LightingColorFilter(Color.WHITE, 0x0000f0));//白光照射,加深蓝色通道值,让图片更蓝
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 600, 350), mPaint);
}
利用add这个参数,我们可以很容易作出图片按压后颜色变深的效果,只要改变相应通道add值即可.
四、PorterDuffColorFilter
PorterDuff颜色过滤器(图形混合过滤器)
这个过滤器类名用Tomas Proter和Tom Duff两个人的名字组合而成,因为这两个人提出图形混合的概念,PorterDuffColorFilter正是图形混合过滤器,所以用这两个人名字命名.
1、构造方法
public PorterDuffColorFilter(int color, PorterDuff.Mode mode)
color:16进制的颜色值(0xAARRGGBB)
mode:图形混合模式,可选值是在PorterDuff这个类中,一共18个:
PorterDuff.Mode.CLEAR
PorterDuff.Mode.SRC
PorterDuff.Mode.DST
PorterDuff.Mode.SRC_OVER
PorterDuff.Mode.DST_OVER
PorterDuff.Mode.SRC_IN
PorterDuff.Mode.DST_IN
PorterDuff.Mode.SRC_OUT
PorterDuff.Mode.DST_OUT
PorterDuff.Mode.SRC_ATOP
PorterDuff.Mode.DST_ATOP
PorterDuff.Mode.XOR
PorterDuff.Mode.DARKEN 变暗
PorterDuff.Mode.LIGHTEN 变亮
PorterDuff.Mode.MULTIPLY 正片叠底
PorterDuff.Mode.SCREEN 滤色
PorterDuff.Mode.ADD 饱和度相加
PorterDuff.Mode.OVERLAY 叠加
这里我们需要关注的只有中文注释的6个Mode,因为PorterDuff.Mode这个类表示:图形混合模式,而不是色彩混合模式,在PorterDuff.Mode这18个Mode中,只有注释的6个Mode是与色彩混合相关的,而本节我们学习的是颜色过滤,使用其它的Mode在这里其实并没有什么用,它们的用处在别的地方,后续的文章会介绍到.
ok,我们来看一下这6个Mode的具体效果:
private void gogogo(Canvas canvas) {
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(300, 0);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.DARKEN));//变暗
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(0, 170);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.LIGHTEN));//变亮
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(0, 170);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.MULTIPLY));//正片叠底
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(0, 170);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SCREEN));//滤色
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(0, 170);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.ADD));//饱和度相加
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
canvas.translate(0, 170);
mPaint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.OVERLAY));//叠加
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 300, 170), mPaint);
}