通过使用Xfermode将绘制的图形的像素和Canvas上对应位置的像素按照一定的规则进行混合,形成新的像素,再更新到Canvas中形成最终的图形。
我们一个像素的颜色都是由四个分量组成,即ARGB,A表示的是我们Alpha值,RGB表示的是颜色
S表示的是源像素,源像素的值表示[Sa,Sc] Sa表示的就是源像素的Alpha值,Sc表示源像素的颜色值
D表示的是目标像素,目标像素的值表示[Da,Dc] Da表示的就是目标像素的Alpha值
混合模式分类
SRC类
----优先显示的是源图片
SRC [Sa, Sc] ---- 处理图片相交区域时,总是显示的是原图片
SRC_IN [Sa * Da, Sc * Da] ---- 处理图片相交区域时,受到目标图片的Alpha值影响
当我们的目标图片为空白像素的时候,目标图片也会变成空白
简单的来说就是用目标图片的透明度来改变源图片的透明度和饱和度,当目标图片的透明度为0时,源图片就不会显示
示例:圆角头像 、倒影图片
SRC_OUT [Sa * (1 - Da), Sc * (1 - Da)] --- 同SRC_IN类似 (1 - Da)
用我们目标图片的透明度的补值来改变源图片的透明度和饱和度,当目标图片的透明度为不透明时,源图片就不会显示
示例:橡皮擦效果
目标图片 --- 手势的轨迹
源图片 --- 擦除的图片
刮刮卡效果
SRC_ATOP [Da, Sc * Da + (1 - Sa) * Dc]
---- 当透明度为100%和0%时,SRC_IN 和 SRC_ATOP是通用的
当透明度不为上述的两个值时,SRC_ATOP 比 SRC_IN 源图像的饱和度会增加,变得更亮一些
示例:用SRC_ATOP来实现 圆角头像 、倒影图片 对比一下SRC_IN
DST类
----优先显示的是目标图片
DST_IN [Sa * Da, Sa * Dc] ----- 对比一下SRC_IN,正好和我们SRC_IN相反,在相交的时候以源图片的透明度来改变目标图片的透明度和饱和度
当源图片的透明度为0的时候,目标图片完全不显示
示例:心电图效果,不规则水波纹效果,当然也可以做SRC_IN 的效果(注意选择谁为源图片,谁为目标图片)
心电图效果-
目标图片 ---心电图
源图片 ---- 不透明的图 就是通过改变透明图片的不透明区域的宽度,来实现心电图的动画效果
3、其他的叠加效果
MULTIPLY[Sa * Da, Sc * Dc] ---
应用:可以把图片的轮廓取出来
LIGHTEN -- 变亮
书架 头顶灯光变亮效果
例子一:圆角头像
public class RoundImageView extends View {
private Paint mBitMapPaint;
private Bitmap mBitMapSRC, mBitMapDST;
public RoundImageView(Context context) {
this(context,null);
}
public RoundImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE,null);
mBitMapPaint = new Paint();
//显示的图片
mBitMapSRC = BitmapFactory.decodeResource(getResources(),R.drawable.test);
//shade为圆角矩形图片
mBitMapDST =BitmapFactory.decodeResource(getResources(),R.drawable.shade);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitMapDST,0,0,mBitMapPaint);
mBitMapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mBitMapSRC,0,0,mBitMapPaint);
mBitMapPaint.setXfermode(null);
}
}
例子二:倒影图片效果
public class InvertedImageView_DSTIN extends View {
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC,BmpRevert;
public InvertedImageView_DSTIN(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
BmpDST = BitmapFactory.decodeResource(getResources(),R.drawable.test,null);
//一张白色透明度渐变的图片 与旋转后的图片合成倒影效果
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.invert_shade,null);
//将目标图片 旋转180度
Matrix matrix = new Matrix();
matrix.setScale(1F, -1F);
// 生成倒影图
BmpRevert = Bitmap.createBitmap(BmpDST, 0, 0, BmpDST.getWidth(), BmpDST.getHeight(), matrix, true);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//先画出目标图片
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
//再画出倒影
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.translate(0,BmpSRC.getHeight());
canvas.drawBitmap(BmpRevert,0,0,mBitPaint);
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
}
例子三:橡皮擦效果
public class EraserView extends View {
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC;
private Path mPath;
private float mPreX,mPreY;
public EraserView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
mBitPaint.setColor(Color.RED);
mBitPaint.setStyle(Paint.Style.STROKE);
mBitPaint.setStrokeWidth(45);
BmpSRC = BitmapFactory.decodeResource(getResources(), R.drawable.test,null);
BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//先把手指轨迹画到目标Bitmap上
Canvas c = new Canvas(BmpDST);
c.drawPath(mPath,mBitPaint);
//然后把目标图像画到画布上
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
//计算源图像区域
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
postInvalidate();
return super.onTouchEvent(event);
}
}
例子四:刮刮卡效果
public class GuaGuaCardView extends View {
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC,BmpText;
private Path mPath;
private float mPreX,mPreY;
public GuaGuaCardView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
mBitPaint.setColor(Color.RED);
mBitPaint.setStyle(Paint.Style.STROKE);
mBitPaint.setStrokeWidth(45);
BmpText = BitmapFactory.decodeResource(getResources(), R.drawable.guaguaka_text1,null);
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.guaguaka,null);
BmpDST = Bitmap.createBitmap(BmpSRC.getWidth(), BmpSRC.getHeight(), Bitmap.Config.ARGB_8888);
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BmpText,0,0,mBitPaint);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
//先把手指轨迹画到目标Bitmap上
Canvas c = new Canvas(BmpDST);
c.drawPath(mPath,mBitPaint);
//然后把目标图像画到画布上
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
//计算源图像区域
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.moveTo(event.getX(),event.getY());
mPreX = event.getX();
mPreY = event.getY();
return true;
case MotionEvent.ACTION_MOVE:
float endX = (mPreX+event.getX())/2;
float endY = (mPreY+event.getY())/2;
mPath.quadTo(mPreX,mPreY,endX,endY);
mPreX = event.getX();
mPreY =event.getY();
break;
case MotionEvent.ACTION_UP:
break;
}
postInvalidate();
return super.onTouchEvent(event);
}
}
例子五:灯光变亮效果
public class LightBookView extends View {
private Paint mBitPaint;
private Bitmap BmpDST,BmpSRC;
public LightBookView(Context context, AttributeSet attrs) {
super(context, attrs);
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
mBitPaint = new Paint();
BmpDST = BitmapFactory.decodeResource(getResources(), R.drawable.book_bg,null);
BmpSRC = BitmapFactory.decodeResource(getResources(),R.drawable.book_light,null);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(BmpDST,0,0,mBitPaint);
mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN));
canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
mBitPaint.setXfermode(null);
canvas.restoreToCount(layerId);
}
}