Paint API - ColorFilter (color filter) (1/3)


Introduction to this section:

We learned about MaskFilter (mask) in the previous section, and used its two subclasses BlurMaskFilter to create a blur effect, EmbossMaskFilter We have created the relief effect, and in this section we are going to learn about another API - ColorFilter (color filter), which is the same as MaskFilter. We do not use this class directly, but use three subclasses of this class:

Color Matrix Color Filter: ColorMatrixColorFilter

Lighting Color FilterLightingColorFilter

Mixed color filter filterPorterDuffColorFilter

This section Let’s learn how to use the first ColorMatrixColorFilter. Open the documentation of ColorMatrixColorFilter.

1.png

roughly says: transform the color through a 4 x 5 color matrix, and you can modify the saturation of the pixels. , Convert YUV to RGB, etc.! The ColorMatrix in the construction method is the color matrix and is also the core of our learning. Let me explain it one by one!

PS: ColorMatrix API documentation


1. Popularization of relevant common sense:

RGBA model:

I don’t know if you have heard of RGBA, but you know yellow, green and blue, the three primary colors of light, and RAGB adds a transparency based on this! R(Red), G(Green), B(Blue), A(Alpha transparency); In addition Three things to do with paint The primary colors are distinguished. The most obvious difference is that the green of the three primary colors of light is replaced by yellow among the three primary colors of the pigment! Just know it, If you are interested, you can Baidu~

Some nouns:

  • Hue/Hue——The color transmitted by the object

2.png

  • Saturation——The purity of the color, from 0 (gray) to 100 % (saturation) to describe

3.png

  • #Brightness/Lightness— —The relative lightness and darkness of the color

4.png


2.Interpretation of ColorMatrix

As the title says, color matrix (4 *5), we can modify the values ​​in the matrix to achieve black and white photos, yellowed old photos, high contrast and other effects! The hand-shred color matrix explanation diagram is as follows:

5.jpg

I don’t know if you understand the above picture. If you have studied advanced mathematics, you must be familiar with it. It is nothing more than the cross product of matrices. If you haven’t studied it before, It doesn't matter The calculation method is the one in the lower right corner, take each row of the color matrix to * each column of the color matrix component!

A very typical example, to compare the results before and after processing, we can also let a certain color value * a constant , for example, let the third row (blue) Multiply by 2, and the effect becomes bluish. Of course, we must write code to verify the above results!


3. Write code to verify the role of ColorMatrix

Here is a bad example, an ImageView, 4 * 5 EditText, and a reset button and a generate button, Let’s take a look at the renderings:

The original image is in order, yellowish, greenish, reddish, high contrast, hue transformation, and yellow retro

6.png7.png

8.png9.png

10.png11.png

13.png

#Next we will write the code to complete the above Effect:

Code implementation:

First is the layout file

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp">

    <ImageView
        android:id="@+id/img_show"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2" />

    <GridLayout
        android:id="@+id/gp_matrix"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="3"
        android:columnCount="5"
        android:rowCount="4"></GridLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_reset"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="重置" />
        <Button
            android:id="@+id/btn_Change"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="变换" />
    </LinearLayout></LinearLayout>

Then is

