AndroidUI之Paint滤镜&XFERMODE解析

目录

写在前面

一、PorterDuff.Mode图层混合模式

1.1、图层混合模式使用介绍

1.2、离屏绘制(离屏缓冲)

1.3、图层混合模式全解析

1.4、刮刮卡案例实操

二、滤镜效果

2.1、LightingColorFilter滤镜

2.2、PorterDuffColorFilter滤镜

2.3、ColorMatrixColorFilter滤镜

2.4、ColorMatrix


写在前面

上一篇中我们对Paint画笔的使用做了一个简单的介绍,详情见:《AndroidUI之Paint画笔高级应用》,本篇我们继续来学点有意思的东西——Paint的滤镜和XFERMODE。

今天要实现的效果主要就是下面两张图,通过图层混合模式实现类似刮刮卡效果,通过滤镜模式实现图片的各种特效,是不是有点期待了,那还等啥呢,开始吧!

 

一、PorterDuff.Mode图层混合模式

1.1、图层混合模式使用介绍

PorterDuff.Mode将所绘制图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素值。

图层混合的模式一共有18种:

每一种图层混合模式就代表了一种混合规则,比如我们以CLEAR为例,来看一下源码:

        /**
         * <p>
         *     <img src="{@docRoot}reference/android/images/graphics/composite_CLEAR.png" />
         *     <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
         * </p>
         * <p>\(\alpha_{out} = 0\)</p>
         * <p>\(C_{out} = 0\)</p>
         */
        CLEAR       (0)

alpha表示混合之后的透明值通道,out表示输出值也就是最终的计算值,C表示颜色值,通过这里可以看出图层混合之后的效果是通过一定的规则计算出alpha通道值以及颜色值,这两个值计算出来之后就决定了最终的效果,这里赋值为0表示清除颜色值的效果,对于其他模式,大家可以自行去查找源码,注释里面有具体的计算规则以及对应的含义,下面我们通过代码来实际看一下究竟该如何使用。

首先新建一个XfermodeView,初始化画笔,并给画笔设置了一些属性,在onMeasure()方法中获取控件的宽高,然后重写onDraw()方法:

public class XfermodeView extends View {

    private Paint mPaint;
    private int mWidth, mHeight;

    public XfermodeView(Context context) {
        this(context, null);
    }

    public XfermodeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        //初始化画笔
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        mWidth = MeasureSpec.getSize(widthMeasureSpec);
        mHeight = MeasureSpec.getSize(heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }

}

在xml文件中引用我们自定义的View,设置宽高各300,居中显示:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.jarchie.androidui.xfermode.XfermodeView
        android:layout_width="300dp"
        android:layout_height="300dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

准备工作做完了之后,下面来看一下有哪些地方使用了图层混合模式的API,主要有一下几种,今天我们主要说的就是Paint.setXfermode()这种:

  • ComposeShader:混合渲染
  • Paint.setXfermode():为画笔设置图层混合模式
  • PorterDuffColorFilter:颜色过滤器

注意:在Android API14之后,图层混合的某些API是不支持硬件加速的,而系统默认是开启硬件加速的,所以首先需要做的就是关闭硬件加速。

接着我们来写一段代码看一下图层混合模式是如何使用的?看下面的代码:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //禁止硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        //添加灰色背景
        setBackgroundColor(Color.GRAY);
        //离屏绘制
        int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
        //目标图
        canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
        //设置混合模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        //源图,重叠区域右下角部分
        canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
        //清除混合模式
        mPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }

    //画矩形Dst
    public Bitmap createRectBitmap(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        dstPaint.setColor(0xFF66AAFF);
        canvas.drawRect(new Rect(width / 20, height / 3, 2 * width / 3, 19 * height / 20), dstPaint);
        return bitmap;
    }

    //画圆src
    public Bitmap createCircleBitmap(int width, int height) {
        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        scrPaint.setColor(0xFFFFCC44);
        canvas.drawCircle(width * 2 / 3, height / 3, height / 4, scrPaint);
        return bitmap;
    }

