Porterduffxfermode use and working principle of canvas drawing in Android

Source: Internet
Author: User
Tags transparent color set background

Overview

Class android.graphics.PorterDuffXfermode inherits from android.graphics.Xfermode . When drawing with canvas in Android, you can update the final pixel color values in the canvas by blending the pixels of the drawing with porterduffxfermode and the pixels in the canvas corresponding to a certain rule to form a new pixel value. , which creates a lot of interesting effects. When using Porterduffxfermode, you need to pass it as an argument to the Paint.setXfermode(Xfermode xfermode) method so that when you draw with the brush paint, Android uses the incoming Porterduffxfermode, and if you don't want to use Xfermode again, Then it can be executed Paint.setXfermode(null) .

Porterduffxfermode the porter and Duff in this class are two names, and these two people wrote a paper in 1984 that was called "compositing Digital Images" and clicked to see the paper. We know that a pixel is composed of four components of RGBA, this paper discusses how to achieve the different digital images of how to mix the pixels, the paper proposed a variety of pixel mixing mode. If you have done image processing development, you will know more about it, and it is similar to the Alpha hybrid technology in OpenGL.

Porterduffxfermode supports the following blending modes for more than 10 pixel colors: CLEAR, SRC, DST, Src_over, Dst_over, src_in, dst_in, Src_out, Dst_out, Src_atop, Dst_atop, XOR, darken, lighten, MULTIPLY, screen.

We will analyze several code snippets below to study porterduffxfermode usage and how it works.

Example One

Before we demonstrate how to use Porterduffxfermode, let's take a look at the following example, as shown in the code below:

