I have already seen fingurePaint.java from API demos. I want to implement touch smooth eraser to erase parts of the image by touch move in android.
fingurePaint told me to implement this
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
But this is not working to erase the image. It is working to erase something which is drawn by touch.
public class SandboxView extends View implements OnTouchListener {
public final Bitmap bitmap;
private final int width;
private final int height;
private Matrix transform = new Matrix();
private Vector2D position = new Vector2D();
private float scale = 1;
private float angle = 0;
public boolean isInitialized = false;
private TouchManager touchManager = new TouchManager(2);
final GestureDetector mGesDetect;
// Debug helpers to draw lines between the two touch points
private Vector2D vca = null;
private Vector2D vcb = null;
private Vector2D vpa = null;
private Vector2D vpb = null;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private Path mPath;
private Canvas mCanvas;
private Paint mPaint;
private Paint mBitmapPaint;
public SandboxView(Context context, Bitmap bitmap) {
super(context);
this.bitmap = bitmap;
this.width = bitmap.getWidth();
this.height = bitmap.getHeight();
this.mGesDetect = new GestureDetector(context, new DoubleTapGestureDetector());
setOnTouchListener(this);
}
private float getDegreesFromRadians(float angle) {
return (float)(angle * 360.0 / Math.PI);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isInitialized) {
Bitmap mBitmap = bitmap.createBitmap(320, 480, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPaint = new Paint();
mPath = new Path();
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);
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaint.setAntiAlias(true);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
int w = getWidth();
int h = getHeight();
position.set(w / 2, h / 2);
isInitialized = true;
}
if(isEraser==1){
canvas.drawColor(80000000);
canvas.drawBitmap(bitmap, transform, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
else{
Paint paint = new Paint();
transform.reset();
transform.postTranslate(-width / 2.0f, -height / 2.0f);
transform.postRotate(getDegreesFromRadians(angle));
transform.postScale(scale, scale);
transform.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(bitmap, transform, paint);
try {
/*paint.setColor(0xFF007F00);
canvas.drawCircle(vca.getX(), vca.getY(), 64, paint);
paint.setColor(0xFF7F0000);
canvas.drawCircle(vcb.getX(), vcb.getY(), 64, paint);
paint.setColor(0xFFFF0000);
canvas.drawLine(vpa.getX(), vpa.getY(), vpb.getX(), vpb.getY(), paint);
paint.setColor(0xFF00FF00);
canvas.drawLine(vca.getX(), vca.getY(), vcb.getX(), vcb.getY(), paint);*/
}
catch(NullPointerException e) {
// Just being lazy here...
}
}
}
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;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
mCanvas.drawPath(mPath, mPaint);
mPath.reset();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
if(isEraser ==1){
float x = event.getX();
float y = event.getY();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
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{
vca = null;
vcb = null;
vpa = null;
vpb = null;
mGesDetect.onTouchEvent(event);
try {
touchManager.update(event);
if (touchManager.getPressCount() == 1) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
position.add(touchManager.moveDelta(0));
}
else {
if (touchManager.getPressCount() == 2) {
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0) {
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
return true;
}
}
class DoubleTapGestureDetector extends GestureDetector.SimpleOnGestureListener {
#Override
public boolean onDoubleTap(MotionEvent e) {
colorseekbar.setVisibility(View.INVISIBLE);
opacityseekbar.setVisibility(View.INVISIBLE);
return true;
}
}
}
So please help me to erase the parts of image by using touch move.
Thanks in advance.
First Declar your paint with all property in your constructor.
Write this code in your onDraw() method
#Override
protected void onDraw(Canvas canvas)
{
System.out.println("come in on draw......");
canvas.drawColor(Color.TRANSPARENT);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
if(eraser==true)
mPaint.setColor(Color.TRANSPARENT):
else
mPaint.setColor(Color.RED):
canvas.drawPath(mPath, mPaint);
super.dispatchDraw(canvas);
}
second solution:
call below method in your touch_move() method
mBitmap.setPixel(x, y, Color.TRANSPARENT); This method will change your bitmap you have to pass only X,Y & COLOR
public void changeBitmap(int x, int y, Bitmap mBitmap)
{
Bitmap tempBitmap = Bitmap.createBitmap(mBitmap); //Edited code
tempBitmap.setPixel(x, y, Color.TRANSPARENT);
}
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;
changeBitmap(x, y, your_bitmap)
}
}
Isn't it better to invert this case ? Just paint on your image with background color and after all when saving, merge those layers if needed.
Define a temporary canvas and bitmap then draw your path or line, circle, image anything on touch events and then pass this temporary bitmap to canvas in onDraw your work will done properly, and also if you want to do erasing then do your erasing on that temporary canvas.
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
TemporaryBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
TemporaryCanvas = new Canvas(TemporaryBitmap);
}
TemporaryCanvas.drawColor(0, PorterDuff.Mode.clear);
public void onDraw(Canvas canv){
canv.drawBitmap(TemporaryBitmap, matrix, paint);
Related
I was using this class to draw like pen tool with multi color it is working good but when i change color ,it does not change color in between choosing color. i checked and try many solution they don't have any solution.
public class DrawingView extends View {
private static final float TOUCH_TOLERANCE = 4;
Paint mPaint;
//MaskFilter mEmboss;
//MaskFilter mBlur;
Bitmap mBitmap;
Canvas mCanvas;
Path mPath;
Paint mBitmapPaint;
ProgressDialog pd;
String color;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Paint> paints = new ArrayList<Paint>();
private ArrayList<Integer> colorlist = new ArrayList<Integer>();
private float mX, mY;
public DrawingView(Context context, String color) {
super(context);
// TODO Auto-generated constructor stub
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
pd = new ProgressDialog(context);
mPath = new Path();
paths.add(mPath);
mBitmapPaint = new Paint();
paints.add(mPaint);
colorlist.add(Color.parseColor(color));
mBitmapPaint.setColor(Color.parseColor(color));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w > 0 && h > 0) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
} else {
mBitmap = Bitmap.createBitmap(300, 250, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
#Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
// canvas.drawPath(mPath, mPaint);
int count = paints.size();
for (int i = 0; i < count; i++) {
mPaint.setColor(colorlist.get(i));
canvas.drawPath(paths.get(i), mPaint);
}
}
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;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
//mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
// kill this so we don't double draw
mPath.reset();
// mPath= new Path();
}
#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 SMILEY;
}
}
i'm using this class to draw painting, i'm using this class in activity like this.
mDrawingView = new DrawingView(getApplicationContext(), color);
mDrawingPad.addView(mDrawingView);
mDrawingPad is a linear layout in which i'm creating view.
I got the solution of this problem from here
I made some changes according to this solution in my code and finally i found it.
public class DrawingView extends View {
private static final float TOUCH_TOLERANCE = 4;
Paint mPaint;
//MaskFilter mEmboss;
//MaskFilter mBlur;
Bitmap mBitmap;
Canvas mCanvas;
Path mPath;
Paint mBitmapPaint;
ProgressDialog pd;
String color;
ArrayList<Pair<Path, Paint>> p = new ArrayList<Pair<Path, Paint>>();
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Paint> paints = new ArrayList<Paint>();
private float mX, mY;
public DrawingView(Context context, String color) {
super(context);
// TODO Auto-generated constructor stub
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
// mPaint.setColor(Color.parseColor(color));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(10);
pd = new ProgressDialog(context);
mPath = new Path();
paths.add(mPath);
mBitmapPaint = new Paint();
paints.add(mPaint);
p.add(new Pair<Path, Paint>(mPath, mPaint));
//colorlist.put(mPath,color);
mBitmapPaint.setColor(Color.parseColor(color));
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w > 0 && h > 0) {
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
} else {
mBitmap = Bitmap.createBitmap(300, 250, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
}
#Override
public void draw(Canvas canvas) {
// TODO Auto-generated method stub
super.draw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
// canvas.drawPath(mPath, mPaint);
for (Pair<Path, Paint> pp : p) {
canvas.drawPath(pp.first, pp.second);
}
}
private void touch_start(float x, float y) {
mPaint.setColor(ImageEditOptionActivity.pencolor);
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);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
//mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
// kill this so we don't double draw
p.add(new Pair<Path, Paint>(mPath, mPaint));
mPath.reset();
mPath = new Path();
//colorlist.put(mPath,color);
mPaint = new Paint(mPaint);
p.add(new Pair<Path, Paint>(mPath, mPaint));
}
#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 SMILEY;
}
}
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);
}
}
this has been driving me a little crazy today. I've implimented this class which enables me to draw lines over an image file. however I want to be able to to add a template mask at creation time. Whatever I try i.e. within the class or from the calling class it causes a Null pointer exception, if however I put the call into a button in the calling class then I can get it to work?
here is the class I'm using. You can see the commented out section, if I call "coverScreen()" from here it causes the NPE. Also from the main Activity where I create the maskBoard if I call "coverScreen()" it causes NPE. But as mentioned if I add a button, and put the "coverScreen()" into this button onclick then it works?
public class MaskBoard extends View {
private String TAG = "Test APP - maskboard";
private int mx = -1;
private int my = -1;
private Bitmap mainImage = null;
private Bitmap bm = null;
private Paint mPaint = null;
public final int CLOSER = 50;
public final int CLOSE = 100;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
public MaskBoard(Context c, AttributeSet s) {
super(c, s);
String fileToMask = "/__temppic.png";
File imagePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES + "/temp/");
File imgFile = new File(imagePath+fileToMask);
if (imgFile.exists()) {
bm = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
Log.i(TAG, "found image");
}
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAntiAlias(false);
mPaint.setDither(true);
mPaint.setColor(0xFFFE0000); // FFFF0000
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(50);
/* coverScreen() */
setEraser();
}
public void coverScreen() {
Bitmap maskImage = BitmapFactory.decodeResource(getResources(), R.drawable.mask); //get mask image
maskImage = Bitmap.createScaledBitmap(maskImage, bm.getWidth(), bm.getHeight(), true);
mPaint.setXfermode(null);
mCanvas.drawBitmap(maskImage,0,0,mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void setEraser() {
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
}
public void setPaint() {
mPaint.setXfermode(null);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.i(TAG, "onsizechanged");
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw");
canvas.drawRect(0,0, getWidth(), getHeight(), mPaint);
canvas.drawColor(0xFFAAAAAA);
canvas.drawBitmap(bm, mx, my, null);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
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;
Log.i(TAG, " touchdown: X(" + x + "), Y(" + 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;
Log.i(TAG, " touchmove: X(" + x + "), Y(" + y + ")");
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// 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);
// coverScreen() ;
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touch_up();
invalidate();
break;
}
return true;
}
}
I want to erase by area where the finger is detected on the gray square bitmap but it don't works.
I display a bitmap with ic_launch as image then i display on the image a grey square Paint where i can to modify the color to the TRANSPARENT
What is the problem? Thank you
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private Paint mPaint;
public CaseView(Context c) {
super(c);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(40);
mBitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
mBitmap.setPixel(i, j, Color.GRAY);
}
}
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(0, 0, oldw, oldh);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawRect(100, 100, 200, 200, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
Bitmap _scratch = BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher);
canvas.drawColor(Color.WHITE);
// logo
canvas.drawBitmap(_scratch, 0, 100, null);
for (int i = 0; i < 100; i++) {
for (int j = 0; j < 100; j++) {
if ((mX < 100 && mX >= 0) && (mY < 100 && mY >= 0)) {
mBitmap.setPixel((int) mY,(int) mX, Color.TRANSPARENT);
}
}
}
canvas.drawBitmap(mBitmap, 0, 100, mBitmapPaint);
Bitmap mutableBitmap = Bitmap.createBitmap(_scratch.getWidth(),
_scratch.getHeight(), Bitmap.Config.ARGB_8888);
mutableBitmap.setPixel(50, 50, 124);
canvas.drawBitmap(mutableBitmap, 0, 100, null);
int pixelColor = mBitmap.getPixel(50, 50);
int red = Color.red(pixelColor);
int green = Color.green(pixelColor);
Log.v("red", "red:" + red + " /green:" + green);
}
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;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// 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();
Log.v("onTouchEvent", "x:" + x + "/y:" + y);
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;
}
For Drawing you need to draw a path using one Paint class. But for erasing again you have to draw one path on your touch Coordinate like this
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
In android-sdk sample they had given a class FingerPaint that explain it very well