我们首先是绘制了两个Bitmap,一个是源图像src圆形Bitmap,一个是目标图像dst矩形Bitmap,然后在onDraw()方法中首先是设置了一个灰色背景,然后是给Paint画笔设置图层混合模式为DST_IN,代码中用到了一个我们之前没见过的概念——离屏绘制,关于离屏绘制接下来就会讲到,这一块的代码是仿照Google官方Demo中的写法来的,官方Demo中对于图层混合模式有一个完整的Demo使用,大家可以参考那个进行学习,为了方便查找,我已经把地址放在下面了,敬请各位大佬笑纳:

https://github.com/THEONE10211024/ApiDemos/blob/master/app/src/main/java/com/example/android/apis/graphics/Xfermodes.java

如果把离屏绘制的代码注释掉,再来跑一次:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //禁止硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        //添加灰色背景
        setBackgroundColor(Color.GRAY);
        //目标图
        canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
        //设置混合模式
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        //源图,重叠区域右下角部分
        canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
        //清除混合模式
        mPaint.setXfermode(null);
    }

两次代码的执行结果如下,左侧是使用了离屏绘制的结果,右侧是没有使用离屏绘制的结果:

 

根据上面的结果,大家有什么发现吗?简单来说就是:在未使用离屏绘制的情况下,直接考虑了Canvas的背景,导致了绘制结果的不正确,看上面右图中灰色背景都不见了,并且背景还参与了图层混合的计算。使用离屏绘制的情况下,会首先创建一个图层,将两个图层混合之后的结果再绘制到画布之上,这个过程才是正确的。

1.2、离屏绘制(离屏缓冲)

这里插一嘴,先来说说什么是离屏绘制,离屏绘制又叫离屏缓冲,特点及使用方式如下图所示:

1.3、图层混合模式全解析

接下来我们根据谷歌的官方Demo进行简单的修改,然后对照着来对18种混合模式都做一个了解:

