赞
踩
上一节概述了Paint进阶需要掌握的API,这一节针对图层混合模式进行讲解,主要是Xfermode的使用。
图层混合模式是将所绘制的像素与canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,最终更新canvas中最终显示的像素值。
图层混合模式使用的三种场景:1.ComposeShader(混合渲染);2.画笔的Paint.setXfermode();3.PorterDuffColorFilter(颜色过滤器)。
1.图层混合模式使用时必须禁止硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
2.离屏绘制
- // //离屏绘制
- int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
-
- //......中间进行绘制
-
- //
- canvas.restoreToCount(layerId);
3.绘制
-
- // //目标图
- canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
- // //设置混合模式
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
- // //源图,重叠区域右下角部分
- canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
- // //清除混合模式
- mPaint.setXfermode(null);
完整的XferMode代码:
- public class XfermodeView extends View {
-
- private Paint mPaint;
- private int mWidth, mHeight;
-
- public XfermodeView(Context context) {
- this(context, null);
- }
-
- public XfermodeView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
-
- private void init() {
- //初始化画笔
- mPaint = new Paint();
- mPaint.setColor(Color.RED);
- mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- mWidth = MeasureSpec.getSize(widthMeasureSpec);
- mHeight = MeasureSpec.getSize(heightMeasureSpec);
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- //1.ComposeShader
- //2.画笔Paint.setXfermode()
- //3.PorterDuffColorFilter
-
- //禁止硬件加速
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-
- setBackgroundColor(Color.GRAY);
-
- // //离屏绘制
- int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
-
- // //目标图Dst
- canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
- // //设置混合模式
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
- // //源图Src,重叠区域右下角部分
- canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
- // //清除混合模式
- mPaint.setXfermode(null);
- //
- canvas.restoreToCount(layerId);
-
- }
-
- //画矩形Dst
- public Bitmap createRectBitmap(int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- dstPaint.setColor(0xFF66AAFF);//蓝色
- canvas.drawRect(new Rect(width / 20, height / 3, 2 * width / 3, 19 * height / 20), dstPaint);
- return bitmap;
- }
-
- //画圆src
- public Bitmap createCircleBitmap(int width, int height) {
- Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(bitmap);
- Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- scrPaint.setColor(0xFFFFCC44);//黄色
- canvas.drawCircle(width * 2 / 3, height / 3, height / 4, scrPaint);
- return bitmap;
- }
-
-
- }

运行后的效果图:
从代码中可以看待现在设置的混合模式为ADD,Xfermode有18种类型,详细的解释见下图
- //所绘制不会提交到画布上
-
- 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),
-
- //取两图层全部区域,交集部分饱和度相加
- new PorterDuffXfermode(PorterDuff.Mode.ADD),
- //取两图层全部区域,交集部分叠加
- new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)

