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.
Related
I am developing an Android app on eclipse and have been using android.graphics.camera to 3d transform images. In a nutshell, my app takes a 2d image and gives it perspective. My problem is that I also want to rotate the image around the screen while it is being transformed.
final Matrix mMatrix = canvas.getMatrix();
canvas.save();
mCamera.save();
mCamera.rotateX(60);
mCamera.rotateY(0);
mCamera.rotateZ(0);
mCamera.getMatrix(mMatrix);
mMatrix.preTranslate(-this.gridWidth / 2, -this.gridHeight);
mMatrix.postTranslate(this.gridWidth / 2, this.gridHeight);
canvas.concat(mMatrix);
mCamera.restore();
//Draw and move image here
canvas.restore();
When the image gets to the bottom of the screen, where the camera is translated, the image becomes distorted. I see pieces of it on the screen but its like its being drawn backwards or sideways. I've also tried rotating the image using the rotateZ property but the same effect occurs. Once the image gets 'behind' the translation point, it just goes nuts.
I thought it might be an emulator bug so I loaded it on my Droid X and Acer Iconia and the effect remains the same.
I haven't seen anybody else have this problem so i figured someone here might have a clue as to what's going on.
Any Ideas?
This issue can be addressed with newer versions of Android that let you change the camera distance:
http://developer.android.com/reference/android/graphics/Camera.html#setLocation(float, float, float)
I am trying to animate several shapes(paths) by drawing them on the surface holders canvas.
At first I was drawing them as paths and everything was fine, the movement was smooth.
As I increased the number of objects(shapes) the performance decreased and I made some
tests to see if instead of drawing shapes drawing bitmaps is faster. And.. drawing
bitmaps seems to be considerable faster (less computation maybe) BUT the movement is
not smooth. It looks like the bitmaps always move from pixel to pixel instead of using anti alias to, I dont know, draw states as half pixel.
The signature of the method looks like :
canvas.drawBitmap(cloudBitmap, float left, float top, Paint p);
which suggests that I should be able to draw a bitmap at 0.5f pixels.
Any idea why ?
I think it might be due to the bitmap being drawn without filtering it for smoothness. Have you set the paint to smooth the bitmap? If not, that might be your solution.
Paint paint = new Paint();
paint.setFilterBitmap(true);
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);
}
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.
I'm writing my first app of any consequence, so I may be going about this the entire wrong way, but...
I have a resource image that is 1600x880. I'd like to fill the entire screen with a subset of that image to my canvas, such that an arbitrary x,y coordinate marks the top-left corner drawn at the top-left corner of the screen. For instance, if I was viewing this image on an N1 and I entered x=100 and y=50, I'd expect to see from 100,50 to 580,850 since it's display area is 480x800.
I think I need to use Canvas.drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint). But, no matter what I plug in to either Rect (even if it's a perfectly sane set of values that shouldn't butt up against any edges of the image), I end up with an unexpected area or a grossly stretched/smooshed output.
I've tried using various combinations of calculations involving display.getWidth() and getHeight(), canvas.getWidth() and getHeight(), and bitmap.getWidth() and getHeight() but nothing seems to be working.
I don't know what I'm doing wrong.