Android 自定义View学习(四)——Paint 关于ColorMatrix学习

上一篇学习了Paint中关于绘制文字时的属性,接下来学习了解在绘制图像时的属性。重点有三个:ColorMatrixPorterDuffXfermoreShader

关于这三个重点,本篇先学习第一个。
学习目标:
了解基本知识点,能够了解都可以用来干嘛,知道有这么个东西,在学习别人写的控件代码时,看到不至于懵逼。 : )


学习资料:

学习过程中,有一个小技巧就是Android Studio的一个快捷键ctrl+h,鼠标放在所要查看的类上,按快捷键就可以查看这个的类的继承关系和结构。希望可以帮助到和我一样的新人 : )


1.setColorFilter(ColorFilter filter)设置颜色过滤器

参数是一个ColorFilter,是一个抽象类,有三个子类:PorterDuffColorFilterColorMatrixColorFilterLightingColorFilter


1.1 PorterDuffColorFilter

A color filter that can be used to tint the source pixels using a single color and a specific {@link PorterDuff Porter-Duff composite mode}.

一个指定单一颜色和特定模式的过滤器

PorterDuff是两个人的人名组合

构造方法:
PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)

  • int color 颜色
  • PorterDuff.Mode mode 模式

未设置过滤器前:

原始效果

简单使用:

