At the time of the monkey Festival, the children here wish you a New Year without bugs. 😄

directory

One, foreword

Second, the PorterDuffXfermode

Third, in actual combat

Write at the end

One, foreword

In the custom UI, it is necessary to overlay a variety of images, and to achieve the desired purpose, we need today’s protagonist Xfermode. Xfermode has three children:

  1. AvoidXfermode
  2. PixelXorXfermode
  3. PorterDuffXfermode

AvoidXfermode and PixelXorXfermode were already marked removed after API 16, so that leaves youngest son PorterDuffXfermode to synthesize the image for us, and of course that’s where we’ll focus today. As usual, we’ll start with a few field shots, and then we’ll start with today’s presentation.

Xfermode gadgets

Scratch CARDS

The heartbeat

Second, the PorterDuffXfermode

PorterDuffXfermode is instantiated in Paint using setXfermode. PorterDuffXfermode is instantiated in Paint using setXfermode.

/ / Paint
public Xfermode setXfermode(Xfermode xfermode) {
    intnewMode = xfermode ! =null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
    intcurMode = mXfermode ! =null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
    if(newMode ! = curMode) { nSetXfermode(mNativePaint, newMode); } mXfermode = xfermode;return xfermode;
}
Copy the code
/ / PorterDuffXfermode class
public class PorterDuffXfermode extends Xfermode {
    public PorterDuffXfermode(PorterDuff.Mode mode) { porterDuffMode = mode.nativeInt; }}Copy the code

Therefore, porterduff.mode finally works. If you go into the source code, you will see the following pattern available. This code is a snippet of API 22. It will be slightly different if you look at it in a higher version, but the formula is the same for the same pattern.

public enum Mode {
    / [0, 0] * * * /
    CLEAR       (0),
    /** [Sa, Sc] */
    SRC         (1),
    /** [Da, Dc] */
    DST         (2),
    /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
    SRC_OVER    (3),
    /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
    DST_OVER    (4),
    /** [Sa * Da, Sc * Da] */
    SRC_IN      (5),
    /** [Sa * Da, Sa * Dc] */
    DST_IN      (6),
    /** [Sa * (1 - Da), Sc * (1 - Da)] */
    SRC_OUT     (7),
    /** [Da * (1 - Sa), Dc * (1 - Sa)] */
    DST_OUT     (8),
    /** [Da, Sc * Da + (1 - Sa) * Dc] */
    SRC_ATOP    (9),
    /** [Sa, Sa * Dc + Sc * (1 - Da)] */
    DST_ATOP    (10),
    /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
    XOR         (11),
    /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
    DARKEN      (12),
    /** [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
    LIGHTEN     (13),
    /** [Sa * Da, Sc * Dc] */
    MULTIPLY    (14),
    /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
    SCREEN      (15),
    /** Saturate(S + D) */
    ADD         (16),
    OVERLAY     (17);

    Mode(int nativeInt) {
        this.nativeInt = nativeInt;
    }

    / * * *@hide* /
    public final int nativeInt;
}
Copy the code

What is the effect of each mode? Let’s take a look at the official Demo. Children also follow a handwritten again, need to see the source of the children’s shoes intoportal However, there is one thing missing from this demo, which is transparency and does not fully capture the power of Xfermode. So we need to explain what parameters mean first, and then give us a more comprehensive demo.

Porterduff.mode source code each Mode is composed of [XX, YY] form, we take SRC_OUT for example.

/** [Sa * (1 - Da), Sc * (1 - Da)] */
SRC_OUT     (7),
Copy the code

“Xx” refers to Sa * (1-da), which determines the opacity of the composite image. The value range of transparency is [0, 1]. Zero means full transparency, and one means full visibility.

“Yy” refers to Sc * (1-da), whose value determines the color value of this composite image.

A smart kid will also notice Sa, Da, Sc, and Dc. They represent:

  • Sa(Source Alpha) : transparency value of the Source image;
  • Da(Destination Alpha) : transparency value of the target image;
  • Sc(Source Color) : the Color value of the Source image;
  • Dc(Destination Color) : the Color value of the target image.

What is the source image and what is the target image? Set the target diagram first (Dst) and the source diagram second (Src).

We have solved all the doubts first, and then we will give a comprehensive Demo. This is the Xfermode small tool provided by children in the 16 modes shown by the official. If you are not sure about the specific mode to use, you can add this tool to think about it. Anyone interested in this gadget should enter the portal.

Let’s go through the modes one by one, using images from the Zinc example of the Xfermode tool.

1, the CLEAR

The comment gives [0, 0],transparency0, namelyCompletely out of sight;colorIs 0, namely colorless; It ended up like this, with nothing.

2, the SRC

The comment gives [Sa, Sc],transparencyFor Sa, namelyDepends on the transparency value of the source diagram;colorFor the Sc, namelyTake the color value of the source graph; The final rendering is shown in the following image, which is the final display because it takes the values of the source imageSource map.