@Overrideprotected void OnDraw (CanvasCanvas) {Super.ondraw (Canvas);//Set background color        Canvas. DRAWARGB (255,139,197,186);intCanvaswidth =Canvas. GetWidth ();intR = canvaswidth/3;//Draw a yellow circlePaint.setcolor (0xffffcc44);Canvas. Drawcircle (R, R, R, paint);//Draw a blue rectanglePaint.setcolor (0xff66aaff);Canvas. DrawRect (R, R, R *2.7F, R *2.7F, paint); }

We rewrote the view's OnDraw method by first setting the background color of the view to green, then drawing a yellow circle, and then drawing a blue rectangle with the effect as follows:

The above demo is the normal drawing flow of canvas, without using Porterduffxfermode. Let's briefly analyze the above code:

    1. First, we call the Canvas.drawargb (255, 139, 197, 186) method to draw the entire canvas into a color, and after executing this code, the color values of all the pixels on the canvas are ARGB (255,139,197,186 ), because the alpha component of the pixel is 255 instead of 0, all pixels are opaque at this point.

    2. When we execute Canvas.drawcircle (R, R, R, paint), Android draws a yellow circle with a yellow brush at the place where the circle is drawn, and the ARGB color of all the pixel color values within the entire circle is 0XFFFFCC44, Then use these yellow pixels to replace the pixels in the canvas with the color value ARGB (255,139,197,186) in the same position, so that the yellow circle is plotted on the canvas.

    3. After we have executed the Canvas.drawrect (R, R, R * 2.7f, R * 2.7f, paint), Android draws a blue rectangle in the position of the drawn rectangle with a blue brush, At this point the ARGB color of all the pixel color values in the entire rectangle is 0xff66aaff, and then the pixels in the same position in the canvas are replaced with these blue pixels, so that the pixels in the lower right part of the yellow circle are replaced with some other background color pixels by blue pixels. This draws the blue rectangle onto the canvas.

The process is simple, but understanding the specific pixel update process of a canvas drawing is the basis for a true understanding of how Porterduffxfermode works.

Example Two

Here we use Porterduffxfermode to modify the above code, the modified code is as follows:

@Override    protected void OnDraw(Canvas canvas) {Super. OnDraw (canvas);//Set background colorCanvas.drawargb (255,139,197,186);intCanvaswidth = Canvas.getwidth ();intR = canvaswidth/3;//Normal drawing of yellow circlePaint.setcolor (0xffffcc44); Canvas.drawcircle (R, R, R, paint);//Use clear as Porterduffxfermode to draw a blue rectanglePaint.setxfermode (NewPorterduffxfermode (PorterDuff.Mode.CLEAR)); Paint.setcolor (0xff66aaff); Canvas.drawrect (R, R, R *2.7F, R *2.7F, paint);///finally remove the brush XfermodePaint.setxfermode (NULL); }

The effect is as follows:

Let's look at the above code for an analysis:

    1. First, we call the Canvas.drawargb (255, 139, 197, 186) method to draw the entire canvas into one color, at which point all pixels are opaque.

    2. Then we draw a yellow circle to the canvas by calling Canvas.drawcircle (R, R, R, paint).

    3. And then we execute code paint.setxfermode (new Porterduffxfermode (PorterDuff.Mode.CLEAR)), Sets the Porterduff mode of the brush to clear.

    4. Then calls the Canvas.drawrect (R, R, R * 2.7f, R * 2.7f, Paint) method to draw the blue rectangle, but a white rectangle appears on the final interface.

    5. After drawing is complete, we call Paint.setxfermode (NULL) to remove the brush xfermode.

We analyze the reason for the appearance of the white rectangle in detail. In general, when we call the Canvas.drawxxx () method, a brush paint object is passed in, and Android will first check that the brush Paint object has no settings xfermode, if not set Xfermode, Then the drawing is directly overwritten with the original pixels of the canvas, and if Xfermode is set, the pixel color of the corresponding position in the canvas will be updated according to Xfermode specific rules. In this case, when the Canvas.drawcirlce () method is executed, the brush paint does not have a Xfermode object set, so the painted yellow circle directly overrides the pixels on the canvas. When we call Canvas.drawrect () to draw a rectangle, Brush paint has already set the value of Xfermode to PorterDuff.Mode.CLEAR, when Android first draws such a rectangle in memory, the pixels in the drawing are called the source pixels (source, or SRC), and the rectangle drawn in the corresponding position in the canvas The pixels inside are called target pixels (destination, or DST). The ARGB four components of the source pixel will be computed with the ARGB four components of the target pixel at the same position on the canvas, forming the final ARGB value, and then updating the ARGB value of the target pixel with that final ARGB value. In this case, the Xfermode is PorterDuff.Mode.CLEAR, the rule is relatively simple and rough, directly requires the target pixel ARGB four components are all 0, that is (0,0,0,0), that is, transparent color, so we pass the Canvas.drawrect () A transparent rectangle is drawn on the canvas, because the activity itself is white on the background of the screen, so a white rectangle is shown here.

Example Three

Let's make a change to the code in example two, put the code that draws the circle and draw the rectangle between Canvas.savelayer () and Canvas.restoretocount (), and the code looks like this:

@Overrideprotected void OnDraw (CanvasCanvas) {Super.ondraw (Canvas);//Set background color        Canvas. DRAWARGB (255,139,197,186);intCanvaswidth =Canvas. GetWidth ();intCanvasheight =Canvas. GetHeight ();intLayerid =Canvas. Savelayer (0,0, canvaswidth, canvasheight, NULL, Canvas.all_save_flag);intR = canvaswidth/3;//Normal drawing of yellow circlePaint.setcolor (0xffffcc44);Canvas. Drawcircle (R, R, R, paint);//Use clear as Porterduffxfermode to draw a blue rectanglePaint.setxfermode (New Porterduffxfermode (PorterDuff.Mode.CLEAR)); Paint.setcolor (0xff66aaff);Canvas. DrawRect (R, R, R *2.7F, R *2.7F, paint);///finally remove the brush XfermodePaint.setxfermode (NULL);Canvas. Restoretocount (Layerid); }

The effect is as follows:

The following is an analysis of the above code:

    1. First, we call the Canvas.drawargb (255, 139, 197, 186) method to draw the entire canvas into a single color, when all pixels are opaque.

    2. Then we put the main code between Canvas.savelayer () and Canvas.restoretocount (), and I indented the code a bit. When we write the code between CANVAS.SAVEXXX () and canvas.restorexxx (), it is recommended to indent the code inside, so that the code is readable, and of course the code indentation is not mandatory, and does not affect the running effect.

There are a few things to note about layer layers in canvas drawings:

    • Canvas is a technique that supports layer layers rendering, and canvas has a layer by default, and when we call the various drawxxx () methods of canvas, we actually draw everything onto the canvas's default layer.

    • We can also create a new layer with Canvas.savelayer (), the new layer is placed at the top of the canvas default layer, and after we have executed Canvas.savelayer (), All of our drawing operations are drawn to our new layer instead of the canvas's default layer.

    • The ARGB value of all pixels of a layer produced by the Canvas.savelayer () method is (0,0,0,0), which is completely transparent when the layer created by the Canvas.savelayer () method is initially full.

    • The Canvas.savelayer () method returns an int value that represents the ID of the layer, which can be called by calling Canvas.restoretocount (layer) or canvas.restore after we have finished drawing this new layer. () draw this layer to the canvas's default layer, which completes the drawing of a layer.

So you may feel strange that we just put the code that draws the circle and the rectangle between Canvas.savelayer () and Canvas.restoretocount (), why is the white rectangle no longer shown as example two?

When we analyzed the example two code, we knew that the target color of the final rectangular area was reset to Transparent (0,0,0,0), and finally it was only shown as a white rectangle because the activity background color was white. In this example, after the new layer is drawn, the target color of the rectangle is also reset to the transparent color (0,0,0,0), so that the whole new layer is only 3/4 of the circle is not transparent, the rest of the pixels are all transparent, We then call Canvas.restoretocount () to draw the layer again onto the canvas. When you draw a new layer to the canvas, Android uses the pixel color above the entire layer to update the color of the pixels on the canvas, not a simple replacement, but the alpha blend of the canvas and the new layer, which can be found in the link here. Since there are only two kinds of pixels in our layer: completely transparent and completely opaque, there are no partially transparent pixels, and four components of the color value of fully transparent pixels are 0, so this example simplifies the rule of alpha blending between canvas and new layer, specifically:

    • If the alpha component of a pixel above the new layer is 255, that is, the pixel is completely opaque, then Android will directly use the pixel's ARGB value as the color value of the pixel on the canvas's corresponding location.
    • If the alpha component of a pixel above the new layer is 0, that is, the pixel is completely transparent, and in this case the alpha component is 0 pixels and its RGB component is 0, then Android retains the color value of the pixel at the corresponding position in the canvas.

This way, when the new layer is drawn onto the canvas, pixels in a completely opaque 3/4 yellow circle will completely overwrite the pixels in the canvas's corresponding position, and because the pixel argb of the rectangular area drawn on the new layer is (0,0,0,0), So the final canvas corresponds to the rectangular area or the previous background color, so that there is no white rectangle.

In most cases, we want the effect in this example, rather than the white rectangle formed in the example two, so in most cases the Porterduffxfermode is combined with canvas.savelayer (), Canvas.restoretocount (), write the key code between the two methods.

A picture of a deity that is not passed through the brain.

If you google or Baidu Porterduffxfermode related blog post, you will certainly see the following diagram, as follows:

This diagram is one of the demo examples of the API that comes with the Android SDK, and the source code corresponding to the physical path is C:\Users\iSpring\AppData\Local\Android\sdk\samples\android-23\ Legacy\apidemos\src\com\example\android\apis\graphics\xfermodes.java.

This diagram shows the effect of drawing a yellow circle first, then setting the brush paint to 16 different porterduffxfermode, and then drawing the blue rectangle.

The effect looks quite normal, but what I'm trying to say is that this crazy-handed developer is very misleading and the starting point of the map is good, and it wants to express the effects of many porterduffxfermode. For this purpose, the yellow and blue shapes drawn in the code are manipulated.

The code in which the yellow circle is created 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*<        Span class= "Hljs-number" >3 /4 ), p);    return  BM; }