public class DrawGraphicView extends View {
    private Paint gPaint;
    public DrawGraphicView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initPaint();
    }
    
    /**
     * 初始化画笔
     */
    private void initPaint() {
        gPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        gPaint.setColor(Color.parseColor("#FF4081"));
        PorterDuffColorFilter filter = new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.OVERLAY);
        gPaint.setColorFilter(filter);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        float x = getWidth()/2;
        float y = getHeight()/2;
        float radius= Math.min(getWidth(),getHeight())/2;
        canvas.drawCircle(x,y,radius,gPaint);
    }

    /**
     * 测量
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, 300);
        } else if (wSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(300, hSpecSize);
        } else if (hSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(wSpecSize, 300);
        }
    }
}

颜色设置的Color.BLUE,模式PorterDuff.Mode.OVERLAY,设置后的效果:

PorterDuffColorFilter

第一个参数就是代表颜色的int值,网上搜索了下rgb的计算,看得有点懵,这里只好先挖坑 ,先朝下学 : )

第二个参数模式。一共有18种。这里不打算一一介绍,到了PorterDuffXfermore再做学习


1.2 LightingColorFilter

A color filter that can be used to simulate simple lighting effects.
A <code>LightingColorFilter</code> is defined by two parameters, one
used to multiply the source color (called <code>colorMultiply</code>)
and one used to add to the source color (called <code>colorAdd</code>).
The alpha channel is left untouched by this color filter.
Given a source color RGB, the resulting R'G'B' color is computed thusly:<p>
R' = R * colorMultiply.R + colorAdd.R
G' = G * colorMultiply.G + colorAdd.G
B' = B * colorMultiply.B + colorAdd.<p>
The result is pinned to the [0..255] range for each channel.

名字的直白翻译就是:光照色彩过滤器

一个可以模拟简单光照影响的色彩过滤器

构造方法:
LightingColorFilter(int mul, int add)

  • mul 全称是colorMultiply意为色彩倍增
  • add 全称是colorAdd意为色彩添加

这两个值都是16进制的色彩值0xAARRGGBB

看得也同样是一脸懵逼,继续朝下学


调用方法:

LightingColorFilter filter = new LightingColorFilter(Color.WHITE,Color.GREEN);
gPaint.setColorFilter(filter);

效果图就不上了,因为控制不了显示的颜色。没有学会计算原理,继续挖坑,先往下继续学习


1.3 ColorMatrixColorFilter

涉及到了第一个重点ColorMatrix,色彩矩阵。

A color filter that transforms colors through a 4x5 color matrix. This filter can be used to change the saturation of pixels, convert from YUV to RGB, etc.

一个通过 4 * 5 色彩矩阵计算进行变换颜色的过滤器

构造方法只需要一个参数:
ColorMatrixColorFilter(ColorMatrix matrix)

尽管不理解参数是干嘛用的还有怎么样的效果,先随便创建一个出来

private void initPaint() {
    gPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    gPaint.setColor(Color.GRAY);

    ColorMatrix colorMatrix = new ColorMatrix(new float[]{
            1.3F,0,0,0,0,
            0,1.5F,0,0,0,
            0,0,1.6F,0,0,
            0,0,0,1.9F,0});
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    gPaint.setColorFilter(filter);
}

这里暂时只知道,每个位置上值,1代表不改变颜色的值,只要不是1就可以看出效果,尽管不知道怎么得来的结果,继续向下学习

三个方法,也只是简单了解怎么调用,向下继续 : )


2.ColorMartix 色彩矩阵

Android中图片处理最常使用到的数据结构就是Bitmap,包含整个图片所有的数据。整个图片由点阵和颜色值组成。

  • 点阵 , 一个包含像素的矩阵,每一个元素对应着图片的像素
  • 颜色值 , ARGB,分别对应透明度,红,绿,蓝四个通道分量,四个共同决定每个像素点显示的颜色

2.1色彩矩阵的运算分析

在色彩处理中,一般用色调,饱和度,亮度来描述一个图像

  • 色调 , 物体传播的颜色
  • 饱和度 ,颜色的纯度,从0(灰)到100%(饱和)来进行描述
  • 亮度 , 颜色的相对明暗程度

色彩矩阵:

ColorMatrix色彩矩阵

写好了才发现简书不支持LaTeX来写矩阵,只好使用截图

用来处理图片色彩

  • abcde 值决定新的颜色值中的R —— 红色
  • fghij 值决定新的颜色值中的G —— 绿色
  • klmno 值决定新的颜色值中的B —— 绿色
  • pqrst 值决定新的颜色值中的A —— 透明度
  • ejot 值决定每个分量重的offset——偏移量

颜色矩阵分量

每一个像素都有一个颜色分量矩阵保存颜色的RGBA


矩阵乘法运算:


矩阵乘法运算

计算过程:

R1 = a * R + b * G + c * B + d * A + e
G1 = f * R + g * G + h * B + i * A + j
B1 = k * R + l * G + m * B + n * A + o
A1 = P * R + q * G + r * B + s * A + t

过程分析:
R1 = a * R + b * G + c * B + d * A + e

设置a = 1b,c,d,e都为0,R1 = R。同理,G1 = G条件为g = 1, fhij = 0,然后依次轮推,便可以得到下面这个矩阵:

初始矩阵

这个矩阵不会对原有颜色值造成改变,被当做初始矩阵

根据R = A * C得知,一般改变颜色值有两种方法可以选择:

  1. 改变offset,改变偏移量来进行改变颜色分量
  2. 改变RGBA值的系数进行调整颜色分量

2.1.1 改变 offset 偏移量

改变R和G的偏移量

改变了R和G的偏移量,图像的红色和绿色的分量就增加了100,红色和绿色混合会得到黄色,整个图像也就会偏黄色

简单进行测试:

private void initPaint() {
    gPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    gPaint.setColor(Color.GRAY);//灰色

    ColorMatrix colorMatrix = new ColorMatrix(new float[]{
            1 , 0 , 0 , 0 , 50 ,
            0 , 1 , 0 , 0 , 50 ,
            0 , 0 , 1 , 0 , 0  ,
            0 , 0 , 0 , 1 , 0
    });
    ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
    gPaint.setColorFilter(filter);
}

原始颜色为系统提供的灰色,R和G的偏移量我设置了50

改变偏移量

灰色是稍微偏了点黄

大于0代表增加,小于0则代表减少


2.1.2 改变颜色系数

改变R和G的色彩系数

简单测试:

private void initPaint() {
        gPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        gPaint.setColor(Color.GRAY);

        ColorMatrix colorMatrix = new ColorMatrix(new float[]{
                1.5f , 0 , 0 , 0 , 0 ,
                0 , 1.5f , 0 , 0 , 0 ,
                0 , 0 , 1 , 0 , 0  ,
                0 , 0 , 0 , 1 , 0
        });
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
        gPaint.setColorFilter(filter);
    }

改变色彩系数

矩阵运算结果R和G色彩分量就会变为1.5倍,也显示黄色

若小于1,则代表分量减少,红和绿减少,就会偏蓝

大于1代表增加分量,小于1则意味着减少

到了这里,对于前面1中挖的坑多少有点了解了


2.2 图像的色光属性

ColorMatrix中,提供了方法来调节图像的色调饱和度亮度

  • setRotate(int axis, float degrees) 设置色调
  1. axis 颜色编号 0,1,2
  2. degrees 需要处理的值

方法调用:

//色调
ColorMatrix rotateMatrix = new ColorMatrix();
rotateMatrix.setRotate(0,hue);//红
rotateMatrix.setRotate(1,hue);//绿
rotateMatrix.setRotate(2,hue);//蓝

0,代表红,1代表绿,2代表蓝


  • setSaturation(float sat) 设置饱和度
    float sat 饱和度值 ;0位灰色图像, 1为原图

方法调用:

//饱和度
ColorMatrix saturationMatrix = new ColorMatrix();
saturationMatrix.setSaturation(saturation);

区间为 0~1


  • setScale(float rScale, float gScale, float bScale, float aScale)设置亮度
    rScale,gScale,bScale代表的三原色相同时,就会显示白色。利用这个原理进行亮度的改变 。0代表全黑 1原图

方法调用:

//亮度
ColorMatrix scaleMatrix = new ColorMatrix();
scaleMatrix.setScale(lum,lum,lum,1);

将三原色比例设置为同一个值


2.2.1 简单测试

Android群英传代码敲了敲

色光属性

public class LightingActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener {
    private ImageView iv;
    private SeekBar sb_rotate;
    private SeekBar sb_saturation;
    private SeekBar sb_scale;
    private Bitmap bitmap;

    private float hue;
    private float saturation;
    private float lum;
    private final float MID_VALUE = 100.0F;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lighting);
        initView();
    }

    private void initView() {
        iv = (ImageView) findViewById(R.id.iv_lighting_activity);
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.text);
        iv.setImageBitmap(bitmap);
        sb_rotate = (SeekBar) findViewById(R.id.sb_rotate_lighting_activity);
        sb_saturation = (SeekBar) findViewById(R.id.sb_saturation_lighting_activity);
        sb_scale = (SeekBar) findViewById(R.id.sb_scale_lighting_activity);

        sb_scale.setOnSeekBarChangeListener(this);
        sb_saturation.setOnSeekBarChangeListener(this);
        sb_rotate.setOnSeekBarChangeListener(this);
    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.sb_rotate_lighting_activity:
                hue = (progress - MID_VALUE) * 1.0f/MID_VALUE * 180;
                break;
            case R.id.sb_saturation_lighting_activity:
                saturation = progress * 1.0f / MID_VALUE;
                break;
            case R.id.sb_scale_lighting_activity:
                lum = progress * 1.0F / MID_VALUE;
                break;
        }
        iv.setImageBitmap(handleImageEffect(bitmap,hue,saturation,lum));
    }

    private Bitmap handleImageEffect(Bitmap bitmap, float hue, float saturation, float lum) {
        Bitmap b = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(b);
        Paint paint = new Paint();
        //色调
        ColorMatrix rotateMatrix = new ColorMatrix();
        rotateMatrix.setRotate(0,hue);
        rotateMatrix.setRotate(1,hue);
        rotateMatrix.setRotate(2,hue);
        //饱和度
        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);
        //亮度
        ColorMatrix scaleMatrix = new ColorMatrix();
        scaleMatrix.setScale(lum,lum,lum,1);


        ColorMatrix imgMatrix = new ColorMatrix();
        imgMatrix.postConcat(rotateMatrix);
        imgMatrix.postConcat(saturationMatrix);
        imgMatrix.postConcat(scaleMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imgMatrix));
        canvas.drawBitmap(bitmap,0,0,paint);
        return b;
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {}

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {}
}

书上的代码,徐医生并没有完全给全,MID_VALUE = 100.0F这个值是我自己猜测的,也测试了50,最后选择了100

这三个方法的对图像的影响大概也有了解了


3.最后

关于ColorMatrix简单了解,依然不是很清晰,具体使用还得再次学习。暂时先了解基本的知识点,而Matrix中知识点更多。下篇学习PorterDuffXfermore

啊,要发工资,下班买吃的去 : )

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

推荐阅读更多精彩内容