How scale bitmap in Canvas android - android

I want to diasplay image(bitmap) in SurfaceView using Canvas. My requirement is that size of the canvas greater than image.I need space(eg:10 dp) in each side of the image in Canvas. How I can scale this?
Thanks
Mikahail

You can draw rectangle of desired Canvas size, and then draw your image with parameters (11, 11) which will draw it 10px from corner for each dimension.

Put this method in your SurfaceView Class :D
public void draw(Canvas canvas) {
final float scaleFactoryX = getWidth() / (WIDTH * 1.f);
final float scaleFactoryY = getHeight() / (HEIGHT * 1.f);
if (canvas != null) {
final int savedState = canvas.save();
canvas.scale(scaleFactoryX, scaleFactoryY);
canvas.restoreToCount(savedState);
}
}

Related

drawBitmap draw nothing android

I'm developing a custom view and I need to draw drawables inside.
Those drawable must have their position relative.
Here is my code :
#Override
protected void onDraw(Canvas canvas) {
//drawBackground(canvas);
float height = (float) getHeight();
float width = (float) getWidth();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(width, height);
drawFlowChart(canvas);
drawDrawables(canvas);
canvas.restore();
}
private void drawDrawables(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
Resources res = getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.motor);
canvas.drawBitmap(bitmap, 0.51f, 0.35f, null);
canvas.restore();
}
But it draw nothing.
Any idea ?
I tried to use the drawBitmap(bitmap, src, dest, paint) but I don't know how to position my bitmap with relative position with this definition.
May be getHeight() and getWidth() function that you call in onDraw return 0 so that canvas scaled and nothing to see.
You can refer two that link to update width and height of canvas to draw:
Android getWidth() return 0 in View's onDraw
Android: How to get a custom View's height and width?

Wrong Stroke width drawing circle on custom view Android

I am writing a custom view in android. I want to draw a circle that cover all width and height of my view. this is my code
private void init() {
bgpaint = new Paint();
bgpaint.setColor(bgColor);
bgpaint.setAntiAlias(true);
bgpaint.setStyle(Paint.Style.STROKE);
bgpaint.setStrokeWidth(strokeWidth);
rect = new RectF();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw background circle anyway
int strokeWidth = 50;
rect.set(strokeWidth, strokeWidth, getwidth()- strokeWidth,
getheight() - strokeWidth);
canvas.drawArc(rect, -90, 360, fill, bgpaint);
}
But when I run result will be like this
I want be like this
What the problem with my code?
Probem is in setting your rectangle's coordinates. You must set half of stroke width, not whole stroke width.
float left=strokeWidth / 2;
float top=strokeWidth / 2;
float right=minDimen - strokeWidth/ 2;
float bottom=minDimen - strokeWidth / 2;
rect.set(left, top, right,bottom);
Because it comes to circle, I assume your width and height of your circleView are the same dimension. That is minDimen in my code. Because I use smaller dimension of those two.
final int minDimen = Math.min(width, height);
setMeasuredDimension(minDimen, minDimen);
(this must be called in onMeasure method)
Anyway, it's good method to set width and height in same dimensions.

Drawable: Understanding canvas width and height