3, DST

The comment gives [Da, Dc],transparencyTo Da, namelyTake the transparency of the target graph;colorFor Dc, namelyTakes the color value of the target graph; The final rendering is shown in the following figure, because both values of the target graph are taken, so the final rendering isThe target figure.

4, SRC_OVER

The comment gives us[Sa + (1 – Sa)*Da, Rc = Sc + (1 – Sa)*Dc]In fact, it isThe source map is overlaid on the target map, and if there is transparency, the next layer is seenFrom the name can also be very good memory.

5, DST_OVER

The comment gives us[Sa + (1 – Sa)*Da, Rc = Dc + (1 – Da)*Sc], andSRC_OVER insteadThe target map is overlaid on top of the source map, where there is transparency, the next layer can be seen.

6, SRC_IN

[Sa * Da, Sc * Da]

The transparency is Sa * Da, indicating that the transparency depends on the respective transparency of the source image and the target image. Only when the transparency of both is 1 (fully visible), the transparency of the final imaging region will be fully visible, otherwise it will be weakened accordingly.

The color value is Sc * Da, indicating that the color value of the rendered image is rendered with the source image.

The final image is as follows, and the resulting image isIntersection of the target diagram and the source diagram

7, DST_IN

[Sa * Da, Sa * Dc]

The transparency is Sa * Da, indicating that the transparency depends on the respective transparency of the source image and the target image. Only when the transparency of both is 1 (fully visible), the transparency of the final imaging region will be fully visible, otherwise it will be weakened accordingly.

The color value is Sa * Dc, indicating that the color value of the rendering image is rendered with the target image.

The final image is as follows, and the resulting image isIntersection of the target diagram and the source diagram

8 SRC_OUT.

[Sa * (1-da), Sc * (1-da)]

The transparency is Sa * (1-da), indicating that the transparency depends on the transparency of the source graph and the target graph. It is worth noting that the larger the transparency value of the target graph is, the weaker the final result will be. That is, the place where the transparency of the target graph is 1 will not be displayed in the final image. If the opacity of the target graph is not 1, the opacity of the final graph will be weakened. If the transparency of the target graph is 0, the final image will not be affected.

The color value is Sc * (1-da), indicating that the color value of the rendered image is rendered with the source image.

The final image is as follows, and the resulting image isWith the source graph as the main, eliminate the intersection with the target graph(It is also subject to transparency).

9 DST_OUT.

Da * (1-sa), Dc * (1-sa)

The transparency is Da * (1-sa), indicating that the transparency depends on the transparency of the source image and the target image. It is worth noting that the larger the transparency value of the source image is, the weaker the final result will be. That is, the place where the transparency of the source image is 1 will not be displayed in the final image. If the opacity of the source image is not 1, the opacity of the final image is reduced. If the source image transparency is 0, the final image will not be affected.

The color value is Dc * (1-sa), indicating that the color value of the rendering image is rendered with the target image.

The final rendering effect is as follows. The imaging result is to take the target image as the main image and eliminate the intersection with the source image (because it is still affected by transparency).

10, SRC_ATOP

Da, Sc * Da + (1-sa) * Dc

The transparency is Da, which means that the visible area of the final image depends only on the target image.

Color value Sc * Da + (1-sa) * Dc, indicating that the target image and the source image jointly determine.

The resulting image is as follows, with the source image overlying the region of the target image.

11, DST_ATOP

[Sa, Sa * Dc + Sc * (1-da)]

Transparency Sa means that the visible area of the final image depends only on the source image.

Color value Sa * Dc + Sc * (1-da), indicating that the target image and the source image jointly determine.

The final image is as follows, resulting in the image being overlaid by the target image in the region of the source image.

12, XOR

[Sa + Da – 2 Sa * Da, Sc * (1-da) + (1-sa) * Dc]

Transparency Sa + Da – 2 * Sa * Da indicates that the transparency is affected by both source and target images. When the transparency of both is 1, the transparency of this region will eventually be 0 instead.

Color value Sa * Dc + Sc * (1-da), indicating that the target image and the source image jointly determine.

The final rendering effect is as follows. The results of the imaging are presented as separate images at disjoint places. The intersection is affected by the transparency of the two.

13 and DARKEN

[Sa + da-sa *Da, Sc*(1-da) + Dc*(1-sa) + min(Sc, Dc)]

The transparency is Sa + Da – Sa*Da. It can be known from the formula that the transparency is affected by both source and target images, and the final transparency value will be larger or remain the same.

Color value Sc*(1-da) + Dc*(1-sa) + min(Sc, Dc), indicating that the target image and the source image are jointly determined.

The result is a slightly heavier image.

14, LIGHTEN

[Sa + da-sa *Da, Sc*(1-da) + Dc*(1-sa) + Max (Sc, Dc)]

