Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)

Android 绘图学习

ComposeShader,PorterDuff.mode及Xfermode

1 ComposeShader 简介

ComposeShader 组合另外两种shader的效果。
ComposeShader构造函数:

/**
 * Create a new compose shader, given shaders A, B, and a combining mode.
 * When the mode is applied, it will be given the result from shader A as its
 * "dst", and the result from shader B as its "src".
 *
 * @param shaderA  The colors from this shader are seen as the "dst" by the mode
 * @param shaderB  The colors from this shader are seen as the "src" by the mode
 * @param mode     The mode that combines the colors from the two shaders. If mode
 *                 is null, then SRC_OVER is assumed.
*/
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) ;
/**
 * Create a new compose shader, given shaders A, B, and a combining PorterDuff mode.
 * When the mode is applied, it will be given the result from shader A as its
 * "dst", and the result from shader B as its "src".
 *
 * @param shaderA  The colors from this shader are seen as the "dst" by the mode
 * @param shaderB  The colors from this shader are seen as the "src" by the mode
 * @param mode     The PorterDuff mode that combines the colors from the two shaders.
*/
public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
        @NonNull PorterDuff.Mode mode) ;

参数说明:
shaderA,shaderB:要混合的两种shader,
Xfermode mode: 组合两种shader颜色的模式,
PorterDuff.Mode mode: 组合两种shader颜色的模式。

2 Xfermode是什么呢?

Xfermode称为图形混合模式也被称为过渡模式,把两个图形混合成一张图。

Xfermode 实现类有AvoidXfermode,PixelXorXfermode,PorterDuffXfermode,但是AvoidXfermode,PixelXorXfermode都已经被标识不推荐使用了,所以就只有PorterDuffXfermode一个实现类。PorterDuffXfermode用于图形合成时的图像过渡模式计算。
前两个实现类在API level 16被标记为Deprecated了(因为不支持硬件加速),用也可以,但是需要关闭硬件加速,高版本api默认打开硬件加速,如果需要关闭硬件加速,无法发挥OpenGLes的作用,也就无法充分利用GPU绘图能力,有关硬件加速请看https://developer.android.com/guide/topics/graphics/hardware-accel

不同版本对硬件加速的支持程度(摘抄自官网):

构造函数:
PorterDuffXfermode(PorterDuff.Mode mode)
mode:混合图像模式。
代码示例:

canvas.drawColor(Color.WHITE);
        RectF rectF = new RectF(0, 0, width, height);
        canvas.saveLayer(rectF,mPaint);
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(srcBmp, 0, 0, mPaint);
        mPaint.setXfermode(null);
        canvas.restore();

3 什么是PorterDuff.Mode?

Porter--Duff 分别是发明“Compositing Digital Images”两位学者的名字缩写,图形混合有很多种模式。PorterDuff.Mode分为Alpha合成模式和混合模式。图像合成就是将源像素和背景像素的颜色进行混合,最终显示的颜色取决于其RGB颜色分量和Alpha值。
Android 中PorterDuff.Mode模式包括:
Mode.CLEAR;
Mode.SRC;
Mode.DST;
Mode.SRC_OVER;
Mode.DST_OVER;
Mode.SRC_IN;
Mode.DST_IN;
Mode.SRC_OUT;
Mode.DST_OUT;
Mode.SRC_ATOP;
Mode.DST_ATOP;
Mode.XOR;
Mode.DARKEN;
Mode.LIGHTEN;
Mode.MULTIPLY;
Mode.SCREEN;
Mode.ADD;
Mode.OVERLAY;

关于PorterDuff.Mode的详细说明,请看android Developer的官网说明:https://developer.android.com/reference/android/graphics/PorterDuff.Mode
比较旧的混合标识图片:

新的标识模式说明:
原图,目标图:先绘制的是目标图,后绘制的是原图。
举例说明:
目标图片:

源图片:


CLEAR:
清除模式,[0, 0],即图像中所有像素点的alpha和颜色值均为0,此时所有图片不显示,所以在学习Canvas,可以设置画笔的Xfermode,mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));可以清空画布。

SRC:
只保留原图像的 alpha 和 color ,所以绘制出来只有源图。

DST:
只保留了目标图像的alpha和color值,所以绘制出来的只有目标图。

SRC_OVER: 源图像被绘制在目标图像的上方

DST_OVER: 目标图像被绘制在源图像的上方

SRC_IN: 在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响

DST_IN: 与SRC_IN 对比,在两者相交的地方绘制目标图像,并且绘制的效果会受到源图像对应地方透明度的影响

SRC_OUT: 在不相交的地方绘制源图像,表示如果相交处的目标色的alpha是完全不透明的,这时候源图像会完全被过滤掉,否则会受到相交处目标色 alpha 影响,呈现出对应色值。

DST_OUT:对比SRC_OUT , 在不相交的地方绘制目标图像,相交处根据源图像alpha进行过滤,完全不透明处则完全过滤,完全透明则不过滤。

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

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

XOR: 在不相交的地方按原样绘制源图像和目标图像,相交的地方受到对应alpha和颜色值影响,按公式进行计算,如果都完全不透明则相交处完全不绘制。

DARKEN: 效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合。

LIGHTEN: 对比DARKEN ,DARKEN 的目的是变暗,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下,色值取源色值和目标色值中的较大值,否则按上面算法进行计算。

