Android 图片处理(Canvas/Paint/Matrix/Path/Bezier曲线)

Android 图片处理(Canvas/Paint/Matrix/Path/Bezier曲线)


本文由 Luzhuo 编写,转发请保留该信息.
原文: https://blog.csdn.net/Rozol/article/details/79683220


加载图片

将图片加载到内存显示之前, 要先根据将要显示的容器的大小来按比例压缩图片, 这样才不至于将内存撑爆.

/**
 * 加载图片
 * <br>步骤: 获取手机和图片分辨率 -> 计算缩放比 -> 加载图片(用最大的缩放比)
 */
public void onclick(View view){
    // 获取手机分辨率
    DispalyHW screenHW = getScreenHeightWidth(this);
    Log.i(TAG, "手机分辨率: width: " + screenHW.width + " height: " + screenHW.height);

    // 获取图片分辨率
    File file = new File(Environment.getExternalStorageDirectory(), "dog.jpg");
    DispalyHW imageHW = getImageHeightWidth(file);
    Log.i(TAG, "图片分辨率: width: " + imageHW.width + " height: " + imageHW.height);

    // 计算缩放比: 图片宽高 ÷ 手机宽高 的比率, 取最大的缩放比
    int scale = 1;
    int scalex =  imageHW.width / screenHW.width;
    int scaley = imageHW.height / screenHW.height;
    if (scalex >= scaley && scalex > scale) {
        scale = scalex;
    }
    if (scaley > scalex && scaley > scale) {
        scale = scaley;
    }
    Log.i(TAG, "缩放比为: " + scale);

    // 加载图片
    Bitmap bitmap = getBitmap(file, scale);

    imageView.setImageBitmap(bitmap);
}
  • 获取手机分辨率

    /**
     * 获取手机分辨率
     * @param context
     */
    public static DispalyHW getScreenHeightWidth(Context context){
        DispalyHW hw = new DispalyHW();
    
        WindowManager wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
    
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB_MR2) {
            hw.width = wm.getDefaultDisplay().getWidth();
            hw.height = wm.getDefaultDisplay().getHeight();
        } else {
            // api>13
            Point point = new Point();
            wm.getDefaultDisplay().getSize(point);
            hw.width = point.x;
            hw.height = point.y;
        }
    
        return hw;
    }
  • 获取图片分辨率

    /**
     * 获取图片的分辨率率
     * @param file 图片文件
     * @return 如果文件不存在返回null
     */
    public static DispalyHW getImageHeightWidth(File file){
        if (!file.exists()) return null;
    
        DispalyHW hw = new DispalyHW();
    
        // bitmap工厂的配置参数
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true; // 加载图片信息, 不加载图片
        BitmapFactory.decodeFile(file.getAbsolutePath(), options);
    
        // 获取图片的宽和高
        hw.width = options.outWidth;
        hw.height = options.outHeight;
        return hw;
    }
  • 加载图片

    /**
     * 加载图片
     * @param file 图片文件
     * @param scale 缩放比 >= 1
     * @return 文件不存在返回null
     */
    public static Bitmap getBitmap(File file, int scale){
        if (!file.exists()) return null;
    
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = scale; // 按缩放比压缩
        options.inJustDecodeBounds = false; // 加载图片
        Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
    
        return bitmap;
    }

图片处理

只能对副本图片进行处理

创建副本

public void onclick(View view){
    // 从资源加载Bitmap
    Bitmap srcBitmap = getBitmapOnRes(this, R.mipmap.abc);
    iv_src.setImageBitmap(srcBitmap);

    // 创作模板
    Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
    Canvas canvas = new Canvas(copyBitmap); // 画布
    Paint paint = new Paint(); // 画笔
    Matrix matrix = new Matrix(); // 矩阵
    canvas.drawBitmap(srcBitmap, matrix, paint); // 绘画

    iv_copy.setImageBitmap(copyBitmap);
}
  • 从资源加载图片

    /**
     * 从资源文件中加载图片
     * @param context 上下文
     * @param drawableID 资源图片id
     */
    public static Bitmap getBitmapOnRes(Context context, @DrawableRes int drawableID){
        Bitmap resBitmap = BitmapFactory.decodeResource(context.getResources(), drawableID);
        return resBitmap;
    }

