Graphics2D API:Paint之颜色过滤

一、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);
    }
Graphics2D API:Paint之颜色过滤_第1张图片

红绿蓝混合后为白色,所以一开始画笔是白色的,第一个圆是白色的,然后通过setColorFilter设置颜色过滤器,过滤器中仅保存B、A信息,所以第二个圆是蓝色的.

3、颜色过滤器原理

最终的颜色其是通过矩阵相乘得到的,计算方式:

Graphics2D API:Paint之颜色过滤_第2张图片
Graphics2D API:Paint之颜色过滤_第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
        });
Graphics2D API:Paint之颜色过滤_第4张图片
(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
        });
Graphics2D API:Paint之颜色过滤_第5张图片
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);
    }

Graphics2D API:Paint之颜色过滤_第6张图片

可以发现,在不同的光照下得到的图片不一样,所以这个类叫: 光照颜色过滤器,上面给出的计算公式只是为了让大家更好理解这个类,实际上 这个类设计的本意不是为了让我们去进行复杂的计算,而是显示光照效果.

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);
    }
Graphics2D API:Paint之颜色过滤_第7张图片

利用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);
    }
Graphics2D API:Paint之颜色过滤_第8张图片

你可能感兴趣的:(Graphics2D API:Paint之颜色过滤)