一、PorterDuff.Mode
1.1 PorterDuff.Mode.DST
/**
* The source pixels are discarded, leaving the destination intact.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DST:[Da, Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xffff0000
}

1.2 PorterDuff.Mode.DST_IN
/**
*Keeps the destination pixels that cover source pixels, discards the remaining source and destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DST_IN:[Sa * Da, Sa * Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x7fff0000
}

1.3 PorterDuff.Mode.DST_OUT
/**
*
个人认为这个说法有问题,因为跟得到的结果不一致
Keeps the destination pixels that are not covered by source pixels. Discards destination pixels that are covered by source pixels. Discards all source pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DST_OUT:[Da * (1 - Sa), Dc * (1 - Sa)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x80ff0000
}

1.4 PorterDuff.Mode.DST_ATOP
/**
*
个人认为说法有问题,因为跟结果不一致。
Discards the destination pixels that are not covered by source pixels. Draws remaining destination pixels over source pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DST_ATOP:[Sa, Sa * Dc + Sc * (1 - Da)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x7fff0000
}

1.5 PorterDuff.Mode.SRC
/**
* The source pixels replace the destination pixels
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* SRC:[Sa, Sc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x7f00ff00
}

1.6 PorterDuff.Mode.SRC_IN
/**
* Keeps the source pixels that cover the destination pixels, discards the remaining source and destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* SRC_IN:[Sa * Da, Sc * Da]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x7f00ff00
}

1.7 PorterDuff.Mode.SRC_OUT
/**
* Keeps the source pixels that do not cover destination pixels. Discards source pixels that cover destination pixels. Discards all destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* SRC_OUT:[Sa * (1 - Da), Sc * (1 - Da)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x00000000
}

1.8 PorterDuff.Mode.SRC_ATOP
/**
* Discards the source pixels that do not cover destination pixels. Draws remaining source pixels over destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* SRC_ATOP:[Da, Sc * Da + (1 - Sa) * Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xff807f00
}

1.9 PorterDuff.Mode.SRC_OVER
/**
* The source pixels are drawn over the destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* SRC_OVER:[Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xff807f00
}

1.10 PorterDuff.Mode.XOR
/**
*
个人认为说法有问题,因为跟结果不一致。
Discards the source and destination pixels where source pixels cover destination pixels. Draws remaining source pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* XOR:[Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x80ff0000
}

1.11 PorterDuff.Mode.DARKEN
/**
*Retains the smallest component of the source and destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DARKEN:[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xff800000
}

1.12 PorterDuff.Mode.LIGHTEN
/**
*Retains the largest component of the source and destination pixel.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* LIGHTEN:[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xffff7f00
}

1.13 PorterDuff.Mode.MULTIPLY
/**
* Multiplies the source and destination pixels.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* MULTIPLY:[Sa * Da, Sc * Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0x7f000000
}

1.14 PorterDuff.Mode.SCREEN
/**
* Adds the source and destination pixels, then subtracts the source pixels multiplied by the destination.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* SCREEN:[Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xffff7f00
}

1.15 PorterDuff.Mode.ADD
/**
*Adds the source pixels to the destination pixels and saturates the result.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* ADD:[max(0,min(Sa+Da,1)),max(0,min(Sc+Dc,1)]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xffff7f00
}

1.16 PorterDuff.Mode.OVERLAY
/**
* Multiplies or screens the source and destination depending on the destination color.
*/
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* OVERLAY:
* [
* Sa+Da-Sa*Da,
* 当2*Dc<Da时,2*Sc*Dc
* 否则,Sa*Da-2(Da-Sc)(Sa-Dc)
* ]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
//混合过后,混合区域中有颜色的结果值是0xffff0000
}

二、源代码
package com.example.imagedemo;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import androidx.annotation.RequiresApi;
public class ViewDemo12 extends View {
private int width = 350;
private int height = 350;
private Bitmap dstBmp;
private Bitmap srcBmp;
private Paint mPaint;
public ViewDemo12(Context context, AttributeSet attrs) {
super(context, attrs);
dstBmp = makeDst(width, height);
srcBmp = makeSrc(width, height);
mPaint = new Paint();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Paint paint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// origin(canvas);
test01(canvas);
//test02(canvas);
}
private void origin(Canvas canvas) {
//判断是否经过硬件加速
//Log.e("View","==="+canvas.isHardwareAccelerated());
//默认画布的颜色
canvas.drawColor(Color.WHITE);
// paint.setColor(Color.RED);
// canvas.drawRect(0,0,400,100,paint);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.DKGRAY);
//目标
// canvas.drawBitmap(dstBmp, 0, 0, mPaint);
/**
*
* DST:[Da, Dc]
* SRC:[Sa, Sc]
* DST_OUT:[Da * (1 - Sa), Dc * (1 - Sa)]
* SRC_OVER:[Sa + (1 - Sa)*Da, Sc + (1 - Sa)*Dc]
*
*
*/
Log.e("View==Blend", "1 PorterDuff.Mode.SRC");
/**
* 什么时候调用setXfermode方法很重要
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
// //源
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
// //默认是SRC_OVER
// Log.e("View==Blend","2 PorterDuff.Mode.DST_IN");
// mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.restore();
}
@RequiresApi(api = Build.VERSION_CODES.Q)
private void test01(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.parseColor("#FFff0000"));
/**
* 什么时候调用setXfermode方法很重要
* DST:[Da, Dc]
* SRC:[Sa, Sc]
* 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:[max(0,min(Sa+Da,1)),max(0,min(Sc+Dc,1)]
* OVERLAY:
* [
* Sa+Da-Sa*Da,
* 当2*Dc<Da时,2*Sc*Dc
* 否则,Sa*Da-2(Da-Sc)(Sa-Dc)
* ]
* 此时DST是画布新建的layer层
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
//0x7F00FF00
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
canvas.restore();
}
private void test02(Canvas canvas) {
canvas.drawColor(Color.WHITE);
RectF rectF = new RectF(0, 0, 350, 350);
canvas.saveLayer(rectF, mPaint);
canvas.drawColor(Color.DKGRAY);
//目标
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
/**
*
* DST:[Da, Dc]
*/
/**
* 什么时候调用setXfermode方法很重要
*/
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(srcBmp, 0, 0, mPaint);
mPaint.setXfermode(null);
canvas.restore();
}
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(0xFFFF0000);
c.drawOval(new RectF(20, 20, 100, 100), p);
return bm;
}
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(0x7F00FF00);
c.drawRect(0, 0, 250, 250, p);
return bm;
}
}