使用 Path 对象裁剪位图

Posted

技术标签:

【中文标题】使用 Path 对象裁剪位图【英文标题】:Crop bitmap with Path object 【发布时间】:2014-01-30 12:07:33 【问题描述】:

在我的应用程序中,我尝试从选定的用户形状中裁剪位图。 我已经阅读了很多关于这个问题的内容,但我仍然找不到任何解决方案。 我裁剪后得到的图像不是我标记的图像。

这是我的代码:

public class TestView extends ImageView implements OnTouchListener 
    private Paint paint;
    public static List<Point> points;

    Bitmap bitmapMain;

    Context mContext;

    private int leftX = 0;
    private int rightX = 0;
    private int upY = 0;
    private int downY = 0;

    public TestView(Context c) 
        super(c);

        mContext = c;
        setFocusable(true);
        setFocusableInTouchMode(true);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setPathEffect(new DashPathEffect(new float[]  10, 20 , 0));
        paint.setStrokeWidth(5);
        paint.setColor(Color.WHITE);

        this.setOnTouchListener(this);
        points = new ArrayList<Point>();


    

    public TestView(Context context, AttributeSet attrs) 
        super(context, attrs);
        mContext = context;
        setFocusable(true);
        setFocusableInTouchMode(true);

        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.WHITE);

        this.setOnTouchListener(this);
        points = new ArrayList<Point>();

    

    public void setBitmap(Bitmap bit)
    
        bitmapMain = bit;
    

    public void onDraw(Canvas canvas) 
        super.onDraw(canvas);

       // canvas.drawBitmap(bitmap, 0, 0, null);

        Path path = new Path();
        boolean first = true;

        for (int i = 0; i < points.size(); i += 2) 
            Point point = points.get(i);
            if (first) 
                first = false;
                path.moveTo(point.x, point.y);
             else if (i < points.size() - 1) 
                Point next = points.get(i + 1);
                path.quadTo(point.x, point.y, next.x, next.y);
             else 
                path.lineTo(point.x, point.y);
            
        
        canvas.drawPath(path, paint);
    

    public boolean onTouch(View view, MotionEvent event) 


        if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_OUTSIDE || event.getAction() == MotionEvent.ACTION_CANCEL) 
        
             return true;
        

        int intX = (int) (event.getX());
        int intY = (int) (event.getY());

        if(event.getAction() == MotionEvent.ACTION_DOWN)
        
            leftX = intX;
            rightX = intX;
            downY = intY;
            upY = intY;
        

        Point point = new Point();

        point.x = intX;
        point.y = intY;

        if(intX < leftX)
            leftX =  intX;
        if(intX > rightX)
            rightX = intX;
        if(intY > downY)
            downY =  intY;
        if(intY < upY)
            upY = intY;

        points.add(point);


       invalidate();

        return true;
    

    public void clear() 
           
        leftX = 0;
        rightX = 0;
        upY = 0;
        downY = 0;

        points.clear();
        paint.setColor(Color.WHITE);
        paint.setStyle(Style.STROKE);
        invalidate();
    


    public Bitmap getImage2()
    

    //getting the size of the new bitmap

        int newHeight = rightX - leftX;
        int newWidth = downY - upY;

        Bitmap resultingImage = Bitmap.createBitmap(newHeight, newWidth, Config.ARGB_8888);

        Canvas canvas = new Canvas(resultingImage);
        Paint paint = new Paint();
        paint.setAntiAlias(true);

        Path path = new Path();
        for (int i = 0; i < points.size() - 1; i++) 

            path.quadTo((float)points.get(i).x, (float)points.get(i).y, (float)points.get(i+1).x, (float)points.get(i+1).y);
           // path.lineTo(points.get(i).x, points.get(i).y);
        

//closing the shape, connection last point to the first one

        path.quadTo((float)points.get(points.size()-1).x, (float)points.get(points.size()-1).y, (float)points.get(0).x, (float)points.get(0).y);

         //drawing user shape

        canvas.drawPath(path, paint);

         //getting image inside the shape

        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));

        canvas.drawBitmap(bitmapMain, 0, 0, paint);

        return resultingImage;
    



   

我的活动:

