图片混合模式(18种)
先画的为目标图(DST)
后画的为源图(SRC)
官方示例采用的drawBitmap,是两个大小相同且绘制区域并不会充满整个bitmap
画图例如drawCircle等,使用setXfermode其作用域是第二次绘制的图案的部分,所以部分的DST(目标图)本应该改变但没有
public class MainActivity extends AppCompatActivity{
static Bitmap makeDst(int w,int h){
Bitmap bm = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);
canvas.drawOval(new RecF(0,0,w*3/4,h*3/4),p);
return bm;
}
static Bitmap makeSrc(int w,int h){
Bitmap bm = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFF66AAFF);
canvas.drawRect(w/3,h/3,w*19/20,h*19/20,p);
return bm;
}
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(new SampleView(this));
}
}
SampleView.java
private static class SampleView extends View{
private static final int W = 200;
private static final int H = 200;
private static final int ROW_MAX = 4;
private Bitmap mSrcB;
private Bitmap mDstB;
private Shader mBG;
private static final Xfermode[] sModes = {
new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
new PorterDuffXfermode(PorterDuff.Mode.SRC),
new PorterDuffXfermode(PorterDuff.Mode.DST),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
new PorterDuffXfermode(PorterDuff.Mode.XOR),
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
new PorterDuffXfermode(PorterDuff.Mode.SCREEN)
};
private static final String[] sLabels = {
"Clear","Src","Dst","SrcOver",
"DstOver","SrcIn","DstIn","SrcOut",
"DstOut","SrcATop","DstATop","Xor",
"Darken","Lighten","Mutiply","Screen"
};
private SampleView(Context context){
super(context);
mSrcB = makeSrc(W,H);
mDstB = makeDst(W,H);
// make a ckeckerboard pattern(棋盘图案)
Bitmap bm = Bitmap.createBitmap(new int[]{0xFFFFFFFF,0xFFCCCCCC,0xFFCCCCCC,0xFFFFFFFF},2,2,Bitmap.Config.RGB_565);
mBG = new BitmapShader(bm,Shader.TileMode.REPEAT,Shader.TileMode.REPEAT);
Matrix m = new Matrix();
m.setScale(6,6);
mBG.setLocalMatrix(m);
}
@Override
protected void onDraw(Canvas canvas){
canvas.drawColor(Color.WHITE);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextAlign(Paint.Align.CENTER);
Paint paint = new Paint();
paint.setFilterBitmao(false);
canvas.translate(15,35);
int x = 0;
int y = 0;
for(int i=0;i
//draw the border
paint.setStyle(Paint.Style.STROKE);
paint.setShader(null);
canvas.drawRect(x-0.5f,y-0.5f,x+W+0.5f,y+H+0.5f,paint);
//draw the cheker-board pattern
paint.setStyle(Paint.Style.FILL);
paint.setShader(mBG);
canvas.drawRect(x,y,x+W,y+H,paint);
//draw the src/dst example into our offscreen bitmap
int sc = canvas.saveLayer(x,y,x+W,y+H,null,Canvas.ALL_SAVE_FLAG);
canvas.translate(x,y);
canvas.drawBitmap(mDstB,0,0,paint);
paint.setXfermode(sModes[i]);
canvas.drawBitmap(mSrcB,0,0,paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
//draw the label
canvas.drawText(sLabels[i],x+W/2,y-labelP.getTextSize()/2,labelP);
x+=W+10;
//wrap around when we've drawn enough for one row
if((i%ROW_MAX)==ROW_MAX-1){
x=0;
y+=H+30;
}
}
}
}
效果图
原始的画图模式
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawARGB(255,255,156,161);//设置背景色
drawDst(canvas,mPaint);
drawSrc(canvas,mPaint);
}
private void drawDst(Canvas canvas,Paint p){
p.setColor(0xFFFFCC44);
int w = canvas.getWidth()/3;
canvas.drawCircle(w,w,w,p);
}
private void drawSrc(Canvas canvas,Paint paint){
paint.setColor(0xFF66AAFF);
int r = canvas.getWidth()/3;
canvas.drawRect(r,r,r*2.7f,r*2.7f,paint);
}
CLEAR
源覆盖的目标图清零
画图protected void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawARGB(255,255,156,161);
drawDst(canvas,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
drawSrc(canvas,mPaint);
mPaint.setXfermode(null);
}
实现了效果,但是是黑色一块
使用两个Bitmap进行操作,但是不加canvas.saveLayer()
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mDstBitmap,0,0,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
mPaint.setXfermode(null);
}
原因:
Activity本身的颜色是黑色,所以CLEAR模式下,两个Bitmap实现了CLEAR效果,所以展示的是Activity的颜色(貌似其余人的都是白色的?
加了图层渲染后@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(mDstBitmap,0,0,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawBitmap(mSrcBitmap,0,0,mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(sc);
}
实现了效果
先画圆(Dst),画笔设置PorterDuff.Mode.CLEAR后,再绘制矩形(此处加了canvas.saveLayer())
实际上作用域为第二次绘画的大小
源像素(SRC)替换目标像素(DST)
传统的画图形式 不加图层渲染int w = 200; int h = 200; mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null);加图层渲染
int w = 200; int h = 200; int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);使用drawBitmap形式
实现官方效果
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null);DST 概念
源像素被丢弃,目标像素保留
这种情况下,不论是传统形式还是官方示例操作,或者是加不加图层渲染,呈现的结果都一样
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); //canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST)); mPaint.setColor(Color.GREEN); //canvas.drawBitmap(mSrcB,0,0,mPaint); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);SRC_OVER 概念
源像素绘制在目标像素之上
结论同上
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); //canvas.drawCircle(w/3,h/3,h/3,mPaint); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); //canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);DST_OVER 概念
源像素绘制在目标像素后面
画图形式 不加图层渲染mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null);图层渲染
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null);drawBitmap
效果对应同上
SRC_IN 概念保留覆盖目标像素的源像素,丢弃剩余的源像素和目标像素
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图形式
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);DST_IN 概念
保留覆盖源像素的目标像素,丢弃剩余的源像素和目标像素
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图形式
作用域是第二次绘制的,第二次绘制的是源像素,所以会显示不见,只有目标像素
mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null);SRC_OUT 概念
保留不覆盖目标像素的源像素,丢弃覆盖目标像素的源像素,丢弃所有目标像素
画图int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);drawBitmap
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);DST_OUT 概念
保留未被源像素覆盖的目标像素,丢弃被源像素覆盖的目标像素,丢弃所有的源像素
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图
同上
SRC_ATOP 概念丢弃不覆盖目标像素的源像素,在目标像素上绘制剩余的源像素
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null);画图
结果同上
DST_ATOP 概念丢弃未被源像素覆盖的目标像素,在源像素上绘制剩余的目标像素
画图int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); mPaint.setColor(Color.GREEN); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);drawBitmap
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);XOR 概念
丢弃源像素覆盖目标像素的源像素和目标像素,,绘制剩余的源像素
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图
同上
DARKEN 概念保留源像素和目标像素的最小分量
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图
同上
LIGHTEN 概念保留源像素和目标像素最大的分量
drawBitmapint sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(Color.RED); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN)); mPaint.setColor(Color.GREEN); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);画图
同上
MULTIPLY 概念源像素和目标像素相乘
画图int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); mPaint.setColor(0xFFFFCC44); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); mPaint.setColor(0xFF66AAFF); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);drawBitmap
int sc = canvas.saveLayer(0,0,canvas.getWidth(),canvas.getHeight(),null,Canvas.ALL_SAVE_FLAG); canvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY)); canvas.drawBitmap(mSrcB,0,0,mPaint); mPaint.setXfermode(null); canvas.restoreToCount(sc);SCREEN 概念
先绘制源像素和目标像素,再减去源像素乘以目标
drawBitmapcanvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN)); canvas.drawBitmap(mSrcB,0,0,mPaint);画图
同上
ADD 概念将源像素添加到目标像素并使结果饱和
drawBitmapcanvas.drawBitmap(mDstB,0,0,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); canvas.drawBitmap(mSrcB,0,0,mPaint);画图
同上
OVERLAY 概念Multiplies or screens the source and destination depending on the destination color.
画图mPaint.setColor(0xFFFFCC44); canvas.drawCircle(w/3,h/3,h/3,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)); mPaint.setColor(0xFF66AAFF); canvas.drawRect(w/3,h/3,w/4*3,h/4*3,mPaint);drawBitmap
同上
注 BitmapShader使用特定的图片来作为纹理来使用
构造函数//bitmap:作为纹理的图片 //tileX/Y:在X/Y方向纹理的绘制模式 public BitmapShader(@NonNull Bitmap bitmap,TileMode tileX,TileMode tileY)TileMode
取值:
CLMP:填充的内容大小超过了bitmap size,就选



