The mentioned class has got the following method:
protected void drawCompass(Canvas canvas, float bearing) {
int offset = Math.max(canvas.getHeight(), canvas.getWidth()) / 8;
Rect r = new Rect(0, 0, 2*offset, 2*offset);
canvas.drawBitmap(compassBase, null, r, paint);
canvas.rotate(-bearing, offset, offset);
canvas.drawBitmap(compassArrow, null, r, paint);
}
Link to the complete src: MyLocationOverlay
There they create a Rect r witch specifies where the both Bitmaps should be drawn on the canvas.
The java doc of drawBitmap says :"Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle. If the source rectangle is not null, it specifies the subset of the bitmap to draw. "
Because both Bitmaps use the same Rect r and because they both are automatically scaled to fit the Rect, why is the result a perfect Compass when I activate the compass in the MapView.
To my mind the result should be crap, because the arrow of the compass is also scaled to fit the Rect.
So where is the error in reasoning?
They are scaled using the same proportion, not to absolute size. To answer your question, the canvas is drawn on using the bitmap as reference - think of it like a painter and a canvas painting one image on top of the other using two photo images as reference.
Related
I have bitmap that need to draw to canvas. The image is of a fixed size, but the canvas will change according to the user's screen size and density (bitmap coule be larger/smaller than the canvas).
I need to draw the bitmap to canvas scaling all the way into the canvas size (without distorting the image), I have done the code as below but the bitmap still filling only a portion of the screen.
Rect dest = new Rect(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
Paint paint = new Paint();
paint.setFilterBitmap(true);
drawCanvas.drawBitmap(canvasBitmap, null, dest, paint);
May I know if anybody can shed light on a good solution? Thanks.
This example is in javascript but it should still help you out scale an image
jsFiddle : https://jsfiddle.net/CanvasCode/7oghuwe2/3/
javascript
var canvas1 = document.getElementById('canvas1');
var context1 = canvas1.getContext('2d')
var canvas2 = document.getElementById('canvas2');
var context2 = canvas2.getContext('2d');
var image1 = new Image();
image1.src = "http://media.giphy.com/media/iNk83OBPzlA8o/giphy.gif";
image1.onload = function () {
context1.fillStyle = "#F00";
context1.fillRect(0, 0, canvas1.width, canvas1.height);
context2.fillStyle = "#00F";
context2.fillRect(0, 0, canvas2.width, canvas2.height);
ratio(context1, canvas1, image1);
ratio(context2, canvas2, image1);
}
function ratio(context1, canvas1, image1) {
var imageRatio = image1.width / image1.height;
var newHeight = canvas1.width / imageRatio;
var newWidth = canvas1.height * imageRatio;
var heightDiff = newHeight - canvas1.height;
var widthDiff = newWidth - canvas1.width;
if (widthDiff >= heightDiff) {
context1.drawImage(image1, 0, 0, canvas1.width, canvas1.width / imageRatio);
} else {
context1.drawImage(image1, 0, 0, canvas1.height * imageRatio, canvas1.height);
}
}
Basically you need to calculate what the width would be if you scaled the image by the canvas height and what the height would be if you scale the image by the canvas width, and which ever is smaller, then you scale by that dimension.
The reason why it might not work for you might be because the function drawBitmap() ignores the density of the bitmap. The following is from the documentation.
public void drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint
paint)
This function ignores the density associated with the bitmap. This is
because the source and destination rectangle coordinate spaces are in
their respective densities, so must already have the appropriate
scaling factor applied.
What you could do is use public void drawBitmap (Bitmap bitmap, Matrix matrix, Paint paint) instead. First you need to map the source Matrix with the desination Matrix. You do this via Matrix.setRectToRect() or Matrix.setPolyToPoly(). This will give you an accurate mapping. Just make sure you map them correctly, otherwise things will be distorted.
For more info refer here: What code should I use with 'drawMatrix.setPolyToPoly' to manipulate and draw just a rectangular part of a bitmap, rather than the entire bitmap?
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)
I'm working on an app that needs to apply perspective distortion correction to a photo taken with the phone's camera.
Once the photo is taken, the idea is to show it on an imageview and let the user mark the four corners of the document (a card, a sheet of paper, etc.) and then apply the correction based on those points.
This is an example of what im trying to achieve:
http://1.bp.blogspot.com/-ro9hniPj52E/TkoM0kTlEnI/AAAAAAAAAbQ/c2R5VrgmC_w/s640/s4.jpg
Any ideas on how to do this on android?
You don't have to use a library for this.
You can just as well use one of the drawBitmap functions of the Canvas class with a matrix that's initialized using the setPolyToPoly function of the Matrix class.
public static Bitmap cornerPin(Bitmap b, float[] srcPoints, float[] dstPoints) {
int w = b.getWidth(), h = b.getHeight();
Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
Canvas c = new Canvas(result);
Matrix m = new Matrix();
m.setPolyToPoly(srcPoints, 0, dstPoints, 0, 4);
c.drawBitmap(b, m, p);
return result;
}
(The Paint object is only needed to enable anti-aliasing.)
Usage:
int w = bitmap.getWidth(), h = bitmap.getHeight();
float[] src = {
0, 0, // Coordinate of top left point
0, h, // Coordinate of bottom left point
w, h, // Coordinate of bottom right point
w, 0 // Coordinate of top right point
};
float[] dst = {
0, 0, // Desired coordinate of top left point
0, h, // Desired coordinate of bottom left point
w, 0.8f * h, // Desired coordinate of bottom right point
w, 0.2f * h // Desired coordinate of top right point
};
Bitmap transformed = cornerPin(bitmap, src, dst);
Where src are the coordinates of the source points, dst are the coordinates of the destination points. Result:
What you want to do goes under various names of art, "corner-pin" being the one commonly used in the visual effects industry. You need to proceed in two steps:
Compute the mapping from the the desired, rectified image, to the original, distorted, image
Actually warp the original image according to the mapping computed in (1).
The 4 (non-collinear, perspective-distorted) corners of the original image, and the 4 corners of the target (undistorted) image, define the mapping. This mapping is called a "homography" - read the pointed wikipedia page for details. Once the mapping is known, the warping at step (2) can be computed by interpolation: for every pixel in the target image, find the corresponding pixel in the original image. As this will typically not be at integer coordinates, you interpolate its color from the neighbors. Various interpolation schemes are used, the common ones being nearest-neighbor, bilinear and bicubic (in increasing order of smoothness in the results).
For Android, I'd recommend installing the OpenCV SDK , and then use the geometry transformation routines (getPerspectiveTransform and warpPerspective for the two steps above).
I'm doing my own pixel calculations for drawing on the screen, and have a sprite bitmap (4x2 images in one file). Each image is 100x100, and the overall file is 400x200. Using the following method my drawing is not pulling the right section of the bitmap, and the placement on the scren isn't quite right:
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint)
Since: API Level 1
Draw the specified bitmap, scaling/translating automatically to fill
the destination rectangle. If the source rectangle is not null, it
specifies the subset of the bitmap to draw.
Note: if the paint contains a maskfilter that generates a mask which
extends beyond the bitmap's original width/height (e.g.
BlurMaskFilter), then the bitmap will be drawn as if it were in a
Shader with CLAMP mode. Thus the color outside of the original
width/height will be the edge color replicated.
This function ignores the density associated with the bitmap. This is
because the source and destination rectangle coordinate spaces are in
their respective densities, so must already have the appropriate
scaling factor applied.
Parameters
bitmap > The bitmap to be drawn
src > May be null. The subset of the bitmap to be drawn
dst > The rectangle that the bitmap will be scaled/translated to fit into
paint > May be null. The paint used to draw the bitmap
Rect source = new Rect();
source.left = (this.currentFrame % 4) * 100;
source.right = source.left + 100;
source.top = (this.currentFrame / 4) * 100;
source.bottom = source.top + 100;
Rect dest = new Rect();
dest.top = (int) top;
dest.left = (int) left;
dest.right = dest.left + 100;
dest.bottom = dest.top + 100;
// draw current frame onto canvas
//canvas.drawBitmap(pics[this.currentFrame], left, top, painter);
canvas.drawBitmap(pics[0], source, dest, painter);
The documentation explicity says densities are ignored, but why is my image being scaled?
As per Renard
How exactly are you loading pics[0]? Have you tried getResources().openRawResource(imageId)?
This solved my problem.
Can someone explain what the rect does in: canvas.drawBitmap(bmp,scr ,dst ,null);
Because I have tried and tried to make any sense of this but I simply don't understand what the two rectangles does.
My goal is to display a portion the bitmap instead of the whole image.
//Simon
Rect src = new Rect(0, 0, 50, 50);
Rect dst = new Rect(50, 50, 200, 200);
canvas.drawBitmap(originalBitmap, src, dst, null);
This code specifies that you want to copy a rectangle with the dimensions 50 width / 50 height of the source starting at position 0x 0y, and draw into the destination bitmap starting at position 50x / 50y and occupy it until 200x 200y - therefore stretching a bit - because as the source is only 50 pixels long, to stretch to 200x and 200y the copy will end up with the size 150width / 150 height.
The Android documentation seems to explain this method quite well.
drawBitmap Documenation
From reading the documentation it appears you can do what you want by specifying a source Rect, which will be the rectangle(subset) from the original bitmap, and it will then be translated into the dest Rectangle.
Bitmap picture; //Assume this is a 1024x768 image and has been initialized.
#Override
public void onDraw(Canvas canvas){
//To Draw only the top left corner of the image
Rect src = new Rect(0,0,512,368);
Rect dest = new Rect(0,0,512,368);
canvas.drawBitmap(picture, src, dest, null);
}