MainActivity.java:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private ImageView img_show;
    private GridLayout gp_matrix;
    private Button btn_reset;
    private Button btn_Change;
    private Bitmap mBitmap;
    private int mEtWidth, mEtHeight;
    private EditText[] mEts = new EditText[20];
    private float[] mColorMatrix = new float[20];
    private Context mContext;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = MainActivity.this;
        bindViews();


        gp_matrix.post(new Runnable() {
            @Override
            public void run() {
                mEtWidth = gp_matrix.getWidth() / 5;
                mEtHeight = gp_matrix.getHeight() / 4;
                //添加5 * 4个EditText
                for (int i = 0; i < 20; i++) {
                    EditText editText = new EditText(mContext);
                    mEts[i] = editText;
                    gp_matrix.addView(editText, mEtWidth, mEtHeight);
                }
                initMatrix();
            }
        });
    }

    private void bindViews() {
        img_show = (ImageView) findViewById(R.id.img_show);
        gp_matrix = (GridLayout) findViewById(R.id.gp_matrix);
        btn_reset = (Button) findViewById(R.id.btn_reset);
        btn_Change = (Button) findViewById(R.id.btn_Change);

        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi);
        img_show.setImageBitmap(mBitmap);

        btn_reset.setOnClickListener(this);
        btn_Change.setOnClickListener(this);
    }


    //定义一个初始化颜色矩阵的方法
    private void initMatrix() {
        for (int i = 0; i < 20; i++) {
            if (i % 6 == 0) {
                mEts[i].setText(String.valueOf(1));
            } else {
                mEts[i].setText(String.valueOf(0));
            }
        }
    }

    //定义一个获取矩阵值得方法
    private void getMatrix() {
        for (int i = 0; i < 20; i++) {
            mColorMatrix[i] = Float.valueOf(mEts[i].getText().toString());
        }
    }

    //根据颜色矩阵的值来处理图片
    private void setImageMatrix() {
        Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(),
                Bitmap.Config.ARGB_8888);
        android.graphics.ColorMatrix colorMatrix = new android.graphics.ColorMatrix();
        colorMatrix.set(mColorMatrix);

        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
        canvas.drawBitmap(mBitmap, 0, 0, paint);
        img_show.setImageBitmap(bmp);
    }
    
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_Change:
                getMatrix();
                setImageMatrix();
                break;
            case R.id.btn_reset:
                initMatrix();
                getMatrix();
                setImageMatrix();
                break;
        }
    }
}

The code is very simple, just load the layout, and then stuff 5 * 4 EditText into the GridLayout, used here The post() method is to ensure that the length and width of the GridLayout are obtained after loading. Otherwise, the length and width of the GridLayout will be obtained. When it is wide, the value cannot be obtained! Then three methods are defined, initial matrix, obtaining matrix value, and according to Processing images using matrix values~ Isn’t it very simple~

But at this point you may have some questions

:

" Is this the only way we can process images? Modifying the color matrix must be troublesome. Who would remember the matrix? What value should be filled in? Is there a simpler way to process images? "

Answer: There must be. We can look back at the documentation and we can find several very common methods:

setRotate(int axis, float degrees): Set the hue

setSaturation(float sat): Set saturation

setScale(float rScale, float gScale, float bScale, float aScale): Set the brightness

Let’s write an example to try these three methods!


4. Use the three methods of ColorMatrix to process the image

Run the rendering :

12.gif

##Code implementation:

First we write a tool class for image processing. We pass in Bitmap, hue, saturation and brightness. After processing, return The processed image:

ImageHelper.java:

/**
 * Created by Jay on 2015/10/28 0028.
 */
public class ImageHelper {
    /**
     * 该方法用来处理图像,根据色调,饱和度,亮度来调节
     *
     * @param bm:要处理的图像
     * @param hue:色调
     * @param saturation:饱和度
     * @param lum:亮度
     *
     */
    public static Bitmap handleImageEffect(Bitmap bm, float hue, float saturation, float lum) {
        Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

        ColorMatrix hueMatrix = new ColorMatrix();
        hueMatrix.setRotate(0, hue);    //0代表R,红色
        hueMatrix.setRotate(1, hue);    //1代表G,绿色
        hueMatrix.setRotate(2, hue);    //2代表B,蓝色

        ColorMatrix saturationMatrix = new ColorMatrix();
        saturationMatrix.setSaturation(saturation);

        ColorMatrix lumMatrix = new ColorMatrix();
        lumMatrix.setScale(lum, lum, lum, 1);


        ColorMatrix imageMatrix = new ColorMatrix();
        imageMatrix.postConcat(hueMatrix);
        imageMatrix.postConcat(saturationMatrix);
        imageMatrix.postConcat(lumMatrix);

        paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
        canvas.drawBitmap(bm, 0, 0, paint);

        return bmp;
    }
}

