PorterDuffXfermode的初级使用

来源:互联网 发布:淘宝手机端装修模板 编辑:程序博客网 时间:2024/05/17 20:27

一、关于PorterDuffXfermode

java.lang.Object   ↳
android.graphics.Xfermode    ↳
android.graphics.PorterDuffXfermode

PorterDuffXfermode继承自Xfermode是个简单图像合成类,源码中位于graphics下。当图像交叉绘制时,可通过PorterDuffXfermode指定Paint的绘制规则,从而达到图像的合成效果。

与常见的类命名方式不同,PorterDuffXfermode中Porter,Duff指的是Thomas Porter和Tom Duff以及其发表于1984年论文《Compositing digital images》(该论文可在ACM Digital Library上花费15美元购得, 详见https://dl.acm.org/purchase.cfm?id=808606&CFID=486449268&CFTOKEN=73298988)

关于PorterDuffXfermode的使用,可以参见API Demos中在Xfermodes.java。

Xfermodes.java源码如下:

public class Xfermodes extends Activity {    // 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);        this.requestWindowFeature(Window.FEATURE_NO_TITLE);        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);//yellow                paint.setXfermode(sModes[i]);                canvas.drawBitmap(mSrcB, 0, 0, paint);//blue                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;                }            }        }    }}


运行结果:

从效果图中可以看出2个图像的重叠绘制可产生16种效果,这16个参数定义在PorterDuff中,以下是PorterDuff.java的源码:

public class PorterDuff {    // 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);        Mode(int nativeInt) {            this.nativeInt = nativeInt;        }        /**         * @hide         */        public final int nativeInt;    }}



二、使用PorterDuffXfermode

通过PorterDuffXfermode通过2层图像的叠加绘制可快速实现类似易信中更换用户头像的遮罩view(灰色的遮罩层,内含圆形的头像选择框):


具体的实现是在Paint中通过setXfermode()设置

<span style="white-space:pre"></span>xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);        mPaint = new Paint();        mPaint.setAntiAlias(true);<span style="white-space:pre"></span>mPaint.setXfermode(xfermode);

完整源码如下:

/** * Created by Farble on 2015/2/3. */public class CoverView extends View {    private Paint mPaint;    private Bitmap mBg;/*background bitmap*/    private Bitmap mCircle;/*circle bitmap*/    private PorterDuffXfermode xfermode;    private float mScanbox;    private int mStatrX;    private int mStatrY;    public CoverView(Context context) {        super(context);        init();    }    public CoverView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public CoverView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        init();    }    @SuppressWarnings("deprecation")    private void init() {        mScanbox = getContext().getResources().getDimension(R.dimen.circle_d);        WindowManager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);        int width = manager.getDefaultDisplay().getWidth();        mStatrX = width / 2 - (int) mScanbox / 2;        mStatrY = (int) getContext().getResources().getDimension(R.dimen.circle_totop);        mBg = BitmapFactory.decodeResource(getResources(), R.drawable.test_bgr);        mCircle = BitmapFactory.decodeResource(getResources(), R.drawable.test_circle);        xfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);        mPaint = new Paint();        mPaint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        mPaint.setFilterBitmap(false);        int sc = canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), 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.drawBitmap(mBg, 0, 0, mPaint);        mPaint.setXfermode(xfermode);//设置MODE        canvas.drawBitmap(mCircle, mStatrX, mStatrY, mPaint);        mPaint.setXfermode(null);//清空MODE        canvas.restoreToCount(sc);    }}

注意点:

1.这里的test_bgr.png是一张中间透明圆,边角全黑的图片

2.若灰色的遮罩图层由代码生成,类似使用以下方法时:

 private Bitmap makeBitmap(int mwidth, int mheight, int resource, int staX, int staY) {        Bitmap bm = Bitmap.createBitmap(mwidth, mheight, Bitmap.Config.ARGB_8888);        Canvas c = new Canvas(bm);        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);        p.setColor(resource);        c.drawRect(staX, staY, mwidth, mheight, p);        return bm;    }
特别注意应建议使用Bitmap.Config.ARGB_8888,之前做项目时为节约内存使用了Bitmap.Config.RGB_565,导致一直达不到效果并且找不出原因。

优点:

  1. 与传统的全面绘制来实现圆形的头像相比,通过这种方式使代码更简洁易懂
  2. 通过这种方式可实现多种形状的头像,如梅花型,心型(只需美工那边提供相应的遮罩图片即可)
  3. 通过此方式实现的圆形头像不存在锯齿的问题(遮罩图像由美工提供,完全可以做到抗锯齿)


0 0
原创粉丝点击