MULTIPLY: 正片叠底,源图像素颜色值乘以目标图像素颜色值除以255得到混合后图像像素颜色值。结果色总是较暗的颜色,任何颜色与黑色复合产生黑色,任何颜色与白色复合保持不变,当用黑色或白色以外的颜色绘画时,绘画工具绘制的连续描边产生逐渐变暗的颜色。

SCREEN: 滤色,保留两个图层中较白的部分,较暗的部分被遮盖;当一层使用了滤色(屏幕)模式时,图层中纯黑的部分变成完全透明,纯白部分完全不透明,其他的颜色根据颜色级别产生半透明的效果。

ADD: 饱和度相加,不常用

OVERLAY: 像素是进行 Multiply (正片叠底)混合还是 Screen (屏幕)混合,取决于底层颜色,但底层颜色的高光与阴影部分的亮度细节会被保留。

4 paint.setXfermode

只对bitmap起作用,修改developer的原图目标图,边上变成透明:
代码说明:
生成两张700*700的图片,在图片的内部绘制两个能够相交的图形,然后在view中分别绘制两个bitmap,就可以看到上面的结果,注意由于两个图片一样大,所以不想交的部分会受另外一张图的透明度的影响。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;

public class ViewDemo12 extends View {

    private int width = 700;
    private int height = 700;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    private Paint mPaint;
    public ViewDemo12(Context context, AttributeSet attrs) {
        super(context, attrs);
        dstBmp = makeDst(width,height);
        srcBmp = makeSrc(width,height);
        mPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        RectF rectF = new RectF(0, 0, 700, 700);
        canvas.saveLayer(rectF,mPaint);
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.OVERLAY));
        canvas.drawBitmap(srcBmp, 0, 0, mPaint);
        mPaint.setXfermode(null);
        canvas.restore();

    }

    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(0xFFE91E63);
        c.drawOval(new RectF(200, 20, 700, 520), p);
        return bm;
    }

    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(0xFF2196F3);
        c.drawRect(20, 200,520,700, p);
        return bm;
    }

}

5 setXfermode 应用

前面我们利用BitmapShader实现了不同形状的图形,利用Xfermode也可以实现,利用SRC_IN。

public class ViewDemo13 extends View {

    private int width = 700;
    private int height = 700;
    private Bitmap dstBmp;
    private Bitmap srcBmp;
    private Paint mPaint;
    public ViewDemo13(Context context, AttributeSet attrs) {
        super(context, attrs);
        dstBmp = BitmapFactory.decodeResource(getResources(), R.drawable.hongbao);
        width = dstBmp.getWidth();
        height = dstBmp.getHeight();
        srcBmp = makeSrc(width,height);
        mPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        RectF rectF = new RectF(0, 0, width, height);
        canvas.saveLayer(rectF,mPaint);
        canvas.drawBitmap(dstBmp, 0, 0, mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(srcBmp, 0, 0, mPaint);
        mPaint.setXfermode(null);
        canvas.restore();
    }

    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(0xFFFFFFFF);
        p.setStyle(Paint.Style.FILL);
        c.drawRoundRect(0, 200,520,700,30,30, p);
        return bm;
    }
}

除此之外还能实现图片变亮,图片局部变暗,图片显示一部分,图片擦除等等诸多效果。

6 ComposeShader

混合两种shader,shader可以是BitmapShader,LinearGradient,RadialGradient,SweepGradient,所以可以有很多种组合。由于最后一个参数跟PorterDuff.mode有关,所以mode应用时将把从shaderA(ComposeShader 第一个参数)中获取的结果作为目标图像,shaderB(ComposeShader第二个参数)中获取的结果作为源图像,进行混合叠加。如果相交的两个图像大小不同,就会存在不想交的部分,不想交的部分是不会相互影响的,下面给出的示例代码,是两个大小相同的图像,完全透明,内部绘制了不透明的图形。

public class ViewDemo14 extends View {

    private int width = 700;
    private int height = 700;
    private Bitmap dstBmp;
    private Paint mPaint;
    BitmapShader bitmapShader;
    LinearGradient linearGradient;
    public ViewDemo14(Context context, AttributeSet attrs) {
        super(context, attrs);
        dstBmp = BitmapFactory.decodeResource(getResources(), R.drawable.hongbao);
        width = dstBmp.getWidth();
        height = dstBmp.getHeight();
        bitmapShader = new BitmapShader(dstBmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        linearGradient = new LinearGradient(0, 0, width, height, Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
        mPaint = new Paint();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        RectF rectF = new RectF(0, 0, width, height);
        canvas.saveLayer(rectF,mPaint);
        ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.ADD);
        mPaint.setShader(composeShader);
        canvas.drawRect(0, 0, width,height,mPaint);
        mPaint.setXfermode(null);
        canvas.restore();

    }
}

由于内容较多,本篇只是概述,后面会更加详细的讲解PorterDuff.mode及Xfermode。

android绘图之Paint(1)
android绘图之Canvas基础(2)
Android绘图之Path(3)
Android绘图之drawText绘制文本相关(4)
Android绘图之Canvas概念理解(5)
Android绘图之Canvas变换(6)
Android绘图之Canvas状态保存和恢复(7)
Android绘图之PathEffect (8)
Android绘图之LinearGradient线性渐变(9)
Android绘图之SweepGradient(10)
Android绘图之RadialGradient 放射渐变(11)
Android绘制之BitmapShader(12)
Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)
Android绘图之drawText,getTextBounds,measureText,FontMetrics,基线(14)
Android绘图之贝塞尔曲线简介(15)
Android绘图之PathMeasure(16)
Android 动态修改渐变 GradientDrawable

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容