// 使用离屏绘制
int sc = canvas.saveLayer(x, y, x + W, y + H, null);
canvas.translate(x, y);
canvas.drawBitmap(makeDst(2 * W / 3, 2 * H / 3), 0, 0, paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(makeSrc(2 * W / 3, 2 * H / 3), W / 3, H / 3, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);

从上面的代码种可以看到,这里我们绘制Bitmap的时候,创建的dst目标图像宽度为矩形区域的2/3,创建的src源图像宽度和高度都是矩形区域的2/3,但是源图像的起点位置是在矩形区域的宽高各1/3处,这一点需要理解清楚。同时需要注意的是,对于图层混合模式来说,所有的混合效果只会作用于源图像区域,这里目标图像是圆形,源图像是矩形,请大家牢记,下面我就放码过来喽:

public class XfermodesView extends View {
    
    private static int W = 250;
    private static int H = 250;

    private static final int ROW_MAX = 4;   // number of samples per row

    private Bitmap mSrcB;
    private Bitmap mDstB;
    private Shader mBG;     // background checker-board pattern

    //其中Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色,[...,..]前半部分计算的是结果图像的Alpha通道值,“,”后半部分计算的是结果图像的颜色值。
    //效果作用于src源图像区域
    private static final Xfermode[] sModes = {
            //所绘制不会提交到画布上
            new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
            //显示上层绘制的图像
            new PorterDuffXfermode(PorterDuff.Mode.SRC),
            //显示下层绘制图像
            new PorterDuffXfermode(PorterDuff.Mode.DST),
            //正常绘制显示,上下层绘制叠盖
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),

            //上下层都显示,下层居上显示
            new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
            //取两层绘制交集,显示上层
            new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
            //取两层绘制交集,显示下层
            new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
            //取上层绘制非交集部分,交集部分变成透明
            new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),

            //取下层绘制非交集部分,交集部分变成透明
            new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
            //取上层交集部分与下层非交集部分
            new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
            //取下层交集部分与上层非交集部分
            new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
            //去除两图层交集部分
            new PorterDuffXfermode(PorterDuff.Mode.XOR),

            //取两图层全部区域,交集部分颜色加深
            new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
            //取两图层全部区域,交集部分颜色点亮
            new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
            //取两图层交集部分,颜色叠加
            new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
            //取两图层全部区域,交集部分滤色
            new PorterDuffXfermode(PorterDuff.Mode.SCREEN),

            //取两图层全部区域,交集部分饱和度相加
            new PorterDuffXfermode(PorterDuff.Mode.ADD),
            //取两图层全部区域,交集部分叠加
            new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
    };

    private static final String[] sLabels = {
            "Clear", "Src", "Dst", "SrcOver",
            "DstOver", "SrcIn", "DstIn", "SrcOut",
            "DstOut", "SrcATop", "DstATop", "Xor",
            "Darken", "Lighten", "Multiply", "Screen", "Add","Overlay"
    };

    public XfermodesView(Context context) {
        this(context, null);
    }

    public XfermodesView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public XfermodesView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
        if (windowManager != null) {
            DisplayMetrics display = new DisplayMetrics();
            windowManager.getDefaultDisplay().getMetrics(display);
            W = H = (display.widthPixels - 64) / ROW_MAX; //得到矩形
        }

        //1,API 14之后,有些函数不支持硬件加速,需要禁用
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        mSrcB = makeSrc(W, H);
        mDstB = makeDst(W, H);

        //根据width和height创建空位图,然后用指定的颜色数组colors来从左到右从上至下一次填充颜色
        //make a ckeckerboard pattern
        Bitmap bm = Bitmap.createBitmap(new int[]{0xFFFFFFFF, 0xFFCCCCCC, 0xFFCCCCCC, 0xFFFFFFFF}, 2, 2, Bitmap.Config.RGB_565);
        mBG = new BitmapShader(bm, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
        Matrix m = new Matrix();
        m.setScale(6, 6);
        mBG.setLocalMatrix(m);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawColor(Color.WHITE);

        Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
        labelP.setTextAlign(Paint.Align.CENTER);

        Paint paint = new Paint();
        paint.setFilterBitmap(false);

        canvas.translate(15, 35);

        int x = 0;
        int y = 0;
        for (int i = 0; i < sModes.length; i++) {
            // draw the border
            paint.setStyle(Paint.Style.STROKE);
            paint.setShader(null);
            canvas.drawRect(x - 0.5f, y - 0.5f, x + W + 0.5f, y + H + 0.5f, paint);

            // draw the checker-board pattern
            paint.setStyle(Paint.Style.FILL);
            paint.setShader(mBG);
            canvas.drawRect(x, y, x + W, y + H, paint);

            // 使用离屏绘制
            int sc = canvas.saveLayer(x, y, x + W, y + H, null);
            canvas.translate(x, y);
            canvas.drawBitmap(makeDst(2 * W / 3, 2 * H / 3), 0, 0, paint);
            paint.setXfermode(sModes[i]);
            canvas.drawBitmap(makeSrc(2 * W / 3, 2 * H / 3), W / 3, H / 3, paint);
            paint.setXfermode(null);
            canvas.restoreToCount(sc);

            // draw the label
            labelP.setTextSize(20);
            canvas.drawText(sLabels[i], x + W / 2, y - labelP.getTextSize() / 2, labelP);

            x += W + 10;

            // wrap around when we've drawn enough for one row
            if ((i % ROW_MAX) == ROW_MAX - 1) {
                x = 0;
                y += H + 30;
            }
        }
    }

    // create a bitmap with a circle, used for the "dst" image
    // 画圆一个完成的圆
    static Bitmap makeDst(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFFFFCC44);
        c.drawOval(new RectF(0, 0, w, h), p);
        return bm;
    }

    // create a bitmap with a rect, used for the "src" image
    // 矩形右下角留有透明间隙
    static Bitmap makeSrc(int w, int h) {
        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bm);
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

        p.setColor(0xFF66AAFF);
        c.drawRect(0, 0, w * 19 / 20, h * 19 / 20, p);
        return bm;
    }

}

每一种图层混合模式的具体含义都添加了注释,下面来看执行结果,一共是一十八种,我们可以根据图片显示的结果对照着代码及注释进行理解:


我就不一 一进行介绍了,简单说几个吧,比如CLEAR模式就是整个源图像都被清空了,也就是第一个小图所示的效果,SRC模式就是源图像显示在了上层,黄色的3/4圆的部分能显示是因为它和源图像并未相交,即第二个小图所示的效果。对于图层混合模式的具体算法我们没必要深究,但是每一种图层的混合模式的具体含义我们应该要掌握其用法,以便于今后遇到这种实际问题场景我们知道具体该使用哪一种混合模式。