The above code first by specifying the size, new a bitmap object, so that the bitmap object default all pixels of the color is (0,0,0,0), because the alpha component of each pixel defaults to 0, so the entire bitmap is transparent. A Canvas object is then constructed with the bitmap so that when C.drawoval () is executed, the yellow circle is drawn to the canvas, and the graphics on the canvas are automatically updated to the bitmap created before the canvas is bound. In this way, there are two pixels in the bitmap, one is the pixel in the circle range, its pixel value is 0XFFFFCC44, the other is a pixel outside the circle range, its pixel value is 0x00000000, that is, the yellow circle area in the bitmap is opaque, The rest of the range is transparent. Finally, the bitmap object is returned so that the yellow circle can be drawn in the OnDraw () method by Canvas.drawbitmap (). Notice the RECF parameter in C.drawoval (), right is W*3/4, not W,bottom is H*3/4, not H. What does that mean? This means that the actual size of the bitmap is larger than the yellow circular area you see in, and the dimensions are inconsistent.

The code for creating the 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 that creates the blue rectangle is similar to the code that creates the yellow circle. It is important to observe the parameters in C.drawrect (), the left value is W/3, instead of the 0,top value is H/3, instead of the 0,right value is W*19/20, instead of the W,bottom value is H*19/20, not H. What does that mean? This means that the actual size of the bitmap is larger than the blue rectangular area you see in, and the dimensions are inconsistent.

