I am trying to sale a bitmap and translating it at each step.
If we look at the following code, I am drawing an image, translating and scaling it and then performing the same operations in reverse so as to get the original configuration back. But after applying the operations, I do get the original scaled image (scale factor 1) but the image is translated t a different position.
Could you please point out the correct method do so ? (In the example above, how do I reach the original configuration? )
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Matrix matrix = new Matrix();
scale = (float)screenWidth/201.0f;
matrix.setTranslate(-40, -40);
matrix.setScale(scale, scale);
canvas.drawBitmap(bitMap, matrix, paint);
//back to original
canvas.drawColor(0, Mode.CLEAR);
matrix.setScale(1.0f/scale, 1.0f/scale);
matrix.setTranslate(40,40);
canvas.drawBitmap(bitMap, matrix, paint);
}
You should just use the Canvas methods for scaling and translating, that way you can then take advantage of the save() and restore() APIs to do what you need. For example:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Save the current state of the canvas
canvas.save();
scale = (float) screenWidth / 201.0f;
canvas.translate(-40, -40);
canvas.scale(scale, scale);
canvas.drawBitmap(bitMap, 0, 0, paint);
//Restore back to the state it was when last saved
canvas.restore();
canvas.drawColor(0, Mode.CLEAR);
canvas.drawBitmap(bitMap, 0, 0, paint);
}
I think the problem with your original code might be because of the way scale and translate use the point around which you scale/translate. IF you specify the correct pivot points in between/for the operations, things will be ok.
Related
Bitmap b;
Matrix mMatrix;
mMatrix.reset();
// move the view so that it's center point is located in 0,0
mMatrix.postTranslate(-sizeX, -sizeY);
// scale the view
mMatrix.postScale(mScaleFactor, mScaleFactor);
// re-move the view to it's desired location
mMatrix.postTranslate(mTouchX, mTouchY);
canvas.drawBitmap(b, mMatrix, null);
I need to know if there is an built in method to tell the info like corner coordinates of where the Bitmap is printed, etc.
Hope it is clear.
Thanks.
yes, it's called mapRect https://developer.android.com/reference/android/graphics/Matrix.html#mapRect(android.graphics.RectF)
RectF r = new RectF(0, 0, b.getHeight(), b.getWidth());
mMatrix.mapRect(r);
// r now have the coordinates where the bitmap was drawn,
// you can test it by calling
canvas.drawRect(r, paint);
I tried to create a Maze with a moving ball and a hole using the Accelerometer Sensor. With the following code, the ball falls into the hole, but the performance is really bad, I set the Accelerometer Frequency to the fastest, but it's everything other than smooth. I made a second canvas, because so I could make a hole.
public RenderView(Context context, int width, int height) {
super(context);
playGround = new Rect(40, 40, width - 40, height - 40);
holes.addElement(new PointF(500f, 500f));
// Set background
this.setBackgroundResource(R.drawable.bottom);
// Set bitmap
woodGround= wood.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(woodGround);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
eraserPaint.setAntiAlias(true);
}
protected void onDraw(Canvas canvas) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG
| Paint.FILTER_BITMAP_FLAG);
paint.setStyle(Style.FILL_AND_STROKE);
paint.setAntiAlias(true);
if (ballInHole)
canvas.drawBitmap(ball, b.x, b.y, paint);
bitmapCanvas.drawBitmap(wall, 0, 0, paint);
bitmapCanvas.drawBitmap(wood, playGround, playGround, paint);
canvas.drawBitmap(bitmap, 0, 0, paint);
for (PointF h : holes) {
bitmapCanvas.drawCircle(h.x + radius, h.y + radius, radius,
eraserPaint);
}
if (!ballInHole)
canvas.drawBitmap(ball, b.x, b.y, paint);
invalidate();
}
It's solved very ugly, because I just draw the ball bellow the other bitmaps when he falls into a hole. Is there another way to do it?
The performance is also really bad, i set the Accelerometer-Sensor-Delay to the fastest, but the ball doesn't run smooth. When I remove the line canvas.drawBitmap(bitmap, 0, 0, paint);, then the ball is smoother, but then the wooden background is away.
The problem here is that you're doing A LOT of drawings all the time and that's take time to draw and the performance gets very low.
here a few tips on how you should approach it.
You probably better have one view with the static stuff (the background image and the holes) and on your layout have a second view on top of it just drawing the ball.
on the background image, do not call invalidate. That way you will draw the background just once.
and the top image (the ball only) you can invalidate, so it can redraw on the new position.
I'm not sure on this last part: but you may need to call invalidate(rect); passing the area where the ball was on the previous time, to make the background only re-draw that small area (instead of the whole screen)
happy coding.
How can I inverse the image drawed into a Canvas?
I has the following:
canvas.save();
canvas.drawBitmap(image.current(), null, currentBounds(), Paints.BLANK);
canvas.restore();
How can I make the current image be drawed fliped on x-axis into the currentBounds()?
I already found some answers indicating usage of Matrix, but I wan't to know if there's a easier way? Such a Paint with some flag turned on.
EDIT:
Following is my try with Matrix transformations:
Rect currentBounds = currentBounds();
currentBounds.offset((int)offset.x(), (int)offset.y());
float scale = image.current().getWidth() / currentBounds.width();
Matrix matrix = new Matrix();
matrix.setScale(- scale - 1, scale + 1);
matrix.postTranslate(currentBounds.left, currentBounds.top);
canvas.drawBitmap(image.current(), matrix, Paints.BLANK);
canvas.drawRect(currentBounds, Paints.STROKE_BLUE);
The following is the result of this draw:
https://dl.dropbox.com/u/28683814/game.png
As can be seen, the sprite is being drawed from 0,0 to left and it's not fully completes the currentBounds(), what I'm doing wrong?
Use
canvas.scale(-1,0,width/2,height/2);
as mentioned by my own answer here.
Although, I would point out that it will take you about a minute to understand Matrices, and it'll make you a better Android programmer.
Use this. I think this is quite easy.
canvas.save();
canvas.rotate(180, x, y);
canvas.drawBitmap(bitmap, x, y, null);
canvas.restore();
Well, I solved this way:
Rect currentBounds = currentBounds();
currentBounds.offset((int)offset.x(), (int)offset.y());
float scale = (float) currentBounds.width() / (float) image.current().getWidth();
boolean leftMovement = movingLeft();
Matrix matrix = new Matrix();
matrix.setScale(leftMovement ? -scale : scale, scale);
matrix.postTranslate(leftMovement ? currentBounds.right : currentBounds.left, currentBounds.top);
canvas.drawBitmap(image.current(), matrix, Paints.BLANK);
But this "leftMovement ? currentBounds.right : currentBounds.left" does not looks right
Hi have a problem regarding rotation. I'm currently drawing a few Objects on a Canvas. Currently this 'Objects' are just Bitmaps. After computing the rotation and passing it inside a Matrix, I'm forced to create a new Bitmap to get it rotated. A small snippet will clearify this:
public void onDraw(Canvas canvas){
mMatrix = new Matrix();
mMatrix.postRotate(getRotation());
Bitmap rotateBmp = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mMatrix, true);
canvas.drawBitmap(rotateBmp, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
rotateBmp.recycle();
}
All I can do to save memory is calling recycle on the rotateBmp, and it will run most of the time correctly. Lets assume I have 10 Bitmap 'Objects' I want to rotate. That means I have to keep ten Bitmaps as 'sample' and create ten new Bitmaps in every draw-cycle plus an additional new Matrix (didn't find a way to 'reset' it). This sounds very weird to me. Is there another Way, to create 'something that can be drawn on a Canvas' on the fly (no XML), while keeping control of their rotation. Any Idea is very welcome. If its a View, a Drawable or another CustomClass doesn't matter. Maybe it is important that getRotation will be all the same for every "Something" that should be drawn. What is the best practice to do that?
You can rotate the Canvas using canvas.rotate(float degrees)
public void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
canvas.rotate(getRotation());
}
With the great help of #Jason Robinson and how To Rotate Text in Canvas I figured out a Way, of how I can do it. Here is a short snippet, that will work:
Bitmap mBitmap = getResources()...
int xPositionOfBitmap
int yPositionOfBitmap
public void onDraw(Canvas canvas) {
int bitmapCenterX = xPositionOfBitmap + (mBitmap.getWidth() / 2)
int bitmapCenterY = yPositionOfBitmap + (mBitmap.getHeight() / 2)
canvas.save()
canvas.rotate(getRotation(),bitmapCenterX, bitmapCenterY)
canvas.drawBitmap(mBitmap, xPositionOfBitmap, yPositionOfBitmap)
canvas.restore()
}
I am drawing a grid and I want it to be larger than the screen size so that a user can drag the screen left/right/up/down to get to the rest of the grid.
What is the best way to do that? I've tried drawing a larger bitmap to the canvas, but didn't get anywhere.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
Bitmap testBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
canvas.drawBitmap(testBitmap, 0, 0, paint);
canvas.drawPaint(paint);
//other grid drawing code here
}
I used the View's scrollBy() method in the onTouch method of the Activity. It worked.
You can probably use the canvas.translate(x, y) method. That will adjust the origin for your canvas in relation to the screen. So canvas.translate(10, 10) will make you canvas origin (0, 0) be at the point of (10, 10) on the screen. Use a negative translation to scroll the screen.