I got stuck with canvas.drawLines().
My application displays track om map view, but the resulting lines are awful.
Did anyone see such behavior? I.e, lines doesn't have same width, are not even "square", and are rhombus, and sometimes even disappear!
I've tested with for(){drawLine()} instead, and drawPath instead - same issue.
Screenshot took on 10" tablet 1280x800, Fujitsu. Lenovo tablet shows the same.
http://jpegshare.net/c8/1d/c81daa6cff15e3d14cb1268679bb1af1.png.html
borderPaint = new Paint();
borderPaint.setAntiAlias(true);
borderPaint.setStrokeWidth(5);
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setColor(Color.WHITE);
//borderPaint.setAlpha(190);
fillPaint = new Paint();
fillPaint.setAntiAlias(true);
fillPaint.setStyle(Paint.Style.STROKE);
fillPaint.setStrokeWidth(3);
fillPaint.setColor(Color.RED);
....
canvas.drawLines(linepoints, 0, linepoints_count, borderPaint);
canvas.drawLines(linepoints, 0, linepoints_count, fillPaint);
where linepoints is array of track lines to draw. Each line is set of 4 float numbers.
Well, I figured out my problem. Hope this can help someone.
Before drawing lines, I do
canvas.translate(X, Y);
where X and Y values are HUGE. Like 10E+6..10E+7 or something.
When I draw lines, their xy values are huge too, but negative, hence in screen coordinates this xy+XY give something inside a screen - but this fact matters and produces issue described in my own first post.
Why this is happening? I found a clue. When drawLine algorithm tries to draw a line, it messes up with line coordinates (division, multiplication) and since machine arithmetic is limited in precision, algorithm gets wrong numbers.
How this was solved?
I dicarded canvas.translate(), and calulate screen coords on the fly, just before drawing.
Now, like that
screen_line_coords_x = line_coord_x - X; //X is offset applied in translate.
screen_line_coords_y = line_coord_y - Y; //Y is offset applied in translate.
canvas.drawLine(screen_line_coords_x, screen_line_coords_y, ...);
... or drawLines() or drawPath() - all give good results.
Hope this might help.
I´m facing same thing.
However, why are you using so huge offset, since the screen is about 1280x800 resolution ?
I believe that system coordinates gets too far from the real display coordinates causing wrong calculations at translation / rotation / scaling operations.
Try to revise your calculations in order to work inside of the display coordinates.
Related
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.
When calling Matrix.postScale( sx, sy, px, py ); the matrix gets scaled and also translated (depending on the given point x, y). That predestines this method to be used for zooming into images because I can easily focus one specific point.
The android doc describes the method like this:
Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
At a first glance this seems ridiculous because M is supposed to be a 3x3-Matrix. Digging around I've found out that android uses a 4x4-Matrix for its computations (while only providing 3x3 on its API). Since this code is written in C I'm having a hard time trying to understand what is actually happening.
What I actually want to know: How can I apply this kind of scaling (with a focused point) to the 3x3 Matrix that I can access within my Java-code?
So i'm making a custom View that displays some graphs (plots), and in some ocasions i want to rotate the canvas 90 degrees.
All i can guess is that there's a bug in my device (HTC Desire, with android 2.3), i haven't test it yet in another device.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
canvas.rotate((float) 90.0);
canvas.drawPoint(10, 10, pointPaint); /* Sorry about missing that these
* coordinates will fall out after being rotated... it's just an
* example. put 10, -10 and it should work, but it doesn't */
canvas.restore();
}
This won't draw the point, but if we change that 90.0 for a 90.014 (i found that this was the minimum, 90.013 doesn't work either) then the point will show up.
So... is it something i'm doing wrong, or it's just Android's bug? I googled a lot, but couldn't find nothing....
-Victor -
edit: Additional info: Only points doesn't work. Rects, lines and circles work perfect.
edit: screenshot of my app, you can see how the red line has dots on the 90.014 degree version, and the 90.0 doesn't.:
(As you can see, the 90.014 it looks like a little bit flipped (you can see how the Y axis is 'broken')
Setting the Paint of the dots to use a ROUND stroke cap resolves the problem, even though it doesn't yet explain the strange behavior you are experiencing. e.g.:
paint.setStrokeCap(Paint.Cap.ROUND)
The canvas rotates through its origin, so if you rotate your canvas by 90 degrees you are effectively rotating your view out of your screen. You should translate the canvas to the axis you want to rotate on first:
canvas.save();
canvas.translate(-axispointx,-axispointy);
canvas.rotate((float) 90.0);
canvas.translate(axispointx,axispointy);
canvas.drawPoint(10, 10, pointPaint);
canvas.restore();
I have been trying to work on a simple code for two days now. I have tried all alterations but none seems to be working.
I am trying to draw vertical rectangles with different colors.
In the first one, I am using only one Rect variable and moving the coordinates. Here is the relevant part of the code.
Rect myRect1=new Rect();
Random colorMe=new Random();
for(int j=0;j<5;j++){
myRect1.set(myCanvas.getWidth()/5*j, 0, myCanvas.getWidth()/5*j, myCanvas.getHeight());
paint.setColor(Color.rgb(colorMe.nextInt(255), colorMe.nextInt(255), colorMe.nextInt(255)));
myCanvas.drawRect(myRect1, paint);
}
Other alteration which I tried was through an array.
Here is the code.
Rect[] myRect=new Rect[5];
Random colorMe=new Random();
for(int j=0;j<5;j++){
myRect[j].set(myCanvas.getWidth()/5*j, 0, myCanvas.getWidth()/5*j, myCanvas.getHeight());
paint.setColor(Color.rgb(colorMe.nextInt(255), colorMe.nextInt(255), colorMe.nextInt(255)));
myCanvas.drawRect(myRect[j], paint);
}
Can somebody please help me what is the problem actually there?
The first thing that seems obviously wrong (there might be more):
The minimum x-coordinate of your rectangle is myCanvas.getWidth()/5*j
The maximum x-coordinate of your rectangle is myCanvas.getWidth()/5*j
They are both the same value, so your rectangle is degenerate.
Offtopic, but very relevant: whenever you run into a problem like this, you need to break it down into smaller parts until you get it to do something. This will help you understand what's wrong:
Extract all subexpressions (like the color, and the generated x/y values) into local variables so you can easily inspect them in the debugger
Replace the random color by a predefined color (COLOR.YELLOW), to rule out the random element
Replace the caclulated rectangle by a fixed rectangle (say (10,10) - (20,20)) to rule out the coordinate calculations.
Replace the loop, to rule out the loop.
Your rect has a width of 0px. The parameters of the set() function are, in order, left/top/right/bottom. You use the same value for left and right, so the width is (right-left)=0.
Let's see your coordinates, first:
myRect1.set(myCanvas.getWidth()/5*j, 0, myCanvas.getWidth()/5*j, myCanvas.getHeight());
Top left corner has the same x coordinate as the bottom right corner, so you are drawing a rectangle with 0 width.
Add this line
myRect[j] = new Rect()
before
myRect[j].set(myCanvas.getWidth()/5*j, 0, myCanvas.getWidth()/5*j, myCanvas.getHeight());
When I call drawCircle (ex. canvas.drawCircle(x, y, r, mPaint);) and I use Paint Style STROKE to initialize the parameter mPaint, the result doesn't quite make a full 360 degrees (2*PI radian) circle in all cases. Sometimes you get a full circle (as I would expect) and sometimes only an arc.
Does someone have an idea what would cause this to happen ?
I don't know what cases work and which don't (yet). I've noticed the ones that don't work seem to be the larger circles I'm drawing (>100.0 radius). Could be size related. I am using floating point for x, y and r. I could try rounding to the nearest int when in the drawing code.
Are you doing anything else to the canvas before the drawCircle? This could happen if, for example you are scaling the canvas or transforming it before you draw the circle.