Through the above analysis, we know that the actual size of the bitmap drawn in API demo is larger than the actual size we see with the naked eye, which results in a lot of confusion for the developers. Let me give an example, for example, when the value in the set Porterduffxfermode is clear, the result that the naked eye sees in the API demo is that the whole circle is not visible, in fact this is not true, because if MAKEDST (), MAKESRC () The actual size of the bitmap obtained by the method is the same as the actual size of the circle and the rectangle, so the effect should be that only the lower right part of the circle that intersects the circle is clipped to be transparent, and the other 3/4 parts of the circle should also be visible. Again, for example, when the value in the set Porterduffxfermode is SRC, the result of the API demo is that the painted yellow circle is completely invisible and the blue rectangle drawn is completely visible, which is not true, because if MAKEDST (), MAKESRC () The actual size of the bitmap obtained by the method is the same as the actual size of the drawn circle and rectangle, so the effect should be the yellow circle that is drawn is visible, the blue rectangle is also visible, but the circle and the rectangle intersect the area is blue, that is, the correct effect should be the blue rectangle gland yellow circle.

Calculation rules for different blending modes

I refer to the API demo to modify the code, so that the MAKEDST (), Makesrc () method to get the actual size of the bitmap is the same as the circle, the actual size of the rectangle, and in order to facilitate the comparison, I set the entire view of the background green, the final effect is as follows:

The above example shows the effect of 16 blending modes, and the key code is placed between Canvas.savelayer () and Canvas.restoretocount ().

Android defines the android.graphics.PorterDuff rules for color blending of 18 source pixels and target pixels in the class, and provides a concise description of how the various mixed rules are computed in the code, as well as reference links on GitHub.

We know that the color of a pixel is made up of four components, namely ARGB, the first component a represents the alpha value, and the next three component RGB represents the color. We use S to represent the source pixel, the color value of the source pixel can be expressed as [Sa, A in SC],SA is an abbreviation for Alpha, Sa represents the alpha value of the source pixel, and C in SC is the abbreviation for colour color, SC represents the 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 represents the alpha value of the target pixel, and the Dc represents the RGB of the target pixel.

The rules for calculating colors 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_i N:[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)

In this paper, we take clear as an example in detail the working principle of porterduffxfermode, the other mixed mode works the same way, except that the source pixel and the target pixel color mixed calculation rules are different. You can verify the above 16 rules according to the calculation rules of various modes.

Finally, you need to explain that darken, lighten, overlay and several other hybrid rules in the GPU hardware acceleration does not work, if you think the mixed mode is not used correctly, you can make the call View.setlayertype (view.layer_type_ software, null) method, disable the GPU hardware acceleration of our view, switch to software rendering mode, so that all the mixed mode can be used normally, see the blog "GPU Hardware acceleration control in Android and its limitations on 2D graphics drawing".

Finally, Porterduffxfermode isused to implement a newly drawn pixel that blends with the existing pixels on the canvas according to the blending rules .

I hope this article is helpful to you to understand the working principle of porterduffxfermode correctly!

Related reading:
My Android Blog Finishing summary
The basics of canvas drawing in Android (with source download)

Porterduffxfermode use and working principle of canvas drawing in Android

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.