当前位置:   article > 正文

Android Paint 进阶之 图层混合模式(Xfermode)_android setxfermode

android setxfermode

    上一节概述了Paint进阶需要掌握的API,这一节针对图层混合模式进行讲解,主要是Xfermode的使用。

1.概念

    图层混合模式是将所绘制的像素与canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,最终更新canvas中最终显示的像素值。

2.使用场景

    图层混合模式使用的三种场景:1.ComposeShader(混合渲染);2.画笔的Paint.setXfermode();3.PorterDuffColorFilter(颜色过滤器)。

3.setXfermode使用

    1.图层混合模式使用时必须禁止硬件加速

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

    2.离屏绘制

  1. // //离屏绘制
  2. int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
  3. //......中间进行绘制
  4. //
  5. canvas.restoreToCount(layerId);

    3.绘制

  1. // //目标图
  2. canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
  3. // //设置混合模式
  4. mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
  5. // //源图,重叠区域右下角部分
  6. canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
  7. // //清除混合模式
  8. mPaint.setXfermode(null);

    完整的XferMode代码:

  1. public class XfermodeView extends View {
  2. private Paint mPaint;
  3. private int mWidth, mHeight;
  4. public XfermodeView(Context context) {
  5. this(context, null);
  6. }
  7. public XfermodeView(Context context, AttributeSet attrs) {
  8. this(context, attrs, 0);
  9. }
  10. public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
  11. super(context, attrs, defStyleAttr);
  12. init();
  13. }
  14. private void init() {
  15. //初始化画笔
  16. mPaint = new Paint();
  17. mPaint.setColor(Color.RED);
  18. mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
  19. }
  20. @Override
  21. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  22. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  23. mWidth = MeasureSpec.getSize(widthMeasureSpec);
  24. mHeight = MeasureSpec.getSize(heightMeasureSpec);
  25. }
  26. @Override
  27. protected void onDraw(Canvas canvas) {
  28. super.onDraw(canvas);
  29. //1.ComposeShader
  30. //2.画笔Paint.setXfermode()
  31. //3.PorterDuffColorFilter
  32. //禁止硬件加速
  33. setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  34. setBackgroundColor(Color.GRAY);
  35. // //离屏绘制
  36. int layerId = canvas.saveLayer(0,0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
  37. // //目标图Dst
  38. canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
  39. // //设置混合模式
  40. mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
  41. // //源图Src,重叠区域右下角部分
  42. canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
  43. // //清除混合模式
  44. mPaint.setXfermode(null);
  45. //
  46. canvas.restoreToCount(layerId);
  47. }
  48. //画矩形Dst
  49. public Bitmap createRectBitmap(int width, int height) {
  50. Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  51. Canvas canvas = new Canvas(bitmap);
  52. Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  53. dstPaint.setColor(0xFF66AAFF);//蓝色
  54. canvas.drawRect(new Rect(width / 20, height / 3, 2 * width / 3, 19 * height / 20), dstPaint);
  55. return bitmap;
  56. }
  57. //画圆src
  58. public Bitmap createCircleBitmap(int width, int height) {
  59. Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  60. Canvas canvas = new Canvas(bitmap);
  61. Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  62. scrPaint.setColor(0xFFFFCC44);//黄色
  63. canvas.drawCircle(width * 2 / 3, height / 3, height / 4, scrPaint);
  64. return bitmap;
  65. }
  66. }

    运行后的效果图:

