«

Android着色器Shader介绍

时间:2024-3-2 19:56     作者:韩俊     分类: Android


Shader

Android中绘图时渐变着色需要使用android.graphics.Shader类,该类有几个子类

LinearGradient 线性梯度渐变
RadialGradient 环形梯度渐变或者灯光渲染
SweepGradient 扫描梯度渐变
BitmapShader 图片渲染
ComposeShader 组合渲染

下面将依次介绍各个渲染器的用法和效果图。

LinearGradient

LinearGradient有两个构造器,我们先看简单的

LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)

该构造器指定一个起点坐标一个终点坐标以及二者的色值,我们先不关心最后的TileMode参数。
先来看一下该渲染器的效果,我将渲染器的范围设定到屏幕的中间12处,起点(左上角)颜色为红色,终点(右下角)为绿色,根据渲染算法,中间颜色值应该为0xFF888800
代码如下1:

float width = getWidth();
float height = getHeight();

float left = width / 4;
float right = width * 3 / 4;
float top = height / 4;
float bottom = height * 3 / 4;

Shader gradient = new LinearGradient(left, top, right, bottom, 0xFFFF0000, 0xFF00FF00, Shader.TileMode.CLAMP);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

canvas.drawRect(left, top, right, bottom, paint);

这段代码的运行效果是这样的

LinearGradient还有一个构造器是这样的:

LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile)

该构造器包含一系列颜色以及对应的位置值,如果不提供位置,那么渲染器会均匀分布所提供的几种颜色。
我们可以这么用,这里使用的绘图范围和上面一致,不在赘述:

int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED};
Shader gradient = new LinearGradient(left, top, right, bottom, colors, null, Shader.TileMode.CLAMP);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

canvas.drawRect(left, top, right, bottom, paint);

效果是这样的

如果再加一组位置初始化LinearGradient,如下(为了显示效果更明显,我把修改了渲染器的方向为垂直的):

int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN};
float[] positions = {0, 0.2f, 0.3f, 0.8f};
Shader gradient = new LinearGradient(left, top, left, bottom, colors, positions, Shader.TileMode.CLAMP);

下面是效果图,从效果图上可以看到红,黄,绿三种颜色比较靠上,在我们提供的位置处。

RadialGradient

环形梯度着色,又叫光束渲染。
同样的,RadialGradient有两个构造器,我们先看简单的:

RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile);

该方法要求一个圆心坐标、半径,圆心色值以及圆环色值。
下面演示一下这个着色器的用法和效果,我使用白色和透明色作为起始色值和边界色值,这是一个灯光的效果,为了更明显写,我把窗帘拉上了。

float width = getWidth();
float height = getHeight();

float cx = width / 2;
float cy = height / 2;
float cr = width / 2;

Shader gradient = new RadialGradient(cx, cy, cr, 0xFFFFFFFF, 0x00000000, Shader.TileMode.CLAMP);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

canvas.drawColor(Color.BLACK);
canvas.drawCircle(cx, cy, cr, paint);

我们这里定义的圆心在屏幕中间,直径为屏幕宽度,效果是这样的,很像一个点光源照射的效果:

另一个构造器和LinearGradient类似,需要一组色值和对应的一组位置,就不赘述了,直接贴代码:

int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN};
float[] positions = {0, 0.2f, 0.3f, 0.8f};
Shader gradient = new RadialGradient(cx, cy, cr, colors, positions, Shader.TileMode.CLAMP);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

圆心位置和半径使用上例中的定义。这段代码展示的效果是这样的,四种颜色在指定位置过渡:

作为对比,我们看一下不带位置参数的效果图:
代码片段:

Shader gradient = new RadialGradient(cx, cy, cr, colors, null, Shader.TileMode.CLAMP);

SweepGradient

扫描着色,其效果好像雷达扫描。
同上面两个着色器类似,该类也有两个构造器,不同的是,该类的构造器不需要TileMode参数。
第一个构造器:

SweepGradient(float cx, float cy, int color0, int color1)