I try to implement my own drawable that will corss fade from a simple colored rectangle (that should have the same size as the ImageView where the drawable is in) to a loaded bitmap (over http).
So what I do is override the draw() method of my Drawable:
public void draw(Canvas canvas) {
boolean done = true;
Log.d("Test",
"canvas w: " + canvas.getWidth() + " " + canvas.getHeight());
final int alpha = mAlpha;
final boolean crossFade = mCrossFade;
Paint paint = mStartPaint;
paint.setColor(blueColor);
if (!crossFade || 255 - alpha > 0) {
if (crossFade) {
paint.setAlpha(255 - alpha);
}
// Draw the rect with the color
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);
if (crossFade) {
paint.setAlpha(0xFF);
}
}
if (alpha > 0) {
Bitmap bitmap = mEnd;
paint = mEndPaint;
paint.setAlpha(alpha);
if (mBitmapScalingType == null)
canvas.drawBitmap(bitmap, mEndX, mEndY, paint);
else
mBitmapScalingType.draw(bitmap, canvas.getWidth(),
canvas.getHeight(), canvas, paint);
paint.setAlpha(0xFF);
}
if (!done) {
mHandler.post(mInvalidater);
}
}
So the canvas.getWidth() and canvas.getHeight() returns 128 (width) and 150 (height) which is the same as the ImageView has. But the result is:
The blue rectangle has not been painted over the complete canvas and I can't get figure out why.
The ImageView that displays my fadeable drawable has no specific scaletype set.
Any ideas what could be wrong?
I have also noticed that the width and height calculated from setBounds() is 180 x 212 px
#Override
public void setBounds(int left, int top, int right, int bottom) {
super.setBounds(left, top, right, bottom);
final int width = right - left;
final int height = bottom - top;
Log.d("Test", "width: "+width+" height: "+height);
}
and if I draw the color with 180 x 212 px
canvas.drawRect(0, 0, 180, 212, paint);
the rectangular is now drawn to fill the canvas completely.
Any idea what could be wrong?
Btw. The ImageView is defined like that:
<ImageView android:id="#+id/playerPic"
android:layout_width="64dp"
android:layout_height="75dp"
android:layout_marginRight="10dp"
/>
in xhdpi 64dp = 128px and 75dp = 150 px
This answer doesn't explain the difference in dimensions, but sometimes it's easier to just do things a different way.
If you just want to fill the canvas with a color, you don't need to use drawRect(), just use drawColor(). There's the simple version that just takes a Color, and one that lets you specify a porter-duff mode. This way you don't need dimensions at all, and you can avoid the whole thing.
Maybe you can try to use DisplayMetrics to get the dimensions.
//Get the width and height of the screen
DisplayMetrics d = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(d);
int width = d.widthPixels;
int height = d.heightPixels;
Then just call drawrect with those values
canvas.drawRect(0, 0, width, height, paint);

Set zoom ratio of a displayed image in an android canvas