Xfermode模式

    从代码中可以看待现在设置的混合模式为ADD,Xfermode有18种类型,详细的解释见下图

  1. //所绘制不会提交到画布上
  2. new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
  3. //显示上层绘制的图像
  4. new PorterDuffXfermode(PorterDuff.Mode.SRC),
  5. //显示下层绘制图像
  6. new PorterDuffXfermode(PorterDuff.Mode.DST),
  7. //正常绘制显示,上下层绘制叠盖
  8. new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
  9. //上下层都显示,下层居上显示
  10. new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
  11. //取两层绘制交集,显示上层
  12. new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
  13. //取两层绘制交集,显示下层
  14. new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
  15. //取上层绘制非交集部分,交集部分变成透明
  16. new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
  17. //取下层绘制非交集部分,交集部分变成透明
  18. new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
  19. //取上层交集部分与下层非交集部分
  20. new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
  21. //取下层交集部分与上层非交集部分
  22. new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
  23. //去除两图层交集部分
  24. new PorterDuffXfermode(PorterDuff.Mode.XOR),
  25. //取两图层全部区域,交集部分颜色加深
  26. new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
  27. //取两图层全部区域,交集部分颜色点亮
  28. new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
  29. //取两图层交集部分,颜色叠加
  30. new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
  31. //取两图层全部区域,交集部分滤色
  32. new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
  33. //取两图层全部区域,交集部分饱和度相加
  34. new PorterDuffXfermode(PorterDuff.Mode.ADD),
  35. //取两图层全部区域,交集部分叠加
  36. new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)

    18种模式对应的效果图如下所示

    上图的实现代码的效果图完整代码如下:

  1. public class XfermodesView extends View {
  2. private static int W = 250;
  3. private static int H = 250;
  4. private static final int ROW_MAX = 4; // number of samples per row
  5. private Bitmap mSrcB;
  6. private Bitmap mDstB;
  7. private Shader mBG; // background checker-board pattern
  8. //其中Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色,[...,..]前半部分计算的是结果图像的Alpha通道值,“,”后半部分计算的是结果图像的颜色值。
  9. //效果作用于src源图像区域
  10. private static final Xfermode[] sModes = {
  11. //所绘制不会提交到画布上
  12. new PorterDuffXfermode(PorterDuff.Mode.CLEAR),
  13. //显示上层绘制的图像
  14. new PorterDuffXfermode(PorterDuff.Mode.SRC),
  15. //显示下层绘制图像
  16. new PorterDuffXfermode(PorterDuff.Mode.DST),
  17. //正常绘制显示,上下层绘制叠盖
  18. new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER),
  19. //上下层都显示,下层居上显示
  20. new PorterDuffXfermode(PorterDuff.Mode.DST_OVER),
  21. //取两层绘制交集,显示上层
  22. new PorterDuffXfermode(PorterDuff.Mode.SRC_IN),
  23. //取两层绘制交集,显示下层
  24. new PorterDuffXfermode(PorterDuff.Mode.DST_IN),
  25. //取上层绘制非交集部分,交集部分变成透明
  26. new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT),
  27. //取下层绘制非交集部分,交集部分变成透明
  28. new PorterDuffXfermode(PorterDuff.Mode.DST_OUT),
  29. //取上层交集部分与下层非交集部分
  30. new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP),
  31. //取下层交集部分与上层非交集部分
  32. new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP),
  33. //去除两图层交集部分
  34. new PorterDuffXfermode(PorterDuff.Mode.XOR),
  35. //取两图层全部区域,交集部分颜色加深
  36. new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
  37. //取两图层全部区域,交集部分颜色点亮
  38. new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN),
  39. //取两图层交集部分,颜色叠加
  40. new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
  41. //取两图层全部区域,交集部分滤色
  42. new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
  43. //取两图层全部区域,交集部分饱和度相加
  44. new PorterDuffXfermode(PorterDuff.Mode.ADD),
  45. //取两图层全部区域,交集部分叠加
  46. new PorterDuffXfermode(PorterDuff.Mode.OVERLAY)
  47. };
  48. private static final String[] sLabels = {
  49. "Clear", "Src", "Dst", "SrcOver",
  50. "DstOver", "SrcIn", "DstIn", "SrcOut",
  51. "DstOut", "SrcATop", "DstATop", "Xor",
  52. "Darken", "Lighten", "Multiply", "Screen", "Add","Overlay"
  53. };
  54. public XfermodesView(Context context) {
  55. this(context, null);
  56. }
  57. public XfermodesView(Context context, AttributeSet attrs) {
  58. this(context, attrs, 0);
  59. }
  60. public XfermodesView(Context context, AttributeSet attrs, int defStyleAttr) {
  61. super(context, attrs, defStyleAttr);
  62. WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
  63. if (windowManager != null) {
  64. DisplayMetrics display = new DisplayMetrics();
  65. windowManager.getDefaultDisplay().getMetrics(display);
  66. W = H = (display.widthPixels - 64) / ROW_MAX; //得到矩形
  67. }
  68. //1,API 14之后,有些函数不支持硬件加速,需要禁用
  69. setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  70. mSrcB = makeSrc(W, H);
  71. mDstB = makeDst(W, H);
  72. //根据width和height创建空位图,然后用指定的颜色数组colors来从左到右从上至下一次填充颜色
  73. //make a ckeckerboard pattern
  74. Bitmap bm = Bitmap.createBitmap(new int[]{0xFFFFFFFF, 0xFFCCCCCC, 0xFFCCCCCC, 0xFFFFFFFF}, 2, 2, Bitmap.Config.RGB_565);
  75. mBG = new BitmapShader(bm, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
  76. Matrix m = new Matrix();
  77. m.setScale(6, 6);
  78. mBG.setLocalMatrix(m);
  79. }
  80. @Override
  81. protected void onDraw(Canvas canvas) {
  82. canvas.drawColor(Color.WHITE);
  83. Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
  84. labelP.setTextAlign(Paint.Align.CENTER);
  85. Paint paint = new Paint();
  86. paint.setFilterBitmap(false);
  87. canvas.translate(15, 35);
  88. int x = 0;
  89. int y = 0;
  90. for (int i = 0; i < sModes.length; i++) {
  91. // draw the border
  92. paint.setStyle(Paint.Style.STROKE);
  93. paint.setShader(null);
  94. canvas.drawRect(x - 0.5f, y - 0.5f, x + W + 0.5f, y + H + 0.5f, paint);
  95. // draw the checker-board pattern
  96. paint.setStyle(Paint.Style.FILL);
  97. paint.setShader(mBG);
  98. canvas.drawRect(x, y, x + W, y + H, paint);
  99. // 使用离屏绘制
  100. int sc = canvas.saveLayer(x, y, x + W, y + H, null);
  101. canvas.translate(x, y);
  102. canvas.drawBitmap(makeDst(2 * W / 3, 2 * H / 3), 0, 0, paint);
  103. paint.setXfermode(sModes[i]);
  104. canvas.drawBitmap(makeSrc(2 * W / 3, 2 * H / 3), W / 3, H / 3, paint);
  105. paint.setXfermode(null);
  106. canvas.restoreToCount(sc);
  107. // draw the label
  108. labelP.setTextSize(20);
  109. canvas.drawText(sLabels[i], x + W / 2, y - labelP.getTextSize() / 2, labelP);
  110. x += W + 10;
  111. // wrap around when we've drawn enough for one row
  112. if ((i % ROW_MAX) == ROW_MAX - 1) {
  113. x = 0;
  114. y += H + 30;
  115. }
  116. }
  117. }
  118. // create a bitmap with a circle, used for the "dst" image
  119. // 画圆一个完成的圆
  120. static Bitmap makeDst(int w, int h) {
  121. Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  122. Canvas c = new Canvas(bm);
  123. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
  124. p.setColor(0xFFFFCC44);//黄色
  125. c.drawOval(new RectF(0, 0, w, h), p);
  126. return bm;
  127. }
  128. // create a bitmap with a rect, used for the "src" image
  129. // 矩形右下角留有透明间隙
  130. static Bitmap makeSrc(int w, int h) {
  131. Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  132. Canvas c = new Canvas(bm);
  133. Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
  134. p.setColor(0xFF66AAFF);//蓝色
  135. c.drawRect(0, 0, w * 19 / 20, h * 19 / 20, p);
  136. return bm;
  137. }
  138. }

