zoukankan      html  css  js  c++  java
  • Android Paint Xfermode 学习小结

    一、setXfermode(Xfermode xfermode)

    Xfermode国外有大神称之为过渡模式,这种翻译比较贴切但恐怕不易理解,大家也可以直接称之为图像混合模式,因为所谓的“过渡”其实就是图像混合的一种,这个方法跟我们上面讲到的setColorFilter蛮相似的。查看API文档发现其果然有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode,这三个子类实现的功能要比setColorFilter的三个子类复杂得多。

    由于AvoidXfermode, PixelXorXfermode都已经被标注为过时了,所以这次主要研究的是仍然在使用的PorterDuffXfermode:

    二、PorterDuffXfermode

    该类同样有且只有一个含参的构造方法PorterDuffXfermode(PorterDuff.Mode mode),虽说构造方法的签名列表里只有一个PorterDuff.Mode的参数,但是它可以实现很多酷毙的图形效果!!而PorterDuffXfermode就是图形混合模式的意思,其概念最早来自于SIGGRAPH的Tomas Proter和Tom Duff,混合图形的概念极大地推动了图形图像学的发展,延伸到计算机图形图像学像Adobe和AutoDesk公司著名的多款设计软件都可以说一定程度上受到影响,而我们PorterDuffXfermode的名字也来源于这俩人的人名组合PorterDuff,那PorterDuffXfermode能做些什么呢?我们先来看一张API DEMO里的图片:

    这张图片从一定程度上形象地说明了图形混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,在API中Android为我们提供了18种(比上图多了两种ADD和OVERLAY)模式: 

      ADD:饱和相加,对图像饱和度进行相加,不常用

      CLEAR:清除图像

      DARKEN:变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合

      DST:只显示目标图像

      DST_ATOP:在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】,相交处的效果受到源图像和目标图像alpha的影响

      DST_IN:只在源图像和目标图像相交的地方绘制【目标图像】,绘制效果受到源图像对应地方透明度影响

      DST_OUT:只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤

      DST_OVER:将目标图像放在源图像上方

      LIGHTEN:变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关

      MULTIPLY:正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值

      OVERLAY:叠加

      SCREEN:滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖

      SRC:只显示源图像

      SRC_ATOP:在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】,相交处的效果受到源图像和目标图像alpha的影响

      SRC_IN:只在源图像和目标图像相交的地方绘制【源图像】

      SRC_OUT:只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤

      SRC_OVER:将源图像放在目标图像上方

      XOR:在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制

    而上面这张图片对应的官方DEMO代码如下:https://android.googlesource.com/platform/development/+/master/samples/ApiDemos/src/com/example/android/apis/graphics/Xfermodes.java

    public class sampleActivity extends AppCompatActivity {
        // 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 = 200;
            private static final int H = 200;
            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;
                    }
                }
            }
        }
    }

    对于上面这些mode的详细介绍在GA_STUDIO的这篇文章和AIGESTUDIO的这篇文章都有非常详尽的介绍

    三、使用场景

    以下是PorterDuffXfermode的一些使用场景:

    1、自定义loading样式:

    代码如下:

    public class LogoLoadingView extends View {
    
        private int totalW,totalH;
        private Paint paint;
        private Bitmap bitmap;
        private int currentTop;
        private RectF rectF;
        private PorterDuffXfermode xfermode;
    
        public LogoLoadingView(Context context) {
            super(context);
            init();
        }
    
        public LogoLoadingView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init();
        }
    
        private void init(){
            paint=new Paint();
            paint.setAntiAlias(true);//设置抗锯齿
            paint.setStyle(Paint.Style.FILL);//设置填充样式
            paint.setDither(true);//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
            paint.setFilterBitmap(true);//加快显示速度,本设置项依赖于dither和xfermode的设置
            bitmap= BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);//从资源文件中解析获取Bitmap
            xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
            /**
             * 设置当前矩形的高度为0
             */
            currentTop=bitmap.getHeight();
            rectF=new RectF(0,currentTop,bitmap.getWidth(),bitmap.getHeight());
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(bitmap.getWidth(),bitmap.getHeight());
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            rectF.top=currentTop;
            /**
             * 设置View的离屏缓冲。在绘图的时候新建一个“层”,所有的操作都在该层而不会影响该层以外的图像
             * 必须设置,否则设置的PorterDuffXfermode会无效,具体原因不明
             */
            int sc=canvas.saveLayer(0,0,totalW,totalH,paint,Canvas.ALL_SAVE_FLAG);
            canvas.drawBitmap(bitmap,0,0,null);
            paint.setXfermode(xfermode);
            paint.setColor(Color.RED);
            canvas.drawRect(rectF,paint);
            paint.setXfermode(null);
            /**
             * 还原画布,与canvas.saveLayer配套使用
             */
            canvas.restoreToCount(sc);
            if (currentTop>0){
                currentTop--;
                postInvalidate();
            }
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            totalW=w;
            totalH=h;
        }
    }

    2、圆形图片

    代码如下:

    public class CircleImageView extends View {
        private int resId;
        private Bitmap bitmap;
        private Paint paint;
        private int bitmapWidth,bitmapHeight;
        private int size;
        private PorterDuffXfermode xfermode;
    
        public CircleImageView(Context context, AttributeSet attrs) {
            super(context, attrs);
            TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CircleImageView);
            resId=array.getResourceId(R.styleable.CircleImageView_imageRes,R.mipmap.ic_launcher);
            array.recycle();
            init();
        }
    
        private void init(){
            paint=new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setDither(true);//设定是否使用图像抖动处理,会使绘制出来的图片颜色更加平滑和饱满,图像更加清晰
            paint.setFilterBitmap(true);//加快显示速度,本设置项依赖于dither和xfermode的设置
            bitmap= BitmapFactory.decodeResource(getResources(),resId);
            bitmapWidth=bitmap.getWidth();
            bitmapHeight=bitmap.getHeight();
            xfermode=new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP);
            size=Math.min(bitmapWidth,bitmapHeight);
        }
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            setMeasuredDimension(size,size);
        }
    
        /**
         * 生成圆形Bitmap
         * @return
         */
        private Bitmap makeCircle(){
            Bitmap bitmap=Bitmap.createBitmap(size,size, Bitmap.Config.ARGB_8888);
            Canvas canvas=new Canvas(bitmap);
            Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setColor(Color.BLUE);
            paint.setStyle(Paint.Style.FILL);
            int radius=size/2;
            canvas.drawCircle(size/2,size/2,radius,paint);
            return bitmap;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            int sc=canvas.saveLayer(0,0,size,size,paint,Canvas.ALL_SAVE_FLAG);
            Bitmap dst=makeCircle();
            canvas.drawBitmap(dst,0,0,paint);
            paint.setXfermode(xfermode);
            canvas.drawBitmap(bitmap,0,0,paint);
            paint.setXfermode(null);
            canvas.restoreToCount(sc);
        }
    }

    对应属性定义:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="CircleImageView">
            <attr name="imageRes" format="reference"/>
        </declare-styleable>
    </resources>

    关于圆形图片的详细介绍可以看鸿洋大大的这篇博文,介绍的非常详尽:

    http://blog.csdn.net/lmj623565791/article/details/42094215

    关于PorterDuffXfermode的应用还有非常多,这里简单介绍这几个。

    以上内容大部分参考自:

    http://blog.csdn.net/tianjian4592/article/details/44783283

    http://blog.csdn.net/aigestudio/article/details/41316141

    http://www.cnblogs.com/tianzhijiexian/p/4297172.html

  • 相关阅读:
    EM
    te2
    te
    XLnet
    GPT
    40.Properties
    38.特殊操作流
    37.I/O流
    35.File
    day68日考
  • 原文地址:https://www.cnblogs.com/libertycode/p/6290497.html
Copyright © 2011-2022 走看看