这个构造器看起来比较简单,仅需要一个圆心坐标和一组色值,我们来看一下它的使用方法和效果

float width = getWidth();
float height = getHeight();

float cx = width / 2;
float cy = height / 2;
float cr = width / 2;

Shader gradient = new SweepGradient(cx, cy, 0xFFFF0000, 0xFFFFFF00);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

canvas.drawCircle(cx, cy, cr, paint);

同样的,我这里使用屏幕中心作为圆心,效果图是这样子的

上图的扫描渲染在起始线(终止线)上的效果不尽如人意,要想颜色在该线上平缓的过渡,至少需要定义有三个值的数组。幸运的是SweepGradient提供了这样一个构造器:

SweepGradient(float cx, float cy, int colors[], float positions[])

这里我们走远一些,看看六种色值绘出的效果(因为第一个色值和最后的色值要相同,所以数组的长度是7)

int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN, Color.BLUE, Color.MAGENTA, Color.RED};
Shader gradient = new SweepGradient(cx, cy, colors, null);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

canvas.drawCircle(cx, cy, cr, paint);

效果图是这样的:

构造带位置参数的扫描着色器,效果和上面的两个着色器类似,我们只看一下效果图吧,我使用如下参数初始化着色器:

int[] colors = {Color.RED, Color.YELLOW, Color.GREEN, Color.CYAN};
float[] positions = {0.125f, 0.375f, 0.625f, 0.8f};

BitmapShader

图片渲染器
图片渲染器只有一个构造器:

BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)

我们提供一个位图和纵向横向的TileMode就行了。讲到这里,也该提一下所谓TileMode是什么了。
TileMode意义过来就是平铺模式,指的是在着色器范围之外的部分使用什么颜色进行着色,TileMode定义了三种模式:

CLAMP 使用边界颜色着色
REPEAT 重复着色器效果
MIRROR 镜像重复使用着色器效果

语言表达不如图片更清楚,下面我们看一下TileMode是如何起作用的。如下使用图片渲染器:

float width = getWidth();
float height = getHeight();

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.girl);
// 图片着色器不定义坐标位置,图片位置在canvas的绘图原点
// 着色器横向使用REPEAT模式,纵向使用MIRROR模式
Shader gradient = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR);

Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShader(gradient);

// 为了让效果更佳清晰,我们把绘图原点放在屏幕开始1/4处
float left = width / 4;
float top = height / 4;
canvas.translate(left, top);

canvas.drawPaint(paint);

这段代码绘制出的效果是这样的:

从该图中我们可以清楚看到TileMode的作用方式。

CLAMP的着色方式就比较有意思使用了,我们仍然使用上面的代码,但是下面的参数初始化构造器:

Shader gradient = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

效果是这样的:

注意图片渲染器的顶部向上的扩展是不正常的,这大概是图片渲染器的bug吧。

TileMode

上面使用图片渲染的时候解释了几种TileMode的效果,同样的,在LinearGradient中,这几种模式的效果我们可以想象,不过值得一提的是在RadialGradient中的效果是这样的,贴图供参考:
MIRROR模式效果:

REPEAT模式效果:

ComposeShader

最后提一下,Shader类还有一个子类叫ComposeShader,故名思议,该着色器是由两个着色器组合而来的。 由于组合着色器涉及到了颜色叠加算法,这里不做讨论。
ComposeShader的使用需要开启软件渲染:

View.setLayerType(LAYER_TYPE_SOFTWARE, mPaint)

如果不开启软件渲染,组合着色器将没有效果,不过有时候我们可以使用叠加绘制的方式来模拟组合着色器,比如下图就是先绘制了一个扫描着色器,再绘制了一次带透明度的环形着色器

参考:AndroidCircleColorPicker

CyanFlxy原创,转载请注明出处

实际绘图的时候不要在onDraw中进行对象的创建,因为该方法会频繁的调用,应该在其他位置中将用到的对象初始化好 ↩

版权声明:本文为博主原创文章,未经博主允许不得转载。

标签: android

热门推荐