Lesson 153. Drawing. ColorFilter, ColorMatrix

Lesson 153. Drawing. ColorFilter, ColorMatrix


In this lesson:

– change the color of the brush with ColorFilter

First, a little clarification about the color. I think everyone is aware that in Android we usually get color by combining three colors – red, green and blue. It’s called RGB. You can also add transparency (Alpha) to this set. ARGB comes out.

We specify the values ​​of these components from 0 to 255. For example, the screen background that I use in all lessons is formed like this (80, 102, 204, 255). Accordingly, transparency is 80, red is 102, green is 204, and blue is 255.

If we set all RGB components to 0, we get black. If everything is 255, then we get white.

Often, values ​​are given not in decimal form, but in hexadecimal. That is, from 00 to FF, instead of 0 and 255. In this case, it is usually written not AARRGGBB but ARGB. For example, red: FFFF0000. If we break into AARRGGBB components, we get AA = FF, RR = FF, GG = 00, BB = 00.

There are int-constants for specifying color. For example, Color.RED. How to get such int-constant from ARGB components? There are methods Color.rgb and Color.argb, where you pass RGB or ARGB components, and the method will return you the int-value of the color.

And the Color.parseColor method will allow you to get an int value from the hexadecimal form: #RRGGBB or #AARRGGBB.

The ColorFilter heirs allow us to influence the color used in drawing objects.

ColorMatrixColorFilter

Let’s start with ColorMatrixColorFilter. This type of filter affects the color using the 4×5 value of the matrix we give it.

The algorithm is a little non-trivial if you are not familiar with algebra matrices.

We define a matrix of this kind

rR, rG, rB, rA, rT
gR, gG, gB, gA, gT
bR, bG, bB, bA, bT
aR, aG, aB, aA, aT

4 lines, each with 5 values.

And let’s have some kind of ARGB color to which the filter will be applied. The filter takes the current color values ​​and subtracts new ones using the matrix. For example, he would consider the new value of red (Rn) as:

Rn = R * rR + G * rG + B * rB + A * rA + rT

That is, the values ​​of the initial color (R, G, B, A) are multiplied by the first 4 values ​​(rR, rG, rB, rA) of the first row of the matrix and the fifth value (rT) of the same row is added.

Of course, we do not have to code anything like this, the filter itself calculates everything. We are only required to provide the matrix. Here I just show how everything works inside.

The new value of green (Gn) is obtained similarly using the original RGBA and the second row of the matrix.

Gn = R * gR + G * gG + B * gB + A * gA + gT

Blue (Bn) and transparency (An) are the third and fourth lines, respectively

Bn = R * bR + G * bG + B * bB + A * bA + bT

An = R * aR + G * aG + B * aB + A * aA + aT

Let’s look at this in the examples.

Let’s create a project:

Project name: P1531_ColorFilter
Build Target: Android 2.3.3
Application name: ColorFilter
Package name: ru.startandroid.develop.p1531colorfilter
Create Activity: MainActivity

MainActivity.java:

package ru.startandroid.develop.p1531colorfilter;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new DrawView(this));
    }
    
    class DrawView extends View {
      
      Paint paint;
      Bitmap bitmap;
      Rect rect;
      
        float[] cmData = new float[]{
                1, 0, 0, 0, 0, 
                0, 1, 0, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 1, 0};
        
        ColorMatrix cm;
        ColorFilter filter;
        Bitmap icon;
      
      public DrawView(Context context) {
        super(context);
        
        rect  = new Rect(0,0,200,200);
        
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        
        icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
        
        cm = new ColorMatrix(cmData);
        filter = new ColorMatrixColorFilter(cm);
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(100, 100);
        drawObjects(canvas);
        
        paint.setColorFilter(filter);
        canvas.translate(0, 300);
        drawObjects(canvas);
      }
      
      void drawObjects(Canvas canvas) {
        canvas.save();
        
        paint.setColor(Color.RED);
        canvas.drawRect(rect, paint);
        
        paint.setColor(Color.GREEN);
        canvas.translate(220, 0);
        canvas.drawRect(rect, paint);
        
        paint.setColor(Color.BLUE);
        canvas.translate(220, 0);
        canvas.drawRect(rect, paint);
        
        paint.setColor(Color.WHITE);
        canvas.translate(220, 0);
        canvas.drawRect(rect, paint);
        
        canvas.translate(220, 0);
        canvas.drawBitmap(icon, null, rect, paint);
        canvas.restore();
      }
      
    }
}