1.4、刮刮卡案例实操

public class XfermodeEraserView extends View {

    private Paint  mPaint;
    private Bitmap mDstBmp, mSrcBmp, mTxtBmp;
    private Path mPath;

    public XfermodeEraserView(Context context) {
        this(context, null);
    }

    public XfermodeEraserView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public XfermodeEraserView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        //初始化画笔
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(80);

        //禁用硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);

        //初始化图片对象
        mTxtBmp = BitmapFactory.decodeResource(getResources(), R.drawable.jarchie);
        mSrcBmp = BitmapFactory.decodeResource(getResources(), R.drawable.ying);
        mDstBmp = Bitmap.createBitmap(mSrcBmp.getWidth(), mSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);

        //路径(贝塞尔曲线)
        mPath = new Path();
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //绘制刮奖结果
        canvas.drawBitmap(mTxtBmp, 0, 0, mPaint);

        //使用离屏绘制
        int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);

        //先将路径绘制到 bitmap上
        Canvas dstCanvas = new Canvas(mDstBmp);
        dstCanvas.drawPath(mPath, mPaint);

        //绘制 目标图像
        canvas.drawBitmap(mDstBmp, 0, 0, mPaint);
        //设置 模式 为 SRC_OUT, 擦橡皮区域为交集区域需要清掉像素
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
        //绘制源图像
        canvas.drawBitmap(mSrcBmp, 0, 0, mPaint);

        mPaint.setXfermode(null);

        canvas.restoreToCount(layerID);
    }

    private float mEventX, mEventY;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        super.onTouchEvent(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mEventX = event.getX();
                mEventY = event.getY();
                mPath.moveTo(mEventX, mEventY);
                break;
            case MotionEvent.ACTION_MOVE:
                float endX = (event.getX() - mEventX) / 2 + mEventX;
                float endY = (event.getY() - mEventY) / 2 + mEventY;
                //画二阶贝塞尔曲线
                mPath.quadTo(mEventX, mEventY, endX, endY);
                mEventX = event.getX();
                mEventY = event.getY();
                break;
        }
        invalidate();
        return true; //消费事件
    }

}

效果这里我做了一张动图,同样使用的是我家颖宝的美照,如下图所示:

既然是刮刮卡肯定要用到手势触摸,这里手势触摸部分实际上就是绘制二阶的贝赛尔曲线,然后调用invalidate()刷新,会调用onDraw()方法进行实际的绘制,绘制方法中首先绘制目标图像,然后进行离屏绘制,离屏绘制的时候会将二阶贝赛尔曲线绘制到一张Bitmap上,也就是目标Bitmap,接着绘制这张Bitmap,然后设置图层混合模式为SRC_OUT,即交集区域图像会被清除,接着绘制源图像,最后清除混合模式。

二、滤镜效果

2.1、LightingColorFilter滤镜

它可以模仿光照效果,参数有mul和add,具体用法见下图:


下面我们来实际演示一下它的使用,新建一个ColorFilterView类,初始化画笔和一张Bitmap对象,然后我们先来加载原图,代码如下所示,根据上面图片中的计算公式可以看出,想要加载原图,只需要让mul=1,add.X=0即可,所以RGB都为f,add=0:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //原始图片效果
        LightingColorFilter lighting = new LightingColorFilter(0xffffff, 0x000000);
        mPaint.setColorFilter(lighting);
        canvas.drawBitmap(mBitmap, 0, 0, mPaint);
    }

效果如下图所示:

接着我们来修改代码,把红色去除掉,修改mul中的R项为0:

//红色去除掉
LightingColorFilter lighting = new LightingColorFilter(0x00ffff,0x000000);
mPaint.setColorFilter(lighting);
canvas.drawBitmap(mBitmap, 0,0, mPaint);

结果如下图所示:

接着我们再来修改一下代码,让红色更突出,修改add的R值:

//红色更亮
LightingColorFilter lighting = new LightingColorFilter(0xffffff, 0xa70000);
mPaint.setColorFilter(lighting);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);