18种模式对应的效果图如下所示
上图的实现代码的效果图完整代码如下:
- public class XfermodesView extends View {
-
-
- private static int W = 250;
- private static int H = 250;
-
- private static final int ROW_MAX = 4; // number of samples per row
-
- private Bitmap mSrcB;
- private Bitmap mDstB;
- private Shader mBG; // background checker-board pattern
-
- //其中Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色,[...,..]前半部分计算的是结果图像的Alpha通道值,“,”后半部分计算的是结果图像的颜色值。
- //效果作用于src源图像区域
- 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),
-
- //取两图层全部区域,交集部分饱和度相加
- new PorterDuffXfermode(PorterDuff.Mode.ADD),
- //取两图层全部区域,交集部分叠加
- new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
- };
-
- private static final String[] sLabels = {
- "Clear", "Src", "Dst", "SrcOver",
- "DstOver", "SrcIn", "DstIn", "SrcOut",
- "DstOut", "SrcATop", "DstATop", "Xor",
- "Darken", "Lighten", "Multiply", "Screen", "Add","Overlay"
- };
-
- public XfermodesView(Context context) {
- this(context, null);
- }
-
- public XfermodesView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public XfermodesView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
-
- WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
- if (windowManager != null) {
- DisplayMetrics display = new DisplayMetrics();
- windowManager.getDefaultDisplay().getMetrics(display);
- W = H = (display.widthPixels - 64) / ROW_MAX; //得到矩形
- }
-
- //1,API 14之后,有些函数不支持硬件加速,需要禁用
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-
- mSrcB = makeSrc(W, H);
- mDstB = makeDst(W, H);
-
- //根据width和height创建空位图,然后用指定的颜色数组colors来从左到右从上至下一次填充颜色
- //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.setFilterBitmap(false);
-
- canvas.translate(15, 35);
-
- int x = 0;
- int y = 0;
- for (int i = 0; i < sModes.length; 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 checker-board pattern
- paint.setStyle(Paint.Style.FILL);
- paint.setShader(mBG);
- canvas.drawRect(x, y, x + W, y + H, paint);
-
- // 使用离屏绘制
- int sc = canvas.saveLayer(x, y, x + W, y + H, null);
- canvas.translate(x, y);
- canvas.drawBitmap(makeDst(2 * W / 3, 2 * H / 3), 0, 0, paint);
- paint.setXfermode(sModes[i]);
- canvas.drawBitmap(makeSrc(2 * W / 3, 2 * H / 3), W / 3, H / 3, paint);
- paint.setXfermode(null);
- canvas.restoreToCount(sc);
-
- // draw the label
- labelP.setTextSize(20);
- 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;
- }
- }
- }
-
- // 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, h), p);
- return bm;
- }
-
- // 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(0, 0, w * 19 / 20, h * 19 / 20, p);
- return bm;
- }
-
-
- }

最后附上一个应用实例,效果图如下
完整代码
- public class XfermodeEraserView extends View {
-
- private Paint mPaint;
- private Bitmap mDstBmp, mSrcBmp, mTxtBmp;
- private Path mPath;
-
- public XfermodeEraserView(Context context) {
- this(context, null);
- }
-
- public XfermodeEraserView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public XfermodeEraserView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
- private void init() {
- //初始化画笔
- mPaint = new Paint();
- mPaint.setColor(Color.RED);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(80);
-
- //禁用硬件加速
- setLayerType(View.LAYER_TYPE_SOFTWARE, null);
-
- //初始化图片对象
- mTxtBmp = BitmapFactory.decodeResource(getResources(), R.drawable.result);
- mSrcBmp = BitmapFactory.decodeResource(getResources(), R.drawable.eraser);
- mDstBmp = Bitmap.createBitmap(mSrcBmp.getWidth(), mSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);
-
- //路径(贝塞尔曲线)
- mPath = new Path();
- }
-
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
-
- //绘制刮奖结果
- canvas.drawBitmap(mTxtBmp, 0, 0, mPaint);
-
- //使用离屏绘制
- int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
-
- //先将路径绘制到 bitmap上
- Canvas dstCanvas = new Canvas(mDstBmp);
- dstCanvas.drawPath(mPath, mPaint);
-
- //绘制 目标图像
- canvas.drawBitmap(mDstBmp, 0, 0, mPaint);
- //设置 模式 为 SRC_OUT, 擦橡皮区域为交集区域需要清掉像素
- mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
- //绘制源图像
- canvas.drawBitmap(mSrcBmp, 0, 0, mPaint);
-
- mPaint.setXfermode(null);
-
- canvas.restoreToCount(layerID);
- }
-
- private float mEventX, mEventY;
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- super.onTouchEvent(event);
- switch (event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mEventX = event.getX();
- mEventY = event.getY();
- mPath.moveTo(mEventX, mEventY);
- break;
- case MotionEvent.ACTION_MOVE:
- float endX = (event.getX() - mEventX) / 2 + mEventX;
- float endY = (event.getY() - mEventY) / 2 + mEventY;
- //画二阶贝塞尔曲线
- mPath.quadTo(mEventX, mEventY, endX, endY);
- mEventX = event.getX();
- mEventY = event.getY();
- break;
- }
- invalidate();
- return true; //消费事件
- }
-
- }

Xfermode的使用其实不难,主要是学会灵活使用,最后附上demo:https://github.com/987570437/PaintDemo
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。