I am trying to write an android app that lets me draw graphics on top of an image then scale and zoom the image with the graphics staying over the same place on the image that have been drawn on top of it while changing the graphics in real time.
However I have been having a lot of issues actually getting it to zoom in while maintaining the center of the image. I have written code where I have a thread that updates the image. Updates are passed in using a class that I created called "PendingUpdate" through an ArrayBlockingQueue. This update contains a desired zoom level which is supposed to be the ratio of the image pixels to the canvas pixels and an image center. However the following code makes it pan while I am zooming which confuses me.
//Scale the image
canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom());
//Translate the image
double updateCx = pendingUpdate.getCenter().getX();
double updateCy = pendingUpdate.getCenter().getY();
double halfCanvasWidthInImagePixels = pendingUpdate.getZoom()*(canvas.getWidth()/2);
double halfCanvasHeightInImagePixels = pendingUpdate.getZoom()*(canvas.getHeight()/2);
double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
canvas.translate(-(float)imageTranslateX, -(float)imageTranslateY);
canvas.drawBitmap(pendingUpdate.getImage(), matrix, new Paint());
Thank you for the help!
Edit: here is the full function, I can also post PendingUpdate if this helps, however its just a data class.
private void doDraw(Canvas canvas, PendingUpdate pendingUpdate) {
int iWidth = pendingUpdate.getImage().getWidth();
int iHeight = pendingUpdate.getImage().getHeight();
Matrix matrix = new Matrix();
canvas.drawColor(Color.BLACK);
//TODO: add scrolling functionality to this
if(pendingUpdate.getZoom()>0) {
//Scale the image
canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom());
//Translate the image
double updateCx = pendingUpdate.getCenter().getX();
double updateCy = pendingUpdate.getCenter().getY();
double halfCanvasWidthInImagePixels = pendingUpdate.getZoom()*(canvas.getWidth()/2);
double halfCanvasHeightInImagePixels = pendingUpdate.getZoom()*(canvas.getHeight()/2);
double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
canvas.translate(-(float)imageTranslateX, -(float)imageTranslateY);
canvas.drawBitmap(pendingUpdate.getImage(), matrix, new Paint());
}else {
//matrix.postTranslate(canvas.getWidth()-iWidth/2, canvas.getWidth()-iHeight/2);
canvas.drawBitmap(pendingUpdate.getImage(),
(canvas.getWidth()-iWidth)/2,
(canvas.getHeight()-iHeight)/2, null);
}
//TODO: draw other stuff on canvas here such as current location
}
edit 2: This is how I finally got it to work, it was simply a matter of scaling it before translating it.
private void doDraw(Canvas canvas, PendingUpdate pendingUpdate) {
int iWidth = pendingUpdate.getImage().getWidth();
int iHeight = pendingUpdate.getImage().getHeight();
canvas.drawColor(Color.BLACK);
//TODO: add scrolling functionality to this
if(pendingUpdate.getZoom()>0) {
//Scale the image
canvas.save();
double updateCx = pendingUpdate.getCenter().getX();
double updateCy = pendingUpdate.getCenter().getY();
double halfCanvasWidthInImagePixels = (canvas.getWidth()/2);
double halfCanvasHeightInImagePixels = (canvas.getHeight()/2);
double imageTranslateX = updateCx - halfCanvasWidthInImagePixels;
double imageTranslateY = updateCy - halfCanvasHeightInImagePixels;
//canvas.scale(pendingUpdate.getZoom(), pendingUpdate.getZoom(), (float)pendingUpdate.getCenter().getX(), (float)pendingUpdate.getCenter().getY());
canvas.scale(pendingUpdate.getZoom(),
pendingUpdate.getZoom(),
canvas.getWidth()/2,
canvas.getHeight()/2);
canvas.translate(-(float)imageTranslateX,
-(float)imageTranslateY);
canvas.drawBitmap(pendingUpdate.getImage(), 0, 0, null);
canvas.restore();
}else {
//TODO: update this so it displays image scaled to screen and updates current zoom somehow
canvas.drawBitmap(pendingUpdate.getImage(),
(canvas.getWidth()-iWidth)/2,
(canvas.getHeight()-iHeight)/2, null);
}
//TODO: draw other stuff on canvas here such as current location
}
}
If I were you, I'd use the Canvas.scale(float sx, float sy, float px, float py) method which does exactly what you want.
However looking at your code I think you might be messing with too many transformations at once, which is harder to debug.
Always (and I mean always) call Canvas.save() and Canvas.restore() on the initial matrix you're getting in Canvas if you plan to alter it. This is because the Canvas that you get to draw on may be the canvas for e.g. the whole window with just clipping set to the boundaries of the control that is currently drawing itself.
Use matrix transformation method provided by the Canvas method and draw bitmap using the simplest invocation.
Following these two advices look at the whole View I have just made up, that scales the bitmap by a factor of 3 with point (16,16) set as the pivot (unchanged point - center of scaling). Tested - working.
public class DrawingView extends View {
Bitmap bitmap;
public DrawingView(Context context) {
super(context);
bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher);
}
#Override
protected void onDraw(Canvas canvas) {
float sx = 3;
float sy = 3;
float px = 16;
float py = 16;
canvas.save();
canvas.scale(sx, sy, px, py);
canvas.drawBitmap(bitmap, 0, 0, null);
canvas.restore();
}
}

autozoom canvas to fit bitmap

In my custom views OnDraw method I draw a Bitmap to the center of the Canvas with
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Rect r = canvas.getClipBounds();
displayWidth = r.right;
displayHeight = r.bottom;
camera.applyToCanvas(canvas);
float zW = (float)bitmapWidth / (float)displayWidth;
float zH = (float)bitmapHeight / (float)displayHeight;
float z = 0.0f;
if (zW>1 || zH>1) {
z = Math.max(zW, zH);
}
canvas.drawColor(Color.DKGRAY);
canvas.drawBitmap(bitmap, (displayWidth/2.0f - (bitmapWidth)/2.0f), (displayHeight/2.0f - bitmapHeight/2.0f), paint);
if (z>0) {
camera.translate(z, -z, z);
}
}
If the Bitmap is larger in height or width is larger then Canvas size (displayWidth, displayHeight), how can I use the Camera class to autozoom to fit the Bitmap and center it to the Canvas. Any ideas?
Try to create a Matrix instance and initialize it using
public boolean setRectToRect (RectF src, RectF dst, Matrix.ScaleToFit stf)
You need to do this only once and keep the matrix in memory. Note that Matrix.ScaleToFit define the CENTER value.
Later when you draw the bitmap use this version of the drawBitmap:
public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint)

Categories

Resources