Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - is there a possibility to make infinite canvas?

Currently I am doing app allowing user to draw. Simple think, just extend Canvas class and most of the thing is done.

That was my initial thinking and idea. But as the canvas is rather small because this is only what user see on the screen there is not much possible space to draw. Going through documentation I found translate() method allowing me to move canvas. What I did find out is when I move it, there is some kind of blank space just as you would move piece of paper. I understand that this is totally normal, as I've said before - canvas is only "the screen".

My question is - is there a possibility to make something like infinite canvas so you can make a huge painting and move everything around?

Before this question I was thinking about two things how something like this can be done:

  1. Move all objects on canvas simultaneously - bad idea, because if you have a lot of them then the speed of moving is very bad.

  2. Do something similar as it is done in ListView when you move it (or better see on the screen) only views that are on the screen together with one before and one after are loaded to memory and rest is uploaded dynamically when needed. I think this is the best option to achieve this goal.

EDIT:

Question/answer given by Kai showed me that it is worth to edit my question to clarify some of the things.

Basic assumptions about what can be done by user:

  • User is given opportunity to draw only circles and rectangles with some (around 80%) having drawable (bitmap) on them on canvas.
  • I assume that on all screens there will be maximum 500-800 rectangles or circles.

First of all thinking about infinity I was thinking about quite big number of screens - at least 30 on zoom 1x in each side. I just need to give my users bigger freedom in what they are doing.

On this screen everything can be done as on normal - draw, scale (TouchListener, ScaleListener, DoubleTapListener). When talking about scaling, there is another thing that has to be concerned and connected with "infinity" of canvas. When user is zooming out then screens, or more precise objects on the invisible "neighbours" should appear with proper scaling as you would zoom out camera in real life.

The other thing that I've just realised is possibility of drawing at small zoom level - that is on two or three screens and then zooming in - I suppose it should cut and recalculate it as a smaller part.

I would like to support devices at least from API 10 and not only high-end.

The question about time is the most crucial. I want everything to be as smooth as possible, so user wouldn't know that new canvas is being created each time.

like image 946
sebap123 Avatar asked Nov 13 '22 00:11

sebap123


1 Answers

I think it really depends on a number of things:

  1. The complexity of this "infinite canvas": how "infinite" would it really be, what operations can be done on it, etc
  2. The devices that you want to support
  3. The amount of time/resource you wish to spend on it

If there are really not that many objects/commands to be drawn and you don't plan to support older/lower end phones, then you can get away with just draw everything. The gfx system would do the checking and only draws what would actually be shown, so you only waste some time to send commands pass JNI boundary to the gfx system and the associated rect check.

If you decided that you needs a more efficient method, you can store all the gfx objects' positions in 4 tree structures, so when you search the upper-left/upper-right/lower-left/lower-right "window" that the screen should show, it'll fast to find the gfx objects that intersects this window and then only draw those.

[Edit]

First of all thinking about infinity I was thinking about quite big number of screens - at least 30 on zoom 1x in each side. I just need to give my users bigger freedom in what they are doing.

If you just story the relative position of canvas objects, there's practically no limit on the size of your canvas, but may have to provide a button to take users to some point on canvas that they are familiar lest they got themselves lost.

When talking about scaling, there is another thing that has to be concerned and connected with "infinity" of canvas. When user is zooming out then screens, or more precise objects on the invisible "neighbours" should appear with proper scaling as you would zoom out camera in real life.

If you store canvas objects in a "virtual space", and using a "translation factor" to translate objects from virtual space to screen space then things like zoom-in/out would be quite trivial, something like

screenObj.left=obj.left*transFactor-offsetX;
screenObj.right=obj.right*transFactor-offsetX;
screenObj.top=obj.top*transFactor-offsetY;
screenObj.bottom=obj.bottom*transFactor-offsetY;
//draw screenObj

As an example here's a screenshot of my movie-booking app: Usage of virtual & real space

The lower window shows all the seats of a movie theater, and the upper window is a zoomed-in view of the same theater. They are implemented as two instances of the same SurfaceView class, besides user input handling, the only difference is that the upper one applies the above-mentioned "translation factor".

I assume that on all screens there will be maximum 500-800 rectangles or circles.

It is actually not too bad. Reading your edit, I think a potentially bigger issue would be if an user adds a large number of objects to the same portion of your canvas. Then it wouldn't matter if you only draw the objects that are actually shown and nothing else - you'd still get bad FPS since the GPU's fill-rate is saturated.

So there are actually two potential sources of issues:

  1. Too many draw commands (if drawing everything on canvas instead of just drawing visible ones)
  2. Too many large objects in the same part of the screen (eats up GPU fill-rate)

The two issues requires very different strategy (1st one using tree structures to sort objects, 2nd one using dynamically generated Bitmap cache). Since how users use your app are likely to different than how you envisioned it to be, I would strongly recommend implementing the functions without the above optimizations, try to get as many people as possible to do testing, and then apply optimizations to each of the bottlenecks you encounter until the satisfactory performance is achieved.

[Edit 2]

Actually with just 500~800 objects, you can just calculate the position of all the objects, and then check to see if they are visible on screen, you don't even really need to use some fancy data structures like a tree with its own overheads.

like image 58
Kai Avatar answered Nov 15 '22 11:11

Kai