(1)PorterDuffXfermode学习总结
这个示意图是建立在上层图和下层图的大小是一模一样大的。例如,上层图400x400,下层图400x400.
圆的半径为100,坐标中心点在(100,100).矩形的长宽都为200.左上角坐标为(100,100).
我们常用的模式有:
SRC_IN:取两层交集部分,交集内容取决于上层,用于遮罩,改变图片的形状
DST_IN:取两层交集部分,交集内容取决于下层,作用同上
SRC_OVER:上下层都显示,运算后上层在上面,假如两张图都是半透明的,这种模式无论谁上谁下,两者都看得到,达不到理想的只显示一个。
DST_OVER:上下层都显示,运算后下层在上面,缺陷同上。
注意:上图示意的效果只适用与完全不透明的两张图的计算效果。如果是半透明,根据公式来算,结果往往不尽人意。
先说使用方式,有三处可以用。
分别是Path类,canvas.clipPath(path,Region.Op.UNION),以及Paint类。
我们详细来说Paint类。
Api:
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC));
PorterDuff的源码如下:
packageandroid.graphics;
publicclassPorterDuff {
//these value must match their native equivalents. See SkPorterDuff.h
publicenumMode {
/**[0, 0] */
CLEAR (0),
/**[Sa, Sc] */
SRC (1),
/**[Da, Dc] */
DST (2),
/**[Sa + (1 -Sa)*Da, Rc = Sc + (1-Sa)*Dc] */
SRC_OVER (3),
/**[Sa + (1 -Sa)*Da, Rc = Dc + (1-Da)*Sc] */
DST_OVER (4),
/**[Sa * Da, Sc * Da] */
SRC_IN (5),
/**[Sa * Da, Sa * Dc] */
DST_IN (6),
/**[Sa * (1 -Da), Sc * (1-Da)] */
SRC_OUT (7),
/**[Da * (1 -Sa), Dc * (1-Sa)] */
DST_OUT (8),
/**[Da, Sc * Da + (1 -Sa) * Dc] */
SRC_ATOP (9),
/**[Sa, Sa * Dc + Sc * (1 -Da)] */
DST_ATOP (10),
/**[Sa + Da -2 * Sa * Da, Sc * (1-Da) + (1-Sa) * Dc] */
XOR (11),
/**[Sa + Da -Sa*Da,
Sc*(1-Da) + Dc*(1 -Sa) + min(Sc, Dc)] */
DARKEN (12),
/**[Sa + Da -Sa*Da,
Sc*(1-Da) + Dc*(1 -Sa) + max(Sc, Dc)] */
LIGHTEN (13),
/**[Sa * Da, Sc * Dc] */
MULTIPLY (14),
/**[Sa + Da -Sa * Da, Sc + Dc-Sc * Dc] */
SCREEN (15),
/**Saturate(S + D) */
ADD (16),
OVERLAY (17);
Mode(intnativeInt) {
this.nativeInt= nativeInt;
}
/**
*@hide
*/
publicfinalintnativeInt;
}
}
Sa:全称为Sourcealpha,表示源(上层,后画的)图的Alpha通道;
Sc:全称为Sourcecolor,表示源(上层,后画的)图的颜色;
Da:全称为Destinationalpha,表示目标图(下层,先画的)的Alpha通道;
Dc:全称为Destinationcolor,表示目标图(下层,先画的)的颜色.
Sa,Sc是上层图的属性。
Da,Dc是下层图的属性。
举例如下:
Paintpaint = new Paint();
canvas3.drawBitmap(clockBitmap,0, 40, null);
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
canvas3.drawBitmap(partBitmap,width/2,0, paint);
paint.setXfermode(null);
首先要明确,画下层图是不为画笔(Paint)添加Xfermode的属性的。
先画出下层图,也就是clockBitmap.我们的图形计算是建立在下层图已经存在的前提下的。如果下层图什么都没画,就相当于下层图的属性Da为0.
然后画上层图,记住,上面源码里面计算得出的alpha值和颜色值都只针对上层图的位置。
比如,上层图和下层图一样大。那么相当于下层图被遮住,上层图处理得到的结果就ok。
当下层图比上层图大的时候,图形处理就只处理上层图的区域,也就是公式结果只会作用与上层图的区域,而上层图边界外的下层图区域,还是和原来一样的。好好揣摩这句话。
例如,上层图大小是(0,0,300,300),下层图为(0,0,500,500)那么下层图的(x,y)(300<x<500&300<y<500)的区域在上层图画完之后,还是保持原样的。
比如我想实现10.27的(4)那个动画问题,类似裁剪的效果。
有个前提是,下层和上层的图片最好至少有一个,满足透明度要么是0,或者是1,也就是说要么是完全透明,要么是完全不透明。
这样才好处理,要是两个都是半透明的。用这种位图运算来进行图形混合除了用src,dst这两种模式,否则会导致计算后的alpha值,或者颜色值达不到要求(这里说的要求是指上层图区域的全部点满足-----要么是上层图的颜色和alpha值或者下层图的)。
(2)PorterDuffXfermode实际运用
必须要配合图层使用。否则大部分时候得不到期望的结果。
原因是onDraw(Canvascanvas)的canvas默认的图层并不是透明图层。它是有背景的。
而我们通过canvas.saveLayer新建的图层是透明的。更合适来进行图像的处理。
所以涉及到图像处理的,步骤如下:
1.新建图层,新图层入栈
intlayer=canvas.saveLayer(0, 0, 500, 500, null,Canvas.ALL_SAVE_FLAG);//设置图层大小。
2.在新图层上画底层图Dst
canvas.draw...不一定要是Bitmap
3.Paint对象设置Xfermode属性
paint.setXfermode(newPorterDuffXfermode(PorterDuff.Mode.DST_IN));
4.在新图层上画上层图Src
canvas.drawBitmap...(..,...,paint,...);...必须要是Bitmap,注意是必须,如果是drawCircle什么的会出问题(实测过的),假设要drawCircle就新建一个Bitmapb=Bitmap.createBitmap...,Canvas newCanvas=newCanvas(b),newCanvas.drawCircle..,canvas.drawBitmap(b,....);
5.新图层出栈
canvas.restoreToCount(layer);
(3)Bitmap裁剪成圆形,并返回bitmap类型
public Bitmap toRoundBitmap(Bitmap bitmap) {
intwidth = bitmap.getWidth();
intheight = bitmap.getHeight();
floatroundPx;
floatleft, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
if(width <= height) {
roundPx= width / 2;
left= 0;
top= 0;
right= width;
bottom= width;
height= width;
dst_left= 0;
dst_top= 0;
dst_right= width;
dst_bottom= width;
}else {
roundPx= height / 2;
floatclip = (width - height) / 2;
left= clip;
right= width - clip;
top= 0;
bottom= height;
width= height;
dst_left= 0;
dst_top= 0;
dst_right= height;
dst_bottom= height;
}
Bitmapoutput = Bitmap.createBitmap(width, height, Config.ARGB_8888);
Canvascanvas = new Canvas(output);
intlayer=canvas.saveLayer(0, 0, width, height, null,Canvas.ALL_SAVE_FLAG);//设置图层大小。
finalint color = 0xff424242;
finalPaint paint = new Paint();
finalRect src = new Rect((int) left, (int) top, (int) right, (int)bottom);
finalRect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right,(int) dst_bottom);
finalRectF rectF = new RectF(dst);
paint.setAntiAlias(true);//设置画笔无锯齿
//canvas.drawARGB(0,0, 0, 0); //填充整个Canvas
paint.setColor(color);
//以下有两种方法画圆,drawRounRect和drawCircle
//canvas.drawRoundRect(rectF, roundPx, roundPx, paint);//画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
canvas.drawCircle(roundPx,roundPx, roundPx, paint);
paint.setXfermode(newPorterDuffXfermode(Mode.SRC_IN));//设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452
canvas.drawBitmap(bitmap,src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已经draw了的Circle
canvas.restoreToCount(layer);
returnoutput;
}
实测有效,以后直接拷贝进去就行了。