美文网首页
Paint/Canvas/Path高级绘制【1】

Paint/Canvas/Path高级绘制【1】

作者: 瑜小贤 | 来源:发表于2019-08-11 18:48 被阅读0次

Paint

1. 概念

画笔,保存了绘制的 几何图形、文本和位图 的样式和颜色信息

2. 常用API

mPaint = new Paint(); 
mPaint.setColor(Color.RED); //设置颜色
mPaint.setARGB(255, 255, 255, 0); //设置Paint对象颜色,范围0~255
mPaint.setAlpha(200); //设置透明度
mPaint.setAntiAlias(true); //设置抗锯齿
mPaint.setStyle(Paint.Style.STROKE); //描边效果 FILL、STROKE、FILL_AND_STROKE
mPaint.setStrokeWidth(4); //描边宽度 
mPaint.setStrokeCap(Paint.Cap.ROUND); //例图1:圆角风格 BUTT(default)、ROUND、SQUARE
mPaint.setStrokeJoin(Paint.Join.MITER); //例图2:拐角风格 MITER、ROUND、BEVEL
mPaint.setShader(new SweepGradient(200, 200, Color.BLUE, Color.RED)); //设置环形渲染器
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DARKEN)); //设置图层混合模式
mPaint.setColorFilter(new LightingColorFilter(0x00ffff, 0x000000)); //设置颜色过滤器
mPaint.setFilterBitmap(true); //设置双线性过滤(例图3)
mPaint.setMaskFilter(new BlurMaskFilter(10, BlurMaskFilter.Blur.NORMAL)); //设置画笔这招滤镜,传入度数和样式
mPaint.setTextScaleX(2); //设置文本缩放倍数
mPaint.setTextSize(38); //设置字体大小
mPaint.setTextAlign(Paint.Align.LEFT); //设置对齐方式
mPaint.setUnderlineText(true); //设置下划线
        
String str = "Android高级工程师";
Rect rect = new Rect();
mPaint.getTextBounds(str, 0, str.length(), rect); //测量文本大小,将文本大小信息存放在rect中
mPaint.measureText(str); //获取文本的宽
mPaint.getFontMetrics(); //获取字体度量对象
例图1:setStrokeCap(Cap cap) 例图2:setStrokeJoin(Join join) 例图3:setFilterBitmap设置效果
重点概念:FontMetrics 字体的度量,是指对于指定字号的某种字体,在度量方面的各种属性,起描述参数包括
  • baseline:字符基线
  • top:字符最高点到baseline的最大距离
  • bottom:字符最低点到baseline的最大距离
  • ascent:字符最高点到baseline的推荐距离
  • descent:字符最低点到baseline的推荐距离
  • leading:行间距,即前一行的descent与下一行的ascent之间的距离


    参照理解FontMetrics各属性

3. 重点API解析:

3.1 setShader(Shader shader) 设置着色器(渲染器)对象,

一般使用的shader的几个子类

  • LinearGradient:线性渲染

 /**
     * LinearGradient (float x0, float y0, float x1, float y1, 
            @NonNull @ColorInt int colors[],
            @Nullable float positions[], @NonNull TileMode tile) 
     * x0 y0 渐变起始点坐标
     * x1 y1渐变结束点坐标
     * color0 渐变开始点颜色
     * color1 渐变技术颜色
     * colors[] 渐变颜色数组
     * positions[] 渐变位置数组,positions若不为null,需与colors数组length相同,表示在对应位置的颜色值,若为null,则线性渐变
     * tile 当指定空间区域大于指定的渐变区域时,空白区域的颜色填充方法
 */
mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE}, null, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 500, 500, mPaint); //效果1

mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE}, new float{0.5f, 1}, Shader.TileMode.CLAMP); 
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 500, 500, mPaint); //效果2

mShader = new LinearGradient(0, 0, 500, 500, new int[]{Color.RED, Color.BLUE, Color.GREEN}, new float{0, 0.7f, 1}, Shader.TileMode.REPEAT); 
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 1000, 1000, mPaint); //效果3
LinearGradient设置效果1
LinearGradient设置效果2
LinearGradient设置效果3
  • RadialGradient:环形渲染

 /**
     * RadialGradient(float centerX, float centerY, float radius,
            @NonNull @ColorInt int colors[], @Nullable float stops[],
            @NonNull TileMode tileMode)
     * centerX, centerY shader的中心坐标,开始渐变的坐标
     * radius 渐变的半径
     * centerColor, edgeColor 中心店渐变颜色,边界的渐变颜色
     * colors[] 渐变颜色数组
     * stops[] 渐变位置数组,类似线性渐变的positions数组,取值[0, 1],中心点为0,半径到达位置为1.0f
     * tile 当指定空间区域大于指定的渐变区域时,空白区域的颜色填充方法
 */
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); //效果1
RadialGradient设置效果1
  • SweepGradient:扫描渲染

 /**
     * SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1)
     * cx cy 渐变中心坐标
     * color0 color1 渐变开始 结束颜色
     * colors[] positions[] 类似LinearGradient中的作用,用于多颜色渐变
 */
