颜色相关
- setColor(int color) 参数具体的颜色值,16进制数值,如:0xFFFF0000
- setARGB(int a, int r, int g, int b) 参数分别为透明度、红、绿、蓝,0~255数值
- setShader(Shader shader) 参数着色器对象,一般使用Shader的几个子类:
- LinearGradient:线性渲染
- RadialGradient:环形渲染
- SweepGradient:扫描渲染
- BitmapShaper:位图渲染
- ComposeShaper:组合渲染,例如:LinearGradient + BitmapShaper
- setColorFilter(ColorFilter colorFilter) 设置颜色过滤,一般使用ColorFilter三个子类:
- LightingCololrFilter:光照效果
- PorterDuffColorFilter:指定一个颜色和一种PorterDuff.Mode与绘制对象进行合成
- ColorMatrixColorFilter:使用一个ColorMatrix来对颜色进行处理
Shader 渲染
Shader.TileMode 渲染模式
Shader.TileMode.CLAMP 超出的部分以最后一个像素来排版
Shader.TileMode.MIRROR 超出的部分以镜像翻转来排版
Shader.TileMode.REPEAT 超出的部分重复排版
使用:
mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
LinearGradient 线性渲染:沿着直线绘制线性渐变的渲染器。
构造方法:
/**
* @param x0 渐变线起点的x坐标
* @param y0 渐变线起点的y坐标
* @param x1 渐变线终点的x坐标
* @param y1 渐变线终点的y坐标
* @param colors 沿渐变线分布的颜色
* @param positions 可能为null。颜色数组中每个对应颜色的相对位置[0..1]。
* 如果为null,则颜色沿渐变线均匀分布。
* @param tile 渲染器拼接模式,端点范围之外的着色规则,类型是TileMode
*/
LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
@Nullable float positions[], @NonNull TileMode tile)
/**
* @param x0 渐变线起点的x坐标
* @param y0 渐变线起点的y坐标
* @param x1 渐变线终点的x坐标
* @param y1 渐变线终点的y坐标
* @param color0 渐变线起点的颜色
* @param color1 渐变线终点的颜色
* @param tile 渲染器拼接模式,端点范围之外的着色规则,类型是TileMode
*/
LinearGradient(float x0, float y0, float x1, float y1,
@ColorInt int color0, @ColorInt int color1, @NonNull TileMode tile)
用法:
mShader = new LinearGradient(0, 0, 250, 250, new int[]{Color.RED, Color.GREEN, Color.BLUE},
new float[]{0.3f, 0.6f, 0.9f}, Shader.TileMode.REPEAT);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 1000, 1000, mPaint);
改变线性渲染的角度:就是改变(x0, y0) ——> (x1, y1)这四个坐标值。
RadialGradient 环形渲染:绘制中心和半径的环形渐变渲染器。
构造方法:
/**
* 创建一个绘制中心和半径的环形渐变渲染器。
*
* @param centerX 辐射中心的x坐标
* @param centerY 辐射中心的y坐标
* @param radius 必须是正值,辐射半径
* @param colors 分布在辐射中心和边缘之间的颜色
* @param stops 可能为空。有效值介于0.0f和1.0f之间。颜色数组中每个相应颜色的相对位置。
* 如果为空,则颜色在辐射中心和边缘之间均匀分布。
* @param tileMode 渲染器的拼接模式,辐射范围之外的渲染规则
*/
RadialGradient(float centerX, float centerY, float radius, @NonNull @ColorInt int colors[],
@Nullable float stops[], @NonNull TileMode tileMode)
/**
* 创建一个绘制中心和半径的环形渐变渲染器。
*
* @param centerX 辐射中心的x坐标
* @param centerY 辐射中心的y坐标
* @param radius 必须是正值,辐射半径
* @param centerColor 辐射中心的颜色。
* @param edgeColor 辐射边缘的颜色。
* @param tileMode 渲染器的拼接模式,辐射范围之外的渲染规则
*/
RadialGradient(float centerX, float centerY, float radius, @ColorInt int centerColor,
@ColorInt int edgeColor, @NonNull TileMode tileMode)
用法:
mShader = new RadialGradient(250, 250, 250, new int[]{Color.GREEN, Color.YELLOW, Color.RED},
null, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
SweepGradient 扫描渲染:围绕中心点绘制扫描渐变的渲染器。
构造方法:
/**
* @param cx 中心的x坐标
* @param cy 中心的y坐标
* @param colors 颜色分布在中心周围。数组中必须至少有两种颜色。
* @param positions 可能为空。颜色数组中每个相应颜色的相对位置,从0开始,到1.0结束。
* 如果值不是单调的,则绘图可能会产生意外的结果。
* 如果位置为空,则颜色将自动均匀分布。
*/
SweepGradient(float cx, float cy, @NonNull @ColorInt int colors[], @Nullable float positions[])
/**
* @param cx 中心的x坐标
* @param cy 中心的y坐标
* @param color0 扫描开始处使用的颜色
* @param color1 扫描结束处使用的颜色
*/
SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1)
使用:
mShader = new SweepGradient(250, 250, Color.RED, Color.GREEN);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint);
BitmapShader 位图渲染:用Bitmap绘制的渲染器
构造方法:
/**
* @param bitmap 在渲染器内部使用的Bitmap(或用来做模板的Bitmap对象)
* @param tileX 横向的渲染规则,类型是TileMode
* @param tileY 纵向的渲染规则,类型是TileMode
*/
BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
this(bitmap, tileX.nativeInt, tileY.nativeInt);
}
private BitmapShader(Bitmap bitmap, int tileX, int tileY) {
if (bitmap == null) {
throw new IllegalArgumentException("Bitmap must be non-null");
}
if (bitmap == mBitmap && tileX == mTileX && tileY == mTileY) {
return;
}
mBitmap = bitmap;
mTileX = tileX;
mTileY = tileY;
}
使用:
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.demo);
mShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 1000, 1000, mPaint);
ComposeShader 组合渲染:由给定的渲染器A、B和组合模式,合成的Shader。
构造方法:
/**
* 创建一个新的合成Shader,给定渲染器A、B和组合模式。
* 当应用该模式时,它将被赋予来自渲染器A的结果作为其“dst”,来自渲染器B的结果作为其“src”。
*
* @param shaderA 此渲染器中的颜色被此模式视为“dst”
* @param shaderB 此渲染器中的颜色被此模式视为“src”
* @param mode 组合来自两个渲染器的颜色的模式。如果模式为空,则假定SRC_OVER。
*/
ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {
this(shaderA, shaderB, mode.porterDuffMode);
}
/**
* 创建一个新的合成Shader,给定渲染器A、B和组合PorterDuff模式。
* 当应用该模式时,它将被赋予来自渲染器A的结果作为其“dst”,来自渲染器B的结果作为其“src”。
*
* @param shaderA 此渲染器中的颜色被此模式视为“dst”
* @param shaderB 此渲染器中的颜色被此模式视为“src”
* @param mode PorterDuff模式,将两个渲染器的颜色组合在一起。
*/
ComposeShader(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull PorterDuff.Mode mode) {
this(shaderA, shaderB, mode.nativeInt);
}
private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
if (shaderA == null || shaderB == null) {
throw new IllegalArgumentException("Shader parameters must not be null");
}
mShaderA = shaderA;
mShaderB = shaderB;
mPorterDuffMode = nativeMode;
}
用法:
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT,
Shader.TileMode.REPEAT);
LinearGradient linearGradient = new LinearGradient(0, 0, 1000, 1600,
new int[]{Color.RED, Color.GREEN, Color.BLUE}, null, Shader.TileMode.CLAMP);
mShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.MULTIPLY);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, mBitmap.getWidth()*3, mBitmap.getHeight()*3, mPaint);
PorterDuff.Mode 图层混合模式
PorterDuffXfermode 继承 Xfermode,它将所绘制图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素颜色值。
18中模式:
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.OVERLAY Mode.ADD
分析一下Google关于Xfermode的示例代码
创建src Bitmap 和 dst Bitmap:
private static int strokeWidth = 1;
// 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 lineP = new Paint(Paint.ANTI_ALIAS_FLAG);
lineP.setColor(Color.BLACK);
lineP.setStyle(Paint.Style.STROKE);
lineP.setStrokeWidth(strokeWidth);
c.drawRect(0, 0, w, h, lineP);
Paint imgP = new Paint(Paint.ANTI_ALIAS_FLAG);
imgP.setColor(0xFF2299EE);
imgP.setStyle(Paint.Style.FILL);
c.drawRect(strokeWidth, h/3-strokeWidth, w*2/3+strokeWidth, h-strokeWidth, imgP);
return bm;
}
// 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 lineP = new Paint(Paint.ANTI_ALIAS_FLAG);
lineP.setColor(Color.BLACK);
lineP.setStyle(Paint.Style.STROKE);
lineP.setStrokeWidth(strokeWidth);
c.drawRect(0, 0, w, h, lineP);
Paint imgP = new Paint(Paint.ANTI_ALIAS_FLAG);
imgP.setColor(0xFFEE2266);
imgP.setStyle(Paint.Style.FILL);
c.drawOval(new RectF(w/3-strokeWidth, strokeWidth, w-strokeWidth, h*2/3+strokeWidth), imgP);
return bm;
}
绘制src和dst,进行图层合成时需要的模式:
//效果作用于src源图像区域
private static final Xfermode[] modes = {
//所绘制不会提交到画布上
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.ADD),
//取两图层交集部分,颜色叠加
new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY),
//取两图层全部区域,交集部分滤色
new PorterDuffXfermode(PorterDuff.Mode.SCREEN),
//取两图层全部区域,交集部分叠加
new PorterDuffXfermode(PorterDuff.Mode.OVERLAY),
//取两图层全部区域,交集部分颜色加深
new PorterDuffXfermode(PorterDuff.Mode.DARKEN),
//取两图层全部区域,交集部分颜色点亮
new PorterDuffXfermode(PorterDuff.Mode.LIGHTEN)
};
private static final String[] labels = {
"Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn",
"DstIn", "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor",
"Add", "Multiply", "Screen", "Overlay", "Darken", "Lighten"
};
自定义的XfermodeView,用来显示18种图层合成效果:
public class XfermodeView extends View {
private static int screen_width = 540;
private static int row_space = 60;
private static int column_space = 10;
private static int column_num = 4;
private static int imgW = 0;
private static int imgH = 0;
private Bitmap srcBm;
private Bitmap dstBm;
public CustomXfermodeView(Context context) {
this(context, null);
}
public CustomXfermodeView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomXfermodeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
if (windowManager!=null) {
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
screen_width = displayMetrics.widthPixels;
}
imgW = (screen_width-(column_num+1)*column_space)/column_num;
imgH = imgW;
srcBm = makeSrc(imgW, imgH);
dstBm = makeDst(imgW, imgH);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.TRANSPARENT);
Paint labelP = new Paint(Paint.ANTI_ALIAS_FLAG);
labelP.setTextAlign(Paint.Align.CENTER);
labelP.setTextSize(30);
Paint paint = new Paint();
canvas.translate(column_space, row_space);
int x = 0;
int y = 0;
for (int i = 0; i < modes.length; i++) {
// draw the src/dst example into our offscreen bitmap
int sc = canvas.saveLayer(x, y, x + imgW, y + imgH, null, Canvas.ALL_SAVE_FLAG);
canvas.translate(x, y);
//目标图像
canvas.drawBitmap(dstBm, 0, 0, paint);
paint.setXfermode(modes[i]);
//源图像
canvas.drawBitmap(srcBm, 0, 0, paint);
paint.setXfermode(null);
canvas.restoreToCount(sc);
// draw the label
canvas.drawText(labels[i], x + imgW / 2, y - labelP.getTextSize() / 2, labelP);
x += imgW + column_space;
// wrap around when we've drawn enough for one row
if ((i % column_num) == column_num - 1) {
x = 0;
y += imgH + row_space;
}
}
}
}
效果图:
我们发现有4个图和Google提供的不一样,他们是Clear、Overlay、Darken、Lighten。为什么会出现这种事情?
有说是因为两张框图的叠加混合造成的,不是一个单纯的方形和圆形的Bitmap图层混合,并且框图中除了图形以外的区域是透明的。有没有道理呢?
可能有道理,没有验证单独的图形,确实不好说。