Wednesday, 26 June 2013

Image with Zoom in-out and draw paint on zoomed image using canvas in Android.


    Here,I used canvas in order to zoom image view with image including using setImageMatrix(). As shown below class use for zoom and paint. In entire screen became to zoom in/out then when i click on zoom button i pass one Boolean as false which stop zooming and draw line on touch of finger.

    PaintView class is used for draw line on zoomed image for perticuler position of image-view. 
imageView.setOnTouchListener(new OnTouchListener()) is used for zoom in-out of image bitmap.
combineImages() function used for merge both image and paint bitmap.


public class PaintScreen extends Activity {

Context mContext;
private Paint mPaint;
MaskFilter mEmboss;
MaskFilter mBlur;
private LinearLayout mPaintBaseLayout, mPaintBaseLayout2;
private PaintView mPaintView;

// These matrices will be used to move and zoom image
Matrix matrix = new Matrix();
Matrix matrix1 = new Matrix();
Matrix savedMatrix = new Matrix();
Matrix savedMatrix2 = new Matrix();
Matrix dmMtrx = new Matrix();

private int WIDTH = 0;
private int HEIGHT = 1;

// We can be in one of these 3 states
static final int NONE = 0;
static final int DRAG = 1;
static final int POINT2 = 2;
static final int ZOOM = 3;
int mode = NONE;
private static final float MIN_ZOOM = 0.0f;
private static final float MAX_ZOOM = 1.0f;


// Remember some things for zooming
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
float newDist;
float distanceOffset = 50f;
float minOffset = 50f;
float maxOffset = 10000f;
private boolean falg = true;
private int startval = 0;
Bitmap newbm;
private float finalscale;
Bitmap bm;
private float scaledImageOffsetX;
private float scaledImageOffsetY;
ImageView imageView;
ProgressDialog dialog;
private float[] matrixValues;
Bitmap temp;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_main);

this.initialize();

this.PaintSet();
imageView = (ImageView) findViewById(R.id.imageview1);
Button button = (Button) findViewById(R.id.btnzoom);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (falg) {
getFlag(false);
} else {
getFlag(true);
}
}
});

Button btnset = (Button) findViewById(R.id.btnset);
btnset.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startval = 1;
mPaintBaseLayout.setDrawingCacheEnabled(true);

imageView.setVisibility(View.VISIBLE);

mPaintBaseLayout.setVisibility(View.GONE);
new SaveImageAsynk().execute();
}
});

imageView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;

switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix1);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
start.set(event.getX(), event.getY());
savedMatrix.set(matrix1);
midPoint(mid, event);
// mode = POINT2;
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
mode = NONE;
distanceOffset = minOffset;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
distanceOffset = minOffset;
break;
case MotionEvent.ACTION_MOVE:
if (mode == POINT2) {
newDist = spacing(event);
if (newDist - oldDist > 5f
|| newDist - oldDist < -5f) {
mode = ZOOM;
} else {
start.set(event.getX(), event.getY());
mode = DRAG;
}
} else if (mode == DRAG) {
matrix1.set(savedMatrix);
matrix1.postTranslate(event.getX() - start.x,
event.getY() - start.y);
} else if (mode == ZOOM) {
newDist = spacing(event);
if (newDist > 10f) {
matrix1.set(savedMatrix);
float scale = newDist / oldDist;
matrix1.postScale(scale, scale, mid.x,
mid.y);
finalscale = scale;
}
}
break;
}

view.setImageMatrix(matrix1);
// matrixTurning(matrix1, view);
return true; // indicate event was handled
}
});
}

