SeniorUI05_Paint_Xfermode模式

高级UI汇总​​​​​​​
##一、Xfermode
设置或清除传输模式对象。传输模式定义了如何进行源像素(由绘图命令生成)与目标像素(渲染目标内容)。
通俗的说就是将绘制的图形的像素和Canvas上对应位置的像素按照一定的规则进行混合,形成新的像素,再更新到Canvas中形成最终的图形

##二 使用效果:
使用方法:Paint.setXfermode

我们一个像素的颜色都是由四个分量组成,即ARGB,A表示的是我们Alpha值,RGB表示的是颜色;
SRC [Sa, Sc] 表示的是原像素,原像素的值表示[Sa,Sc] Sa表示的就是源像素的Alpha值,Sc表示源像素的颜色值;
DST [ Da, Dc] 表示的是目标像素,目标像素的值表示[Da,Dc] Da表示的就是目标像素的Alpha值

SRC类:包含蓝色矩形的整个区域
DST类:包含黄色圆形的整个区域
这里写图片描述

  • ADD:
    饱和相加,对图像饱和度进行相加,不常用
    Saturate(S + D)

  • CLEAR:
    清除图像
    [0, 0]

  • DARKEN:
    变暗,较深的颜色覆盖较浅的颜色,若两者深浅程度相同则混合
    [Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]

  • DST:
    只显示目标图像
    [Da, Dc]

  • DST_ATOP:
    在源图像和目标图像相交的地方绘制【目标图像】,在不相交的地方绘制【源图像】,相交处的效果受到源图像和目标图像alpha的影响
    [Sa, Sa * Dc + Sc * (1 - Da)]

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

  • DST_OUT:
    只在源图像和目标图像不相交的地方绘制【目标图像】,在相交的地方根据源图像的alpha进行过滤,源图像完全不透明则完全过滤,完全透明则不过滤
    [Da * (1 - Sa), Dc * (1 - Sa)]

  • DST_OVER:
    将目标图像放在源图像上方
    [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc]

  • LIGHTEN:
    变亮,与DARKEN相反,DARKEN和LIGHTEN生成的图像结果与Android对颜色值深浅的定义有关
    [Sa + Da - SaDa, Sc(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]

  • MULTIPLY:
    正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值
    [Sa * Da, Sc * Dc]

  • OVERLAY:
    叠加
    ( alpha_{out} = alpha_{src} + alpha_{dst} - alpha_{src} * alpha_{dst} )
    (C_{out} = begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} lt alpha_{dst} * alpha_{src} * alpha_{dst} - 2 ( alpha_{dst} - C_{src}) ( alpha_{src} - C_{dst}) & otherwise end{cases})

  • SCREEN:
    滤色,色调均和,保留两个图层中较白的部分,较暗的部分被遮盖
    [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]

  • SRC:
    只显示源图像
    [Sa, Sc]

  • SRC_ATOP:
    在源图像和目标图像相交的地方绘制【源图像】,在不相交的地方绘制【目标图像】,相交处的效果受到源图像和目标图像alpha的影响
    [Da, Sc * Da + (1 - Sa) * Dc]

  • SRC_IN:
    只在源图像和目标图像相交的地方绘制【源图像】
    [Sa * Da, Sc * Da]

  • SRC_OUT:
    只在源图像和目标图像不相交的地方绘制【源图像】,相交的地方根据目标图像的对应地方的alpha进行过滤,目标图像完全不透明则完全过滤,完全透明则不过滤
    [Sa * (1 - Da), Sc * (1 - Da)]

  • SRC_OVER:
    将源图像放在目标图像上方
    [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc]

  • XOR:
    在源图像和目标图像相交的地方之外绘制它们,在相交的地方受到对应alpha和色值影响,如果完全不透明则相交处完全不绘制
    [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]

##三、效果图代码

/**
 - Xfermode
 - 注意:
 -  1. Src和DST的 大小最好一致(先创建一个Bitmap,再在Bitmap中设置内容,从而分别形成Src和Dist,要有src和dst重合然后绘制的概念)
 -  2. canvas 要saveLayer
 -  3. 有些效果要取消硬件加速
 */
public class XfermodeActivity 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 = 250;
        private static final int H = 250;
        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
                @SuppressLint("WrongConstant") 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
                labelP.setAntiAlias(true);
                labelP.setTextSize(24);
                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 + 60;
                }
            }
        }
    }
}

##四 基本使用

  • 保存layer
 int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
  • 先画目标图Dst
 canvas.drawBitmap(BmpSRC,0,0,mBitPaint);
  • 设置xfermode
  mBitPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
  • 画源图Src
 canvas.drawBitmap(BmpRevert,0,0,mBitPaint);
  • 清空xfermode
  mBitPaint.setXfermode(null);
  • canvas恢复到layerId
 canvas.restoreToCount(layerId);

##五 注意事项

  • 需要设置layer然后,在绘制,最终canvas回退到保存的layerId
  • 示例中绘制的是Bitmap,而且绘制的区域是重合的,注意SrcBitmap和DstBitmap的宽高,不仅仅是圆形区域或者矩形区域,实际如果绘制的是path的效果时,结果可能和示例中不一样
  • xfermode的效果炫酷,代码简单,但是效果转换成代码困难,可以这样理解:我们先在canvas创建一个layer;layer画效果如圆角、半透明效果;再画实际我们要的内容,两个内容会合并到一起去;canvas回去,paint清空Xfermode

##六 实例
倒影图片
这里写图片描述
圆角图片
这里写图片描述
橡皮擦效果 (刮刮卡效果)
这里写图片描述

心电图效果( 目标图片 —心电图、源图片 ---- 不透明的图 就是通过改变透明图片的不透明区域的宽度,来实现心电图的动画效果)
这里写图片描述

不规则水波纹效果,当然也可以做SRC_IN 的效果(注意选择谁为源图片,谁为目标图片)
这里写图片描述

TwitterView
这里写图片描述

书柜
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值