How to Use and work with porterduxfermode for Canvas plotting in Android

Source: Internet
Author: User

How to Use and work with porterduxfermode for Canvas plotting in Android
Overview

Classandroid.graphics.PorterDuffXfermodeInherited fromandroid.graphics.Xfermode. When you use the Canvas in Android to draw images, you can use PorterDuffXfermode to mix the pixels of the drawn images with the pixels at the corresponding position in the Canvas according to certain rules to form a new pixel value, update the final pixel color value in the Canvas to create many interesting effects. When using porterduxfermode, you need to pass it as a parameterPaint.setXfermode(Xfermode xfermode)In this way, Android uses the imported porterduxfermode when using the paint brush for plotting. If you do not want to use Xfermode, You can executePaint.setXfermode(null).

In the porterduxfermode class, Porter and Duff are two people. In 1984, these two people wrote a paper named Compositing Digital Images. Click here to view the paper. We know that a pixel is composed of four components: RGBA. This paper discusses how to mix pixels of different digital images, this paper proposes a variety of pixel mixing modes. If you have done image processing and development, you will have a better understanding of it. This technology is similar to the Alpha hybrid technology in OpenGL.

Porterduxfermode supports the following 10 pixel-color mixing modes: CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, SRC_ATOP, DST_ATOP, XOR, DARKEN, LIGHTEN, MULTIPLY, and SCREEN.

Below we will analyze several code snippets to study the usage and working principle of porterduduxfermode.

Example 1

Before demonstrating how to use porterduduxfermode, let's take a look at the following example. The Code is as follows:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // sets the background color canvas. drawARGB (255,139,197,186); int canvasWidth = canvas. getWidth (); int r = canvasWidth/3; // draw a yellow circle paint. setColor (0xFFFFCC44); canvas. drawCircle (r, paint); // draw a blue rectangle paint. setColor (0xFF66AAFF); canvas. drawRect (r, r, r * 2.7f, r * 2.7f, paint );}

We have rewritten the onDraw method of the View. First, we set the background color of the View to green, then draw a yellow circle, and then draw a blue rectangle. The effect is as follows:

The above demonstration shows the normal Drawing Process of Canvas, without using porterduduxfermode. Let's briefly analyze the above Code:

First, we call canvas. the drawARGB (255,139,197,186) method draws the entire Canvas into a color. After this code is executed, the ARGB color of the color values of all pixels on the canvas is (255,139,197,186 ), since the alpha component of the pixel is 255 rather than 0, all pixels are not transparent at this time.

When we run canvas. after drawCircle (r, paint), Android uses a yellow paint brush to draw a yellow circle at the position of the circle, at this time, the ARGB color of all the pixel color values in the circle is 0xFFFFCC44, and then replace these yellow pixels with the pixels whose color value ARGB is (255,139,197,186) in the same position in the Canvas, in this way, the yellow circle is drawn to the Canvas.

When we run canvas. after drawRect (r, r, r * 2.7f, r * 2.7f, paint), Android uses a blue paint brush to draw a blue rectangle at the position of the rectangle, in this case, the ARGB color of all the pixel color values in the entire rectangle is 0xFF66AAFF, and the blue pixels are used to replace the pixels in the same position in the Canvas, in this way, the pixels in the lower right corner of the yellow circle and other background color pixels are replaced by blue pixels, so that the blue rectangle is drawn to the Canvas.

Although the above process is simple, understanding the specific pixel update process when drawing a Canvas is the basis for truly understanding the working principle of porterduxfermode.

Example 2

Next we will use porterduxfermode to modify the above Code. The modified code is as follows:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // sets the background color canvas. drawARGB (255,139,197,186); int canvasWidth = canvas. getWidth (); int r = canvasWidth/3; // The yellow circle is painted normally. setColor (0xFFFFCC44); canvas. drawCircle (r, paint); // use CLEAR as the porterduxfermode to draw the blue rectangle paint. setXfermode (new porterduxfermode (PorterDuff. mode. CLEAR); paint. setColor (0xFF66AAFF); canvas. drawRect (r, r, r * 2.7f, r * 2.7f, paint); // remove the paint brush from Xfermode paint. setXfermode (null );}

The effect is as follows:

Next we will analyze the above Code:

First, we call the canvas. drawARGB (255,139,197,186) method to draw the entire Canvas into a color. At this time, all pixels are not transparent.