刮刮卡效果

最后附上一个应用实例,效果图如下

完整代码

  1. public class XfermodeEraserView extends View {
  2. private Paint mPaint;
  3. private Bitmap mDstBmp, mSrcBmp, mTxtBmp;
  4. private Path mPath;
  5. public XfermodeEraserView(Context context) {
  6. this(context, null);
  7. }
  8. public XfermodeEraserView(Context context, AttributeSet attrs) {
  9. this(context, attrs, 0);
  10. }
  11. public XfermodeEraserView(Context context, AttributeSet attrs, int defStyleAttr) {
  12. super(context, attrs, defStyleAttr);
  13. init();
  14. }
  15. private void init() {
  16. //初始化画笔
  17. mPaint = new Paint();
  18. mPaint.setColor(Color.RED);
  19. mPaint.setStyle(Paint.Style.STROKE);
  20. mPaint.setStrokeWidth(80);
  21. //禁用硬件加速
  22. setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  23. //初始化图片对象
  24. mTxtBmp = BitmapFactory.decodeResource(getResources(), R.drawable.result);
  25. mSrcBmp = BitmapFactory.decodeResource(getResources(), R.drawable.eraser);
  26. mDstBmp = Bitmap.createBitmap(mSrcBmp.getWidth(), mSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);
  27. //路径(贝塞尔曲线)
  28. mPath = new Path();
  29. }
  30. @Override
  31. protected void onDraw(Canvas canvas) {
  32. super.onDraw(canvas);
  33. //绘制刮奖结果
  34. canvas.drawBitmap(mTxtBmp, 0, 0, mPaint);
  35. //使用离屏绘制
  36. int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);
  37. //先将路径绘制到 bitmap上
  38. Canvas dstCanvas = new Canvas(mDstBmp);
  39. dstCanvas.drawPath(mPath, mPaint);
  40. //绘制 目标图像
  41. canvas.drawBitmap(mDstBmp, 0, 0, mPaint);
  42. //设置 模式 为 SRC_OUT, 擦橡皮区域为交集区域需要清掉像素
  43. mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
  44. //绘制源图像
  45. canvas.drawBitmap(mSrcBmp, 0, 0, mPaint);
  46. mPaint.setXfermode(null);
  47. canvas.restoreToCount(layerID);
  48. }
  49. private float mEventX, mEventY;
  50. @Override
  51. public boolean onTouchEvent(MotionEvent event) {
  52. super.onTouchEvent(event);
  53. switch (event.getAction()) {
  54. case MotionEvent.ACTION_DOWN:
  55. mEventX = event.getX();
  56. mEventY = event.getY();
  57. mPath.moveTo(mEventX, mEventY);
  58. break;
  59. case MotionEvent.ACTION_MOVE:
  60. float endX = (event.getX() - mEventX) / 2 + mEventX;
  61. float endY = (event.getY() - mEventY) / 2 + mEventY;
  62. //画二阶贝塞尔曲线
  63. mPath.quadTo(mEventX, mEventY, endX, endY);
  64. mEventX = event.getX();
  65. mEventY = event.getY();
  66. break;
  67. }
  68. invalidate();
  69. return true; //消费事件
  70. }
  71. }

 

Xfermode的使用其实不难,主要是学会灵活使用,最后附上demo:https://github.com/987570437/PaintDemo

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/332715
推荐阅读
相关标签
  

闽ICP备14008679号