I am loading an imageview with a jpg from the SD card, and scaling it so that it is maximally enlarged without clipping.
I then want to overlay the bitmap with a circle, centered on the coordinates of a touch event.
I save the current bitmap from the imageview, create a new overlay bitmap, add a canvas, draw the original bitmap, draw the circle, reapply scaling code to the new overlay bitmap, then reload the imageview with the new overlay bitmap. The first time I touch the image to draw the circle, the circle is accurately drawn, but the image scaling is not correct. On the second touch, the image scaling is corrected, but the circle is drawn in the "wrong" place- it is drawn where I touched on the screen, but now the image is scaled correctly, so the target has moved. On the third and all subsequent touches, things work as I'd hoped they would from the start.
Here is my code:
private static Matrix curMatrix = new Matrix();
public boolean onTouch(View v, MotionEvent event) {
int eventType = event.getAction() & MotionEvent.ACTION_MASK;
switch (eventType) {
case MotionEvent.ACTION_UP:
ImageView i = (ImageView) v;
//Save the current bitmap
i.buildDrawingCache();
Bitmap bm = i.getDrawingCache();
//Create new overlay bitmap
Bitmap bmOverlay = Bitmap.createBitmap(i.getWidth(), i.getHeight(), Bitmap.Config.ARGB_8888);
//Create new drawing canvas and paint
Canvas c = new Canvas(bmOverlay);
Paint p = new Paint();
p.setColor(Color.RED);
p.setAlpha(50);
//Draw the saved bitmap onto the canvas
c.drawBitmap(bm, new Matrix(), null);
//Draw a circle on the current canvas, centered on event coordinates
c.drawCircle(event.getX(), event.getY(), 100F, p);
//Autosize canvas to previous imageview settings
RectF drawableRect = new RectF(0, 0, (float) c.getWidth(), (float) c.getHeight());
RectF viewRect = new RectF(0, 0, (float) i.getWidth(), (float) i.getHeight());
curMatrix.setRectToRect(drawableRect, viewRect, Matrix.ScaleToFit.CENTER);
//Apply the autosize transformation
i.setImageMatrix(curMatrix);
//Reload the imageview with the new bitmap
i.setImageBitmap(bmOverlay);
}
return true;
}
And here are some images to better explain what is happening:
Start - I want to click on the fish:
First click - click is accurate, scaling is lost:
Second click - scaling reappears, click applied to original scaling though, so it is off:
Third and all subsequent clicks - works as I'd hoped it would from the start:
Thanks for any help!
As usual, I was over complicating things. For those interested, the following simpler code works. Simply set this as a TouchListener for an ImageView, and when you touch the image, it will draw a 100(pixel?) radius semi-transparent red circle centered on your touch point:
public class GetCoordinatesTouchListener implements OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
int eventType = event.getAction() & MotionEvent.ACTION_MASK;
switch (eventType) {
case MotionEvent.ACTION_UP:
ImageView i = (ImageView) v;
//Save the current bitmap from the imageview
i.buildDrawingCache();
Bitmap bm = i.getDrawingCache();
//Create new overlay bitmap
Bitmap bmOverlay = Bitmap.createBitmap(i.getWidth(), i.getHeight(), Bitmap.Config.ARGB_8888);
//Create new drawing canvas and paint
Canvas c = new Canvas(bmOverlay);
Paint p = new Paint();
p.setColor(Color.RED);
p.setAlpha(50);
//Draw the saved bitmap onto the canvas
c.drawBitmap(bm, new Matrix(), null);
//Draw a circle on the current canvas, centered on event coordinates
c.drawCircle(event.getX(), event.getY(), 100F, p);
//Reload the imageview with the new bitmap with FIT_XY scaling
i.setScaleType(ImageView.ScaleType.FIT_XY);
i.setImageBitmap(bmOverlay);
}
return true;
}
}
Related
I have subclass of ImageView on which I can draw (pen, rect, elipse etc..) and add EditText subviews (draggable and can be rotated).
Problem is when I want to get final image I'm not sure how to merge those EditText into new image, because all the tools are drawing directly on canvas while I need to draw EditText texts at the end, since they need to be draggable and changable before user clicks Save.
After many different approaches, this one is most correct but it doesn't work if the text is rotated by some degree.
public Bitmap GetFinalBitmap()
{
this.DrawingCacheEnabled = false;
this.DrawingCacheEnabled = true;
var finalImage = Bitmap.CreateBitmap(GetDrawingCache(true));
foreach (var item in this.pathLists)
{
if (item.TextView != null)
{
item.TextView.BuildDrawingCache(true);
var txt = item.TextView.GetDrawingCache(true);
finalImage = Overlay(finalImage, txt, item.TextView.GetX(), item.TextView.GetY());
}
}
return finalImage;
}
public Bitmap Overlay(Bitmap bmp1, Bitmap bmp2, float x, float y)
{
Bitmap bmOverlay = Bitmap.CreateBitmap(bmp1.Width, bmp1.Height, bmp1.GetConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.DrawBitmap(bmp1, new Matrix(), null);
canvas.DrawBitmap(bmp2, x, y, null);
return bmOverlay;
}
Any help would be appreciated (update of solution or maybe new approach on how to draw EditText with correct coordinates on ImageView's bitmap).
Basically all I had to do was draw second bitmap (one of the EditText) using matrix. Here is updated code that works even for rotated EditText.
public Bitmap Overlay(Bitmap bmp1, Bitmap bmp2, float x, float y, float rotation)
{
Bitmap bmOverlay = Bitmap.CreateBitmap(bmp1.Width, bmp1.Height, bmp1.GetConfig());
Canvas canvas = new Canvas(bmOverlay);
canvas.DrawBitmap(bmp1, new Matrix(), null);
var matrix = new Matrix();
matrix.Reset();
if (rotation > 0)
matrix.PostRotate(rotation, bmp2.Width / 2, bmp2.Height / 2);
matrix.PostTranslate(x, y);
canvas.DrawBitmap(bmp2, matrix, null);
return bmOverlay;
}
I want to draw at specific coordinates of an image which is displayed in an imageview. I use the src attribute of a imageview to load the image into the view. This code is used in an custom imageview to draw on the image:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float scaleh =(float)canvas.getHeight()/(float)orginalheight();
float scalew = ((float)canvas.getWidth()/(float)orginalwidth());
canvas.drawRect(10*scaleh,9*scalew,20*scaleh,30*,scalewpaint);
}
This code draws the rectangle at the wrong location. What is wrong?
You should get the dimensions of the View from the ImageView itself rather than Canvas especially when the ulterior goal is to Draw on Image inside ImageView.
A Canvas works for you as a pretense, or interface, to the actual
surface upon which your graphics will be drawn — it holds all of your
"draw" calls. Via the Canvas, your drawing is actually performed upon
an underlying Bitmap, which is placed into the window.
Firstly, in order to get the Coordinates, implement onTouchListener for ImageView. This will give you X and Y co ordinates to draw the Canvas on.
imageView.setOnTouchListener(new OnTouchListener(){
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
xDown = event.getX();
yDown = event.getY();
break;
case MotionEvent.ACTION_MOVE:
xUp = event.getX();
uUp = event.getY();
canvas.drawLine(xDown, yDown, xUp, uUp, paint);
imageView.invalidate();
xDown = xUp;
yDown = uUp;
break;
case MotionEvent.ACTION_UP:
xUp = event.getX();
uUp = event.getY();
canvas.drawLine(xDown, yDown, xUp, uUp, paint);
imageView.invalidate();
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}});
Then create a Canvas to draw on top of ImageView.
//Create a new image bitmap and attach a brand new canvas to it
Bitmap tempBitmap = Bitmap.createBitmap(myBitmap.getWidth(), myBitmap.getHeight(), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(tempBitmap);
paint = new Paint();
paint.setColor(YOUR_DESIRED_COLOR);
paint.setStrokeWidth(INT_VALUE);
matrix = new Matrix();
canvas.drawBitmap(bmp, matrix, paint);
//Attach the canvas to the ImageView
imageViewsetImageBitmap(tempBitmap);
you can also use canvas.drawRect(left,top,right,bottom,paint); if you want to draw Rectangle simply.
Hope it helps.
I am trying to programmatically create a gray Rect and black Rect with BlurMaskFilter layer (drop shadow effect) by overriding onDraw in a custom View. I am able to get it to draw on screen without any issues, but when I try to draw the view to a bitmap the results are different. In drawing to bitmap, it appears BlueMaskFilter is applied to the View rather than the specific layer of the black Rect.
What am I missing to make the drawn bitmap same as the output drawn on screen?
CustomView's onDraw override:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw black box (shadow) first
RectF rectShadow = new RectF();
rectShadow.set(10, 10, 120, 120);
Paint shadowPaint = new Paint();
shadowPaint.setMaskFilter(new BlurMaskFilter(5, Blur.NORMAL));
shadowPaint.setStyle(Paint.Style.FILL);
shadowPaint.setColor(Color.DKGRAY);
shadowPaint.setAntiAlias(true);
canvas.drawRect(rectShadow, shadowPaint);
setLayerType(View.LAYER_TYPE_SOFTWARE, shadowPaint);
// draw grey box
RectF rectGray = new RectF();
rectGray.set(0, 0, 100, 100);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.parseColor("#656565"));
paint.setAntiAlias(true);
canvas.drawRoundRect(rectGray, paint);
}
Function to save view to bitmap:
public void saveViewToBitmap( View customView int outWidth, int outHeight ) {
customView.setLayoutParams(new ViewGroup.LayoutParams(outWidth, outHeight));
customView.measure(
ViewGroup.MeasureSpec.makeMeasureSpec(customView.getLayoutParams().width,
ViewGroup.MeasureSpec.EXACTLY),
ViewGroup.MeasureSpec.makeMeasureSpec(customView.getLayoutParams().height,
ViewGroup.MeasureSpec.EXACTLY));
customView.layout(0, 0,
customView.getMeasuredWidth(), customView.getMeasuredHeight());
customView.requestLayout();
Bitmap outputBitmap = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outputBitmap);
customView.draw(canvas);
return outputBitmap;
}
Note in the following images the screen image has crisp edges for the gray rect, whereas the drawn bitmap has the top and left edges of the gray rect blurred demonstrating the problem.
Image on screen:http://i.stack.imgur.com/0Hl0t.png
Image saved from bitmap created by saveViewToBitmap: http://i.stack.imgur.com/xNzi5.png
Thanks!
I am struggling with bitmap rotations. I wish to rotate a graphic around an alternate axis but I can only ever get it to rotate around the center point no matter what I do, putting in postTranlate. preTranslate, set Translate in any order doesnt work I have also tried the postRotate(45,0,0) but it always rotates around the center.
Code below taken of internet what would I do to alter its rotation point, the code below uses the launcher icon which is square I am using a long thin graphic like an arrow.
// Rotate image to 45 degrees.
public void RotateImage(){
ImageView image;
Bitmap bMap;
Matrix matrix;
//Get ImageView from layout xml file
image = (ImageView) findViewById(R.id.imageView1);
//Decode Image using Bitmap factory.
bMap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
//Create object of new Matrix.
matrix = new Matrix();
//set image rotation value to 45 degrees in matrix.
matrix.postRotate(45);
//Create bitmap with new values.
Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0,
bMap.getWidth(), bMap.getHeight(), matrix, true);
//put rotated image in ImageView.
image.setImageBitmap(bMapRotate);
}
I have tried the code below but its still rotates around the center or at least appears too
public void RotateImage{
ImageView image;
Bitmap bMap;
Matrix matrix;
//Get ImageView from layout xml file
image = (ImageView) findViewById(R.id.imageView);
//Decode Image using Bitmap factory.
bMap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
//Create object of new Matrix.
matrix = new Matrix();
//set image rotation value to 45 degrees in matrix.
matrix.setTranslate(-100,-200);
matrix.postRotate(angle);
matrix.postTranslate(100,200);
//Create bitmap with new values.
Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0,
bMap.getWidth(), bMap.getHeight(), matrix, true);
//put rotated image in ImageView.
image.setImageBitmap(bMapRotate);
Thanks
If you want your Bitmap to rotate 45 degree around x axis with pivot (a,b), you can call matrix.rotate(45, a, b)
Perhaps you want to use the Camera class to rotate around X, Y or Z axis:
matrix = new Matrix();
Camera camera = new Camera();
camera.save();
camera.rotateX(45f);
camera.getMatrix(matrix);
camera.restore();
The way I understand your question is that you want to rotate the image around some point, say (x, y). Conceptually you need to perform the following transformations on the image:
Translate by (-x, -y)
Rotate by 45 degrees
Translate by (x, y)
You can use this method to rotate an object from center create this method in sprite class
public void paintFromCenter(float angle, Canvas c) {
Bitmap b = sprite;
Bitmap h = b;
// Canvas canvas = new Canvas(a);
Matrix matrix = new Matrix();
matrix.postRotate(angle, h.getWidth() / 2, h.getHeight());
matrix.postTranslate(getX(), getY());
// canvas.drawBitmap(bitmap, matrix, new Paint());
Bitmap bmp2 = Bitmap.createBitmap(h, 0, 0, frameWidth, frameHeight,
matrix, true);
c.drawBitmap(h, matrix, null);
// g.getCanvas().drawBitmap(bmp2, getX(), getY(), null);
}
Now in onTouchEvent()
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
evX = (int) event.getX();
evY = (int) event.getY();
int initX = objectSprite.getX();
int inity = objectSprite.getY();
if ((evX > objectSprite.getX()
&& evX < objectSprite.getX() + objectSprite.getWidth()
&& evY > objectSprite.getY() && evY < objectSprite.getY()
+ objectSprite.getHeight())) {
if (angle < 90) {
angle += 5;
} else if (angle < -180)
angle -= 5;
}
}
return true;
}
in draw() method paint the image/object
private void draw(Canvas canvas) {
objectSprite.paintFromCenter(angle, canvas);
}
try it
I'm trying to overlay a bitmap onto another, placing it at the location the user touches. Here's the code:
public static Bitmap mergeImage(Bitmap base, Bitmap overlay, float x, float y)
{
Bitmap mBitmap = Bitmap.createBitmap(base.getWidth(), base.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmap);
canvas.drawBitmap(base, 0, 0, null);
canvas.drawBitmap(overlay, x, y, null);
return mBitmap;
}
The issue here is, even though the x & y coordinates are obtained correctly (I checked), the overlay bitmap does not place correctly.
When around the top left portion of the image, placement is correct. However, as I move right and down, the location seems to scale differently (i.e. if I touch the bottom right corner of the screen, the overlay places somewhere near the middle of the image, if I touch bottom left, it places near the middle left of the image and so on)
Both images have the same density (320).
Edit: New issue, i reduced the sizes of both images and now placement is roughly accurate. But saving the image to SD card skews the overlay image to a different (and quite random) location
I found the solution using Matrix for set location and scale x,y
public static Bitmap mergeImage(Bitmap base, Bitmap overlay, float x, float y)
{
Bitmap mBitmap = Bitmap.createBitmap(base.getWidth(), base.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(mBitmap);
Matrix matrix = new Matrix ();
matrix.postTranslate( x,y);
canvas.drawBitmap(base, 0, 0, null);
canvas.drawBitmap(overlay, matrix, null);
return mBitmap;
}
I spend something similar and I found the solution, you can see my post in the siguiete link
canvas.drawBitmap bad Location and size (not placing image correctly)