菜鸡wing遇敌PorterDuffXferMode,不料过于轻敌,应战吃力。
随后与其大战三天三夜。三百余回合不分胜负。
幸得 @咪咪控 相助,侥幸获胜。
keyword:PorterDuffXferMode 错误 不对 不达到预期 bug
上一篇带来一个使用PorterDuffXferMode 做的 水波纹loadingview,中间遇到了点小困难。
(说人话) PorterDuffXferMode总是不能依照效果图预期的效果执行。关于PorterDuffXferMode的错误显示是一个对刚開始学习的人十分深的坑,究竟是bug呢,还是有须要注意的地方呢。这里就尾随我 带上手电筒,去一探究竟。
转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50534175
首先,大家都知道有一个图片:
然后,大部分时候 是看到了认为非常奇妙,就跃跃欲试,尤其是src_in 和dstIn能够实现遮罩效果。比如圆角图片 圆形图片都用了这种模式。
于是就挨个測试各种模式是否生效。结果往往不能达到预期效果。我们来做个測试。
从最简单的開始:
1.直接在canvas上面绘制图形:
@Override protected void onDraw(Canvas canvas) { //dst canvas.drawRect(20,20,80,80,mDstPaint); //src canvas.drawCircle(30,30,30,mSrcPaint); }
原图效果是这种:
如今加一个mode上来,XOR
@Override protected void onDraw(Canvas canvas) { //dst canvas.drawRect(20,20,80,80,mDstPaint); mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); //src canvas.drawCircle(30,30,30,mSrcPaint); }跑起来的结果是这种:
WTF!!
?? 这是什么鬼。
不应该是相交部分消失吗。
网上说“硬件加速”对这个有影响,那么在构造器里关闭硬件加速试一下:
public TestView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDstPaint = new Paint(); mSrcPaint = new Paint(); mDstPaint.setColor(Color.YELLOW); mSrcPaint.setColor(Color.BLUE); setLayerType(View.LAYER_TYPE_SOFTWARE, null); }
执行的结果:
这下正常了。
相交的部分消失了。
结论1:硬件加速对PorterDuffXferMode有影响,使用前请关闭硬件加速。
那么这下真的天下太平了吗?nonono~不要太天真,不然怎么能叫万丈深渊呢。
继续试验其它模式: 将模式改为SRC_IN
WTF?????跟效果图根本不一致好吗。。!
!
在试试DST_IN
你确定你没有在逗我???? 怎么是这个鬼东西。
(当时鼓捣了我三天四夜,一直在日狗,只是先别急,慢慢来。)
为什么一定要依照那个效果图来呢。
。。 由于特么的那个图是官方的一个demo。
。 那么我们就来看看这个demo长什么样!
package io.appium.android.apis.graphics; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Xfermode; import android.os.Bundle; import android.view.View; public class Xfermodes extends GraphicsActivity { // 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*3/4, h*3/4), 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(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)); } private static class SampleView extends View { private static final int W = 64; private static final int H = 64; 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 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", "Multiply", "Screen" }; public 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.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); // draw the src/dst example into our offscreen bitmap int sc = canvas.saveLayer(x, y, x + W, y + H, null, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_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; } } } } }
一点一点看。截取onDraw里面的片段。这里
canvas.drawBitmap(mDstB, 0, 0, paint); paint.setXfermode(sModes[i]); canvas.drawBitmap(mSrcB, 0, 0, paint); paint.setXfermode(null);他是画了两个bitmap。网上有人说好像仅仅对bitmap生效。那究竟是不是这样呢。我们来试验一下。
我们新定义一个canvas 再定义一个bitmap 如今bitmap上画出来src 然后将bitmap画到canvas上:
public TestView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mDstPaint = new Paint(); mSrcPaint = new Paint(); mDstPaint.setColor(Color.YELLOW); mSrcPaint.setColor(Color.BLUE); setLayerType(View.LAYER_TYPE_SOFTWARE, null); mSrcBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mSrcBitmap); }
@Override protected void onDraw(Canvas canvas) { //dst canvas.drawRect(20,20,80,80,mDstPaint); //src // mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mCanvas.drawCircle(25,25,25,mSrcPaint); canvas.drawBitmap(mSrcBitmap,0,0,null); }如今的效果是这种:
加一个XOR 试试。。
日了狗了!!
。!!没反应啊。究竟是什么鬼。
是不是两个都须要bitmap才干够呢。再创建一个dstBitmap和dstCanvas?
mDstBitmap = Bitmap.createBitmap(50,50, Bitmap.Config.ARGB_8888); mDstCanvas = new Canvas(mDstBitmap);
加一个XOR 试试
@Override protected void onDraw(Canvas canvas) { //dst mDstCanvas.drawRect(20,20,80,80,mDstPaint); canvas.drawBitmap(mDstBitmap,0,0,mDstPaint); //src mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); mSrcCanvas.drawCircle(25,25,25,mSrcPaint); canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint); }效果例如以下:
最终他妈的出来了!!!
!那其它效果呢?
clear
一致的!
!!
!
好激动有没有。!
!
!搞了4天 越来越接近结论了。!
!
结论2:仅仅有两个bitmap的时候,才干够生效。
不要高兴太早。。
假设坑到这里就完了,那还叫坑么。
继续试。。嗯 好多模式都是一致的。
直到!!
!
SRC_IN和DST_IN 。会发现。
。。
都消失了。
为毛呢??
检查代码 发现 在往bitmap画圆之前就set了mode 这样会有影响
纠正
@Override protected void onDraw(Canvas canvas) { //dst mDstCanvas.drawRect(20,20,80,80,mDstPaint); canvas.drawBitmap(mDstBitmap,0,0,mDstPaint); //src mSrcCanvas.drawCircle(25,25,25,mSrcPaint); //再画圆之后 设置mode mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR)); canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint); }
发现
艹!!!!!。最终好了!
!
!!
!。!!
经过不懈努力。。!
撒花!
*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。
事实上我们刚才bitmap的大小是一样的。 然后都是从0,0開始 全然覆盖了。
那么错开一点点 是什么效果呢。调整代码例如以下
protected void onDraw(Canvas canvas) { //dst mDstCanvas.drawRect(20,20,80,80,mDstPaint); canvas.drawBitmap(mDstBitmap,20,20,mDstPaint); //src mSrcCanvas.drawCircle(25,25,25,mSrcPaint); mSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint); }
效果例如以下:
能够看有效果了!!。! 可是是一个什么鬼。!
! 矩形缺角??加蓝色一点??
这样看是非常难看出效果的。。来调整一下bitmap的底色 和矩形的大小:
把两个bitmap的底色都画成灰色, 矩形不要占满画布 留出空间
mDstCanvas.drawColor(Color.GRAY);
@Override protected void onDraw(Canvas canvas) { //dst 黄色 mDstCanvas.drawRect(0,0,40,40,mDstPaint); canvas.drawBitmap(mDstBitmap,20,20,mDstPaint); //src 蓝色 mSrcCanvas.drawCircle(25,25,25,mSrcPaint); canvas.drawBitmap(mSrcBitmap,0,0,mSrcPaint); }
嗯 这样就是两个bitmap了。
。非常明了 bitmap的内容 位置。
然后再搞SRC_IN模式
dst_in
那么把bitmap底色去掉。 改成透明的呢?
dst_in:
SRC_IN:
总结3:两个bitmap位置不全然重叠的效果如上,并不能总结出效果。要按实际效果来。
---------------------------------------------------------------------------------------------------------华丽丽的切割线-----------------------------------------------------------------------------------------
最终大总结,假设想让PorterDuffXferMode依照预期Demo(或者效果图)的效果图像实现,必须满足下面条件:
1、关闭硬件加速。
2、两个bitmap大小尽量一样。
3、背景色为透明色。
4、假设两个bitmap位置不全然一样,可能也是预期效果,仅仅只是你看到的效果和你自己脑补的预期效果不一致。
最后想再说几句。
鼓捣这个模式鼓捣了差点儿一周。每天晚上下班都在搞。查了无数资料。可是好多不完整。甚至有一些误导性。
所以为了避免后来者入坑。亲自试验,尽量总结。 假设有说的不对的地方请及时向我提出。我会及时改正。
假设本文帮助到了你。请点一个顶。或者评论一下,蟹蟹!
!!
!