Mark Renouf
Mark Renouf

Reputation: 31020

Android: Efficiently scrolling the contents of a Canvas?

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) ?

Upvotes: 5

Views: 1939

Answers (4)

AlfredoVR
AlfredoVR

Reputation: 4307

Since i can take as granted that you are storing the graph data in memory, redrawing it shouldn't be a problem. It's not intensive at all to redraw a set of points every frame. Shifting memory would be intensive, it's moving everything versus just painting only what you need. Worst case scenario, since it's a function of time, only one value per column of the display, aprox 800 pixels/values in landscape that the system has to draw. It's trivial. Have you profiled this? EDIT: Remember, it's not that the system has to draw each point, it only draws on memory, then makes the use of it's primitives. Don't think like it iterates drawing the point, dumping to the video memory, then again.

Upvotes: 0

Will
Will

Reputation: 75683

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.)

Upvotes: 1

Tal Pressman
Tal Pressman

Reputation: 7317

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.

Upvotes: 1

themightyjon
themightyjon

Reputation: 1436

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?

Upvotes: 0

Related Questions