PorterDuffXfermode 学习总结

(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.我们的图形计算是建立在下层图已经存在的前提下的。如果下层图什么都没画,就相当于下层图的属性Da0.

然后画上层图,记住,上面源码里面计算得出的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);

//以下有两种方法画圆,drawRounRectdrawCircle

//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;

}


实测有效,以后直接拷贝进去就行了


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值