Android高级渲染 - 用ColorMatrix做美图小软件

隔了一年多去看这篇文章,发现很多地方会有问题,比如7.0后拍照的问题,当时用的6.0的系统,所以7.0以上的系统一定会崩溃。还有就是加完滤镜之后无法修改模糊效果。
源码等可能暂时不维护,存在不少问题。

1、效果展示

用ColorMatrix可以调节图片颜色比例,做到滤镜的效果。这里用ColorMatrix基本使用写了一个小的APP,源码地址:https://github.com/AxeChen/ColorFilter
先上一波效果图:

效果图一

效果图二

利用安卓自带的API即可完成一些简单的图片滤镜,调整图片的对比度亮度等属性。

2、实现思路

主要思路:选择相册图片或者拍照获取图片,然后通过修改ColorMatrix来实现各种不同的滤镜效果或者通过修改BlurMaskFilter来实现边框的虚化等效果。

3、主要技术

3.1、主要技术点

(1)颜色矩阵ColorMatrix的应用(滤镜,调整亮度等主要用到的技术)。
(3)BlurMaskFilter边框虚化。
(4)6.0相机相册权限处理。
(5)Bitmap的一些处理:压缩,生成新的bitmap等。
(6)其他的一些技术点:一些控件的简单使用,例如(RecyclerView,Toobar)、分享图片等。

3.2 颜色矩阵ColorMatrix

应用ColorMatrix可以调节图片颜色比例,实现一些滤镜,调节图片亮度、对比度等效果。

3.2.1 ColorMatrix简单介绍

在Android中使用颜色矩阵ColorMatrix,来处理图像的色彩效果。对于图像的每个像素点,都有一个颜色矩阵A用来保存颜色的RGBA值。在处理图像是将颜色矩阵和颜色矩阵分量C相乘。

颜色矩阵

颜色处理

在安卓中使用一个一维数组表示颜色矩阵:

    public static final float[] src = {
            1f, 0, 0, 0, 0,    
            0, 1f, 0, 0, 0,
            0, 0, 1f, 0, 0,
            0, 0, 0, 1f, 0,
    };

如果对颜色矩阵基本知识有些难理解。可以不纠结这些基本的知识,只抓住重点:只要改变颜色矩阵对应的数组中元素的值,就可以改变图片中颜色的比例!

对应一维数组控制的颜色属性:
第一行决定新的颜色值中的红色R。
第二行决定新的颜色值中的绿色G。
第三行决定新的颜色值中的蓝色B。
第四行决定新的颜色值中的透明度A。
第五列表示每个颜色的偏移量。

因此只要合理改变这个数组中不同元素的值就可以实现不同颜色效果了。

3.2.1 ColorMatrix使用

直接上关键代码:

public class FilterImageView extends FilterView {

     // ... 省略若干代码

    private Paint paint;
    private ColorMatrixColorFilter colorMatrixColorFilter;
    private ColorMatrix colorMatrix;


    @Override
    protected void onDraw(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        if (colorMatrixColorFilter != null) {
               paint.setColorFilter(colorMatrixColorFilter);
         }
        canvas.drawBitmap(bitmap, null, rectF, paint);
        canvas.save();
    }

    @Override
    public void setFloat(float[] floats) {
        colorMatrix = new ColorMatrix();
        colorMatrix.set(floats);
        colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        invalidate();
    }


   // ... 省略若干代码
}

代码步骤:
1、初始化ColorMatrixColorFilter 和 ColorMatrix 。
2、在onDraw()方法中通过paint.setColorFilter()方法将ColorMatrixColorFilter设置给paint。
3通过 canvas.drawBitmap()将Bitmap画出来即可。(不仅仅是drawBitmap只要drawXX方法中传入了设置过ColorMatrixColorFilter的paint都可实现颜色改变的效果)。

3.2.2 修改颜色矩阵对应的数组

改变颜色矩阵对应的数组的数组元素值,可以展示不同的颜色效果。
一些简单的修改:
1、改变颜色的偏移量的值(下面的r、g、b、a);
2、改变对应 RGBA(下面的R、G、B、A) 值的系数。
如下说明:

 public static final float[] src = {
            R, 0, 0, 0, r,          
            0, G, 0, 0, g,          
            0, 0, B, 0, b,          
            0, 0, 0, A, a,          
    };