效果如下图所示:

2.2、PorterDuffColorFilter滤镜

指定一种颜色和一个图层进行混合:

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN);
mPaint.setColorFilter(porterDuffColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);

效果如下,变为了暗红效果:

2.3、ColorMatrixColorFilter滤镜

它的构造方法需要传入一个数组,数组长度是4行5列共20位代表一个颜色矩阵,当改变颜色矩阵对应数组中元素的值就可以改变图片中颜色的比例,四行分别依次决定红绿蓝三色和透明度,第5列表示每个颜色值的偏移量,当你想要颜色值更倾向于某种颜色时,可以对应调整第5列的值

关于色彩矩阵,这里给出部分参考资料:


改变颜色值一般有两种方式,第一种就是修改偏移量,第二种就是修改对应颜色的系数,也就是矩阵中斜角线上的值,下面简单来实践一下:

float[] colorMatrix = {
                1,0,0,0,500,   //red
                0,1,0,0,0,   //green
                0,0,1,0,0,   //blue
                0,0,0,1,0    //alpha
        };
        mColorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        mPaint.setColorFilter(mColorMatrixColorFilter);
        canvas.drawBitmap(mBitmap, 100, 0, mPaint);

修改了红色的偏移量,很明显图片会红色加重,来看一下效果:

接着来修改一下矩阵中的绿色系数:

float[] colorMatrix = {
                1,0,0,0,0,   //red
                0,2,0,0,0,   //green
                0,0,1,0,0,   //blue
                0,0,0,1,0    //alpha
        };

实际效果如下,绿色更加突出:

接下来我们来看一下几种常见的滤镜效果,这里简单列举几种,分别是“黑白”、“怀旧”和“胶片”:

    // 黑白
    public static final float colormatrix_heibai[] = {
            0.8f, 1.6f, 0.2f, 0, -163.9f,
            0.8f, 1.6f, 0.2f, 0, -163.9f,
            0.8f, 1.6f, 0.2f, 0, -163.9f,
            0, 0, 0, 1.0f, 0};
    // 怀旧
    public static final float colormatrix_huajiu[] = {
            0.2f, 0.5f, 0.1f, 0, 40.8f,
            0.2f, 0.5f, 0.1f, 0, 40.8f,
            0.2f, 0.5f, 0.1f, 0, 40.8f,
            0, 0, 0, 1, 0};
    // 胶片
    public static final float colormatrix_fanse[] = {
            -1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
            0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
            0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
            0.0f, 0.0f, 0.0f, 1.0f, 0.0f};

效果如下:

    

还有一些其它的常见效果,后面我整理好了会把完整代码放出来,网上应该也能找到。

2.4、ColorMatrix

除了使用矩阵数组来实现滤镜效果之外,还可以通过ColorMatrix这个类的一些API来实现:

  • setScale(float rScale, float gScale, float bScale,float aScale):分别表示红绿蓝透明度四个分量通道对应的系数,全部传1表示原图
  • setSaturation(float sat):饱和度调节0-五色彩,1-默认效果,>1饱和度加强
  • setRotate(int axis, float degrees):色调调节,第一个参数表示颜色通道,第二个参数表示角度,第一个值如果是0表示修改的是红色,1是绿色,2是蓝色,内部同样是操作矩阵数组,进行sin和cos的运算


下面通过代码来实际看一下具体的用法:

首先我们来调节亮度,这里将蓝色调亮:

ColorMatrix cm = new ColorMatrix();
//亮度调节
cm.setScale(1,1,2,1);
mColorMatrixColorFilter = new ColorMatrixColorFilter(cm);
mPaint.setColorFilter(mColorMatrixColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);

效果如下图所示:

接着我们来调节饱和度,让图片显得更加饱满:

cm.setSaturation(2);

效果就是这样的:

最后我们将绿色旋转30°角来看一下是什么效果:

cm.setRotate(1, 30);

效果如下:

好了,到这里今天的内容就算是结束了,我感觉不多不少刚刚好,主要是得巩固加深印象,自定义View这玩意,一段时间不写就很容易忘记!

OK,各位老铁,再会!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值