Next we draw out the layout,

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="5dp">

    <ImageView
        android:id="@+id/img_meizi"
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="24dp"
        android:layout_marginTop="24dp" />


    <TextView
        android:id="@+id/txt_hue"
        android:layout_width="wrap_content"
        android:layout_height="32dp"
        android:layout_below="@id/img_meizi"
        android:gravity="center"
        android:text="色调    :"
        android:textSize="18sp" />

    <SeekBar
        android:id="@+id/sb_hue"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_below="@id/img_meizi"
        android:layout_toRightOf="@id/txt_hue" />


    <TextView
        android:id="@+id/txt_saturation"
        android:layout_width="wrap_content"
        android:layout_height="32dp"
        android:layout_below="@id/txt_hue"
        android:gravity="center"
        android:text="饱和度:"
        android:textSize="18sp" />

    <SeekBar
        android:id="@+id/sb_saturation"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_below="@id/sb_hue"
        android:layout_toRightOf="@id/txt_saturation" />


    <TextView
        android:id="@+id/txt_lun"
        android:layout_width="wrap_content"
        android:layout_height="32dp"
        android:layout_below="@id/txt_saturation"
        android:gravity="center"
        android:text="亮度    :"
        android:textSize="18sp" />

    <SeekBar
        android:id="@+id/sb_lum"
        android:layout_width="match_parent"
        android:layout_height="32dp"
        android:layout_below="@id/sb_saturation"
        android:layout_toRightOf="@id/txt_lun" /></RelativeLayout>

Finally: Our

MainActivity.java:

public class MainActivity extends AppCompatActivity implements SeekBar.OnSeekBarChangeListener{

    private ImageView img_meizi;
    private SeekBar sb_hue;
    private SeekBar sb_saturation;
    private SeekBar sb_lum;
    private final static int MAX_VALUE = 255;
    private final static int MID_VALUE = 127;
    private float mHue = 0.0f;
    private float mStauration = 1.0f;
    private float mLum = 1.0f;
    private Bitmap mBitmap;



    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi);
        bindViews();
    }

    private void bindViews() {
        img_meizi = (ImageView) findViewById(R.id.img_meizi);
        sb_hue = (SeekBar) findViewById(R.id.sb_hue);
        sb_saturation = (SeekBar) findViewById(R.id.sb_saturation);
        sb_lum = (SeekBar) findViewById(R.id.sb_lum);

        img_meizi.setImageBitmap(mBitmap);
        sb_hue.setMax(MAX_VALUE);
        sb_hue.setProgress(MID_VALUE);
        sb_saturation.setMax(MAX_VALUE);
        sb_saturation.setProgress(MID_VALUE);
        sb_lum.setMax(MAX_VALUE);
        sb_lum.setProgress(MID_VALUE);

        sb_hue.setOnSeekBarChangeListener(this);
        sb_saturation.setOnSeekBarChangeListener(this);
        sb_lum.setOnSeekBarChangeListener(this);

    }

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        switch (seekBar.getId()) {
            case R.id.sb_hue:
                mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
                break;
            case R.id.sb_saturation:
                mStauration = progress * 1.0F / MID_VALUE;
                break;
            case R.id.sb_lum:
                mLum = progress * 1.0F / MID_VALUE;
                break;
        }
        img_meizi.setImageBitmap(ImageHelper.handleImageEffect(mBitmap, mHue, mStauration, mLum));
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {}

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

The code is also very simple, so I won’t explain it here~


5. Download the code sample in this section:

ColorMatrixDemo.zip

ColorMatrixDemo2.zip


Summary of this section:

Okay Yes, this section introduces to you the first

ColorMatrixColorFilter in ColorFilter, the color matrix filter In fact, the core is ColorMatrix. We can use this class to process images by setting the value of the 4*5 matrix ourselves, or directly calling ColorMatrix provides us with methods to set hue, saturation, and brightness! Image processing is nothing more than this, and there is another way to modify it In the form of pixels, we will talk about it later. The content of this section refers to the doctor (Xu Yisheng)’s Muke.com video: Android image processing-creating beautiful pictures starts from it, don’t want to read text You can watch the video, it’s quite good~