Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: Efficiently scrolling the contents of a Canvas?

Tags:

android

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

like image 233
Mark Renouf Avatar asked Nov 06 '22 21:11

Mark Renouf


2 Answers

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.

like image 143
Tal Pressman Avatar answered Nov 15 '22 09:11

Tal Pressman


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

like image 32
Will Avatar answered Nov 15 '22 09:11

Will