Then, a yellow circle is drawn to the canvas by calling Canvas. drawCircle (r, paint.

Then, run the code paint. setXfermode (new porterduxfermode (PorterDuff. Mode. CLEAR) to set the PorterDuff Mode of the paint brush to CLEAR.

Call the canvas. drawRect (r, r, r * 2.7f, r * 2.7f, paint) method to draw a blue rectangle, but a White Rectangle appears on the interface.

After the painting is complete, we call paint. setXfermode (null) to remove the paint brush from Xfermode.

The reason for the appearance of white rectangles is analyzed in detail. Generally, we call canvas. when drawXXX () method is used, a Paint object is passed in. Android will first check whether Xfermode is set for the Paint object. If Xfermode is not set, then, the drawn image will directly overwrite the original pixel at the corresponding position of the Canvas. If Xfermode is set, the pixel color at the corresponding position of the Canvas will be updated according to the specific rules of Xfermode. In this example, when the canvas. drawCirlce () method is executed, the Xfermode object is not set for the Paint brush Paint. Therefore, the drawn yellow circle directly overwrites the pixels on the Canvas. When we call canvas. when drawRect () is used to draw a rectangle, The Xfermode value of Paint has been set to PorterDuff. mode. CLEAR. At this time, Android first draws such a rectangle in the memory. The pixels in the drawn image are called source pixels (src ), the pixels in the rectangle corresponding to the position of the drawn rectangle in the Canvas are called the destination pixel (dst ). The four ARGB components of the source pixel and the four ARGB components of the target pixel at the same position on the Canvas are calculated according to the rules defined by Xfermode to form the final ARGB value, then, the final ARGB value is used to increase the ARGB value of the new target pixel. In this example, the Xfermode is PorterDuff. mode. CLEAR, this rule is relatively simple and crude, and requires that the four ARGB components of the target pixel be set to 0, that is, (0, 0, 0), that is, transparent color, so we use canvas. drawRect () draws a transparent rectangle on the Canvas. Because the background of the Activity screen is white, a white rectangle is displayed here.

Example 3

Let's modify the code in the second example. Place the Code related to the Circle and the rectangle to the canvas. saveLayer () and canvas. restoreToCount (). The Code is as follows:

@ Override protected void onDraw (Canvas canvas) {super. onDraw (canvas); // sets the background color canvas. drawARGB (255,139,197,186); int canvasWidth = canvas. getWidth (); int canvasHeight = canvas. getHeight (); int layerId = canvas. saveLayer (0, 0, canvasWidth, canvasHeight, null, Canvas. ALL_SAVE_FLAG); int r = canvasWidth/3; // normally draws a yellow circle paint. setColor (0xFFFFCC44); canvas. drawCircle (r, paint); // use CLEAR as the porterduxfermode to draw the blue rectangle paint. setXfermode (new porterduxfermode (PorterDuff. mode. CLEAR); paint. setColor (0xFF66AAFF); canvas. drawRect (r, r, r * 2.7f, r * 2.7f, paint); // remove the paint brush from Xfermode paint. setXfermode (null); canvas. restoreToCount (layerId );}

The effect is as follows:

The following code is analyzed:

First, we call the canvas. drawARGB (255,139,197,186) method to draw the entire Canvas into a color. At this time, all pixels are not transparent.

Then we place the main code between canvas. saveLayer () and canvas. restoreToCount (), and I indent the code. When we write the code in canvas. saveXXX () and canvas. we recommend that you indent the code in restoreXXX () to facilitate code readability. Of course, code indentation is not mandatory and does not affect the running effect.

The layer in a canvas drawing must be described as follows:

Canvas supports layer rendering. canvas has a layer by default. When we call various drawXXX () Methods of canvas, in fact, all things are drawn to the default layer of canvas.

We can also use canvas. saveLayer () creates a new layer. The new layer is placed in the upper part of the default canvas layer. When canvas is executed. after saveLayer (), all our painting operations are drawn to the layer we created, rather than the default canvas layer.

The ARGB values of all pixels of the layer produced by the canvas. saveLayer () method are (,), that is, the layer generated by the canvas. saveLayer () method is completely transparent at the beginning.

Canvas. the saveLayer () method returns an int value, which indicates the layer ID. After drawing the new layer, you can call canvas. restoreToCount (layer) or canvas. restore () draws the layer to the default layer of the canvas. This completes the painting of a layer.

Then you may feel very strange. We just put the code for drawing the circle and the rectangle into the canvas. saveLayer () and canvas. why is the White Rectangle no longer displayed as in example 2 between restoreToCount?

When analyzing the sample code 2, we realized that the target colors of the final rectangle were reset to transparent colors (,), and finally the Activity background color was white, so it is displayed as a white rectangle. In this example, after the new layer is drawn, the target color of the rectangular area is actually reset to transparent color, in this way, only 3/4 of the circle of the newly created layer is not transparent, and the remaining pixels are transparent. Then we call canvas. restoreToCount () draws the layer to the Canvas. When a new layer is drawn to a Canvas, Android uses the pixel color on the entire layer to update the pixel color at the corresponding position of the Canvas, which is not a simple replacement, instead, the Canvas and the new layer are mixed with Alpha. Because our layer only has two pixels: completely transparent and completely opaque, there is no part of transparent pixels, and the four components of the completely transparent pixel color value are 0, therefore, this example simplifies the Alpha-mixing rule between the Canvas and the new layer, specifically:

If the Alpha component of a pixel on the new layer is 255, that is, the pixel is completely opaque, Android uses the ARGB value of the pixel as the color value of the pixel at the corresponding position of the Canvas. If the Alpha component of a pixel on the new layer is 0, that is, the pixel is completely transparent. In this example, if the Alpha component is 0, the RGB component is also 0, android retains the color value of pixels at the corresponding position of the Canvas.

In this way, when the new layer is drawn to the Canvas, the pixels in the completely opaque 3/4 yellow circle completely overwrite the pixels at the corresponding position of the Canvas, the pixel ARGB of the rectangle area drawn on the new layer is (,), so the background color of the rectangle area on the Canvas remains unchanged, in this way, no White Rectangle will appear.

In most cases, we want to achieve the effect in this example, rather than the White Rectangle formed in the second example. Therefore, in most cases, when using porterduduxfermode, canvas is used. saveLayer (), canvas. restoreToCount (), write the key code between the two methods.

A mind map that is not passed by the brain

If you have a blog post related to Google or Baidu porterduxfermode, you will surely see the following figure, as shown below:

This figure shows a Demo of the API provided by the Android sdk. the physical path of the source code is C: \ Users \ iSpring \ AppData \ Local \ Android \ sdk \ samples \ android-23 \ legacy \ ApiDemos \ src \ com \ example \ android \ apis \ graphics \ Xfermodes. java.

This figure shows how to first draw a yellow circle, then set the paint brush to 16 different porterduxfermode, and then draw a blue rectangle.

The above results seem quite normal, but what I want to say is that this crazy story is very misleading to developers. The starting point of this image is good. It wants to intuitively express the effects of multiple porterduxfermodes, in order to achieve this purpose, the yellow and blue figures drawn in this Code are all done.

The code for creating a yellow circle in the Code is as follows:

// 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*3/4, h*3/4), p);        return bm;    }

The code above first sets a new Bitmap object by specifying the size. By default, the color of all pixels of such Bitmap objects is (0, 0, 0, 0 ), since the alpha component of each pixel is 0 by default, the entire Bitmap is transparent. Then, the Bitmap is used to construct a Canvas object. when drawOval () is used, a yellow circle is drawn to the Canvas, and the image on the Canvas is automatically updated to the Bitmap created before the Canvas is bound. In this way, there are two types of pixels in Bitmap: one is the pixel in the circular range, the pixel value is 0xFFFFCC44, and the other is the pixel out of the circular range, the pixel value is 0x00000000, that is, the yellow circular area in the Bitmap is not transparent, and the remaining range is transparent. Finally, the Bitmap object is returned. In this way, you can use canvas. drawBitmap () in the onDraw () method to draw a yellow circle. Note that the RecF parameter in c. drawOval () is w * 3/4 rather than w, and bottom is h * 3/4 rather than h. What does this mean?This indicates that the actual size of the Bitmap is larger than the yellow circular area you see in the center. The sizes of the Bitmap are different.

The code for creating a blue rectangle is as follows:

// 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(w/3, h/3, w*19/20, h*19/20, p);        return bm;    }

The code for creating a blue rectangle is similar to the Code for creating a yellow circle. Observe c. in drawRect (), the left value is w/3, instead of 0. The top value is h/3, instead of 0. the right value is w * 19/20, not w, the bottom value is h * 19/20, not h. What does this mean?This indicates that the actual size of the Bitmap is larger than that of the blue rectangle you see in the center. The sizes of the Bitmap are different.

After the above analysis, we know that the actual Bitmap size drawn in the API Demo is larger than the actual size we can see with the naked eye, which leads to great confusion for developers. For example, if the value of PorterDuffXfermode is CLEAR, the whole circle is invisible to the naked eye in API Demo. In fact, this is not correct, because if the actual Bitmap size obtained by the makeDst () and makeSrc () methods is the same as the actual size of the circle and rectangle, the effect should be that only the lower-right corner of the circle with the rectangle intersecting is cropped to transparent, and the other 3/4 of the circle should be visible; for example, if the value of porterduxfermode is SRC, The result displayed in API Demo is that the yellow circle is completely invisible and the blue rectangle is completely visible, this is not true, because if the actual Bitmap size obtained by the makeDst () and makeSrc () methods is the same as the actual size of the circle and rectangle, the effect should be visible in the yellow circle and the blue rectangle, but the area where the circle and the rectangle intersect is blue, that is, the correct effect should be that the blue rectangle is covered with a yellow circle.

Computing rules for different mixed modes

I modified the code by referring to the API Demo, so that the actual Bitmap size obtained by the makeDst () and makeSrc () methods is the same as the actual size of the circle and rectangle. In order to facilitate observation and comparison, I set the background of the entire View to green, and the final running effect is as follows:

The above example demonstrates the effects of 16 mixed modes, and the key code is placed between canvas. saveLayer () and canvas. restoreToCount.

Android in classandroid.graphics.PorterDuffThe rules for color mixing of 18 source pixels and target pixels are defined, and the calculation methods of various mixed rules are briefly introduced in the Code in the form of annotations, refer to the source code link on GitHub.

We know that the color of A pixel consists of four components, namely, ARGB. The first component A represents the Alpha value, and the last three components RGB represents the color. We use S to represent the source pixel. The color value of the source pixel can be expressed as [Sa, SC]. a in Sa is the abbreviation of alpha, and Sa represents the Alpha value of the source pixel, c In SC stands for color, and SC stands for RGB OF THE SOURCE pixel. We use D to represent the target pixel. The color value of the target pixel can be expressed as [Da, Dc], Da to represent the Alpha value of the target pixel, and Dc to represent the RGB value of the target pixel.

The color calculation rules for source and target pixels in different blending modes are as follows:

CLEAR: [0, 0]

SRC: [Sa, SC]

DST: [Da, Dc]

SRC_OVER: [Sa + (1-Sa) * Da, Rc = SC + (1-Sa) * Dc]

DST_OVER: [Sa + (1-Sa) * Da, Rc = Dc + (1-Da) * SC]

SRC_IN: [Sa * Da, SC * Da]

DST_IN: [Sa * Da, Sa * Dc]

SRC_OUT: [Sa * (1-Da), SC * (1-Da)]

DST_OUT: [Da * (1-Sa), Dc * (1-Sa)]

SRC_ATOP: [Da, SC * Da + (1-Sa) * Dc]

DST_ATOP: [Sa, Sa * Dc + SC * (1-Da)]

XOR: [Sa + Da-2 * Sa * Da, SC * (1-Da) + (1-Sa) * Dc]

DARKEN: [Sa + Da-Sa * Da, SC * (1-Da) + Dc * (1-Sa) + min (SC, Dc)]

LIGHTEN: [Sa + Da-Sa * Da, SC * (1-Da) + Dc * (1-Sa) + max (SC, Dc)]

MULTIPLY: [Sa * Da, SC * Dc]

SCREEN: [Sa + Da-Sa * Da, SC + Dc-SC * Dc]

ADD: Saturate (S + D)

OVERLAY: Saturate (S + D)

This article uses CLEAR as an example to describe how porterduxfermode works. Other mixed modes work in the same way, except that the color mixing rules of source and target pixels are different. You can verify the preceding 16 rules based on the calculation rules of various modes.

At last, you need to note that several mixed rules, such as DARKEN, LIGHTEN, and OVERLAY, cannot be effectively accelerated by GPU hardware. If you think that the mixed mode is not properly used, you can call View. setLayerType (View. LAYER_TYPE_SOFTWARE, null) method, disable GPU hardware acceleration for our View, switch to the software rendering mode, so that all mixed modes can be used normally.

To sum up,Porterduduxfermode is used to achieve color mixing between newly drawn pixels and existing pixels at the corresponding position on the Canvas according to the mixing rules..

I hope this article will help you understand how porterduduxfermode works.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.