以上:
R、G、B的值可以增大或者变小。
A的范围为0f-1f(可以设置比1f更大,但是效果和1f是一样的)
而偏移量r、g、b、a的范围是0f-255f(可以设置比255f更大,但是效果和255f是一样的)
改变R、G、B、A或者r、g、b、a的值都可以改变图片的颜色的比例和透明度。
这些值越大,图片中对应颜色所占的比例就越大或者越不透明。
例如:

    public static final float[] green = {
            1f, 0, 0, 0, 0,
            0, 1.2f, 0, 0, 0,
            0, 0, 1f, 0, 0,
            0, 0, 0, 1f, 0,
    };

以上把G值调整成1.2f 。就能增加图片中绿色的比例。

绿色加强

一些复杂的修改:
通过调节数组中多个元素来做出不同的效果。
例如灰度效果:

    /**
     * 灰度效果
     */
    public static final float[] gray = {
            0.213f, 0.715f, 0.072f, 0, 0,
            0.213f, 0.715f, 0.072f, 0, 0,
            0.213f, 0.715f, 0.072f, 0, 0,
            0, 0, 0, 1, 0,
    };
灰度效果

修改数组中多个元素的值来改变图片中颜色的比例,从而达到灰度的效果。

3.2.2 调节亮度和对比度

ColorMatrix提供了多个方法来修改图片,例如:

直接将R、G、B、A传入,修改颜色所占比重:

public void setScale(float rScale, float gScale, float bScale,
                         float aScale) 

调节图片的对比度:

public void setSaturation(float sat)

颜色旋转(可以做色调效果):

public void setRotate(int axis, float degrees)

修改对比度和亮度代码如下:


     // ... 省略若干代码

    private Paint paint;
    private ColorMatrixColorFilter colorMatrixColorFilter;
    private ColorMatrix colorMatrix;


    @Override
    protected void onDraw(Canvas canvas) {
        paint.reset();
        paint.setAntiAlias(true);
        if (colorMatrixColorFilter != null) {
               paint.setColorFilter(colorMatrixColorFilter);
         }
        canvas.drawBitmap(bitmap, null, rectF, paint);
        canvas.save();
    }
    /**
     * @param light 0f -2f
     */
    public void changeLight(float light) {
        colorMatrix = new ColorMatrix();
        colorMatrix.setScale(light, light, light, 1f);
        colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        invalidate();
    }

    /**
     * @param saturaction 0f - 2f
     */
    public void changeSaturation(float saturaction) {
        colorMatrix = new ColorMatrix();
        colorMatrix.setSaturation(saturaction);
        colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        invalidate();
    }

其中修改亮度的修改,就是将R、G、B三种颜色的比例不断增大。

调节对比度和亮度

3.3 BlurMaskFilter虚化边框

MaskFilter

MaskFilter有两个子类:
BlurMaskFilter :可实现模糊效果。
EmbossMaskFilter:可实现凸起效果。
这里用到了BlurMaskFilter 做虚化边框的效果:
BlurMaskFilter 的构造函数:

public BlurMaskFilter(float radius, Blur style)

radius:模糊的宽度。
style:枚举变量,提供不同的效果:

public enum Blur {
        /**
         * Blur inside and outside the original border.
         */
        NORMAL(0),

        /**
         * Draw solid inside the border, blur outside.
         */
        SOLID(1),

        /**
         * Draw nothing inside the border, blur outside.
         */
        OUTER(2),

        /**
         * Blur inside the border, draw nothing outside.
         */
        INNER(3);
        
        Blur(int value) {
            native_int = value;
        }
        final int native_int;
    }

使用模糊效果会用到几个关键类:BlurMaskFilter、Paint 、canvas。
代码步骤:
1、初始化BlurMaskFilter。
2、然后通过paint.setMaskFilter()法将BlurMaskFilter设置给paint。
3、通过canvas.drawBitmap()方法即可实现图片的模糊效果。(不仅仅是drawBitmap只要drawXX方法中传入了设置过BlurMaskFilter的paint都可实现模糊效果)


