• Android绘图及Bitmap几个知识点整理


    Android Path绘制的折线如何变得平滑

    多个点可以连成一个折线,如何将折线的拟合处变为曲线,使得整个线看上去更加平滑呢?

    分下来有以下三种实现方法:
    方法1:

    Paint.setStrokeJoin(Paint.Join.ROUND)
    这个方法可以将path中所有线段的Join方式设置为ROUND,实际效果就是拟合处变成了更加平滑的曲线;
    

    方法2:

    CornerPathEffect cornerPathEffect = new CornerPathEffect(200);
    Paint.setPathEffect(cornerPathEffect);
    此处的200就是平滑的度数;
    

    方法3:

    自己去实现,原理很简单,就是在两条线段相连的地方
    原始做法:
        moveTo(x1,y1);
        lineTo(x2,y2);
        lineTo(x3,y3);
        此时就是两条线段相连;
    平滑做法:
        moveTo(x1,y1);
        lineTo(x21,y21);(x21,y21)是(x1,y1)--(x2,y2)上的一个点,很接近(x2,y2);
        quadTo(x31,y31);(x31,y31)是(x2,y2)--(x3,y3)上的一个点,很接近(x2,y2);
        lineTo(x3,y3);
    具体实现上还要考虑很多,例如两条线段的长度啊,形成的角度等等;
    

    这里推荐使用第一种,实现简单,而且不需要像方法2那样设置一个固定的角度(设置固定角度的效果不是很适用于所有角度的折线);

    将Bitmap中某个颜色替换成其他颜色

    下面例子中,我们使用ARGB(255,0,0,0)也就是黑色替换bitmap中的0x616161,容忍度为50范围的颜色:

    private Bitmap handleCrossImageBack(Bitmap crossimage){
         // start with a Bitmap bmp
        Bitmap newBmp = crossimage.copy(Bitmap.Config.ARGB_8888, true);
        Canvas c = new Canvas(newBmp);
    
        // get the int for the colour which needs to be removed
        Paint paint = new Paint();// 去锯齿
        paint.setAntiAlias(true);// 防抖动
        paint.setDither(true);// 图像过滤
        paint.setFilterBitmap(true);
        paint.setARGB(255, 0, 0, 0); // ARGB for the color to replace,black replace gray
        paint.setXfermode(new AvoidXfermode(0x616161, 50, AvoidXfermode.Mode.TARGET));
        c.drawPaint(paint);
        return newBmp;
    }
    

    根据现有Bitmap生成相同图案指定大小的新Bitmap

    通过一张现有的Bitmap,画出一张同样的但是大小使我们指定的Bitmap;
    直接createBitmap的话不允许生成的bitmap的宽高大于原始的,因此需要特定方法来将一张Bitmap的大小进行调整;

    //crossImage为一张现有的bitmap
    Bitmap target = Bitmap.createBitmap(MIDDLE_LINE_WIDTH, MIDDLE_LINE_WIDTH, crossImage.getConfig());
    Canvas temp_canvas = new Canvas(target);
    temp_canvas.drawBitmap(crossImage, null, new Rect(0, 0, target.getWidth(), target.getHeight()), null);
    //此时的target就是一张指定大小,但是内容和crossImage一样的bitmap了
    

    根据Path对Bitmap进行截取

    方法1:

    //mRectPath为一条现有的path
    Path bitmap_path = new Path();
    bitmap_path.moveTo(mPoints.get(0).x-width/2+LINE_WIDTH/2,mPoints.get(0).y-height+LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x+width/2-LINE_WIDTH/2,mPoints.get(0).y-height+LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x+width/2-LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x-width/2+LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
    bitmap_path.close();
    if(bitmap_path.op(mRectPath,Op.INTERSECT)){
        paint.setStyle(Paint.Style.FILL);
        bmpCanvas.drawPath(bitmap_path, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
    }else{
        bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);                  
    }
    

    问题:在同一个画布上绘制原本的图形以及截取后的bitmap,需要先对bitmap进行截取绘制,然后再绘制原本的图形,否则使用setXfermode操作会出错;

    方法2:

    //绘制路线
    bmpCanvas.drawPath(mRectPath,paint);
    //create 100,100 bitmap
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    int width = mBufferBmpRect.width();
    int height =  mBufferBmpRect.height();
    
    
    Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
    Canvas temp_canvas = new Canvas(target);
    
    //TODO 做截取操作
    Path bitmap_path = new Path();
    bitmap_path.moveTo(mPoints.get(0).x-bitmap.getWidth()/2+LINE_WIDTH/2,mPoints.get(0).y-bitmap.getHeight()+LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x+bitmap.getWidth()/2-LINE_WIDTH/2,mPoints.get(0).y-bitmap.getHeight()+LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x+bitmap.getWidth()/2-LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
    bitmap_path.lineTo(mPoints.get(0).x-bitmap.getWidth()/2+LINE_WIDTH/2,mPoints.get(0).y-LINE_WIDTH/2);
    bitmap_path.close();
    if(bitmap_path.op(mRectPath,Op.INTERSECT)){
        paint.setStyle(Paint.Style.FILL);
        temp_canvas.drawPath(bitmap_path, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        temp_canvas.drawBitmap(bitmap,mPoints.get(0).x-bitmap.getWidth()/2,mPoints.get(0).y-bitmap.getHeight(), paint);     
    //                  bmpCanvas.drawPath(bitmap_path, paint);
    //                  bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
    }else{
        temp_canvas.drawBitmap(bitmap,mPoints.get(0).x-bitmap.getWidth()/2,mPoints.get(0).y-bitmap.getHeight(), null);      
    //                  bmpCanvas.drawBitmap(target, mPoints.get(0).x-width/2,mPoints.get(0).y-height, paint);
    }
    
    //去除Xfermode
    paint.setXfermode(null);
    bmpCanvas.drawBitmap(target, 0,0, paint);
    

    思路:

    1. 先绘制图形;
    2. 创建一个跟原本图形所在画布一样大的bitmap;
    3. 在该bitmap上绘制需要被截取的bitmap同时将其截取;
    4. 将bitmap绘制到图形所在bitmap上;

    实现步骤:

    1. 获取到原路径的RectPath。
    2. 得到需要被截取的bitmap。
    3. 得到显示区域的大小。
    4. 得到路径起点。
    5. 根据起点以及bitmap的大小创建一个bitmapPath代表bitmap所占区域。
    6. 将rectPath与bitmapPath进行op运算。
    7. 如果有相交:
      1. 绘制相交运算后的bitmapPath。
      2. setXfermode(SRC_IN)。
      3. 再绘制bitmap,使用指定的paint。
    8. 如果没有相交:
      1. 直接绘制bitmap到图形所在画布上即可。

    优点:最终得到的bitmap就是一个等大的bitmap,且该bitmap上只有被截取后剩余的一部分bitmap;

    根据一条线的坐标求出线两侧形成的路的坐标

    private void points2path() {
        /**
        *A(a,b) B(m,n) BC = L
        *
        *x1= m - (b-n) /√[(a-m)^2+(b-n)^2]
        *x2= m + (b-n) /√[(a-m)^2+(b-n)^2]
        *y1 = n + L(a-m) /√[(a-m)^2+(b-n)^2]
        *y2 = n - L(a-m) /√[(a-m)^2+(b-n)^2]
         */
        //获取所有计算后的点的集合
        int path_width = 30;
        
        //获取一侧点的集合
        mLeftPoints.clear();
        for(int i=0;i<mPoints.size();i++){
            Point currentPoint = mPoints.get(i);
            Point secondPoint;
            int m = currentPoint.x;
            int n = currentPoint.y;
            if(i==mPoints.size()-1){
                secondPoint= mPoints.get(i-1);
            }else{
                secondPoint= mPoints.get(i+1);
            }
            int a;
            int b;
            a = secondPoint.x;
            b = secondPoint.y;
            
            int x;
            int y;
            
            /**
            *C1(x,y) c2(x3,y3) A(x2,y2) B(x1,y1) BC=a
            *
            *x=x1-a*sin{arctan[(y2-y1)/(x2-x1)]}
            *y=y1+a*cos{arctan[(y2-y1)/(x2-x1)]}
            *x3=x1+a*sin{arctan[(y2-y1)/(x2-x1)]} 
            *y3=y1- a*cos{arctan[(y2-y1)/(x2-x1)]}
             */
            
            //m,n为B,a,b为A
            int x1=m,y1=n;
            int x2=a,y2=b;
            if(y2==y1){
                x = (int) (m - (b-n) / Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
                y = (int) (n + (path_width/2)*(a-m) /Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
            }else if(x2==x1){
                x = x1+(path_width/2);
                y = y1;
            }else if(x2<x1 && y2>y1){
                x=(int) (x1+(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
                y=(int) (y1-(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));               
            }else{
                x=(int) (x1-(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
                y=(int) (y1+(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
            }
            
            mLeftPoints.add(new Point(x, y));
        }
        
        //获取另一侧点的集合
        mRightPoints.clear();
        for(int i=0;i<mPoints.size();i++){
            Point currentPoint = mPoints.get(i);
            Point secondPoint;
            int m = currentPoint.x;
            int n = currentPoint.y;
            if(i==mPoints.size()-1){
                secondPoint= mPoints.get(i-1);
            }else{
                secondPoint= mPoints.get(i+1);
            }
            int a;
            int b;
            a = secondPoint.x;
            b = secondPoint.y;
            
            int x;
            int y;
            
            
            int x1=m,y1=n;
            int x2=a,y2=b;
            if(y2==y1){
                x = (int) (m + (b-n) / Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
                y = (int) (n - (path_width/2)*(a-m) /Math.sqrt(Math.pow((a-m),2)+Math.pow((b-n),2)));
            }else if(x2==x1){
                x = x1-(path_width/2);
                y = y1;
            }else if(x2<x1 && y2>y1){
                x=(int) (x1-(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
                y=(int) (y1+(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));               
            }else{
                x=(int) (x1+(path_width/2)*Math.sin(Math.atan((y2-y1)/(x2-x1))));
                y=(int) (y1-(path_width/2)*Math.cos(Math.atan((y2-y1)/(x2-x1))));
            }
            
            mRightPoints.add(new Point(x, y));
        }
        
        //由于最后一个点的坐标是反向计算出来的,因此它的left和right是反的,在此做交换处理
        Point temp = mLeftPoints.remove(mLeftPoints.size()-1);
        mLeftPoints.add(mRightPoints.remove(mRightPoints.size()-1));
        mRightPoints.add(temp);
        
        mPointsPath.clear();
        mPointsPath.addAll(mLeftPoints);
        mPointsPath.addAll(mRightPoints);
        
        //将点集合转成成矩形Path
        mRectPath.reset();
        Point point = mPointsPath.get(0);
        mRectPath.moveTo(point.x,point.y);
        for(int i=1;i<mPointsPath.size();i++){
            if(i<mLeftPoints.size()){
                point = mLeftPoints.get(i);
            }else{
                point = mRightPoints.get(mRightPoints.size()-(i-mLeftPoints.size()+1));
            }
            mRectPath.lineTo(point.x, point.y);
        }
        
        
        //将点集合转换成Path集合,Path集合个数为原始点的个数减一(此处可表示为left或者right集合长度减一)
        mPaths.clear();
        for(int i=0;i<mLeftPoints.size()-1;i++){
            Path path = new Path();
            Point leftCurrentPoint = mLeftPoints.get(i);
            Point leftNextPoint = mLeftPoints.get(i+1);
            Point rightCurrentPoint = mRightPoints.get(i);
            Point rightNextPoint = mRightPoints.get(i+1);
            path.moveTo(leftCurrentPoint.x,leftCurrentPoint.y);
            path.lineTo(leftNextPoint.x,leftNextPoint.y);
            path.lineTo(rightNextPoint.x,rightNextPoint.y);
            path.lineTo(rightCurrentPoint.x,rightCurrentPoint.y);
            path.close();
            mPaths.add(path);
        }
    }
    
  • 相关阅读:
    JsonResponse
    python导出数据到excel
    ftp上传文件到服务器
    js设计模式 —— 订阅发布模式
    reducer的作用
    redux设置和使用三大原则
    js事件监听
    图片占位符
    正则练习
    正则基础
  • 原文地址:https://www.cnblogs.com/helongBlog/p/13086971.html
Copyright © 2020-2023  润新知