class SaveImageAsynk extends AsyncTask<String, String, String>{
Bitmap tempBm;
@Override
protected String doInBackground(String... params) {
temp = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, false);
newbm = Bitmap.createBitmap(mPaintBaseLayout.getWidth(),
mPaintBaseLayout.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newbm);
mPaintBaseLayout.draw(canvas);
tempBm = combineImages(newbm, temp);
if (newbm != null) {
newbm = null;
}
if (temp != null) {
temp = null;
}
return null;
}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
imageView.setImageBitmap(tempBm);
try {
dialog.dismiss();
if (dialog != null) {
dialog = null;
}
} catch (Exception e) {
}
}

@Override
protected void onPreExecute() {
super.onPreExecute();
dialog = new ProgressDialog(PaintScreen.this);
dialog.setMessage("Loading...");
dialog.show();
}

}
public Bitmap combineImages(Bitmap c, Bitmap s) {
Bitmap cs = null;

int width, height = 0;

if (c.getWidth() > s.getWidth()) {
width = c.getWidth();
height = c.getHeight() + s.getHeight();
} else {
width = s.getWidth();
height = c.getHeight() + s.getHeight();
}
Log.e("hw :", "X = "+width + " Y = "+height);
cs = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

Canvas comboImage = new Canvas(cs);
comboImage.drawBitmap(s,new Matrix(), null);
comboImage.drawBitmap(c,Math.abs(scaledImageOffsetX),Math.abs(scaledImageOffsetY), null);

return cs;
}

private void initialize() {
mPaintBaseLayout = (LinearLayout) findViewById(R.id.paint_paint_base_layout);
mPaintBaseLayout2 = (LinearLayout) findViewById(R.id.paint_paint_base_layout2);

mContext = this;
mPaint = new Paint();
mPaintView = new PaintView(mContext);
mPaintView.setBackgroundColor(Color.TRANSPARENT);
mPaintBaseLayout.addView(mPaintView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mPaintBaseLayout.setBackgroundColor(Color.TRANSPARENT);

// mPaintView.setScaleType(ScaleType.FIT_XY);
mPaintView.setAdjustViewBounds(true);
mPaintView.setMPaint(mPaint);
bm = BitmapFactory.decodeResource(getResources(), R.drawable.nat);

mPaintView.setImageBitmap(bm);

mPaintView.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
PaintView view = (PaintView) v;
view.setScaleType(ImageView.ScaleType.MATRIX);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (falg) {
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
} else {
view.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (falg) {
oldDist = spacing(event);
if (oldDist > 10f) {
start.set(event.getX(), event.getY());
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
}
break;
case MotionEvent.ACTION_UP:
if (falg) {
mode = NONE;
distanceOffset = minOffset;
}
case MotionEvent.ACTION_POINTER_UP:
if (falg) {
mode = NONE;
distanceOffset = minOffset;
}
break;
case MotionEvent.ACTION_MOVE:
if (falg) {
if (mode == POINT2) {
newDist = spacing(event);
if (newDist - oldDist > 5f
|| newDist - oldDist < -5f) {
mode = ZOOM;
} else {
start.set(event.getX(), event.getY());
mode = DRAG;
}
} else if (mode == DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - start.x,
event.getY() - start.y);
} else if (mode == ZOOM) {
newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
finalscale = scale;
}
}
} else {
view.onTouchEvent(event);
}
break;
}

limitZoom(matrix);
view.setImageMatrix(matrix);

matrixTurning(matrix, view);
RectF r = new RectF();
matrix.mapRect(r);
scaledImageOffsetX = r.left;
scaledImageOffsetY = r.top;

return true;
}
});
}


private void limitZoom(Matrix m) {

        float[] values = new float[9];
        m.getValues(values);
        float scaleX = values[Matrix.MSCALE_X];
        float scaleY = values[Matrix.MSCALE_Y];
        if(scaleX > MAX_ZOOM) {
            scaleX = MAX_ZOOM;
        } else if(scaleX < MIN_ZOOM) {
            scaleX = MIN_ZOOM;
        }

        if(scaleY > MAX_ZOOM) {
            scaleY = MAX_ZOOM;
        } else if(scaleY < MIN_ZOOM) {
            scaleY = MIN_ZOOM;
        }

        values[Matrix.MSCALE_X] = scaleX;
        values[Matrix.MSCALE_Y] = scaleY;
        m.setValues(values);
    }