public class MainActivity extends Activity implements OnClickListener 

    private TestView drawing;
     private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100;
     public static final int MEDIA_TYPE_IMAGE = 1;
     private static final String IMAGE_DIRECTORY_NAME = "demo pictures";
     private Uri fileUri;


    @Override
    protected void onCreate(Bundle savedInstanceState) 

        this.requestWindowFeature(Window.FEATURE_NO_TITLE);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //drawing = (SignatureViewModed) findViewById(R.id.myTest);
        findViewById(R.id.button1).setOnClickListener(this);
        findViewById(R.id.button2).setOnClickListener(this);
        findViewById(R.id.button3).setOnClickListener(this);

        drawing = (TestView) findViewById(R.id.images);

    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    



    @Override
    public void onClick(View arg0) 
        // TODO Auto-generated method stub

        switch(arg0.getId())
        

        case R.id.button1:
        
            drawing.clear();
            break;
        

        case R.id.button2:
        
            if(isDeviceSupportCamera())
                captureImage();
            else
                Toast.makeText(this, "No camera", Toast.LENGTH_SHORT).show();
            break;
        

        case R.id.button3:
        
            showCutImage();
            break;
        

        

    

    private boolean isDeviceSupportCamera() 
        if (getApplicationContext().getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA)) 
            // this device has a camera
            return true;
         else 
            // no camera on this device
            return false;
        
    

    private void captureImage() 

        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

        fileUri = getOutputMediaFileUri();

        intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

        // start the image capture Intent
        startActivityForResult(intent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);
    

    public Uri getOutputMediaFileUri() 
        return Uri.fromFile(getOutputMediaFile());
    

    /**
     * returning image / video
     */
    private static File getOutputMediaFile() 

        // External sdcard location
        File mediaStorageDir = new File(
                Environment
                        .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
                IMAGE_DIRECTORY_NAME);

        // Create the storage directory if it does not exist
        if (!mediaStorageDir.exists()) 
            if (!mediaStorageDir.mkdirs()) 
                Log.d(IMAGE_DIRECTORY_NAME, "Oops! Failed create "
                        + IMAGE_DIRECTORY_NAME + " directory");
                return null;
            
        

        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
                Locale.getDefault()).format(new Date());
        File mediaFile;

         mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");

        return mediaFile;
    

    @Override
    protected void onSaveInstanceState(Bundle outState) 
        super.onSaveInstanceState(outState);

        // save file url in bundle as it will be null on scren orientation
        // changes
        outState.putParcelable("file_uri", fileUri);
    

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) 
        super.onRestoreInstanceState(savedInstanceState);

        // get the file url
        fileUri = savedInstanceState.getParcelable("file_uri");
    


    @Override
    protected void onActivityResult(int requestCode, int resultCode,
            Intent data2) 

        Bitmap bitmap = null;

        if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) 

            if (resultCode == RESULT_OK)
            

                String filePath = fileUri.getPath();
                bitmap = decodeSampledBitmapFromPath(filePath, true);

                drawing.setBitmap(bitmap);

                drawing.setImageBitmap(bitmap);
            

        

            super.onActivityResult(requestCode, resultCode, data2);
        

    public Bitmap decodeSampledBitmapFromPath(String path, boolean isFullScreen) 

        Bitmap bmp = null;
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeFile(path, options);

        if(isFullScreen)
            options.inSampleSize = calculateInSampleSize(options, getScreenWidth() , getScreenHeight());
        else
            options.inSampleSize = calculateInSampleSize(options, 200, 200);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        bmp = BitmapFactory.decodeFile(path, options);

        return bmp;
        

    public int calculateInSampleSize(BitmapFactory.Options options,
            int reqWidth, int reqHeight) 

        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) 
            if (width > height) 
                inSampleSize = Math.round((float) height / (float) reqHeight);
             else 
                inSampleSize = Math.round((float) width / (float) reqWidth);
             
         
         return inSampleSize;
        

    @SuppressLint("NewApi")
    public int getScreenHeight()
    
        Display display = ((WindowManager) this.getSystemService(WINDOW_SERVICE))
                .getDefaultDisplay();

        int height = 0;

        if (android.os.Build.VERSION.SDK_INT >= 13) 
            Point size = new Point();
            display.getSize(size);
            height = size.y;

         else 
            height = display.getHeight();

        

        return height;
    

    @SuppressLint("NewApi")
    public int getScreenWidth()
    
        Display display = ((WindowManager) this.getSystemService(WINDOW_SERVICE))
                .getDefaultDisplay();

        int width = 0;

        if (android.os.Build.VERSION.SDK_INT >= 13) 
            Point size = new Point();
            display.getSize(size);

            width = size.x;
         else 

            width = display.getWidth();
        

        return width;
    

    public void showCutImage()
    
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        View prefView = View.inflate(this,R.layout.image_show, null);

        ImageView image = (ImageView) prefView.findViewById(R.id.imageView1);

        image.setImageBitmap(drawing.getImage2());

        builder.setView(prefView);

        builder.show();

    

这是我的结果的 2 张图片。 你可以看到我得到的图片不是我标记的整个区域,它有额外的角落。

【问题讨论】:

我没有分析你的长代码,但用路径裁剪很容易:canvas.clipPath(...); canvas.draw*(...) 嗨,我试过你的解决方案,但它不起作用。 clipPath 不工作?嗯... 【参考方案1】:

你应该关闭路径。 将testView的onDraw方法改为

public void onDraw(Canvas canvas) super.onDraw(画布);

   // canvas.drawBitmap(bitmap, 0, 0, null);

    Path path = new Path();
    boolean first = true;

    for (int i = 0; i < points.size(); i += 2) 
        Point point = points.get(i);
        if (first) 
            first = false;
            path.moveTo(point.x, point.y);
         else if (i < points.size() - 1) 
            Point next = points.get(i + 1);
            path.quadTo(point.x, point.y, next.x, next.y);
         else 
            path.lineTo(point.x, point.y);
        
    
    path.lineTo(points.get(0).x,points.get(0).y);
    canvas.drawPath(path, paint);

【讨论】:

以上是关于使用 Path 对象裁剪位图的主要内容,如果未能解决你的问题,请参考以下文章

如何在不创建新位图的情况下拥有圆形、中心裁剪的 imageView?

如何裁剪位图并获得图像的下 50%

将位图图像裁剪为自定义正方形

如何以最少的内存使用裁剪位图?

如何使用firemonkey在选定区域裁剪位图?

如何裁剪矩形的软件位图 - UWP