mShader = new SweepGradient(250, 250, Color.RED, Color.GREEN);
mPaint.setShader(mShader);
canvas.drawCircle(250, 250, 250, mPaint); //效果1
SweepGradient设置效果1
  • BitmapShader:位图渲染

 /**
     * BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX,    
          @NonNull TileMode tileY) 
     * Bitmap 构造shader使用的bitmap
     * tileX tileY X轴 Y轴方向上的TileMode(CLAMP,REPEAT,MIRROR)
     * REPEAT 绘制区域超出渲染区域的部分,重复排版
     * CLAMP 绘制区域超出渲染区域的部分,以最后一个像素拉伸排版
     * MIRROR 绘制区域超出渲染区域的部分,镜像翻转排版
 */
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.beauty);
mShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mPaint); //效果1
canvas.drawRect(0, 0, 500, 500, mPaint); //效果2 

mShader = new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 500, 500, mPaint); //效果3

mShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
mPaint.setShader(mShader);
canvas.drawRect(0, 0, 500, 500, mPaint); //效果4
BitmapShader设置效果1
BitmapShader设置效果2
BitmapShader设置效果3
BitmapShader设置效果4
  • ComposeShader:组合渲染,例如LinearGradient+BitmapShader

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.drawCircle(250, 250, 250, mPaint); //效果1
ComposeShader设置效果1
重点概念:Xfermode以及PorterDuff.Mode图层混合模式

它将从绘制图形的像素与Canvas中对应位置的像素按照一定规则进行混合,形成新的像素值,从而更新Canvas中最终的像素颜色值。
注意:效果只作用于src源图像区域
共18种模式

Mode.CLEAR         Mode.MULTIPLY   Mode.SRC       Mode.DST
Mode.SRC_OVER      Mode.SRC_OUT    Mode.SRC_IN   Mode.SRC_ATOP
Mode.DST_OVER      Mode.DST_OUT    Mode.DST_IN   Mode.DST_ATOP
Mode.XOR           Mode.ADD        Mode.DARKEN   Mode.LIGHTEN
Mode.SCREEN        Mode.OVERLAY

通过查看PorterDuff.java源码可看到Mode枚举各个类型的注释:

public enum Mode {
        /**
         * <p>\(\alpha_{out} = 0\)</p>
         * <p>\(C_{out} = 0\)</p>
         */
        CLEAR       (0),
//alpha 混合后的alpha通道,out代表输出值(最终结果值)C代表颜色值
//src代表原图像,dst代表目标图像
//alpha与C共同决定混合后的效果
  ...
}

举例 自定义View XfermodeView
官方demo 地址

public class XfermodeView extends View{
  private Paint mPaint;
  private int mWidth, mHeight;
  
  public XfermodeView(Context context){
    this(context, null);
  }

  public XfermodeView(Context context, AttributeSet attrs){
    this(context, attrs, 0);
  }

  public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr){
    super(context, attrs, defStyleAttr);
    init();
  }
  
  private void init(){
    mPaint = new Paint();
    mPaint.setColor(Color.RED);
    mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
  }

  @Override
  protect void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    mWidth = MeasureSpec.getSize(widthMeasureSpec);
    mHeight = MeasureSpec.getSize(heightMeasureSpec);
  }

  @Override
  protect void onDraw(Canvas canvas){
    super.onDraw(canvas);
    //Xfermode 几个用处
    //1.ComposerShader 构造方法需传入一个Xfermode参数
    //2.画笔Paint.setXfermode()
    //3.PorterDuffColorFilter

    //图层混合前,要禁止硬件加速
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    //离屏绘制
    int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint, Canvas.ALL_SAVE_FLAG);

    setBackgroundColor(Color.GRAY);

    //目标图
    canvas.drawBitmap(createRectBitmap(mWidth, mHeight), 0, 0, mPaint);
    //设置混合模式
    mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    //源图,重叠区域右下角部分
    canvas.drawBitmap(createCircleBitmap(mWidth, mHeight), 0, 0, mPaint);
    //清楚混合模式
    mPaint.setXfermode(null);

    canvas.restoreToCount(layerId)
  }

  //画矩形Dst
  public Bitmap createRectBitmap(int width, int height){
    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    dstPaint.setColor(0xFF66AAFF);
    canvas.drawRect(new Rect(width/20, height/3, width * 2/3, height*19/20), dstPaint);
    return bitmap;
  }

  //画圆形Src
  public Bitmap createCircleBitmap(int width, int height){
    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    srcPaint.setColor(0xFFFFCC44);
    canvas.drawCircle(width*2/3, height/3, height/4, srcPaint);
    return bitmap;
  }
}
运行效果1
重点概念:离屏绘制

