一.先展示一下效果吧。
,
PorterDuffXfermode
上代码之前我们先来了解一个类PorterDuffXfermode。
使用 PorterDuff 模式可以创建一个图层混合模式,下面就是18中混合模式
// these value must match their native equivalents. See SkPorterDuff.h
public enum Mode {
/** [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);
其中 Sa 代表source alpha ,即源 alpha 值 ,Da 代表 Destination alpha ,即 目标alpha值 ,Sc 代表 source color ,即源色值 ,Dc 代表 Destination color ,即目标色值,并且这所有的计算都以像素为单位,在某一种混合模式下,对每一个像素的alpha 和 color 通过对应算法进行运算,得出新的像素值,进行展示;具体的例子请网上寻找例子探索。
制作圆形头像
现在直接进入我们的主题---制作圆形头像!
从结果上来看应该需要一个圆,一张正方形的头像。
预想方法一:底部一个圆,然后在绘制一张头像在圆上,利用圆来限制头像的显示大小。
public void initBitmap() {
//禁用硬件加速器,因为有些硬件加速器不支持
setLayerType(LAYER_TYPE_SOFTWARE, null);
//设置抗锯齿
mpaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mpaint.setColor(Color.YELLOW);
<span style="line-height: 21.06px;"><span style="white-space: pre;"> </span>//创建以头像为底图</span>
mbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.head);
//以后的绘制都将显示在moutbitmap上面
moutbitmap = Bitmap.createBitmap(mbitmap.getWidth(), mbitmap.getHeight(), Config.ARGB_8888);
Canvas canvas_outbitmap = new Canvas(moutbitmap);
//Dst
canvas_outbitmap.drawCircle(mbitmap.getWidth() / 2, mbitmap.getHeight() / 2, mbitmap.getWidth() / 2, mpaint);
PorterDuffXfermode mode = new PorterDuffXfermode(Mode.SRC_IN);
mpaint.setXfermode(mode);
//Src
canvas_outbitmap.drawBitmap(mbitmap, 0, 0, mpaint);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i("clx", "onDraw");
canvas.drawBitmap(moutbitmap, 0, 0, null);
}
mbitmap = BitmapFactory.decodeResource(getResources(), R.drawable.head);
//以后的绘制都将显示在moutbitmap上面
moutbitmap = Bitmap.createBitmap(mbitmap.getWidth(), mbitmap.getHeight(), Config.ARGB_8888);
Canvas canvas_outbitmap = new Canvas(moutbitmap);
创建一张BitMap图,大小为原图的大小mbitmap.getWidth(), mbitmap.getHeight()。
因为在直接在BitMap上修改,所以创建一个Canvas画布,new Canvas(moutbitmap),在该canvas上绘制相当于在刚创建的Bitmap上绘制。
接下来就在该画布上涂涂改改咯。
混合模式就是两个图层叠加绘制,所以首先先绘制目标图层(可以理解成最底下的图层)
canvas_outbitmap.drawCircle(mbitmap.getWidth() / 2, mbitmap.getHeight() / 2, mbitmap.getWidth() / 2, mpaint);
从参数来看绘制一个mbitmap大小的圆。
第二步就是把头像给混合上去。
需要什么样的混合模式首先要先设置,部分该mode的alpha是怎么混合了,直接看英文意思,Source In 就是接下来绘制源图层将限制在目标图层里。
PorterDuffXfermode mode = new PorterDuffXfermode(Mode.SRC_IN);
mpaint.setXfermode(mode);
第三步:绘制源图层。
//Src
canvas_outbitmap.drawBitmap(mbitmap, 0, 0, mpaint);
最后一步就是只需要把混合好的bitmap绘制到你的屏幕就好了。
canvas.drawBitmap(moutbitmap, 0, 0, null);
-----待续------
“老师!!!,我还有另一种做法!!!!”
上面一种方法的最底层的图层是使用drawCircle绘制的,现在我们使用另一种方式,我们不用画圆,直接使用一张圆的图。
绘制的步骤差不多。
// 从canvas层面去除锯齿
canvas.setDrawFilter(mDrawFilter);
canvas.drawColor(Color.TRANSPARENT);
int sc = canvas.saveLayer(0, 0, mTotalWidth, mTotalHeight, null, Canvas.ALL_SAVE_FLAG);
// 绘制波纹部分
canvas.drawBitmap(mSrcBitmap, mSrcRect, mDestRect, mBitmapPaint);
// 设置图像的混合模式
mBitmapPaint.setXfermode(mPorterDuffXfermode);
// 绘制遮罩圆
canvas.drawBitmap(mMaskBitmap, mMaskSrcRect, mMaskDestRect, mBitmapPaint);
mBitmapPaint.setXfermode(null);
/**
* canvas.saveLayer()方法会返回一个int值,用于表示layer的ID,
* 在我们对这个新layer绘制完成后可以通过调用canvas.restoreToCount(layer)
* 或者canvas.restore()把这个layer绘制到canvas默认的layer上去,这样就完成了一个layer的绘制工作。
*/
canvas.restoreToCount(sc);
这里就不分析了。
代码代码