cmData – float array, here we write values ​​for the matrix.

Variable cm – this is the matrix – ColorMatrix. We give it the cmData array.

We specify this matrix when creating a filter filter. The filter now has a matrix and he knows what color transformations he will need to make.

In the method drawObjects draw 4 squares – red, green, blue, white, and display the android icon. We will test color changes on these objects. We use paint brush.

IN onDraw we draw objects with the drawObjects method, then for the paint brush we apply the filter with the setColorFilter method and again output the objects. Because paint is used to draw objects, applying a filter to the brush will affect the colors of the painted figures.

Now our matrix looks like this

1, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0

If we take the RGBA and apply the matrix, we get

Rn = R * 1 + G * 0 + B * 0 + A * 0 + 0 = R

Gn = R * 0 + G * 1 + B * 0 + A * 0 + 0 = G

Bn = R * 0 + G * 0 + B * 1 + A * 0 + 0 = B

An = R * 0 + G * 0 + B * 0 + A * 1 + 0 = A

The new values ​​are equal to the output. That is, the matrix is ​​configured so that the RGBA values ​​of any color will not change at all. Let’s see that. run the program

result:

The colors on the top (original) and bottom (after applying the filter) are the same. The filter though applied, but did not change anything in the values ​​of the colors.

We change the matrix:

        float[] cmData = new float[]{
                1, 0, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 1, 0};

result:

Now obviously something has changed. Let’s see exactly what

It was red with RGBA = (255,0,0,255). Apply filter:

Rn = 255 * 1 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 255

Gn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0

Bn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0

An = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 1 + 0 = 255

The new RGBA values ​​were as follows (255,0,0,255). That is, nothing has changed for red. It is also visible in the screenshot, red square in place.

But green is black, we see why

RGBA green = (0,255,0,255). We apply a filter.

Rn = 0 * 1 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0

Gn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0

Bn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0

An = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 1 + 0 = 255

New RGBA value of green = (0,0,0,255), which is black. In the same way, blue (0,0,255,255) became black.

And white (255,255,255,255) after conversion

Rn = 255 * 1 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 255

Gn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0

Bn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0

An = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 1 + 0 = 255

became so (255,0,0,255) – that is, red. The screenshot confirms this.

That is, by applying a filter, we have zeroed the values ​​of blue (B) and green (G) for all colors. Only the red (R) component was left. This is visible on the android icon.

But here it is important to understand one thing. We didn’t paint everything red. We completely removed the green and blue, and left the red as it was.

That is, in the red square, the value of red was 255. That remained.

In the blue and green squares the value of red was 0. That is how it remained.

The android icon shows that the red is heterogeneous, somewhere brighter, somewhere darker. That is, initially the icon was painted in different shades, which used different RGB combinations. And we in these combinations removed G and B, left only R. That is, where R was, for example, 50 remained 50. Where was 150 – remained 150. And G and B are now everywhere = 0.

Let’s adjust the matrix so that red is maximal everywhere. Regardless of the initial value. And the blue and green will be reset again.

        float[] cmData = new float[]{
                0, 0, 0, 0, 255, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 1, 0};

We removed factor 1 from the first number of the first line. That is, now the new value of R will not be equal to the old value of R multiplied by 1. Now it will be multiplied by 0. But the last number of the first line = 255. It will be added to zero and we will get a full red in place of the original shades of red.

Blue and green will also turn red. Because G and B values ​​we have zeroed in them, and R will be 255, that is (255,0,0,255).

result