The transparency is Sa + Da – Sa*Da. It can be known from the formula that the transparency is affected by both source and target images, and the final transparency value will be larger or remain the same.

Color value Sc*(1-da) + Dc*(1-sa) + Max (Sc, Dc), indicating that the target image and the source image jointly determine.

The final result is as follows. The result of imaging is that the color of the intersecting part of the image will be brighter.

15, MULTIPLY

The comments give [Sa * Da, Sc * Dc], and the final image is shown as follows, similar to DST_IN and SRC_IN, but in grayscale.

16 and SCREEN

The comment gives us[Sa + Da – Sa * Da, Sc + Dc – Sc * Dc], the final image is as follows, which will weaken the color of the intersecting part and present a brighter color.

17, the ADD

The comment gives usSaturate(S + D), the effect picture is as follows

In the 18th and OVERLAY

Third, in actual combat

1. Scratch card

(1) Effect drawing

(2) Effect analysis

As you can see, there are two layers, one for the “black spider” and one for the gray mask. “Wipe off” the gray mask in the area based on the movement of our fingers. Finally, when the finger is lifted, judge whether the area “wiped off” has exceeded 20%. If so, no mask will be drawn to achieve the effect of the underlying picture.

(3) Concrete implementation

The first step is to record the track of finger sliding through onTouchEvent. However, it is worth noting that a small optimization was made to use bezier curves to make the sliding trajectory more smooth. The specific code is as follows

For children who are interested in “Bezier curve”, you can see the principle and practice of bezier curve in another article of children’s own beauty.

public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            mPreX = event.getX();
            mPreY = event.getY();
            mPath.moveTo(mPreX, mPreY);
            break;
        case MotionEvent.ACTION_MOVE:
            float endX = (mPreX + event.getX()) / 2;
            float endY = (mPreY + event.getY()) / 2;
            // Bezier curves are used here
            mPath.quadTo(mPreX, mPreY, endX, endY);
            mPreX = endX;
            mPreY = endY;
            break;
        case MotionEvent.ACTION_UP:
            post(calculatePixelsRunnable);
            break;
    }
    postInvalidate();
    return true;
}
Copy the code

In the second step, we need to apply the obtained track to the gray coating to achieve the “scratch card” effect. More than one mode can be used here, depending on the order of the gray coating and finger paths. We use DST_OUT.

It is important to note that a new layer needs to be created so that the mode effect does not apply to other images. The specific code is as follows

// Create a new layer
int layer = canvas.saveLayer(0.0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);

canvas.drawBitmap(mCoatingLayerBitmap, 0.0, mPaint);
mPaint.setXfermode(mXfermode);
canvas.drawPath(mPath, mPaint);

mCanvas.drawPath(mPath, mPaint);

mPaint.setXfermode(null);

canvas.restoreToCount(layer);
Copy the code

After these two steps, the effect is achieved. Since we inherit the ImageView, the “black spider” layer has been added.

The third step is to automatically remove the “gray layer”. Every time the finger is lifted, a thread will be opened to calculate the pixel value of the “gray layer”. If more than 20% is wiped, the “gray layer” can be removed. The specific code is as follows:

private Runnable calculatePixelsRunnable = new Runnable() {
    @Override
    public void run(a) {

        int width = getWidth();
        int height = getHeight();

        float totalPixel = width * height;

        int[] pixel = new int[width * height];

        mCoatingLayerBitmap.getPixels(pixel, 0, width, 0.0, width, height);

        int cleanPixel = 0;
        for (int col = 0; col < height; ++col) {
            for (int row = 0; row < width; ++row) {
                if (pixel[col * width + row] == 0) { cleanPixel++; }}}float result = cleanPixel / totalPixel;

        if (result >= PERCENT) {
            isShowAll = true; postInvalidate(); }}};Copy the code

The core three steps are already there, and the rest is assembled. I won’t go into too much detail here, but the complete code is in the portal.

2, the heartbeat

(1) Effect drawing

(2) Animation analysis

We explain with the help of a picture drawn by the above children. The green heartbeat is used as the target picture, and the blue one is used as the source picture. By increasing the distance of DX continuously, the width of the blue source picture keeps shrinking, and the final use is madeDST_INPattern composition can achieve the effect of little by little.

To make the DX a little bigger, we used property animation. This is a simple example, so we’re not going to paste the code. If you are interested, please enter the portal.

There’s another blog about attribute animation for kids that goes into detail about how it works and how it works. If you’re interested, you can enter the portal.

Write at the end

Through the combination of various modes of Xfermode can draw some cool images and effects, we are always limited by our imagination and that lazy hands 😄. Finally, if you learn anything from this article, please give me a thumbs up at ❤️ and follow me. If there is any misunderstanding or obscure sentence in this article, please leave a comment in the comment section, and we will discuss and progress together. Your encouragement is the biggest motivation for me to move forward.

Advanced UI series Github address: please enter the portal, if you like to give me a star bar 😄