美文网首页
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