Paint - 颜色相关

颜色相关

  1. setColor(int color) 参数具体的颜色值,16进制数值,如:0xFFFF0000
  2. setARGB(int a, int r, int g, int b) 参数分别为透明度、红、绿、蓝,0~255数值
  3. setShader(Shader shader) 参数着色器对象,一般使用Shader的几个子类:
    • LinearGradient:线性渲染
    • RadialGradient:环形渲染
    • SweepGradient:扫描渲染
    • BitmapShaper:位图渲染
    • ComposeShaper:组合渲染,例如:LinearGradient + BitmapShaper
  4. 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);

LinearGradient_TileMode

改变线性渲染的角度:就是改变(x0, y0) ——> (x1, y1)这四个坐标值。

LinearGradient_xy


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);

RadialGradient_TileMode


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);

SweepGradient


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);

BitmapShader


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);

ComposeShader

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;
            }
        }
    }
}

效果图:

Xfermode

我们发现有4个图和Google提供的不一样,他们是Clear、Overlay、Darken、Lighten。为什么会出现这种事情?

有说是因为两张框图的叠加混合造成的,不是一个单纯的方形和圆形的Bitmap图层混合,并且框图中除了图形以外的区域是透明的。有没有道理呢?

可能有道理,没有验证单独的图形,确实不好说。

发布了19 篇原创文章 · 获赞 0 · 访问量 865

猜你喜欢

转载自blog.csdn.net/qq_32036981/article/details/103819974