画布

 /**
  * 对画布Canvas进行操作, 画布具有 移动, 状态保存与恢复, 绘制等操作
  */
 public void onclick(View view){
     Bitmap srcBitmap = getBitmapOnRes(this, R.mipmap.abc);
     iv_src.setImageBitmap(srcBitmap);
     Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth() * 3, srcBitmap.getHeight() * 3, srcBitmap.getConfig());
     Paint paint = new Paint(); // 画笔
     paint.setStyle(Paint.Style.STROKE); // 只画线不填充
     Matrix matrix = new Matrix(); // 矩阵

     Path path = new Path(); // 路径
     path.moveTo(10, 50);
     path.lineTo(30, 100);
     path.lineTo(60, 300);
     path.close();


     Canvas canvas = new Canvas(copyBitmap); // 画布
     // --- 效果叠加 ---
     // 平移(参数: x, y)
     canvas.translate(10, 10);
     // 缩放(参数: 缩放倍数, x, y)
     canvas.scale(0.5f, 0.5f, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     // 旋转(参数: 角度, x, y)
     canvas.rotate(30, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);


     // --- 画布操作动作的保存与恢复 (仅对 平移等操作有效, 对绘制操作无效) ---
     // 保存画布修改状态
     int save = canvas.save();
     // 还原到最近保存的状态
     // canvas.restore();
     // 还原到指定状态
     canvas.restoreToCount(save);


     // --- 绘制 ---
     // 绘制点(参数: x,y / [x,y,x,y,x,y])
     canvas.drawPoint(10, 10, paint);
     canvas.drawPoints(new float[]{11, 11, 12, 11, 13, 11}, paint);
     // 绘制文字(参数: 文字, x,y)
     canvas.drawText("luzhuo.me", 50, 50, paint);
     // 根据路径绘制文字(参数: 文字, 路径, 起始偏移, 上下编译)
     canvas.drawTextOnPath("luzhuo.meluzhuo.meluzhuo.meluzhuo.meluzhuo.me", path, 0, 0, paint);
     // 绘制直线(参数, x,y, x1,y1)
     canvas.drawLine(100, 100, 300, 300, paint);
     // 绘制矩形(参数: 左上x,y, 右下x,y)
     canvas.drawRect(0, 0, 200, 200, paint);
     // 绘制圆形(参数: 圆心x,y, 半径)
     canvas.drawCircle(100,100, 50, paint);
     // 绘制椭圆(参数: 左上右下)
     canvas.drawOval(new RectF(100, 100, 300, 200), paint);
     // 绘制弧线(参数: RectF, 起始角度, 扫过角度, 是否中心连线)(矩形擦除部分)
     canvas.drawArc(new RectF(100, 100, 300, 200), 30, 180, false, paint);
     // 绘制路径(参数: 路径)
     canvas.drawPath(path, paint);
     // 绘制图片
     canvas.drawBitmap(srcBitmap, matrix, paint); // 绘画


     iv_copy.setImageBitmap(copyBitmap);
 }

