Shader

作者: CZKGO | 来源:发表于2019-07-13 14:15 被阅读0次

            Shader基于Skia图形库,是在绘制对象时使用的水平范围颜色变化的基类。其子类通过Paint类的setShader方法加载给paint对象,在这之后,使用该paint对象绘制其他任何对象时(bitmap除外)都将从Shader中获得其颜色变化。

    TileMode

            中定义枚举TileMode,TileMode包含三种模式,分别是:

    • CLAMP 如果绘制范围超过Shader定义的范围,则剩余部分重复绘制其边缘颜色
    • REPEAT 如果绘制范围超过Shader定义的范围,则在垂直和水平方向重复绘制Shader定义的范围
    • MIRROR 在超出后,同样在垂直和水平方向重复绘制,不过是以镜面的形式

    Shader子类

            Shader有五个子类,分别是:

    • BitmapShader(位图渲染)
    • LinearGradient(线性渲染)
    • SweepGradient(梯度渲染)
    • RadialGradient(光束渲染)
    • ComposeShader(组合渲染)
              接下来将分别介绍这五个子类

    1.BitmapShader

            BitmapShader的构造方法如下:

    /**
     * Call this to create a new shader that will draw with a bitmap.
     * 调用它来创建一个使用bitmap绘制的新shader
     * @param bitmap shader里使用的bitmap
     * @param tileX 横向绘制模式
     * @param tileY 竖向绘制模式
     */
    public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
        this(bitmap, tileX.nativeInt, tileY.nativeInt);
    }
    
            下面将通过一个气球图片介绍来介绍BitmapShader,气球图片如下:

            下面表格横向标题表示参数tileX,竖向标题表示tileY,表格中图片表示绘制结果

    参数 CLAMP REPEAT MIRROR
    CLAMP
    REPEAT
    MIRROR

    2.LinearGradient

            LinearGradient有两个构造函数,如下:

    /**
     * @param x0           线性渐变的起始x坐标
     * @param y0           线性渐变的起始y坐标
     * @param x1           线性渐变的结束x坐标
     * @param y1           线性渐变的结束y坐标
     * @param colors       沿线性渐变分布的颜色
     * @param positions    对应颜色数组里面个个颜色对应的位置,如果为null,则个个颜色将均匀分布
     * @param tile         Shader的渲染模式
    */
    public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
            @Nullable float positions[], @NonNull TileMode tile) {
        //代码省略
    }
    
    /**
     * 和上面同名的参数,表示含义相同
     * @param color0   线性渐变的起始颜色
     * @param color1   线性渐变的终止颜色
    */
    public LinearGradient(float x0, float y0, float x1, float y1,
            @ColorInt int color0, @ColorInt int color1,
            @NonNull TileMode tile) {
        //代码省略
    }
    

            从上可知,构造方法二只是构造方法一的一种简化,它将colors[]替换成了color0,color1,并去掉了positions[]参数,所以下面将以构造方法一来说明LinearGradient。
            首先来演示一下:

    paint.setShader(new LinearGradient(0, 0, 0, height,
            new int[]{Color.RED, Color.YELLOW, Color.BLUE},
            null, Shader.TileMode.REPEAT));
    Rect rect = new Rect(0, 0, width, height);
    canvas.drawRect(rect, paint);
    
            上文代码中定义了线性渐变的颜色依次是红黄蓝,渐变的起始坐标是(0,0),终止坐标是(0,height),渲染模式是REPEAT,绘制效果如下:

           同样,如果我们将起始坐标保持(0,0),终止坐标设为(width,0)或(width, height),将得到:

    横向渐变 对角渐变

            实际上,在上面三张图中,都只是将起始坐标到终止坐标的一条渐变线通过渲染模式REPEAT沿着该渐变线绘制完成。渐变的起始坐标和终止坐标可以在绘制范围内,也可以在绘制范围外。

    描述 坐标在绘制范围内 坐标在绘制范围外
    渐变颜色 红,黄,蓝 红,黄,蓝
    起始坐标 (width / 3, height / 3) (0, 0)
    终止坐标 (width*2 / 3, height / 3) (width, height)
    绘制范围 Rect(0, 0, width, height)                                           Rect(width / 3, height / 3, width * 2 / 3, height * 2 / 3)
    绘制结果

    上面“坐标在绘制范围内”的“绘制结果”中的白色线条就是最初的渐变线位置

            从上边表格可以看出,当渐变的起始坐标和终止坐标在绘制范围内时,多余部分会根据渲染模式来进行绘制(这里的渲染模式是REPEAT)。
            当渐变的起始坐标和终止坐标在绘制范围外时,绘制范围内的内容还是是渐变通过渲染模式绘制到该范围内。
            在线性渐变中,会以定义的渐变线的垂直和水平方向以设置好渲染模式来填充多余部分,的下面表格展示三种渲染模式通过渐变线渲染的结果,图中白色中就是定义的渐变线位置:

    CLAMP REPEAT MIRROR

    3.SweepGradient

            SweepGradient和LinearGradient类似,同样有两个构造方法,且第二个是第一个的简化,如下:

    /**
     * @param cx        中心x坐标
     * @param cy        中心y坐标
     * @param colors    围绕中心渐变的颜色数组,
     * @param positions 可以是null。
     *                  对应颜色组中各个颜色的位置,从0开始到1.0结束。
     *                  如果数值不是单调的,将会产生不可预知的结果
     *                  如果为null,颜色将均匀
     */
    public SweepGradient(float cx, float cy,
            @NonNull @ColorInt int colors[], @Nullable float positions[]) {
        //代码省略
    }
    
    /**
     * 和上面同名的参数,表示含义相同
     * @param color0   sweep的起始颜色
     * @param color1   sweep的终止颜色
     */
    public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
        //代码省略
    }
    

            SweepGradient将以设定的圆心为中心,向外依次辐射设定的颜色值,理论上没有辐射不到的位置,所以不需要通过渲染模式填充绘制不到的地方。首先来演示一下:

    paint.setShader(new SweepGradient(width / 2, height / 2,
            new int[]{Color.RED, Color.YELLOW, Color.BLUE},
            null));
    Rect rect = new Rect(0, 0, width, height);
    canvas.drawRect(rect, paint);
    

            上述代码定义了渲染颜色为红黄蓝,且以view的中心为圆心,器绘制结果如下:

            上图的可以明显看出在红色和蓝色之间有明显的分割,我们可以通过给颜色数组的首尾设置相同的颜色来消除这种分割,如设置颜色数组new int[]{Color.RED, Color.YELLOW, Color.BLUE, Color.RED},其结果如下:

    4. RadialGradient

            RadialGradient同样也是两个构造方法,且后者是前者的简化。

    /**
     * @param centerX  圆心的x坐标
     * @param centerY  圆心的y坐标
     * @param radius   必须是正值,表示圆形梯度的半径
     * @param colors   分布在圆形中心到边界之间的颜色
     * @param stops    可以是null。有效值介于0.0f到1.0f之间。 
     *                 对应颜色数组中每个颜色的相对位置。
     *                 如果为null,颜色将均匀分布在圆心和边缘之间
     * @param tileMode Shader的渲染模式
     */
    public RadialGradient(float centerX, float centerY, float radius,
            @NonNull @ColorInt int colors[], @Nullable float stops[],
            @NonNull TileMode tileMode) {
        //代码省略
    }
    
    /**
     * 同名参数含义同上
     * @param centerColor 圆心的颜色
     * @param edgeColor   圆边界的颜色
     */
    public RadialGradient(float centerX, float centerY, float radius,
            @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
        //代码省略
    }
    

            首先,我们还是和上面一样,先来演示一下,代码如下:

    paint.setShader(new RadialGradient(width / 2f, height / 2f, width/3f,
            new int[]{Color.RED, Color.YELLOW, Color.BLUE},
            null, Shader.TileMode.CLAMP));
    Rect rect = new Rect(0, 0, width, height);
    canvas.drawRect(rect, paint);
    

            上面代码中使用的渲染模式是CLAMP,这里我直接放出三种渲染模式使用相同代码的对比表格:

    CLAMP REPEAT MIRROR

    白色圆圈用来标记渐变范围

            从上面表格可以看出,渐变范围以外的部分都是按照渲染模式和渐变规则,继续向外绘制。

    5.ComposeShader

            ComposeShader同样有两个构造方法,分别针对Xfermode或PorterDuff.Mode定义的过渡模式。

    public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {
        this(shaderA, shaderB, mode.porterDuffMode);
    }
    
    public ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB,
            @NonNull PorterDuff.Mode mode) {
        this(shaderA, shaderB, mode.nativeInt);
    }
    

            ComposeShader的作用是实现两个颜色渐变效果的叠加,如SweepGradient与LinearGradient的混合渲染效果等,叠加的效果由过渡模式(Xfermode)或者PorterDuff.Mode来决定。Xfermode其实还是PorterDuff.Mode,其代码如下:

    public class Xfermode {
        static final int DEFAULT = PorterDuff.Mode.SRC_OVER.nativeInt;
        int porterDuffMode = DEFAULT;
    }
    
    

            所以这里我们以构造方法二为例,展现一下渲染模式的叠加,代码如下:

    int[] colors = {Color.RED, Color.YELLOW, Color.BLUE};
    RadialGradient radialGradient = new RadialGradient(width / 2f, height / 2f, width / 2f,
            colors, null, Shader.TileMode.CLAMP);
    LinearGradient linearGradient = new LinearGradient(0, 0, width, height,
            colors, null, Shader.TileMode.CLAMP);
    paint.setShader(new ComposeShader(radialGradient,linearGradient, PorterDuff.Mode.LIGHTEN));
    Rect rect = new Rect(0, 0, width, height);
    canvas.drawRect(rect, paint);
    
            上面代码中我们使用用RadialGradient和LinearGradient,采用PorterDuff.Mode.LIGHTEN进行叠加混合,得到如下效果:

            更多的效果可以详细了解PorterDuff.Mode。

    总结

            到这里,Shader基本就介绍完了,可以看出不论是那种Shader,其渲染范围和绘制范围不具有相关性。我们可以利用这一点绘制一些特殊效果。

    相关文章

      网友评论

          本文标题:Shader

          本文链接:https://www.haomeiwen.com/subject/vsqekctx.html