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.
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?
int currentX = maze.getCurrentX(),currentY = maze.getCurrentY();
//draw the ball
canvas.drawCircle((currentX * totalCellWidth)+(cellWidth/2), //x of center
(currentY * totalCellHeight)+(cellWidth/2), //y of center
(cellWidth*0.45f), //radius
ball);
//draw the finishing point indicator
canvas.drawText("F",
(mazeFinishX * totalCellWidth)+(cellWidth*0.25f),
(mazeFinishY * totalCellHeight)+(cellHeight*0.75f),
ball);
}
You have to add the png to your drawable folder. then you can create a Bitmap using BitmapFactory.decodeResource(resourceId);. The bitmap can be drawn on the canvas using canvas.drawBitmap(Bitmap bmp, Rect source, Rect dest, Paint p). bmp is the bitmap to be drawn, obviously, source is the part of the bitmap that schould be drawn (in your case the whole bitmap), dest is the location on the canvas where the bitmap is drawn, p is the paint, which you don't need, so just use null.
I have been battling with trying to draw a bitmap and then highlighting a region on it with a rectangle. Originally, I was drawing a bitmap with alpha black in paint to make image darker and then on top drawing original bitmap in a region creating effect of highlight. I discovered that largest slowdown was because of alpha in Paint. So I have reworked the code and ended up with following in my draw thread:
private synchronized void drawSquare(int xStart, int yStart, int xEnd, int yEnd) {
Canvas c = holder.lockCanvas();
if(c != null) {
// Draw the background picture on top with some changed alpha channel to blend
Paint paint = new Paint();
paint.setAntiAlias(true);
if(bg != null && cWidth > 0 && cHeight > 0) {
c.clipRect(xStart, yStart, xEnd, yEnd, Region.Op.DIFFERENCE);
c.drawBitmap(bg, gTransform, blackSqr); // Draw derker background
c.clipRect(xStart, yStart, xEnd, yEnd, Region.Op.REPLACE);
c.drawBitmap(bg, gTransform, paint); ///draw original in selection
c.clipRect(0, 0, cWidth, cHeight,Region.Op.REPLACE);
}
Matrix RTcorner = new Matrix();
RTcorner.setRotate(90);
RTcorner.postTranslate(xEnd + 13, yStart - 13);
Matrix RBcorner = new Matrix();
RBcorner.setRotate(180);
RBcorner.postTranslate(xEnd + 13, yEnd + 13);
Matrix LBcorner = new Matrix();
LBcorner.setRotate(270);
LBcorner.postTranslate(xStart - 13, yEnd + 13);
// Draw the fancy bounding box
c.drawRect(xStart, yStart, xEnd, yEnd, linePaintB);
// Draw corners for the fancy box
c.drawBitmap(corner, xStart - 13, yStart - 13, new Paint());
c.drawBitmap(corner, RBcorner, new Paint());
c.drawBitmap(corner, LBcorner, new Paint());
c.drawBitmap(corner, RTcorner, new Paint());
}
holder.unlockCanvasAndPost(c);
}
So this clips out my selection area, I draw with paint that has this code to make it darker.
blackSqr.setColorFilter(new LightingColorFilter(Color.rgb(100,100,100),0));
And in the area inside the clip I draw my original bitmap. It works. But I am not happy with response time. After profiling Bitmap is what takes the longest. I have scaled the bitmap to the size of the screen already so it's drawing 300x800-ish image. The biggest resource hog seems to be the Lighting effect. Because when I turn it off I get decent response time.
So I was wondering if I have missed anything to improve how quickly bitmap is drawn, maybe caching? Or am I just stuck with this because I want darker image and actually should rethink the "highlighting/selection" altogether? Why is is so expensive to draw a bitmap with alpha colour in 2D image?
if I understand what you want, you want a rectangle (with rounded corners) to highlight a part from another image.
if it is that, then I would use an image with the square wit draw9patch and use it as a floating view over the image view
RelativeLaoyut (Image container)
+- ImageView (your actual image)
+- view (it has the square as a background, and you only have to move it to the area you want to highlight)
I'm sorry, I'm not good explaining myself.
For anyone that is interested, perhaps facing similar problem. This solution applies to my particular situation, but I have a separate background bitmap with darkened pixels manually set using:
for(int i = 0; i < cWidth; i++){
for(int j = 0; j < cHeight; j++){
int c = bg2.getPixel(i, j);
float mult = 0.15f;
int r = (int) (Color.red(c) * mult);
int g = (int) (Color.green(c) * mult);
int b = (int) (Color.blue(c) * mult);
bg2.setPixel(i, j, Color.rgb(r, g, b));
}
}
Then use the bg2 to draw main part and the original (not darkened) for the clip rectangle of the selection. There is a bit of overhead for creating and maintaining the second bitmap but the draw speed and response time is quick and smooth in comparison to bitmaps with alpha.
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.
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);
}