矩阵

 /**
  * Matrix矩阵的变换
  */
 public void onclick(View view){
     Bitmap srcBitmap = getBitmapOnRes(this, R.mipmap.abc);
     iv_src.setImageBitmap(srcBitmap);

     Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth() * 2, srcBitmap.getHeight() * 2, srcBitmap.getConfig()); // 白纸
     Canvas canvas = new Canvas(copyBitmap); // 画布
     Paint paint = new Paint(); // 画笔
     paint.setStyle(Paint.Style.STROKE); // 只画线不填充

     /*
        x: [MSCALE_X,   MSKEW_X,    MTRANS_X]
        y: [MSKEW_Y,    MSCALE_Y,   MTRANS_Y]
        z: [MPERSP_0,   MPERSP_1,   MPERSP_2]

        MSCALE:缩放, MSKEW:错切, MTRANS:位移, MPERSP:透视
     */
     Matrix matrix = new Matrix(); // 矩阵


     // --- setxxx 覆盖 ---
     // 平移(参数: x, y)
     matrix.setTranslate(100, 100);
     // 缩放(参数: 缩放倍数, x, y)
     matrix.setScale(0.5f, 0.5f, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     // 旋转(参数: 角度, x, y绝对坐标)
     matrix.setRotate(30, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     // 旋转(参数: sin, cos, x,y)
     matrix.setSinCos(1, 0, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     // 错切(参数:x错切因子,y因子, 中心x,y) (错切因子为tan值: 45° tan=1)
     matrix.setSkew(1, 0);

     // 重置
     matrix.reset();



     // --- prexxx 前乘叠加 √ M' = M * T(dx, dy) ---
     matrix.preTranslate(100, 100);
     matrix.preScale(0.5f, 0.5f, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     matrix.preRotate(30, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     matrix.preSkew(1, 0);


     // --- postxxx 后乘叠加 M' = T(dx, dy) * M ---
     matrix.postTranslate(100, 100);
     matrix.postScale(0.5f, 0.5f, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     matrix.postRotate(30, srcBitmap.getWidth() / 2, srcBitmap.getHeight() / 2);
     matrix.postSkew(1, 0);



     // --- 自由变换矩阵 ---
     // 参数: 源点[左上x,y, 右上x1,y1, 左下x2,y2, 右下x3,y3], 第一个src点的索引, 目标点[x,y,x1,y1,...], 第一个dst点的索引, 一次使用多少个点(0-4)
     // public boolean setPolyToPoly(float[] src, int srcIndex,float[] dst, int dstIndex,int pointCount)
     matrix.setPolyToPoly(new float[]{0, 0}, 0, new float[]{100, 100}, 0, 1); // 平移
     matrix.setPolyToPoly(new float[]{0, 0, copyBitmap.getWidth(), 0}, 0, new float[]{copyBitmap.getHeight(), 0, copyBitmap.getHeight(), copyBitmap.getWidth()}, 0, 2); // 旋转
     matrix.setPolyToPoly(new float[]{0, 0, 0, copyBitmap.getHeight(), copyBitmap.getWidth(), copyBitmap.getHeight()}, 0, new float[]{0, 0, 100, copyBitmap.getHeight(), copyBitmap.getWidth() + 100, copyBitmap.getHeight()}, 0, 3); // 错切
     matrix.setPolyToPoly(new float[]{0, 0, copyBitmap.getWidth(), 0, 0, copyBitmap.getHeight(), copyBitmap.getWidth(), copyBitmap.getHeight()}, 0, new float[]{0 + 100, 0, copyBitmap.getWidth() - 100, 0, 0, copyBitmap.getHeight(), copyBitmap.getWidth(), copyBitmap.getHeight()}, 0, 4); // 透视


     canvas.drawBitmap(srcBitmap, matrix, paint); // 绘画

     iv_copy.setImageBitmap(copyBitmap);
 }

画笔

 /**
  * Paint画笔的操作, 可设置, 绘画, 渐变, 滤镜
  */
 public void onclick(View view){
     Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
     Canvas canvas = new Canvas(copybitmap); // 画布
     Paint paint = new Paint(); // 画笔

     // --- 样式 ---
     // 填充: STROKE:画线, FILL:填充, FILL_AND_STROKE:画线并填充
     paint.setStyle(Paint.Style.STROKE);
     // 线帽(线条末端): BUTT:无(直角头), ROUND:圆头, SQUARE:直角头
     paint.setStrokeCap(Paint.Cap.ROUND);
     // 角: MITER:直角, ROUND:圆角, BEVEL:平角
     paint.setStrokeJoin(Paint.Join.BEVEL);
     // 排列: LEFT:左, CENTER:中, RIGHT:右
     paint.setTextAlign(Paint.Align.CENTER);



     // --- 属性 ---
     // 是否抗锯齿 (可抗锯齿)
     paint.setAntiAlias(true);
     // 是否抖动 (可使线条柔和)
     paint.setDither(true);
     // 对位图进行滤波处理 (可加快显示)
     paint.setFilterBitmap(true);
     // 设置着色器: BitmapShader / ComposeShader / LinearGradient / RadialGradient / SweepGradient
     paint.setShader(new LinearGradient(0, 0, copybitmap.getWidth(), 0, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, LinearGradient.TileMode.CLAMP));
     // 设置颜色过滤器: ColorMatrixColorFilter / LightingColorFilter / PorterDuffColorFilter
     paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[]{1, 0, 0, 0, 0,  0, 1, 0, 0, 0,  0, 0, 1, 0, 0,  0, 0, 0, 1, 0,})));
     // 设置叠加模式 PorterDuffXfermode(ADD / CLEAR / DARKEN / DST / DST_ATOP / DST_IN / DST_OUT / DST_OVER / LIGHTEN / MULTIPLY / OVERLAY / SCREEN / SRC / SRC_ATOP / SRC_IN / SRC_OUT / SRC_OVER / XOR)
     paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
     // 设置路径效果: ComposePathEffect / CornerPathEffect / DashPathEffect / DiscretePathEffect / PathDashPathEffect / SumPathEffect
     paint.setPathEffect(new CornerPathEffect(50));
     // 设置滤镜: BlurMaskFilter(模糊) / EmbossMaskFilter(浮雕)
     // paint.setMaskFilter(new BlurMaskFilter(20, BlurMaskFilter.Blur.SOLID));

     // 是否不缓存文本
     paint.setLinearText(true);
     // 设置亚像素 (可使文字清晰)
     paint.setSubpixelText(true);
     // 设置文本下划线
     paint.setUnderlineText(true);
     // 设置文本删除线
     paint.setStrikeThruText(true);
     // 设置文本粗体
     paint.setFakeBoldText(true);
     // 设置字体样式
     paint.setTypeface(Typeface.DEFAULT_BOLD);
     // 设置字体大小
     paint.setTextSize(100);
     // 设置阴影 (参数: 角度, 距离x,y, 颜色)
     paint.setShadowLayer(5, 10, 10, Color.BLACK);
     // 设置语言 api>17
     // paint.setTextLocale(Locale.getDefault());
     // 水平缩放 (放大 > 1 > 缩小)
     paint.setTextScaleX(1);
     // 倾斜
     paint.setTextSkewX(0);
     // 行间距 api>21
     // paint.setLetterSpacing(0);
     // 测量文字长度
     float width = paint.measureText("luzhuo.me");

     // 颜色
     paint.setColor(Color.RED);
     paint.setColor(Color.argb(50, 255, 100, 100));
     paint.setARGB(50, 255, 100, 100);
     // 透明度 [0,255]
     paint.setAlpha(50);
     // 宽度
     paint.setStrokeWidth(15);


     // 重置
     paint.reset();


     canvas.drawLine(20, 20, 500, 20, paint);
     canvas.drawText("luzhuo.me", 60, 60, paint);
     iv_copy.setImageBitmap(copybitmap);
 }

Shader

/**
  * Shader着色器, 具有图片填充模式, 颜色填充模式(渐变), 图片与颜色组合填充 的操作
  */
 public void shaderclick(View view){
     Bitmap srcbitmap = getBitmapOnRes(this, R.mipmap.abc);
     Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
     Canvas canvas = new Canvas(copybitmap);
     Paint paint = new Paint();

     Shader shader;
     Shader shader1;
     // 着色器: BitmapShader / ComposeShader / LinearGradient / RadialGradient / SweepGradient
     // BitmapShader(图片填充模式)  TileMode(平铺模式):CLAMP(拉伸最后一个像素) / MIRROR(翻转) / REPEAT(重复)
     shader = new BitmapShader(srcbitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
     shader = new BitmapShader(srcbitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
     shader1 = new BitmapShader(srcbitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

     // LinearGradient(线性渐变)
     // (参数:起点x,y, 终点x,y, 渐变颜色[],  颜色分布[0,1]/null, 填充模式)
     shader = new LinearGradient(0, 0, copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.CLAMP);
     shader = new LinearGradient(0, 0, copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.MIRROR);
     shader = new LinearGradient(0, 0, copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.REPEAT);

     // RadialGradient(径向渐变)
     shader = new RadialGradient(copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, 300, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.CLAMP);
     shader = new RadialGradient(copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, 300, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.MIRROR);
     shader = new RadialGradient(copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, 300, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null, Shader.TileMode.REPEAT);

     // SweepGradient(扫描渐变)
     shader = new SweepGradient(copybitmap.getWidth() / 2, copybitmap.getHeight() / 2, new int[]{Color.RED, Color.YELLOW, Color.BLUE}, null);

     // ComposeShader(组合渲染)
     // (参数: 渲染器1, 渲染器2, 组合模式Xfermode/PorterDuff.Mode)
     shader = new ComposeShader(shader, shader1, new PorterDuffXfermode(PorterDuff.Mode.ADD));

     paint.setShader(shader);


     // 矩形方框用于限制 Shader 的范围
     canvas.drawRect(0, 0, 1000, 1000, paint);
     iv_copy.setImageBitmap(copybitmap);
 }

ColorFilter

 /**
  * colorFilter颜色过滤操作, 可通过 颜色矩阵过滤 光照过滤 颜色叠加过滤 等过滤方式
  */
 public void colorFilterClick(View view){
     Bitmap srcbitmap = MainActivity.getBitmapOnRes(this, R.mipmap.abc);
     Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
     Canvas canvas = new Canvas(copybitmap); // 画布
     Paint paint = new Paint(); // 画笔

     /*
     ColorMatrix颜色矩阵4*5 (矩阵不会调, 可以调用其方法)
     m = [ a, b, c, d, e,   C = [ R
           f, g, h, i, j,         G
           k, l, m, n, o,         B
           p, q, r, s, t ]        A ]

     R' = a*R + b*G + c*B + d*A + e
     G' = f*R + g*G + h*B + i*A + j
     B' = k*R + l*G + m*B + n*A + o
     A' = p*R + q*G + r*B + s*A + t
     */
     ColorMatrix colorMatrix = new ColorMatrix(new float[]{
             1, 0, 0, 0, 0,
             0, 1, 0, 0, 0,
             0, 0, 1, 0, 0,
             0, 0, 0, 1, 0,});
     colorMatrix.setRotate(0, 50); // 色调(参数: 颜色:0RED/1GREEN/2BLUE)
     colorMatrix.setSaturation(99); // 饱和度
     colorMatrix.setScale(5, 5, 5, 10); // 亮度

     ColorFilter colorFilter;
     // 设置颜色过滤器: ColorMatrixColorFilter / LightingColorFilter / PorterDuffColorFilter
     // ColorMatrixColorFilter(颜色矩阵过滤)
     colorFilter = new ColorMatrixColorFilter(colorMatrix);

     // LightingColorFilter(光照过滤) (参数:与原色相乘, 与原色相加) R' = R * mul.R + add.R
     colorFilter = new LightingColorFilter(0xFFFFFF00, 0x00000000);

     // PorterDuffColorFilter(波特达夫混合模式)
     colorFilter = new PorterDuffColorFilter(0XFFFFFF00, PorterDuff.Mode.MULTIPLY);

     /*
     PorterDuff.Mode:

     CLEAR       (0), 清除图像
     SRC         (1), 只显示src
     DST         (2), 只显示dst
     SRC_OVER    (3), src局顶
     DST_OVER    (4), dst局顶
     SRC_IN      (5), 交集, 取src
     DST_IN      (6), 交集, 取dst
     SRC_OUT     (7), 不相交, 取src
     DST_OUT     (8), 不相交, 取dst
     SRC_ATOP    (9), 交集src, 不相交dest
     DST_ATOP    (10), 交集dst, 不相交src
     XOR         (11), 异或

     DARKEN      (16), 取全部, 变暗
     LIGHTEN     (17), 取全部, 变亮
     MULTIPLY    (13), 取全部, 正片叠底
     SCREEN      (14), 取全部, 滤色
     ADD         (12), 取全部, 线性减淡
     OVERLAY     (15); 取全部, 叠加
      */

     paint.setColorFilter(colorFilter);
     canvas.drawBitmap(srcbitmap, new Matrix(), paint);
     iv_copy.setImageBitmap(copybitmap);
 }

PorterDuff.Mode

PorterDuffXfermode

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));

见 PorterDuff.Mode

PathEffect

/**
  * PathEffect的操作, 可对path进行 角处理, 线段处理, 线段组合等操作
  */
 public void pathEffectClick(View view){
     Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
     Canvas canvas = new Canvas(copybitmap); // 画布
     Paint paint = new Paint(); // 画笔
     paint.setStyle(Paint.Style.STROKE);
     paint.setStrokeWidth(10);
     Path path = new Path();
     path.addRect(10, 10, 600, 600, Path.Direction.CW);


     PathEffect pathEffect, pathEffect1, pathEffect2;
     Path path1 = new Path();
     path1.addCircle(100, 100, 5, Path.Direction.CW);
     // --- 属性 ---
     // 设置路径效果: ComposePathEffect / CornerPathEffect / DashPathEffect / DiscretePathEffect / PathDashPathEffect / SumPathEffectSumPathEffect
     // CornerPathEffect(线段之间的直角变圆角)
     pathEffect1 = new CornerPathEffect(50);
     // DashPathEffect(虚线) (参数:[实线长度, 空线长度, ...] , 起始偏移量)
     pathEffect2 = new DashPathEffect(new float[]{10, 10}, 1);
     // DiscretePathEffect(离散线段) (参数:线段长度, 线段偏移量)
     pathEffect = new DiscretePathEffect(10, 10);
     // PathDashPathEffect(用指定path实线的虚线) (参数:间隔, 起始偏移量, 模式) 模式:TRANSLATE平移, ROTATE旋转, MORPH连接处平滑
     pathEffect = new PathDashPathEffect(path1, 30, 0, PathDashPathEffect.Style.TRANSLATE);
     pathEffect = new PathDashPathEffect(path1, 30, 0, PathDashPathEffect.Style.ROTATE);
     pathEffect = new PathDashPathEffect(path1, 30, 0, PathDashPathEffect.Style.MORPH);
     // SumPathEffectSumPathEffect(path叠加)
     pathEffect = new SumPathEffect(pathEffect1, pathEffect2);
     // ComposePathEffect(path组合)(注意path先后顺序)
     pathEffect = new ComposePathEffect(pathEffect2, pathEffect1);


     paint.setPathEffect(pathEffect);
     canvas.drawPath(path, paint);
     iv_copy.setImageBitmap(copybitmap);
 }

MaskFilter

 /**
  * MaskFilter操作, 可绘制 阴影, 浮雕 效果
  */
 public void maskFilterClick(View view){
     Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
     Canvas canvas = new Canvas(copybitmap); // 画布
     Paint paint = new Paint(); // 画笔
     paint.setColor(Color.argb(100, 255, 0, 0));


     MaskFilter maskFilter;
     // 设置滤镜: BlurMaskFilter(模糊) / EmbossMaskFilter(浮雕)
     // BlurMaskFilter(模糊) (参数:扩散范围, 类型) 类型:SOLID(阴影不融合) / NORMAL(阴影融合) / OUTER(外阴影) / INNER(内阴影)
     maskFilter = new BlurMaskFilter(30, BlurMaskFilter.Blur.SOLID);
     maskFilter = new BlurMaskFilter(20, BlurMaskFilter.Blur.NORMAL);
     maskFilter = new BlurMaskFilter(20, BlurMaskFilter.Blur.OUTER);
     maskFilter = new BlurMaskFilter(20, BlurMaskFilter.Blur.INNER);

     // EmbossMaskFilter(浮雕) (参数:光源tan[x,y,z], 暗部光线, 亮部光线, 突出的距离)
     maskFilter = new EmbossMaskFilter(new float[]{1, 1, 1}, 0.1f, 10, 10);


     paint.setMaskFilter(maskFilter);
     canvas.drawRect(10, 10, 600, 600, paint);
     iv_copy.setImageBitmap(copybitmap);
 }

BlurMaskFilter.Blur

路径

/**
 * Path路径的操作, 具有 线, 布尔, 反向, 平移 等操作
 */
public void onclick(View view){
    Bitmap copybitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888); // 白纸
    Canvas canvas = new Canvas(copybitmap); // 画布
    Paint paint = new Paint(); // 画笔
    paint.setStyle(Paint.Style.STROKE);

    Path path = new Path(); // 路径


    // 移动画笔
    path.moveTo(10, 50);
    // 绘制直线
    path.lineTo(30, 100);
    // 绘制贝塞尔曲线(参数:控制点x,y, 终点x1,y1)
    path.quadTo(100, 50, 150, 150); // 2次
    path.cubicTo(0, 150, 300, 450, 0, 600); // 3次 (参数: 控制点x,y, 控制点x1,y1, 终点x2,y2)
    // 绘制圆弧 (参数:矩形, 起始角度, 绘制角度, 是否首尾相连:false相连)
    path.arcTo(new RectF(0, 0, 300, 300), 0, 90, false);
    // 闭合路径(首尾相连)
    path.close();


    // --- addxxx 添加xxx到路径中 ---
    // addArc、addRoundRect、addOval、addRect、addCircle
    // 添加圆弧到路径中
    path.addArc(new RectF(10, 10, 100, 100), 0, 180);
    // 添加矩形
    path.addRect(new RectF(10, 110, 200, 200), Path.Direction.CW); // CW:顺时针 / CCW:逆时针
    // 添加圆角矩形
    path.addRoundRect(new RectF(10, 210, 300, 300), 25, 25, Path.Direction.CW);
    // 添加椭圆
    path.addOval(new RectF(10, 310, 400, 400), Path.Direction.CW);
    // 添加圆
    path.addCircle(100, 410, 50, Path.Direction.CW);

    canvas.drawPath(path, paint);
    paint.setStyle(Paint.Style.FILL);
    paint.setARGB(80, 255, 100, 100);



    path = new Path();
    Path path1 = new Path();
    path.addCircle(200, 200, 100, Path.Direction.CW);
    path1.addRect(200, 200, 300, 300, Path.Direction.CW);

    // --- Path.Op 路径布尔模式 api >= 19 (叠加) ---
    // DIFFERENCE:删除
    path.op(path1, Path.Op.DIFFERENCE);
    // INTERSECT:相交
    path.op(path1, Path.Op.INTERSECT);
    // REVERSE_DIFFERENCE:反向删除(DIFFERENCE相反)
    path.op(path1, Path.Op.REVERSE_DIFFERENCE);
    // UNION:相加
    path.op(path1, Path.Op.UNION);
    // XOR:异或
    path.op(path1, Path.Op.XOR);

    path.addRect(200, 200, 300, 300, Path.Direction.CW);
// --- FillType 填充样式 ---
    // WINDING:相加
    path.setFillType(Path.FillType.WINDING);
    // EVEN_ODD:异或
    path.setFillType(Path.FillType.EVEN_ODD);
    // INVERSE_WINDING:相加后反向
    path.setFillType(Path.FillType.INVERSE_WINDING);
    // INVERSE_EVEN_ODD:异或后反向
    path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
    // 是否是反向填充
    boolean isInverse = path.isInverseFillType(); // false:WINDING / EVEN_ODD; true:INVERSE_WINDING / INVERSE_EVEN_ODD
    // 进行反向填充
    path.toggleInverseFillType();



    // 平移
    path.offset(100, 100);


    canvas.drawPath(path, paint);
    iv_copy.setImageBitmap(copybitmap);
}

Path.Op

Path.FillType

Bezier曲线

基础知识

代码实现

 // 二阶曲线的绘制方法 (三个点: 起点x,y, 控制点x,y, 终点x,y)
mPath.moveTo(mStartPointX, mStartPointY);
 mPath.quadTo(mFlagPointX, mFlagPointY, mEndPointX, mEndPointY); // quadTo绝对坐标 / rQuadTo 相对坐标

 // 三阶曲线的绘制方法 (四个点: 起点x,y, 控制点x1,y1, 控制点x2,y2, 终点x,y)
mPath.moveTo(mStartPointX, mStartPointY);
 mPath.cubicTo(mFlagPointOneX, mFlagPointOneY, mFlagPointTwoX, mFlagPointTwoY, mEndPointX, mEndPointY);

// Android Path路径只提供两种贝塞尔曲线绘制方法

图片绘制与属性动画结合

详见 Android 绘制动画 部分


版权声明:本文为Rozol原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。