虚化边框效果

不过BlurMaskFilter遇到几个问题:

1、 虚化的颜色由背景由图片的边缘颜色决定的,如果图片边缘的颜色为白色,那么虚化部分的颜色是白色的。
2、 虚化宽度的最大值并没有找到。这个类调用的是native的方法,我不知道怎么去处理。
3、保存图片之后有点失真。

3.4 其他需要注意的问题

3.4.1、选择相册图片和使用相机的6.0权限处理

第一步:AndroidManifest.xml文件中申明权限:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />

动态申请权限:

    //申请相机权限
    protected boolean requestCameraPermiss() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_CAMERA);
            return false;
        }
        return true;
    }

    //申请读取文件权限
    protected boolean requestAlbumPermiss() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_CAMERA);
            return false;
        }
        return true;

    }
3.4.2、Bitmap的压缩、生成新的bitmap等
3.4.2.1 生成新的Bitmap

如果需要通过一个Bitmap生成一个新的Bitmap,需要将Bitmap画在一个新的Bitmap画布上。看代码注释。

    @Override
    public Bitmap getChangeBitmap() {
        //初始化一个Bitmap
        Bitmap bitmapAltered = Bitmap.createBitmap((int) rectF.right, (int) rectF.bottom, bitmap.getConfig());
        //将初始化的Bitmap当作画布
        Canvas canvas = new Canvas(bitmapAltered);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColorFilter(colorMatrixColorFilter);
        if (drawType == DRAW_TYPE_MASK) {
            if (blurMaskFilter != null) {
                paint.setMaskFilter(blurMaskFilter);
            }
        }
      //将一个bitmap画在画布上(注:这里的bitmap是一个全局的变量,它就是一个普通的Bitmap)
        canvas.drawBitmap(bitmap, null, rectF, paint);
     //返回第一个Bitmap即可
        return bitmapAltered;
    }
3.4.2.2 图片压缩的方法:

防止图片过大导致OOM,这里通过 BitmapFactory.Options 对图片进行压缩之后再展示。

  /**
     * 读取图片,按照缩放比保持长宽比例返回bitmap对象
     * <p>
     *
     * @param path  图片全路径
     * @param scale 缩放比例(1到10, 为2时,长和宽均缩放至原来的2分之1,为3时缩放至3分之1,以此类推)
     * @return Bitmap
     */
    public synchronized static Bitmap readBitmap(String path, int scale) {
        try {
            BitmapFactory.Options options = new BitmapFactory.Options();
            // 设置缩放比例
            options.inSampleSize = scale;
            // 设置为false,解析Bitmap对象加入到内存中
            options.inJustDecodeBounds = false;
            // 设置内存不足时,比bitmap对象可以被回收
            options.inPurgeable = true;
            options.inInputShareable = true;
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            Bitmap bitmap = BitmapFactory.decodeFile(path, options);
            return bitmap;
        } catch (Exception e) {
            return null;
        }
    }
3.4.3、图片的分享

这里通过安卓自带的方法来做分享:

    Intent sendIntent = new Intent();
    sendIntent.setAction(Intent.ACTION_SEND);
    sendIntent.putExtra(Intent.EXTRA_STREAM, imageUrl);
    sendIntent.setType("image/");
    startActivity(Intent.createChooser(sendIntent, "分享图片"));
分享图片

3.5、推荐的链接

colorMatrx :
//www.greatytc.com/p/9a44d04f39fc
//www.greatytc.com/p/a870fb9684d5
6.0权限:
//www.greatytc.com/p/5e05691d9c76
http://blog.csdn.net/yanzhenjie1003/article/details/52503533/
分享图片:
//www.greatytc.com/p/f790833e669d
//www.greatytc.com/p/25c84ed9046d

源码地址:https://github.com/AxeChen/ColorFilter

原创不易,如果大佬对这篇文章感兴趣,还望点个赞,给点鼓励!

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

推荐阅读更多精彩内容