Now let’s change the matrix to zero only blue. The red and green components will remain unchanged.

        float[] cmData = new float[]{
                1, 0, 0, 0, 0, 
                0, 1, 0, 0, 0, 
                0, 0, 0, 0, 0, 
                0, 0, 0, 1, 0};

result

Removing from the blue (0,0,255,255) blue component we got black (0,0,0,255).

And having removed from the white (255,255,255,255) blue component we got the yellow (255,255,0,255).

That is

red + green + blue = white

white – blue = red + green = yellow.

We changed the color components (RGB), now let’s try to change the transparency (A). Recall that if A = 255, then the color is completely opaque. If A = 0, then the color is completely invisible.

        float[] cmData = new float[]{
                1, 0, 0, 0, 0, 
                0, 1, 0, 0, 0, 
                0, 0, 1, 0, 0, 
                0, 0, 0, 0.3f, 0};

We set a factor of 0.3 to calculate the new transparency value. That is An = A * 0.3. That is, all colors will become transparent by 30% of the current level.

result

A couple more examples of matrices I found on the internet:

Black and white

        float[] cmData = new float[]{
                0.3f, 0.59f, 0.11f, 0, 0, 
                0.3f, 0.59f, 0.11f, 0, 0, 
                0.3f, 0.59f, 0.11f, 0, 0, 
                0, 0, 0, 1, 0,};

result:

color inversion

        float[] cmData = new float[]{
                -1, 0, 0, 0, 255, 
                0, -1, 0, 0, 255, 
                0, 0, -1, 0, 255, 
                0, 0, 0, 1, 0,};

result:

The matrix also has several methods that allow you to adjust its value.

setScale – allows us to specify which values ​​should be multiplied by the RGBA color value.

rewrite the constructor DrawView:

    public DrawView(Context context) {
        super(context);
        
        rect  = new Rect(0,0,200,200);
        
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
        
        icon = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
        
        cm = new ColorMatrix();
        cm.setScale(1, 1, 0, 0.5f);
        filter = new ColorMatrixColorFilter(cm);
        
      }

When creating ColorMatrix we did not use an array, and the matrix was created as we had in the original example, that is, it does not change anything. But now we adjust it.

We use the setScale method. Its parameters include the coefficients for the R, G, B and A components, respectively.

That is, the values ​​1, 1, 0, 0.5f adjust the matrix so that

Rn = R * 1;

Gn = G * 1;

Bn = B * 0;

An = A * 0.5f

That is, the red and green will remain the same, the blue will be zeroed, and the transparency will be 0.5 from the old value.

result

It seems that in the fourth example, only transparency is different.

There is also an interesting setSaturation method. Responsible for color saturation. Takes the input from 0 to 1.

If you set 0, we get a black and white picture.

If for example 0.5f, then it will be half saturation

The default value of this method = 1, all the colors will be as they are.

There is also a setRotate method. This is a rotation of one of the RGB components by the specified number of degrees. I honestly do not understand it and cannot explain its meaning. Perhaps it will be clear to those who actively use graphic editors and work with color.

LightingColorFilter

LightingColorFilter accepts two colors

mul – RGB components of this color will be multiplied by the corresponding RGB components of the original color. Moreover, the components of the mul should be considered as numbers in the range from 0 to 1. That is, if in the mul component, for example, R is 255, then the R-component of the original color will remain unchanged (multiplying by 1). If in the mul component R = 0, then the R component will be nullified (multiplied by 0). If the mul component is R = 127, then the R component will be reduced twice (multiplication by 0.5)

add – RGB components of this color will be added to the corresponding RGB components of the original color, and the result trimmed to 255.

All in all, I have to say that all these color games are dark woods for me. I do not really understand the practical benefits of these mechanisms. But now we have a clue how they work and how to use them. It may someday be useful when working with graphics.

There is another third ColorFilter class: PorterDuffColorFilter. Let’s talk about it in one of the following lessons.

In the next lesson:

– parse PorterDuff modes using PorterDuffXfermode




Discuss in the forum [2 replies]

Leave a Comment