通过使用离屏绘制缓冲,把要绘制的内容单独绘制在缓冲层,保证Xfermode的使用不会出现错误的结果
使用离屏缓冲有两种方式

1. Canvas.saveLayer()
//可以做短时的离屏缓冲,在绘制之前保存,绘制之后恢复。
int saveId = canvas.saveLayer(0, 0, width, height, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(rectBitmap, 0, 0, paint);  //画方
paint.setXfermode(xfermode); //设置Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); //画圆
paint.setXfermode(null); //用户及时清除Xfermode
canvas.restoreToCount(saveId);
2. View.setLayerType()
//直接把整个View都绘制在离屏缓冲中
setLayerType(LAYER_TYPE_HARDWARE); 使用GPU来缓冲
setLayerType(LAYER_TYPE_SOFTWARE); 使用一个Bitmap来缓冲

3.2 setColorFilter(ColorFilter colorFilter) 设置颜色过滤

一般使用ColorFilter三个子类

  • LightingColorFilter:光照效果

 /**
     * LightingColorFilter(@ColorInt int mul, @ColorInt int add)
     * mul 和 add 都是和颜色值格式相同的int值,其中mul用来和目标像素相乘,add用来和目标像素相加
     * R' = R * mul.R / 0xff + add.R
     * G' = G * mul.G / 0xff + add.G
     * B' = B * mul.B / 0xff + add.B
 */
ColorFilter lighting = new LightingColorFilter(0xffffff, 0x000000); //原图效果
ColorFilter lighting = new LightingColorFilter(0x00ffff, 0x000000); //去除红色
paint.setColorFilter(lighting);
canvas.drawBitmap(bitmap, 0, 0, paint);
  • PorterDuffColorFilter:制定一个颜色和一种PorterDuff.Mode与绘制对象进行合成

 /**
     * PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)
     * color 具体的颜色值,例如Color.RED
     * mode 指定PorterDuff.Mode混合模式
     * 与之前说的paint.setXfermode()设置图层混合模式,是指图片与图片进行图层混合,此处只能进行颜色与图片进行图层混合。
 */
PorterDuffColorFilter porterDuffColorFilter = 
    new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN);
paint.setColorFilter(porterDuffColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, paint);
  • ColorMatrixColorFilter:使用一个ColorMatrix来对颜色进行处理

/**
     * ColorMatrixColorFilter(@NonNull float[] colorMatrix)
     * colorMatrix 4行5列矩阵数组 第5列是偏移量
     * 
     * ColorMatrixColorFilter(@NonNull ColorMatrix matrix)
     * matrix
 */
float[] colorMatrix = {
  1, 0, 0, 0, 0, //red
  0, 1, 0, 0, 0, //green
  0, 0, 1, 0, 0, //blue
  0, 0, 0, 1, 0, //alpha
}
mColorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(mColorMatrixColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);

ColorMatrix cm = new ColorMatrix();
 //亮度调节
cm.setScale(1, 1, 1, 1);  //分别是红绿蓝透明的调节系数
//饱和度调节 0-无色彩 1-默认效果 >1饱和度加强
cm.setSaturation(1);
//色度调节 axis-0 红色的旋转角度 axis-1 绿色的旋转角度  axis-2 蓝色旋转角度
c.setRotate(1, 45)
mColorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
mPaint.setColorFilter(mColorMatrixColorFilter);
canvas.drawBitmap(mBitmap, 100, 0, mPaint);
重点概念 色彩矩阵

在Android中,系统使用一个颜色矩阵 ColorMatrix,来处理图像的色彩效果。对于图像的每个像素点,都有一个颜色分量矩阵来保存颜色的TGBA值(下图矩阵C),Android中的颜色矩阵就是一个4*5的数字矩阵,它用来对图片的色彩进行处理(下图矩阵A)


颜色矩阵

相关文章

网友评论

      本文标题:Paint/Canvas/Path高级绘制【1】

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