How can we Convert Bitmap to Path and draw on canvas in Android
I want to create a mask using the path that we get and overlay it over another image, I dont want another bitmap, but a canvas where we have the bitmap as a path and then use the drawingview to edit it .
I have an Image ,
I want to convert it to a Path and draw it on a Canvas in in Android Like this
The Drawing view is a custom view Like this
public class DrawingView extends View {
private static final float TOUCH_TOLERANCE = 4;
private Bitmap bitmap;
private Canvas canvas;
private Path path;
private Paint bitmapPaint;
private Paint paint;
private boolean drawMode;
private float x, y;
private float penSize = 10;
private float eraserSize = 10;
public DrawingView(Context c) {
this(c, null);
}
public DrawingView(Context c, AttributeSet attrs) {
this(c, attrs, 0);
}
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
init();
}
private void init() {
path = new Path();
bitmapPaint = new Paint(Paint.DITHER_FLAG);
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(penSize);
drawMode = true;
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
}
#Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
canvas.drawPath(path, paint);
}
private void touchStart(float x, float y) {
path.reset();
path.moveTo(x, y);
this.x = x;
this.y = y;
canvas.drawPath(path, paint);
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - this.x);
float dy = Math.abs(y - this.y);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(this.x, this.y, (x + this.x) / 2, (y + this.y) / 2);
this.x = x;
this.y = y;
}
canvas.drawPath(path, paint);
}
private void touchUp() {
path.lineTo(x, y);
canvas.drawPath(path, paint);
path.reset();
if (drawMode) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
} else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
}
#Override public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!drawMode) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
} else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
}
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
if (!drawMode) {
path.lineTo(this.x, this.y);
path.reset();
path.moveTo(x, y);
}
canvas.drawPath(path, paint);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchUp();
invalidate();
break;
default:
break;
}
return true;
}
public void initializePen() {
drawMode = true;
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(penSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
}
public void initializeEraser() {
drawMode = false;
paint.setColor(Color.parseColor("#f4f4f4"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(eraserSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void clear() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
invalidate();
}
#Override public void setBackgroundColor(int color) {
if (canvas == null) {
canvas = new Canvas();
}
canvas.drawColor(color);
super.setBackgroundColor(color);
}
public void setEraserSize(float size) {
eraserSize = size;
initializeEraser();
}
public void setPenSize(float size) {
penSize = size;
initializePen();
}
public float getEraserSize() {
return eraserSize;
}
public float getPenSize() {
return penSize;
}
public void setPenColor(#ColorInt int color) {
paint.setColor(color);
}
public #ColorInt int getPenColor() {
return paint.getColor();
}
public void loadImage(Bitmap bitmap) {
this.bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
canvas.setBitmap(this.bitmap);
bitmap.recycle();
invalidate();
}
public boolean saveImage(String filePath, String filename, Bitmap.CompressFormat format,
int quality) {
if (quality > 100) {
Log.d("saveImage", "quality cannot be greater that 100");
return false;
}
File file;
FileOutputStream out = null;
try {
switch (format) {
case PNG:
file = new File(filePath, filename + ".png");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
case JPEG:
file = new File(filePath, filename + ".jpg");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
default:
file = new File(filePath, filename + ".png");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}
The main problem is converting the image to path to draw it on canvas which can be used by the drawing view
Related
I am trying to create app for Display testing .Redmi Mobile- Display test screenShot
For that, I am using Canvas. You can check below code for more details.
I am trying to use this function but app stuck during execution of this code
boolean CheckPixelColor() {
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (bitmapData.getPixel(i, j) == Color.MAGENTA) {
System.out.println("Not Done");
return false;
}
}
}
return true;
}
This code is working. This code is for create path during initialization and erase that later.
public class DrawingView extends View {
private static final float TOUCH_TOLERANCE = 4;
private Bitmap bitmap;
private Canvas canvas;
private Path path;
private Paint bitmapPaint;
private Paint paint;
private boolean drawMode;
private float x, y;
private float penSize = 20;
private float eraserSize = 50;
private Bitmap bitmapData;
Context context;
public DrawingView(Context c) {
this(c, null);
}
public DrawingView(Context c, AttributeSet attrs) {
this(c, attrs, 0);
}
public DrawingView(Context c, AttributeSet attrs, int defStyle) {
super(c, attrs, defStyle);
this.context = c;
init();
}
private void init() {
path = new Path();
bitmapPaint = new Paint(Paint.DITHER_FLAG);
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(penSize);
drawMode = true;
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
this.setDrawingCacheEnabled(true);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
}
canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT);
Init();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, bitmapPaint);
canvas.drawPath(path, paint);
bitmapData = this.getDrawingCache(true);
}
private void touchStart(float x, float y) {
path.reset();
path.moveTo(x, y);
this.x = x;
this.y = y;
canvas.drawPath(path, paint);
}
private void touchMove(float x, float y) {
float dx = Math.abs(x - this.x);
float dy = Math.abs(y - this.y);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(this.x, this.y, (x + this.x) / 2, (y + this.y) / 2);
this.x = x;
this.y = y;
}
canvas.drawPath(path, paint);
}
private void touchUp() {
path.lineTo(x, y);
canvas.drawPath(path, paint);
path.reset();
if (drawMode) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
} else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!drawMode) {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
} else {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
}
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
if (!drawMode) {
path.lineTo(this.x, this.y);
path.reset();
path.moveTo(x, y);
}
canvas.drawPath(path, paint);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchUp();
invalidate();
break;
default:
break;
}
return true;
}
public void initializePen() {
drawMode = true;
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(penSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
}
public void initializeEraser() {
drawMode = false;
paint.setColor(Color.parseColor("#f4f4f4"));
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(eraserSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void clear() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
invalidate();
}
#Override
public void setBackgroundColor(int color) {
if (canvas == null) {
canvas = new Canvas();
}
canvas.drawColor(color);
super.setBackgroundColor(color);
}
public void setEraserSize(float size) {
eraserSize = size;
initializeEraser();
}
public void setPenSize(float size) {
penSize = size;
initializePen();
}
public float getEraserSize() {
return eraserSize;
}
public float getPenSize() {
return penSize;
}
public void setPenColor(#ColorInt int color) {
paint.setColor(color);
}
public #ColorInt
int getPenColor() {
return paint.getColor();
}
public void loadImage(Bitmap bitmap) {
this.bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
canvas.setBitmap(this.bitmap);
bitmap.recycle();
invalidate();
}
public boolean saveImage(String filePath, String filename, Bitmap.CompressFormat format,
int quality) {
if (quality > 100) {
Log.d("saveImage", "quality cannot be greater that 100");
return false;
}
File file;
FileOutputStream out = null;
try {
switch (format) {
case PNG:
file = new File(filePath, filename + ".png");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
case JPEG:
file = new File(filePath, filename + ".jpg");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out);
default:
file = new File(filePath, filename + ".png");
out = new FileOutputStream(file);
return bitmap.compress(Bitmap.CompressFormat.PNG, quality, out);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
void Init() {
initializePen();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
System.out.println("height: " + height);
System.out.println("width: " + width);
path.reset();
path.moveTo(0, 0);
path.lineTo(0, height);
path.lineTo(width, height);
path.lineTo(width, 0);
path.lineTo(0, 0);
path.lineTo(width, height);
path.moveTo(width, 0);
path.lineTo(0, height);
canvas.drawPath(path, paint);
invalidate();
initializeEraser();
}
boolean CheckPixelColor() {
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int height = displayMetrics.heightPixels;
int width = displayMetrics.widthPixels;
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
if (bitmapData.getPixel(i, j) == Color.MAGENTA) {
System.out.println("Not Done");
}
}
}
return false;
}
}
Now I want to check that all path has been erased.
Thanks #Radhey , As per your suggestion now i am comparing complete bitmap with starting bitmap and now it s working .
Working code :
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (bitmap == null) {
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
bitmapSateSave = bitmap;
}
canvas = new Canvas(bitmap);
canvas.drawColor(Color.TRANSPARENT);
Init();
}
private void init() {
path = new Path();
bitmapPaint = new Paint(Paint.DITHER_FLAG);
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(penSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
this.setDrawingCacheEnabled(true); // Enable Cache
}
#Override
protected void onDraw(Canvas canvas)
{ super.onDraw(canvas);
canvas.drawBitmap(bitmap,0,0,bitmapPaint);
canvas.drawPath(path,paint);
bitmapData = this.getDrawingCache(true); // Save Bitmap data
}
boolean CompareBitmap() {
if (bitmapData.sameAs(bitmapSateSave)) {
System.out.println("Same");
} else {
System.out.println("Different"); }
return false;
}
I've create an Activity for draw a sign, save it into internal storage, reaload it and continue to draw starting from Bitmap saved.
At first launch the file is not loaded because it doesn't exists. So I draw and when I click back to previos Activity I save the Bitmap in file. From second launch the file exists and it is correctly loaded (I view image from debug in android studio). The problem is that the canvas appear blank. I have not Exception. Any ideas?
public class PhysicalSignatureActivity extends AppCompatActivity {
DrawingView dv ;
private Paint mPaint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setTitle("Physical sign");
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(3);
if (!(getIntent().getStringExtra("filename") == null)){
try {
InputStream inputStream = getApplicationContext().openFileInput(getIntent().getStringExtra("filename"));
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
Bitmap bitmap_mutable = bitmap.copy(Bitmap.Config.ARGB_8888, true);
dv = new DrawingView(this,bitmap_mutable);
} catch (FileNotFoundException ex) {
}
} else {
dv = new DrawingView(this);
}
setContentView(dv);
}
#Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(false);
builder.setMessage("Save sign?");
builder.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String filename = UUID.randomUUID().toString()+".png";
FileOutputStream outputStream = null;
try {
outputStream = openFileOutput(filename, Context.MODE_PRIVATE);
dv.buildDrawingCache();
Bitmap bitmap = dv.getDrawingCache();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
} catch (FileNotFoundException ex) {
} catch (IOException e) {
e.printStackTrace();
} finally {
Intent i = new Intent(PhysicalSignatureActivity.this, PerformanceActivity.class);
i.putExtra("performance_id",getIntent().getStringExtra("performance_id"));
i.putExtra("authentication_token",getIntent().getStringExtra("authentication_token"));
i.putExtra("filename",filename);
startActivity(i);
finish();
}
}
});
builder.setNegativeButton("No",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
AlertDialog alert=builder.create();
alert.show();
}
public class DrawingView extends View {
public int width;
public int height;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
Context context;
private Paint circlePaint;
private Path circlePath;
public DrawingView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
}
public DrawingView(Context c, Bitmap b) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
circlePaint = new Paint();
circlePath = new Path();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
mBitmap = b;
mCanvas = new Canvas();
mCanvas.drawBitmap(mBitmap,0,0,mBitmapPaint);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap( mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath( mPath, mPaint);
canvas.drawPath( circlePath, circlePaint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
circlePath.reset();
circlePath.addCircle(mX, mY, 30, Path.Direction.CW);
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
circlePath.reset();
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
}
This is my custom view class:
package com.example.drawwithfinger;
public class CustomTextview extends View {
Paint paint;
Bitmap bitmap;
Canvas canvas;
Path path;
Paint BitmapPaint;
Context context;
private Context Context;
public CustomTextview(Context context, AttributeSet attrs) {
super(context, attrs);
Context=context;
paint = new Paint();
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(0xFFFF0000);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(20);
path = new Path();
BitmapPaint = new Paint();
BitmapPaint.setColor(Color.RED);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
}
#Override
public void draw(Canvas canvas)
{
super.draw(canvas);
canvas.drawBitmap(bitmap, 0, 0, BitmapPaint);
canvas.drawPath(path, paint);
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
path.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
path.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
path.lineTo(mX, mY);
canvas.drawPath(path, paint);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);invalidate();
break;case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
This is my code for draw on canvas. This code create an app which allow you to draw with your finger, and I want to save that finger-printed image onto SD card. Please help me to do this. Explain that for such thing what should I write in my main activity class.
Try this....
YourCustomTextView.setDrawingCacheEnabled(true);
Bitmap bitmap = content.getDrawingCache();
File file,f;
if (android.os.Environment.getExternalStorageState().eq uals(android.os.Environment.MEDIA_MOUNTED))
{
file =new File(android.os.Environment.getExternalStorageDirectory(),"TTImages_cache");
if(!file.exists())
{
file.mkdirs();
}
f = new File(file.getAbsolutePath()+file.seperator+ "filename"+".png");
}
FileOutputStream ostream = new FileOutputStream(f);
bitmap.compress(CompressFormat.PNG, 10, ostream);
ostream.close();
}
catch (Exception e){
e.printStackTrace();
}
Mainfest Permission
<uses-permission. android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
I have a drawing class i have created that has some performance issues. I imagine it has to do with the way i am handling the drawing actions and undo/redo functions. Can anyone offer some advice as to how to improve the performance?
public class KNDrawingSurfaceView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
public Bitmap mBitmap;
public Canvas mCanvas;
public Path mPath;
public Paint mBitmapPaint;
float myWidth;
float myHeight;
public Paint mPaint;
public MaskFilter mEmboss;
public MaskFilter mBlur;
public ArrayList<Path> paths = new ArrayList<Path>();
public ArrayList<Paint>paints = new ArrayList<Paint>();
public ArrayList<Path> undonePaths = new ArrayList<Path>();
public ArrayList<Paint>undonePaints = new ArrayList<Paint>();
private KNSketchBookActivity _parent;
public KNDrawingSurfaceView(Context c, float width,float height, KNSketchBookActivity parent) {
super(c);
myWidth = width;
myHeight = height;
_parent =parent;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
mBitmap = Bitmap.createBitmap((int)myWidth, (int)myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
Log.v("onDraw:", "curent paths size:"+paths.size());
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
Paint tile = new Paint();
Bitmap tileImage = BitmapFactory.decodeResource(getResources(),R.drawable.checkerpattern);
BitmapShader shader = new BitmapShader(tileImage, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
tile.setShader(shader);
canvas.drawRect(0, 0, myWidth, myHeight, tile);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath,mPaint);
}
public void onClickUndo () {
if (paths.size()>0)
{
undonePaths.add(paths.remove(paths.size()-1));
undonePaints.add(paints.remove(paints.size()-1));
invalidate();
}
else
{
}
_parent.checkButtonStates();
}
public void onClickRedo (){
if (undonePaths.size()>0)
{
paths.add(undonePaths.remove(undonePaths.size()-1));
paints.add(undonePaints.remove(undonePaints.size()-1));
invalidate();
}
else
{
}
_parent.checkButtonStates();
}
public void onClickClear (){
paths.clear();
undonePaths.clear();
invalidate();
_parent.checkButtonStates();
}
public void saveDrawing(){
FileOutputStream outStream = null;
String fileName = "tempTag";
try {
outStream = new FileOutputStream("/sdcard/" + fileName + ".png");
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
paths.add(mPath);
paints.add(mPaint);
_parent.checkButtonStates();
mPath = new Path();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if(x>myWidth){
x=myWidth;
}
if(y>myHeight){
y=myHeight;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
please share with me any experience or links you may have dealing with drawing/canvas optimization
Use the below for reference and modify the below according to your requirements.
You have the below in onDraw()
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// will clear the draw
Everytime you can invalidate() will call onDraw(canvas). Your draw will be refreshed.
I don't know what you are trying to do but i removed the above
Moved the inside onSizeChanged
mBitmap = Bitmap.createBitmap((int)myWidth, (int)myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
Your code works fine. Tested it on emulator.
public class MainActivity extends Activity {
DrawingView dv ;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dv = new DrawingView(this);
setContentView(dv);
}
public class DrawingView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
public Bitmap mBitmap,tileImage;
public Canvas mCanvas;
public Path mPath;
public Paint mBitmapPaint;
float myWidth;
float myHeight;
Paint tile ;
public Paint mPaint;
public MaskFilter mEmboss;
public MaskFilter mBlur;
public ArrayList<Path> paths = new ArrayList<Path>();
public ArrayList<Paint>paints = new ArrayList<Paint>();
public ArrayList<Path> undonePaths = new ArrayList<Path>();
public ArrayList<Paint>undonePaints = new ArrayList<Paint>();
BitmapShader shader;
public DrawingView(Context c) {
super(c);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(Color.RED);
tile = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
tileImage = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
shader = new BitmapShader(tileImage, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
tile.setShader(shader);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
myWidth =w;
myHeight = h;
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
Log.v("onDraw:", "curent paths size:"+paths.size());
for (Path p : paths){
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath,mPaint);
}
public void onClickUndo () {
if (paths.size()>0)
{
undonePaths.add(paths.remove(paths.size()-1));
undonePaints.add(paints.remove(paints.size()-1));
invalidate();
}
else
{
}
}
public void onClickRedo (){
if (undonePaths.size()>0)
{
paths.add(undonePaths.remove(undonePaths.size()-1));
paints.add(undonePaints.remove(undonePaints.size()-1));
invalidate();
}
else
{
}
}
public void onClickClear (){
paths.clear();
undonePaths.clear();
invalidate();
}
public void saveDrawing(){
FileOutputStream outStream = null;
String fileName = "tempTag";
try {
outStream = new FileOutputStream("/sdcard/" + fileName + ".png");
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
paths.add(mPath);
paints.add(mPaint);
mPath = new Path();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
if(x>myWidth){
x=myWidth;
}
if(y>myHeight){
y=myHeight;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
You are recreating 2 bitmaps, paint and BitmapShader in onDraw() method. This is causing your performance issues.
try this:
- move object creations to constructor.
- i think that you can remove this part completely:
mBitmap = Bitmap.createBitmap((int)myWidth, (int)myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
(if you need to get bitmap from your canvas, create separate method for that and call it when needed)
Do not create or instantiate any variable on your "loop", do that before and outside onDraw and reuse the same variables. This will surely increase performance!
As you all suggested i pulled all my var initiations out of onDraw and put them in the constructor.
This particular piece is needed to clear the canvas when the user undoes or redoes something they have drawn:
mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
so i created a new method that i just call on undo/redo:
public void clearCanvasCache() {
mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
Works great now..
The Whole class:
public class KNDrawingSurfaceView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
public Bitmap mBitmap;
public Canvas mCanvas;
public Path mPath;
public Paint mBitmapPaint;
float myWidth;
float myHeight;
public Paint mPaint;
public MaskFilter mEmboss;
public MaskFilter mBlur;
public ArrayList<Path> paths = new ArrayList<Path>();
public ArrayList<Paint> paints = new ArrayList<Paint>();
public ArrayList<Path> undonePaths = new ArrayList<Path>();
public ArrayList<Paint> undonePaints = new ArrayList<Paint>();
private KNSketchBookActivity _parent;
Paint tile;
Bitmap tileImage;
BitmapShader shader;
public KNDrawingSurfaceView(Context c, float width, float height, KNSketchBookActivity parent) {
super(c);
myWidth = width;
myHeight = height;
mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
_parent = parent;
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
tile = new Paint();
tileImage = BitmapFactory.decodeResource(getResources(), R.drawable.checkerpattern);
shader = new BitmapShader(tileImage, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
tile.setShader(shader);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
Log.v("onDraw:", "curent paths size:" + paths.size());
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
canvas.drawRect(0, 0, myWidth, myHeight, tile);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
for (Path p : paths) {
canvas.drawPath(p, mPaint);
}
canvas.drawPath(mPath, mPaint);
}
public void onClickUndo() {
if (paths.size() > 0) {
undonePaths.add(paths.remove(paths.size() - 1));
undonePaints.add(paints.remove(paints.size() - 1));
clearCanvasCache();
invalidate();
} else {
}
_parent.checkButtonStates();
}
public void onClickRedo() {
if (undonePaths.size() > 0) {
paths.add(undonePaths.remove(undonePaths.size() - 1));
paints.add(undonePaints.remove(undonePaints.size() - 1));
clearCanvasCache();
invalidate();
} else {
}
_parent.checkButtonStates();
}
public void onClickClear() {
paths.clear();
undonePaths.clear();
clearCanvasCache();
invalidate();
_parent.checkButtonStates();
}
public void saveDrawing() {
FileOutputStream outStream = null;
String fileName = "tempTag";
try {
outStream = new FileOutputStream("/sdcard/" + fileName + ".png");
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, outStream);
outStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
undonePaths.clear();
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
paths.add(mPath);
paints.add(mPaint);
_parent.checkButtonStates();
mPath = new Path();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!_parent.isDrawerOpen()) {
float x = event.getX();
float y = event.getY();
if (x > myWidth) {
x = myWidth;
}
if (y > myHeight) {
y = myHeight;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
} else {
return false;
}
}
public void clearCanvasCache() {
mBitmap = Bitmap.createBitmap((int) myWidth, (int) myHeight, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
I have a relative layout on which i add a view. this is the code in the activity:
paint = (RelativeLayout) findViewById(R.id.paint);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
myView = new CustomImage(getBaseContext());
paint.addView(myView);
String imagePath = (String) getIntent().getExtras().get("selectedImagePath");
LogService.log("PaintActivity", "Imagepath: " + imagePath);
// Drawable background = BitmapDrawable.createFromPath(imagePath);
// bitmap = ((BitmapDrawable) background).getBitmap();
try {
fis = new FileInputStream(imagePath);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
mBitmap = loadBitmap(fis.getFD());
bitmap = getResizedBitmap(mBitmap, 412*2, 1024*2);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
LogService.log("PaintActivity", "Bitmap = " + mBitmap);
myView.setBitmap(bitmap);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFF000000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(50);
myView.setPaint(mPaint);
}
}, 50);
This is my View:
public class CustomImage extends View {
private boolean hasResized = false;
private Bitmap bitmap, bitmap2;
public Context context;
Paint paint;
private Canvas canvas;
public String text = null;
private Paint mBitmapPaint, paintText;
private Path mPath = new Path();
private float mX, mY, pX, pY, tX, tY;
private static final float TOUCH_TOLERANCE = 4;
private float screenDensity;
int height, width;
private Bitmap mBitmap;
private RandomAccessFile randomAccessFile;
private int bh;
private int bw;
public CustomImage(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
public CustomImage(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CustomImage(Context context) {
super(context);
init(context);
}
private void init(Context context) {
this.context = context;
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
public void setImg(Context context, Canvas canvas, Bitmap bitmapw, int width, int height) {
bw = bitmapw.getWidth();
bh = bitmapw.getHeight();
float scaleWidth = ((float) width)/ bw;
float scaleHeight = ((float) height)/ bh;
System.out.println("CustomImage.setImg()");
bitmap = bitmapw;
}
public void setPaint(Paint paint) {
this.paint = paint;
LogService.log("in setPaint", "paint = " + paint);
}
public void setBitmap(Bitmap bitmap) throws Exception {
File file = new File("/mnt/sdcard/sample/temp.txt");
file.getParentFile().mkdirs();
randomAccessFile = new RandomAccessFile(file, "rw");
int bWidth = bitmap.getWidth();
int bHeight = bitmap.getHeight();
FileChannel channel = randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, bWidth*bHeight*4);
bitmap.copyPixelsToBuffer(map);
bitmap.recycle();
this.bitmap = Bitmap.createBitmap(bWidth, bHeight, Config.ARGB_8888);
map.position(0);
this.bitmap.copyPixelsFromBuffer(map);
channel.close();
randomAccessFile.close();
// this.bitmap = bitmap;
// canvas = new Canvas();
}
public void setBitmap2(Bitmap bitmap) {
bitmap2 = bitmap;
invalidate();
}
public void getText(String text) {
this.text = text;
paintText = new Paint();
paintText.setColor(0xFFFFFFFF);
paintText.setStrokeWidth(10);
paintText.setTextSize(20);
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(bitmap, 0, 0, mBitmapPaint);
if (PaintActivity.isPic == 1) {
if (bitmap2 != null) {
canvas.drawBitmap(bitmap2, mX, mY, mBitmapPaint);
pX = mX;
pY = mY;
}
if(text != null){
canvas.drawText(text, tX, tY, paintText);
}
} else if (PaintActivity.isPic == 2) {
if(text != null){
canvas.drawText(text, mX, mY, paintText);
tX = mX;
tY = mY;
}
if (bitmap2 != null) {
canvas.drawBitmap(bitmap2, pX, pY, mBitmapPaint);
}
}else{
canvas.drawPath(mPath, paint);
if (bitmap2 != null) {
canvas.drawBitmap(bitmap2, pX, pY, mBitmapPaint);
}
if(text != null){
canvas.drawText(text, tX, tY, paintText);
}
}
}
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
canvas.drawPoint(x, y, paint);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if ((dx >= TOUCH_TOLERANCE) || (dy >= TOUCH_TOLERANCE)) {
mPath.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mPath.moveTo(mX, mY);
canvas.drawPath(mPath, paint);
mPath.reset();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
System.out.println("CustomImage.onSizeChanged()");
super.onSizeChanged(w, h, oldw, oldh);
if ((w != 0) && (h != 0)) {
if (!hasResized) {
hasResized = true;
renderImage();
}
}
}
public void renderImage() {
System.out.println("======in renderimage=======w " + getMeasuredWidth() + "h " + getMeasuredHeight());
width = getMeasuredWidth();
height = getMeasuredHeight();
canvas = new Canvas(bitmap);
setImg(context, canvas, bitmap, width, height);
}
}
As you can see, I have a resizing function in the activity:
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
Now if i comment this function out of the code, the image looks normal, but not scaled. If i leave it there, then i get an ugly image, but it is resized. Example:
normal not resized: http://imgur.com/BD5Pp,YWDTi
altered but resized pic: http://imgur.com/BD5Pp,YWDTi#1
As OP stated answering my question, the bitmap is converted to RGB_565, which is a known bug in createBitmap() - #16211, fixed in Android 3.0. It affects createScaledBitmap() as well. The bitmap wouldn't look distorted if it remained ARGB_8888.
As a workaround you have to create target bitmap manually (which gives you full control of the pixel format), then create canvas attached to this bitmap and paint a scaled version of the original bitmap on it. Here's a sample from my project:
public class BitmapUtils {
private static Matrix matrix = new Matrix();
private static Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
public static final Bitmap resizeBitmap(Bitmap bitmap, float scale, Bitmap.Config targetConfig) {
int srcWidth = bitmap.getWidth();
int srcHeight = bitmap.getHeight();
int newWidth = (int) (srcWidth * scale);
int newHeight = (int) (srcHeight * scale);
float sx = newWidth / (float) srcWidth;
float sy = newHeight / (float) srcHeight;
matrix.setScale(sx, sy);
Bitmap target = Bitmap.createBitmap(newWidth, newHeight, targetConfig);
Canvas c = new Canvas(target);
c.drawBitmap(bitmap, matrix, paint);
return target;
}
}
Note: this code is not reentrant, due to global matrix instance. Also, it takes scale as parameter, but adapting it to your needs (new width/height as parameters) is trivial.