使用 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 对象裁剪位图的主要内容,如果未能解决你的问题,请参考以下文章