Problem with draw bitmap on rotated canvas around X axis - android

Good day,
I have weird problem with drawing image. Code and pictures say more then words.
mCamera.save();
mCamera.rotateX(rotateX);
mCamera.getMatrix(mMatrix);
mCamera.restore();
canvas.save();
canvas.setMatrix(mMatrix);
// here I draw images
...
canvas.restore();
and results are at images below. First picture have angle 0, second around 45 degree and last something more. It depend on size of area (number of bitmaps) I want to draw. I notice that when I draw bitmaps that whole fits canvas bounds, it works fine. But problem is mainly when I draw images with something like (so with part outside of canvas)
canvas.drawBitmap(image, -100, -100, null)
and images (hmm, because I'm new here, I cannot post images directly ...)
ttp://locus.asamm.cz/data/_temp/1311873666794.png
http://locus.asamm.cz/data/_temp/1311873695945.png
http://locus.asamm.cz/data/_temp/1311873782054.png
So question is, if there is any working solution or at least any tip. Also if anyone experienced can tell if drawing with OpenGL can help with this and if so, please point me to any information source that can help me with drawing bitmaps that change a lot (it's map application so user move with map), because I still cannot find any simple and clear source of info.
Thanks to all very much

It's a limitation of the 2D drawing API. To do true 3D you should use OpenGL.

I expect you are trying to produce a google maps tilt/rotate effect which I have successfull done for google maps since they don't think we need an update ;-) and I needed it for my application, even if you are using your own tiles it's basically the same thing.
One thing that helps is realize that the rotations for the Camera Matrix are performed in the upper left corner.
So you should transform to the appropriate location (dependent on axis of rotation and your expected results) and then back, something like ... the effects you are getting were very similar to mine before I read the docs and figured that out.
camera.save();
camera.rotateX(tilt); // tilt forward or backward
camera.rotateY(0);
camera.rotateZ(angle); // Rotate around Z access (similar to canvas.rotate)
camera.getMatrix(cameraMatrix);
// This moves the center of the view into the upper left corner (0,0)
// which is necessary because Matrix always uses 0,0, as it's transform point
// use the center of the canvas, or the bottom center dependent on your results
cameraMatrix.preTranslate(-centerX, -centerY);
// NOTE: Camera Rotations logically happen here when the canvas has the matrix applied
// This happens after the camera rotations are applied, moving the view back to
// where it belongs, allowing us to rotate around the center or any point we choose
// Move it back after the rotations are applied
cameraMatrix.postTranslate(centerX, centerY);
camera.restore();
canvas.concat(cameraMatrix);
The other thing to do is to oversize the container so you don't have empty space (or at least limit it) after your tilt/rotate operations, you can do that by calculating the needed oversize, the diagonal of your display works for basic rotation, + a percentage based on tilt if you are using that.
Playing around with the pre/postTranslate can can give you some interesting results.
As to moving the bitmap I am not really sure why you are doing this, probably panning, but as long as the canvas is getting filled it shouldn't matter
Update
Here is how I oversize the canvas, I tried to use the LayoutParams to do this but didn't have much luck with it so on Measure was the way to go for me.
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (!isInEditMode())
super.onMeasure(View.MeasureSpec.makeMeasureSpec(scaledDimensionsW,
View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(scaledDimensionsH,
View.MeasureSpec.EXACTLY));
else
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

Related

Android Canvas - restrict draw area

So on my canvas I have few rectangular areas where I draw different bitmaps.
Sometimes bitmaps get outside of their respective areas. I want these bitmaps to be cut off, so only the portion of bitmap inside of it's area is drawn.
I sure can calculate it manually (srcRect and dstRect)...
But isn't there a simpler solution?
I found clipBounds, but it doesn't seem to restrict drawing area in any way.
What you want is Canvas.clipRect(). You will need to know the bounds of the clipping rectangle, of course, but you don't need to worry about calculating a custom srcRect.
To use, save() the Canvas, do a single drawBitmap(), then restore() it to get your original clipping state back:
canvas.save();
canvas.clipRect(...);
canvas.drawBitmap(...);
canvas.restore();

Rotate a Bitmap(a clocks hand) in a corner of Surface View, like an old stop watch timer?

I am trying to make an app, in which at the bottom corner is a mock timer with the seconds-hand(needle) going round and round like an old stop watch. My dial and needle are .png files drawn out in the "onDraw(Canvas canvas)", that is repeatedly called by my GameLoopThread(no problem here). The needle is rotated by separate anchor points which I have tested, this too works fine.
PROBLEM: when the new angle of the needle is calculated and is drawn, it appears on the top/left point of the surface view, possibly (0,0). while i need it at the bottom corner where i am drawing my dial!?
Heres my code
private void rotateCalc() {
matrix = new Matrix();
newAngle++;
matrix.postRotate(newAngle, 95, 10);//anchor points(95,10) for rotation
}
#Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(dialimg, null, dstbmp, null);
canvas.drawBitmap(needle, null,dstneedle,null);
rotateCalc();
canvas.drawBitmap(needle, matrix, null);
}
I guess its possibly because when using .drawBitmap method with matrix, i am not able to pass the Rect(demensions) or x,y (cordinates), that i would want the rotated Bitmap to be drawn to.
An idea how i may get around this?
Is this way inefficient when you compare with Creating a bitmap in which you can scale it and apply the matrix in the same function?
After that its the regular onDraw method on the new bitmap we have created
WOW! that works well. Heres my code, just checking if this sequence is fine? no particular logical order, right?
private void rotateCalc() {
matrix = new Matrix();
i++;
matrix.postRotate(i, 145, 8);
matrix.postScale(0.5f, 0.5f);
matrix.postTranslate(149, 337);
}
One last question, Im finding the Bitmaps to be a little choppy, as in.. I mean jagged, not really smooth. what can be done about that?
I've read to encorporate "Bitmap.Config.ARGB_8888", is that really needed? If so, could you give me an example. Thanx a lot #pskink, you've been a great help
EDIT
its very important to call matrix.postRotate(angle, x, y) ONLY after matrix.postScale(dx, dy), Because if you rotate first you will have to calculate the Anchor Points on the basis of the original Image you have chosen as your Bitmap. I preferred Scaling it, then finding out the new Width/Height and then deciding my anchor points.

Android bitmap shift/move issues

I have a problem that I can not really solve. I have created a graphing app which will contain data with alot of data points. To enable scrolling in the graph i create a bitmap of the graph data, and the shift / move the bitmap to the right or left of the screen depending on the user input. The bitmap is always the graph view height and graph view width. When the user moves the bitmap, i shift the bitmap with:
memoryCanvas.drawBitmap(memoryBitmap, bitmapShift, 0.0f, paint);
where shift is a a float value containing the shifting values.
Now, on most devices i have tried this is, it works very nice. I have tried it on HTC Desire, Galaxy Tab and Galaxy S. When testing this on my own Galaxy S however, i get strange results that i can not explain. Do note that my Galaxy S contains a custom rom (with android 4.0.4), so that is probably the reason why i get this behavior, but i still can not understand it or properly mitigate it.
So in a normal use case behavior, the bitmaps get shifted by bitmapShift number of pixels, and then i fill in the empty space by normal line drawing. But when using my phone, dragging the bitmap either direction slowly, so the bitmapShift values are around 0.5, the bitmap does not move, or only moves sometimes. I have compared the bitmapShift on the other platforms and they are in the same range, 0.5 when dragging slowly. This behavior does of course screw up my drawings a lot. This does also happen when doing fast dragging, but its most visible when doing it slowly.
I can not really figure out what causes this behavior. The bitmap does not move according to my bitmapShift value. It does on all other platforms i have tried. If i skip using bitmaps, and only draw lines according to the shifting values, everything works fine.
Does anyone have any idea on what could cause this behavior? Im kinda running out after sitting some days trying to figure it out. The critical code is below. This code is in my onDraw function.
memoryCanvas.setBitmap(emptyBitmap); //First set another bitmap to clear
memoryCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //Clear it
memoryCanvas.drawBitmap(memoryBitmap, bitmapShift, 0.0f, paint); //Draw shifted bitmap on it
memoryCanvas.drawLines(lineDrawPoints, 0 , cnt, paint); //Draw remaining lines
memoryCanvas.setBitmap(memoryBitmap); //Set the original
memoryCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //Clear original
memoryCanvas.drawBitmap(emptyBitmap, 0.0f, 0.0f, paint); //Draw the final image
canvas.drawBitmap(memoryBitmap, 0, 0.0f, paint); //And finally draw on real canvas
Any help, tips, suggestions are very welcome.
Cheers
Wilhelm
When there is only a simple transform set on Canvas (a simple transform = translate only, no rotate, no scale), Skia, Android's 2D rendering library, aligns bitmaps to the pixel grid. This means that a move by less than 1 pixel might not be visible at all. A silly workaround is to set a very, very small scale or rotate transform on Canvas before drawing your bitmap. This has the side effect of not snapping bitmaps to the pixel grid.
I think I should just add a new API on Paint to let apps do subpixel positioning of bitmaps no matter what transform is set.

Scroll Speed Disparity Android 2D Graphics

I am experimenting with 2D graphics in Android during my off time, and I'm intrigued by the idea of creating a sprite based "sandbox" for playing around. I've looked around online and found some good tutorials, but I'm stuck with a fairly basic problem: My sprite moves faster than the terrain and "glides" over it. Also, he slowly outpaces the terrain scrolling and moves to the edge of the view.
I've got an animated sprite that I want to move around the world. I do so by changing his absolute coordinates(setting X and Y position in an 'update()' function and applying a scalar multiple to speed up or slow down the rate at which he's moving around.
this.setXPos(currX + (speed * dx2));
this.setYPos(currY + (speed * dy2));
Underneath that sprite, I'm drawing "terrain" from a bitmap. I want to continue to move that sprite around via the coordinate accessors above, but also move my view and scroll the terrain so that the sprite stays in the same relative screen location but moves through the "world". I have an imperfect implementation where player is my sprite and field is my terrain:
#Override
protected void onDraw(Canvas canvas)
{
player.updateLocation(GameValues.speedScale);
field.update(new Point(player.getXPos(), player.getYPos()));
field.draw(canvas);
player.draw(canvas);
//more stuff here...
}
And field.update() looks like(Warning: Hard-coded scariness):
public void update(Point pt)
{
sourceRect.left = pt.x - 240;
sourceRect.right = pt.x + 240;
sourceRect.top = pt.y - 400;
sourceRect.bottom = pt.y + 400;
}
The thinking there was that I would eventually just get screen dimensions and make it less 'hack-y', but get something together quickly. This could easily be where my issue is coming from. Of immediate interest is field.draw():
#Override
public void draw(Canvas canvas)
{
try
{
canvas.drawBitmap(fieldSheet, sourceRect, destRect, null);
}
catch(Exception e)
{
//Handle Exception...
}
}
You'll notice I'm using the overload of drawBitmap() that accepts a source and destination Rect and that the field.update() method moves that sourceRect to match the movement of the sprite. How can I keep the "camera (so to speak)" centered on the sprite and scroll the terrain appropriately? I had thought that moving just the sourceRect and maintaining a constant destRect would do so, but now I'm thinking I have to move the destRect around the "world" with the sprite, while maintaining the same dimensions.
Is there a better (read functional) way of making this work? I'm coming here somewhat shamefully, since I think this should be somewhat easier than it seems to be for me. Any and all help or suggestions are appreciated. Thanks!
*Edit:*Does it make sense to also move the destination Rect at the same speed as the sprite? I think I conflated the source and destination Rect's and left the destRect immobile. I'll update with the results after I get a chance to try it (maybe during lunch).
*Edit_2:*Changing the destination rectangle didn't get me there, but using the View.scrollBy(x, y) method gets me close to totally satisfied. The remaining question to be satisfied is how to "clip" the View scrolling to a rectangle that represents the "field". I believe that the View.getLeft() and View.getTop() functions, offset by the screen width and height, can be used to specify a Rect that can be virtually moved around within the constraints of the "world" and block further deltas from being argued to the View.scrollBy() method. The reason I look toward this approach is because the View doesn't seem to be positioned in absolute space, and a View.getLeft() call, even after a View.scrollBy(x, y) where x > 0, returns a 0.

Dynamically create / draw images to put in android view

I'm not sure I'm doing this the "right" way, so I'm open to other options as well. Here's what I'm trying to accomplish:
I want a view which contains a graph. The graph should be dynamically created by the app itself. The graph should be zoom-able, and will probably start out larger than the screen (800x600 or so)
I'm planning on starting out simple, just a scatter plot. Eventually, I want a scatter plot with a fit line and error bars with axis that stay on the screen while the graph is zoomed ... so that probably means three images overlaid with zoom functions tied together.
I've already built a view that can take a drawable, can use focused pinch-zoom and drag, can auto-scale images, can switch images dynamically, and takes images larger than the screen. Tying the images together shouldn't be an issue.
I can't, however, figure out how to dynamically draw simple images.
For instance: Do I get a BitMap object and draw on it pixel by pixel? I wanted to work with some of the ShapeDrawables, but it seems they can only draw a shape onto a canvas ... how then do I get a bitmap of all those shapes into my view? Or alternately, do I have to dynamically redraw /all/ of the image I want to portray in the "onDraw" routine of my view every time it moves or zooms?
I think the "perfect" solution would be to use the ShapeDrawable (or something like it to draw lines and label them) to draw the axis with the onDraw method of the view ... keep them current and at the right level ... then overlay a pre-produced image of the data points / fit curve / etc that can be zoomed and moved. That should be possible with white set to an alpha on the graph image.
PS: The graph image shouldn't actually /change/ while on the view. It's just zooming and being dragged. The axis will probably actually change with movement. So pre-producing the graph before (or immediately upon) entering the view would be optimal. But I've also noticed that scaling works really well with vector images ... which also sounds appropriate (rather than a bitmap?).
So I'm looking for some general guidance. Tried reading up on the BitMap, ShapeDrawable, Drawable, etc classes and just can't seem to find the right fit. That makes me think I'm barking up the wrong tree and someone with some more experience can point me in the right direction. Hopefully I didn't waste my time building the zoom-able view I put together yesterday :).
First off, it is never a waste of time writing code if you learned something from it. :-)
There is unfortunately still no support for drawing vector images in Android. So bitmap is what you get.
I think the bit you are missing is that you can create a Canvas any time you want to draw on a bitmap. You don't have to wait for onDraw to give you one.
So at some point (from onCreate, when data changes etc), create your own Bitmap of whatever size you want.
Here is some psuedo code (not tested)
Bitmap mGraph;
void init() {
// look at Bitmap.Config to determine config type
mGraph = new Bitmap(width, height, config);
Canvas c = new Canvas(mybits);
// use Canvas draw routines to draw your graph
}
// Then in onDraw you can draw to the on screen Canvas from your bitmap.
protected void onDraw(Canvas canvas) {
Rect dstRect = new Rect(0,0,viewWidth, viewHeight);
Rect sourceRect = new Rect();
// do something creative here to pick the source rect from your graph bitmap
// based on zoom and pan
sourceRect.set(10,10,100,100);
// draw to the screen
canvas.drawBitmap(mGraph, sourceRect, dstRect, graphPaint);
}
Hope that helps a bit.

Categories

Resources