美文网首页
Android Paint系列之滤镜效果

Android Paint系列之滤镜效果

作者: Samlss | 来源:发表于2018-06-22 17:32 被阅读114次
    图像结构

    我们首先了解一下图像的构成,例如一张PNG图片:
    图片文件头由位固定的字节来描述的,以便向外说明这个文件是一个PNG文件。

    十进制数
    137 80 78 71 13 10 26 10
    十六进制数
    89 50 4E 47 0D 0A 1A 0A
    

    用UE打开一个PNG文件的内容为:


    image.png

    可以看到都为十六进制的数据,我们不知道这些数据是什么,假定第一行的数据是这个PNG的标志,第2-8行可能记录的是解析规则等信息,再之后的则为数据,那么

    在一个图像文件中,总体包含的数据分为两部分:
    1.文件的标志信息;
    2.文件的数据信息;

    标志的信息主要用来识别该文件是不是PNG文件,而数据块则储存了图片的图像信息;

    PNG定义了两种类型的数据块,一种是称为关键数据块(critical chunk),这是必需的数据块,另一种叫做辅助数据块(ancillary chunks),这是可选的数据块。关键数据块定义了4个标准数据块,每个PNG文件都必须包含它们,PNG读写软件也都必须要支持这些数据块。虽然PNG文件规范没有要求PNG编译码器对可选数据块进行编码和译码,但规范提倡支持可选数据块。
    每个数据块都由4个域组成:


    image.png
    关键数据块
    image.png
    IHDR

    文件头数据块IHDR(header chunk):它包含有PNG文件中存储的图像数据的基本信息,并要作为第一个数据块出现在PNG数据流中,而且一个PNG数据流中只能有一个文件头数据块。

    image.png
    颜色通道

    保存图像颜色信息的通道称为颜色通道。

    RGB color有合成意义的变化(color channel),就是人有合成意义地(只用肢体或使用工具)将红蓝绿三种(实色料或颜色)合成出(一个或多个实色料空间显示、阻挡显示、隐藏、消失,从合成后可以赋予意义、认为无意义、没有赋予的意义、没有发现的意义、没有认同的意义等多种意义;一种颜色或多种颜色显示、阻挡显示、隐藏、消失,从合成后可以赋予意义、认为无意义、没有赋予的意义、没有发现的意义、没有认同的意义等多种意义)。

    每个图像都有一个或多个颜色通道,图像中默认的颜色通道数取决于其颜色模式,即一个图像的颜色模式将决定其颜色通道的数量。例如,CMYK图像默认有4个通道,分别为青色、洋红、黄色、黑色。在默认情况下,位图模式、灰度、双色调和索引颜色图像只有一个通道。RGB和Lab图像有3个通道,CMYK图像有4个通道。

    颜色模式

    颜色模式,是将某种颜色表现为数字形式的模型,或者说是一种记录图像颜色的方式。分为:RGB模式、CMYK模式、HSB模式、Lab颜色模式、位图模式、灰度模式、索引颜色模式、双色调模式和多通道模式。

    总结

    颜色通道取决于颜色模式。

    通俗点说,我们要显示图片中的色彩的时候,用数字表示,例如RGB模式,例如R255-G0-B0用来显示当前像素的色彩,在显示的时候为红色,RGB为红绿蓝三种色彩进行混合,其对应颜色数值的大小范围为0~255, 即每个点都是由颜色模式所决定的颜色通道(色彩数值)混合形成我们想要的颜色,而要实现滤镜效果,其实就是对颜色通道值进行处理,在其原本的通道数值进行更改,达到更改图像色彩效果的目的,这就是我们所谓的滤镜。

    颜色矩阵

    在安卓中,颜色模式为RGBA,即在RGB模式上加了一个A(alpha)透明值,即为一个四通道的模式。

    在安卓中有一个类叫ColorMatrix(颜色矩阵),其用一个4x5矩阵来进行描述,用于转换位图的颜色和alpha分量。
    矩阵可以作为单个数组传递,并作如下处理:

    ColorMatrix类的注解为:
    
    [ a, b, c, d, e,
      f, g, h, i, j,
      k, l, m, n, o,
      p, q, r, s, t ]
    
    当应用到颜色 [R,G,B,A],结果颜色计算为:
    
    R' = a*R + b*G + c*B + d*A + e;
    G' = f*R + g*G + h*B + i*A + j;
    B' = k*R + l*G + m*B + n*A + o;
    A' = p*R + q*G + r*B + s*A + t;
    
    由此产生的 [R,A,G,B]的值在0~255之间
    
    

    我们可以理解为
    安卓中的颜色矩阵是一个4×5的数字矩阵:


    image.png

    会以一维数组的形式来存储:(float[]类型)

    [ a, b, c, d, e,
      f, g, h, i, j,
      k, l, m, n, o,
      p, q, r, s, t ]
    
    即
    new float[]{
        a, b, c, d, e,
        f, g, h, i, j,
        k, l, m, n, o,
        p, q, r, s, t 
        };
    
    矩阵运算
    image.png

    当矩阵A的列数等于矩阵B的行数时,A与B可以相乘。
    矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。
    乘积C的第m行第n列的元素等于矩阵A的第m行的元素与矩阵B的第n列对应元素乘积之和。

    颜色矩阵运算

    我们可通过ColorMatrix类生成指定的颜色矩阵A,


    image.png

    与颜色通道的原始色彩矩阵进行矩阵乘法运算。

    这里有一个色彩矩阵分量C,代表着我们颜色通道的原始色彩:


    image.png

    矩阵R则代表通过矩阵乘法运算AC而得到的新的颜色:


    image.png
    R' = aR + bG + cB + dA + e;
    G' = fR + gG + hB + iA + j;
    B' = kR + lG + mB + nA + o;
    A' = pR + qG + rB + sA + t;
    

    我们这里可以看到,最后会加上最后一列的e, j, o, t,这是因为在安卓中(这个是安卓的颜色矩阵规则),前4列组成的才是四阶颜色矩阵:

    image.png

    而最后一列则是代表增量值,即4阶矩阵相乘后再加上这个增量,从而得到最终的颜色值。

    颜色矩阵中,

    • 第一行决定红色值
    • 第一行决定绿色值
    • 第一行决定蓝色值
    • 第一行决定透明值

    下面这个为原始矩阵,即对原色值不做任何改变:


    image.png
    下面我们通过代码来进行实现

    首先我们先定义主页面

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
            <LinearLayout
                android:gravity="center"
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
                <Button
                    android:id="@+id/btn_filter"
                    android:text="滤镜"
                    android:textSize="22sp"
                    android:onClick="onStartFilterActivity"
                    android:textAllCaps="false"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
    
                <Button
                    android:id="@+id/btn_custom"
                    android:layout_marginTop="10dp"
                    android:text="自定义"
                    android:textSize="22sp"
                    android:onClick="onStartCustomActivity"
                    android:textAllCaps="false"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            </LinearLayout>
    
    </android.support.constraint.ConstraintLayout>
    

    MainActivity

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void onStartFilterActivity(View view) {
            startActivity(new Intent(this, FilterActivity.class));
        }
    
        public void onStartCustomActivity(View view) {
            startActivity(new Intent(this, CustomFilterActivity.class));
        }
    }
    
    效果图

    实现两种功能,一个是采用固定的滤镜方式,一个是采用自定义的滤镜方式。

    固定的滤镜方式

    布局

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <TextView
                android:id="@+id/tv_tip1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="10dp"
                android:text="原图"
                android:layout_marginLeft="10dp"
                android:textColor="@android:color/holo_red_dark"
                android:textSize="22sp" />
    
            <ImageView
                android:id="@+id/tv_src"
                android:layout_gravity="center_horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/pic_xxx_01" />
    
            <TextView
                android:id="@+id/tv_tip2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginTop="10dp"
                android:text="效果图"
                android:textColor="@android:color/holo_red_dark"
                android:textSize="22sp" />
    
            <com.iigo.paint.FilterView
                android:id="@+id/fv_filter"
                app:img_id="@mipmap/pic_xxx_01"
                android:layout_gravity="center_horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
            <LinearLayout
                android:gravity="center"
                android:layout_marginTop="10dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <Button
                    android:id="@+id/btn_draw_normal"
                    android:textAllCaps="false"
                    android:text="原图"
                    android:onClick="onDrawNormal"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
    
                <Button
                    android:id="@+id/btn_draw_negative"
                    android:textAllCaps="false"
                    android:text="底片"
                    android:onClick="onDrawNegative"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            </LinearLayout>
    
            <LinearLayout
                android:gravity="center"
                android:layout_marginTop="10dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <Button
                    android:id="@+id/btn_draw_retro"
                    android:textAllCaps="false"
                    android:text="复古"
                    android:onClick="onDrawRetro"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
    
                <Button
                    android:id="@+id/btn_draw_fair"
                    android:textAllCaps="false"
                    android:text="美颜"
                    android:onClick="onDrawFair"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            </LinearLayout>
    
            <LinearLayout
                android:gravity="center"
                android:layout_marginTop="10dp"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="horizontal">
    
                <Button
                    android:id="@+id/btn_draw_bandw"
                    android:textAllCaps="false"
                    android:text="黑白"
                    android:onClick="onDrawBAndW"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
    
                <Button
                    android:id="@+id/btn_draw_change"
                    android:textAllCaps="false"
                    android:text="发色"
                    android:onClick="onDrawChange"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" />
            </LinearLayout>
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>
    

    FilterActivity

    public class FilterActivity extends AppCompatActivity {
        private FilterView filterView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_filter);
            filterView = findViewById(R.id.fv_filter);
        }
    
        public void onDrawNormal(View view){
            filterView.drawNormal();
        }
    
        public void onDrawNegative(View view){
            filterView.drawNegative();
        }
    
        public void onDrawRetro(View view){
            filterView.drawRetro();
        }
    
        public void onDrawFair(View view){
            filterView.drawFair();
        }
    
        public void onDrawBAndW(View view){
            filterView.drawBlackAndWhite();
        }
    
        public void onDrawChange(View view){
            filterView.drawChange();
        }
    }
    
    效果图

    点击对应效果的按钮进行效果查看

    自定义的滤镜方式

    布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <com.iigo.paint.FilterView
            android:id="@+id/fv_filter"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            app:img_id="@mipmap/pic_xxx_01" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:orientation="vertical">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:orientation="horizontal">
    
                <EditText
                    android:id="@+id/et_11"
                    android:text="1"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_12"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_13"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_14"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_15"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:orientation="horizontal">
    
                <EditText
                    android:id="@+id/et_21"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_22"
                    android:text="1"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_23"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_24"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_25"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
            </LinearLayout>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:orientation="horizontal">
    
                <EditText
                    android:id="@+id/et_31"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_32"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_33"
                    android:text="1"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_34"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
    
                <EditText
                    android:id="@+id/et_35"
                    android:text="0"
                    android:inputType="numberSigned|numberDecimal"
                    android:layout_width="match_parent"
                    android:layout_height="50dp"
                    android:layout_weight="1"
                    android:gravity="center" />
            </LinearLayout>
    
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="15dp"
                    android:layout_marginRight="15dp"
                    android:orientation="horizontal">
    
                    <EditText
                        android:id="@+id/et_41"
                        android:text="0"
                        android:inputType="numberSigned|numberDecimal"
                        android:layout_width="match_parent"
                        android:layout_height="50dp"
                        android:layout_weight="1"
                        android:gravity="center" />
    
                    <EditText
                        android:id="@+id/et_42"
                        android:text="0"
                        android:inputType="numberSigned|numberDecimal"
                        android:layout_width="match_parent"
                        android:layout_height="50dp"
                        android:layout_weight="1"
                        android:gravity="center" />
    
                    <EditText
                        android:id="@+id/et_43"
                        android:text="0"
                        android:inputType="numberSigned|numberDecimal"
                        android:layout_width="match_parent"
                        android:layout_height="50dp"
                        android:layout_weight="1"
                        android:gravity="center" />
    
                    <EditText
                        android:id="@+id/et_44"
                        android:text="1"
                        android:inputType="numberSigned|numberDecimal"
                        android:layout_width="match_parent"
                        android:layout_height="50dp"
                        android:layout_weight="1"
                        android:gravity="center" />
    
                    <EditText
                        android:id="@+id/et_45"
                        android:text="0"
                        android:inputType="numberSigned|numberDecimal"
                        android:layout_width="match_parent"
                        android:layout_height="50dp"
                        android:layout_weight="1"
                        android:gravity="center" />
                </LinearLayout>
    
            <Button
                android:id="@+id/btn_draw"
                android:layout_marginTop="10dp"
                android:text="执行"
                android:onClick="onDraw"
                android:layout_gravity="center_horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
            <Button
                android:id="@+id/btn_recover"
                android:layout_marginTop="10dp"
                android:text="还原为最原始数据"
                android:onClick="onRecover"
                android:layout_gravity="center_horizontal"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
    
        </LinearLayout>
    </LinearLayout>
    

    CustomeFilterActivity代码为

    public class CustomFilterActivity extends AppCompatActivity {
        /**
         * <pre>
         *  [ a, b, c, d, e,
         *    f, g, h, i, j,
         *    k, l, m, n, o,
         *    p, q, r, s, t ]</pre>
         *
         */
        private EditText et11;
        private EditText et12;
        private EditText et13;
        private EditText et14;
        private EditText et15;
    
        private EditText et21;
        private EditText et22;
        private EditText et23;
        private EditText et24;
        private EditText et25;
    
        private EditText et31;
        private EditText et32;
        private EditText et33;
        private EditText et34;
        private EditText et35;
    
        private EditText et41;
        private EditText et42;
        private EditText et43;
        private EditText et44;
        private EditText et45;
    
        private FilterView filterView;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom);
    
            initViews();
        }
    
        private void initViews() {
            et11 = findViewById(R.id.et_11);
            et12 = findViewById(R.id.et_12);
            et13 = findViewById(R.id.et_13);
            et14 = findViewById(R.id.et_14);
            et15 = findViewById(R.id.et_15);
    
            et21 = findViewById(R.id.et_21);
            et22 = findViewById(R.id.et_22);
            et23 = findViewById(R.id.et_23);
            et24 = findViewById(R.id.et_24);
            et25 = findViewById(R.id.et_25);
    
            et31 = findViewById(R.id.et_31);
            et32 = findViewById(R.id.et_32);
            et33 = findViewById(R.id.et_33);
            et34 = findViewById(R.id.et_34);
            et35 = findViewById(R.id.et_35);
    
            et41 = findViewById(R.id.et_41);
            et42 = findViewById(R.id.et_42);
            et43 = findViewById(R.id.et_43);
            et44 = findViewById(R.id.et_44);
            et45 = findViewById(R.id.et_45);
    
            et11.addTextChangedListener(new MyTextWatcher(et11, "1"));
            et12.addTextChangedListener(new MyTextWatcher(et12, "0"));
            et13.addTextChangedListener(new MyTextWatcher(et13, "0"));
            et14.addTextChangedListener(new MyTextWatcher(et14, "0"));
            et15.addTextChangedListener(new MyTextWatcher(et15, "0"));
    
            et21.addTextChangedListener(new MyTextWatcher(et21, "0"));
            et22.addTextChangedListener(new MyTextWatcher(et22, "1"));
            et23.addTextChangedListener(new MyTextWatcher(et23, "0"));
            et24.addTextChangedListener(new MyTextWatcher(et24, "0"));
            et25.addTextChangedListener(new MyTextWatcher(et25, "0"));
    
            et31.addTextChangedListener(new MyTextWatcher(et31, "0"));
            et32.addTextChangedListener(new MyTextWatcher(et32, "0"));
            et33.addTextChangedListener(new MyTextWatcher(et33, "1"));
            et34.addTextChangedListener(new MyTextWatcher(et34, "0"));
            et35.addTextChangedListener(new MyTextWatcher(et35, "0"));
    
            et41.addTextChangedListener(new MyTextWatcher(et41, "0"));
            et42.addTextChangedListener(new MyTextWatcher(et42, "0"));
            et43.addTextChangedListener(new MyTextWatcher(et43, "0"));
            et44.addTextChangedListener(new MyTextWatcher(et44, "1"));
            et45.addTextChangedListener(new MyTextWatcher(et45, "0"));
    
            filterView = findViewById(R.id.fv_filter);
        }
    
        public void onRecover(View view){
            et11.setText("1");
            et12.setText("0");
            et13.setText("0");
            et14.setText("0");
            et15.setText("0");
    
            et21.setText("0");
            et22.setText("1");
            et23.setText("0");
            et24.setText("0");
            et25.setText("0");
    
            et31.setText("0");
            et32.setText("0");
            et33.setText("1");
            et34.setText("0");
            et35.setText("0");
    
            et41.setText("0");
            et42.setText("0");
            et43.setText("0");
            et44.setText("1");
            et45.setText("0");
        }
    
        public void onDraw(View view){
            //若EditText为空,默认值为0
    
            int  n11 = Integer.valueOf(et11.getText().toString());
            int  n12 = Integer.valueOf(et12.getText().toString());
            int  n13 = Integer.valueOf(et13.getText().toString());
            int  n14 = Integer.valueOf(et14.getText().toString());
            int  n15 = Integer.valueOf(et15.getText().toString());
    
            int  n21 = Integer.valueOf(et21.getText().toString());
            int  n22 = Integer.valueOf(et22.getText().toString());
            int  n23 = Integer.valueOf(et23.getText().toString());
            int  n24 = Integer.valueOf(et24.getText().toString());
            int  n25 = Integer.valueOf(et25.getText().toString());
    
            int  n31 = Integer.valueOf(et31.getText().toString());
            int  n32 = Integer.valueOf(et32.getText().toString());
            int  n33 = Integer.valueOf(et33.getText().toString());
            int  n34 = Integer.valueOf(et34.getText().toString());
            int  n35 = Integer.valueOf(et35.getText().toString());
    
            int  n41 = Integer.valueOf(et41.getText().toString());
            int  n42 = Integer.valueOf(et42.getText().toString());
            int  n43 = Integer.valueOf(et43.getText().toString());
            int  n44 = Integer.valueOf(et44.getText().toString());
            int  n45 = Integer.valueOf(et45.getText().toString());
    
            ColorMatrix colorMatrix = new ColorMatrix(new float[]{
                    n11, n12, n13, n14, n15,
                    n21, n22, n23, n24, n25,
                    n31, n32, n33, n34, n35,
                    n41, n42, n43, n44, n45,
            });
    
            filterView.drawCustom(colorMatrix);
        }
    
        private class MyTextWatcher implements TextWatcher {
            private EditText editText;
            private String defaultValue;
    
            public MyTextWatcher(EditText editText, String defaultValue){
                this.editText = editText;
                this.defaultValue = defaultValue;
            }
    
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    
            }
    
            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                if (TextUtils.isEmpty(s.toString())){
                    editText.setText(defaultValue);
                }
            }
    
            @Override
            public void afterTextChanged(Editable s) {
    
            }
        }
    }
    
    

    点击执行按钮进行绘制,点击还原按钮还原为原始颜色矩阵数据。


    效果图

    最后自定义FilterView的代码为:

    public class FilterView extends View {
        private int imageId = -1;
        private Paint paint;
        private Bitmap bitmap;
        private ColorMatrix cColorMatrix;
    
        public enum TYPE{
            NORMAL, //正常图片
            NEGATIVE, //底片
            RETRO, //复古
            FAIR, //美颜
            BAW,//黑白(black and white)
            CHANGE, //发色,改变颜色
            CUSTOM,//自定义
        }
    
        private TYPE currentType = TYPE.NORMAL;
    
        public FilterView(Context context, int imageId) {
            super(context);
    
            this.imageId = imageId;
            init(null);
        }
    
        public FilterView(Context context) {
            super(context);
    
            init(null);
        }
    
        public FilterView(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
    
            init(attrs);
        }
    
    
        public FilterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
    
            init(attrs);
        }
    
        @SuppressLint("NewApi")
        public FilterView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
    
            init(attrs);
        }
    
        private void init(AttributeSet attrs) {
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    
            if (attrs == null){
                return;
            }
    
            TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.FilterView);
    
            int count = a.getIndexCount();
            for (int i = 0; i < count; i++){
                int attr = a.getIndex(i);
                switch (attr){
                    case R.styleable.FilterView_img_id:
                        imageId = a.getResourceId(attr, 0);
                        break;
                }
            }
            a.recycle();
    
            if (imageId == -1){
                return;
            }
    
            try{
                bitmap = BitmapFactory.decodeResource(getResources(), imageId);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (bitmap == null){
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                return;
            }
    
            //根据图片大小设置当前view的大小
            super.onMeasure(MeasureSpec.makeMeasureSpec(bitmap.getWidth(), MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(bitmap.getHeight(), MeasureSpec.EXACTLY));
    
            //若要实现图片的自适应view的大小,可自行实现..
            //...
            //
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            if (bitmap == null){
                return;
            }
    
            setLayerType(View.LAYER_TYPE_SOFTWARE,null);
    
            paint.reset();
    
            ColorMatrix colorMatrix = null;
            RectF rectF = new RectF(0,0, bitmap.getWidth(), bitmap.getHeight());
            paint.reset();
    
            switch (currentType){
                case NORMAL:
                    break;
    
                case NEGATIVE:
                    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,
                    });
                    break;
    
                case RETRO:
                    colorMatrix = new ColorMatrix(new float[]{
                            1/2f,1/2f,1/2f,0,0,
                            1/3f, 1/3f,1/3f,0,0,
                            1/4f,1/4f,1/4f,0,0,
                            0,0,0,1,0,
                    });
                    break;
    
                case FAIR:
                    colorMatrix = new ColorMatrix(new float[]{
                            1.25f, 0,0,0,0,
                            0,1.25f,0,0,0,
                            0,0,1.25f,0,0,
                            0,0,0,1.25f,0,
                    });
                    break;
    
                case BAW:
                    colorMatrix = new ColorMatrix(new float[]{
                            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,
                    });
                    break;
    
                case CHANGE:
                    colorMatrix = new ColorMatrix(new float[]{
                            1, 0, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 1, 0, 0, 0,
                            0, 0, 0, 1, 0,
                    });
                    break;
    
                case CUSTOM:
                    colorMatrix = cColorMatrix;
                    break;
    
                    default: break;
            }
    
            if (colorMatrix != null){
                paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
            }
    
            canvas.drawBitmap(bitmap,null, rectF, paint);
        }
    
        private void performDraw(){
            if (bitmap == null){
                return;
            }
    
            invalidate();
        }
    
        /**
         * 显示正常图片
         * */
        public void drawNormal(){
            currentType = TYPE.NORMAL;
            performDraw();
        }
    
        /**
         * 显示底片效果
         * */
        public void drawNegative(){
            currentType = TYPE.NEGATIVE;
            performDraw();
        }
    
        /**
         * 显示复古效果
         * */
        public void drawRetro(){
            currentType = TYPE.RETRO;
            performDraw();
        }
    
        /**
         * 显示美颜效果
         * */
        public void drawFair(){
            currentType = TYPE.FAIR;
            performDraw();
        }
    
        /**
         * 显示黑白效果
         * */
        public void drawBlackAndWhite(){
            currentType = TYPE.BAW;
            performDraw();
        }
    
        /**
         * 显示发色效果,这里为红色和绿色交换
         * */
        public void drawChange(){
            currentType = TYPE.CHANGE;
            performDraw();
        }
    
        /**
         * 显示自定义效果
         *
         * @param colorMatrix 指定的滤镜效果
         * */
        public void drawCustom(ColorMatrix colorMatrix){
            currentType = TYPE.CUSTOM;
            cColorMatrix = colorMatrix;
            performDraw();
        }
    }
    
    滤镜实现

    我们只需要调用接口

    colorMatrix = new ColorMatrix(new float[]{
                            1, 0, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 1, 0, 0, 0,
                            0, 0, 0, 1, 0,
                    });
    paint.setColorFilter(new ColorMatrixColorFilter(colorMartrix));
    

    便可实现滤镜效果

    我们将R即红色增加1倍,即

    colorMatrix = new ColorMatrix(new float[]{
                            2, 0, 0, 0, 0,
                            0, 1, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 0, 0, 1, 0,
                    });
    

    即可看到图片变为红色,


    效果图

    将G即绿色增加1倍,即

    colorMatrix = new ColorMatrix(new float[]{
                            1, 0, 0, 0, 0,
                            0, 2, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 0, 0, 1, 0,
                    });
    

    即可看到图片变为绿色,


    效果图

    将R即红色和G即绿色同时增加1倍,即

    colorMatrix = new ColorMatrix(new float[]{
                            2, 0, 0, 0, 0,
                            0, 2, 0, 0, 0,
                            0, 0, 1, 0, 0,
                            0, 0, 0, 1, 0,
                    });
    

    即可看到图片颜色混合变为黄色,


    效果图

    我们还可以指定类似于底片的颜色,即

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

    底片效果其实就是反相效果,将RGB的值取反,例如RGB的值分别为r,g,b,反相的意思就是取
    255 - r, 255 - g, 255 - b的值。

    效果图

    黑白照片效果,即

    colorMatrix = new ColorMatrix(new float[]{
                            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,
                    });
    

    是将我们的三通道变为单通道的灰度模式,去色原理:只要把R G B 三通道的色彩信息设置成一样,那么图像就会变成灰色, 同时为了保证图像亮度不变,同一个通道里的R+G+B =1。


    image.png

    demo地址为:https://github.com/samlss/Paint

    相关文章

      网友评论

          本文标题:Android Paint系列之滤镜效果

          本文链接:https://www.haomeiwen.com/subject/miahyftx.html