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);
    }

红绿蓝混合后为白色,所以一开始画笔是白色的,第一个圆是白色的,然后通过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);
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容