tags:

views:

768

answers:

3

I want to draw a graph that updates in real time (grows from the right). The most efficent way I can think of to do that would be to copy everything from x[0 .. width-2] left by 1 pixel, then draw the new value at x[width-1].

I have little experience with Android, but from what I can tell, Canvas doesn't operate on it's contents at all. Do I need to repaint the entire screen each time? This involves scaling and smoothing so I'm worried it will be slow.

Should I draw into a byte[][] then use this to paint to the screen (shifting the contents of my buffer left each time) ?

A: 

Just a thought, which you may have considered already, but I wouldn't shift the contents of the buffer - I'd just try using it like a circular buffer. Keep an index to the current column and once you've wrapped around to the left-most column again you can draw to the destination in two segments - what is on the right side of the current column and then what is to the left, including the most recently filled column. This way you'll not have to shift anything around, and each screen refresh is just two blits (bitmap copies) for the two segments. If that bit's too slow you could still always paint those into a second off-screen buffer before blitting the whole thing to the screen in one go. Surely one large blit to the screen is fairly quick, no?

themightyjon
themightyjon
A: 

If your graph is bounded, try rendering all of it once to an Image, and then blit the relevant parts from that Image to your Canvas. Try to avoid actually "moving" pixels in the buffer, as that might introduce dependencies between your reads and writes and could really kill the performance. It might actually be better to copy from 1 buffer to another and alternate which one gets blitted to the screen. Finally, if you end up having to manually work on pixels, make sure you run on the image in lines rather than columns and that you start from the beginning of the line to help with the caching.

Tal Pressman
+1  A: 

Regarding performance, without profiling we cannot say.

It may be that line drawing is hardware accelerated on your target phone, and you should draw the graph from scratch using line-drawing primitives each frame.

On the other hand, the straightforward pixel manipulation of an image buffer would be:

Create an image that is the right size and clear it to a "background_color". This image needs to have setpixel() functionality.

Have an array of values that record the y of each x time, so for any column you know where you last plotted your graph.

Treat this "chart_image" and "chart_array" as a circular buffer. For each time step:

Y = ...;
X = time_since_start % chart_width;
chart_image.setpixel(X,chart_array[X],background_color); // clear previous line
chart_array[X] = Y;
chart_image.setpixel(X,chart_array[X],foreground_color); // draw new plot

And now you need to blit it. You need to blit the image twice:

X = time_since_start % chart_width;
// the newest data is on the left of the chart_image but gets drawn on the right side of the output
blit(out_x+X,out_y, // destination coordinates
    chart_image,
    0,0, // top left of part of chart_image to blit
    X,chart_height); // bottom right of chart_image part
// the oldest data is on the right of the chart_image but gets drawn on the left side of the output
blit(out_x,out_y,
    chart_image,
    X,0,
    chart_width,chart_height);

Things get more tricky if you want to use lines rather than individual pixels, but a drawline() instead of a setpixel() can make that work with this approach too.

(Apologies for not knowing the Android APIs; but the approach is generic.)

Will