public boolean getFlag(boolean b) {
return falg = b;
}

/** Determine the space between the first two fingers */
private static float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}

/** Calculate the mid point of the first two fingers */
private static void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}

private void matrixTurning(Matrix matrix, ImageView view) {

float[] value = new float[9];
matrix.getValues(value);
float[] savedValue = new float[9];
savedMatrix2.getValues(savedValue);

// view size
int width = view.getWidth();
int height = view.getHeight();

// image size
Drawable d = view.getDrawable();
if (d == null)
return;
int imageWidth = d.getIntrinsicWidth();
int imageHeight = d.getIntrinsicHeight();
int scaleWidth = (int) (imageWidth * value[0]);
int scaleHeight = (int) (imageHeight * value[0]);

if (value[2] < width - scaleWidth)
value[2] = width - scaleWidth;
if (value[5] < height - scaleHeight)
value[5] = height - scaleHeight;
if (value[2] > 0)
value[2] = 0;
if (value[5] > 0)
value[5] = 0;

if (value[0] > 10 || value[4] > 10) {
value[0] = savedValue[0];
value[4] = savedValue[4];
value[2] = savedValue[2];
value[5] = savedValue[5];
}

if (imageWidth > width || imageHeight > height) {

if (scaleWidth < width && scaleHeight < height) {
int target = WIDTH;

if (imageWidth < imageHeight)
target = HEIGHT;

if (target == WIDTH)
value[0] = value[4] = (float) width / imageWidth;
if (target == HEIGHT)
value[0] = value[4] = (float) height / imageHeight;

scaleWidth = (int) (imageWidth * value[0]);
scaleHeight = (int) (imageHeight * value[4]);

if (scaleWidth == width)
value[0] = value[4] = (float) width / imageWidth;
if (scaleHeight == height)
value[0] = value[4] = (float) height / imageHeight;
}

} else {
if (value[0] < 1)
value[0] = 1;
if (value[4] < 1)
value[4] = 1;
}

scaleWidth = (int) (imageWidth * value[0]);
scaleHeight = (int) (imageHeight * value[4]);

if (scaleWidth < width) {
value[2] = (float) width / 2 - (float) scaleWidth / 2;
}
if (scaleHeight < height) {
value[5] = (float) height / 2 - (float) scaleHeight / 2;
}

matrix.setValues(value);
savedMatrix2.set(matrix);

}

public void PaintSet() {

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(10);

// getWindowManager().getDefaultDisplay().getMetrics(mDisplayMetrics);
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 }, 0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(24, BlurMaskFilter.Blur.NORMAL);
}

public void colorChanged(int color) {
mPaint.setColor(color);
}

}

class PaintView extends ImageView {

private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;

// onDraw
private Paint mPaint;

// onTouch
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;

public PaintView(Context context) {
this(context, null);
}

public PaintView(Context context, AttributeSet attrs) {
super(context, attrs);

mBitmap = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_8888);
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) {
// canvas.drawColor(0xFFAAAAAA);
super.onDraw(canvas);
mCanvas = canvas;
// canvas = mCanvas;
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
// canvas.drawBitmap(mBitmap, PaintScreen.matrix, mBitmapPaint);
canvas.drawPath(mPath, mPaint);

}

public void clear() {
mPaint.reset();
// invalidate();
}

public void setMPaint(Paint paint) {
mPaint = paint;
}

private void touchStart(float x, float y) {
// mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}

private void touchMove(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 touchUp() {
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.d("PaintView", "ev ->" + event.getAction());

switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchStart(x, y);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touchMove(x, y);
invalidate();
break;
case MotionEvent.ACTION_UP:
touchUp();
invalidate();
break;
}
return true;
}

public void cMatrix(Matrix matrix) {
mCanvas.setMatrix(matrix);
}
}

XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainrel"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    tools:context=".ImageMainActivity" >

    <Button
        android:id="@+id/btnzoom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Zoom" />

    <Button
        android:id="@+id/btnset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_toRightOf="@+id/btnzoom"
        android:text="Done" />

    <ImageView
        android:id="@+id/imageview1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@+id/btnzoom"
        android:scaleType="matrix"
        android:visibility="gone" />

    <LinearLayout
        android:id="@+id/paint_paint_base_layout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnzoom"
        android:orientation="horizontal" />

    <LinearLayout
        android:id="@+id/paint_paint_base_layout2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btnzoom"
        android:orientation="horizontal"
        android:visibility="gone" />

</RelativeLayout>

22 comments:

  1. can we have the layout file.....

    ReplyDelete
  2. I love it when someone publish something like a pro and then I see that he/she has gotten the solution of people of stackoverflow.com

    ReplyDelete
  3. Hi.. thanks for the post..

    Why it s taking more time to render the image if i zoom the image..

    Kindly advise the solution plz..

    ReplyDelete
  4. Kindly provide buttons

    (i) to pick a color from a list of colors.

    (ii) to save the image after paint

    (iii) to clear the paint

    ReplyDelete
  5. Hi!

    Not able to paint clearly by using the above source. Can you suggest any idea to fix this?

    Thanks in advance..

    ReplyDelete
    Replies
    1. U can change paint code with new.

      Delete
    2. Hi Anonymous

      you can use below URL hope it will help you .

      URL : http://stackoverflow.com/questions/26924062/android-zoom-image-and-draw-line-on-image-with-current-zoom

      Delete
  6. But it takes more time while zooming the image. Kindly advise the solution plz..
    Thanks..

    ReplyDelete
  7. hi kamaliya
    As we are following your blog or code
    http://kyogs.blogspot.in/2013/06/image-with-zoom-and-paint-on-zoomed.html
    but we have an issue in saving image
    can you please help me out
    i have posted my issue on stackoverflow also
    https://stackoverflow.com/questions/26924062/android-zoom-image-and-draw-line-on-image-with-current-zoom
    if possible have a look and guide me the right path.....

    ReplyDelete
    Replies
    1. Hi,
      in my code there is SaveImageAsynk class for save bitmap after draw on image.I think there is Matrix problem in your code.refer above code its working.

      Delete
    2. hi Kamaliya ,

      Thanks a lot for your response ,
      As using your same code, after i save the image by pressing done button , i am getting 2 images overlapping each other .
      Also , its does not allow me to paint on image when i zoom out the image , it allow me to paint on original size only ...

      Please look the problem and update the code if possible ....

      Delete
  8. Hi ,
    I had also try to save image in your code there are also getting two images after Pressing DONE.
    Please update if anything is missing.

    ReplyDelete
  9. Hi Mr.Yogesh, i refer your code but when i want paint after pinch zoom then at time of paint it comes small size (original size) , i can't draw/ paint at zoom able screen so please guild/suggest me. see i want zoom the pic then paint on it and after pinch zoom out the draw show me on the image after press save button.

    ReplyDelete
  10. Hi Mr.Yogesh, i am facing same problem described by arpan and megha, so i heardly request you to solve this problem as soon as possible.

    ReplyDelete
  11. Hi!

    Not able to paint in zoomeble size by using the above source. Can you suggest any idea to fix this?

    Thanks in advance..

    ReplyDelete
  12. Hi Megha & Kamliya

    I am also getting same issue during saving image. i want functionality like zoom and draw.

    waiting for you replay

    ReplyDelete
  13. Hi, I am getting same error as the guys above these comments. Can we have a solution to these ? Thanks a lot for the support in advance.

    ReplyDelete
  14. Bad code, totally unreadable, 0 comments.

    ReplyDelete