I have a drawing application in Android that allows the user to draw with their finger, and then stores the resulting shapes as Android Paths. To allow the user to delete individual Paths they have drawn, I have implemented this solution that uses a bounding Rect for each Path, and then uses an inner multi-dimensional binary array to represent the pixels inside the bounding Rect. I then populate the array by taking the Path's control points and track along it using the mathematical equation for a quadratic bezier curve, setting each element in the array that would have a pixel underneath it to 1.
Using the above setup, when in erasing mode, I first check for collision between the users finger and the bounding Rect, and if that collides, I then check to see if the pixel being touched by the user is set to a 1 in the array.
Now, when a user loads a note, I load all of the shapes into an ArrayList of 'stroke' objects so that I can then easily display them and can loop through them checking for collision when in erase mode. I also store the Rect and binary array with the strokes in the custom object. Everything is working as expected, but the memory footprint of storing all of this data, specifically the binary array for each Path's bounding Rect, is getting expensive, and when a user has a large number of strokes I am getting a java.lang.OutOfMemoryError on the portion of my code that is creating the array for each stroke.
Any suggestions on a better way to accomplish this? Essentially, I am trying to determine collision between two Android Paths (the drawing Path, and then a Path that the user creates when in erase mode), and while the above works in theory, in practice it is not feasible.
Thanks,
Paul
What is the actual representation of the "binary array"? I think if you tweak the representation to reflect the actual data you need to store (for example RLE encode the bits: at this y starting at this x and for z pixels) you will be able to store what you need to without excessive size.
Storing an actual array of bytes with one byte per pixel, or per 8 pixels (if that is what you are doing) isn't necessary for this use.
Another alternative is not to store a bitmap at all, just the control points and bounding boxes. If a touch intersects a bounding box, you calculate the bitmap on the fly from the control points.
Related
The most common way to draw with your finger on screen is to use android's built in canvas, the usual steps are:
1) Create a custom view (drawing view).
2) Set a bitmap to the canvas.
3) initialize a path (set paint, ...).
4) implement onTouchEvent (DOWN/UP/MOVE) to get position points (X && Y).
5) call onDraw() to draw each path according to the X && Y.
so my data here is X && Y (the finger position on screen).
Problem
Some note apps, use a special technique called (drawing with vectors), at first I didn't get the idea (because all I know is canvas and the old way of finger drawing).
After research
I researched a bit and found out that vectors are graphics that scale without losing quality.
I found out a similar post to my problem this one.
If you opened the link and read the answer you will see that #Trevor stated a very good answer on that. He said that you must get the path data using your finger and store them as vector data in memory.
Store them as vector data in memory??
Okay what is that supposed to mean, where should I store them and what is the format knowing that I only can use the finger to get two float positions X && Y...so how I save them as vector? and where should I save them?
If I was able to save them
How to retrieve them and draw them back using canvas onDraw() method?
Do you think this is the meaning of drawing with vectors? If yes, What is the purpose?
I am looking at a way to enhance drawing technique on canvas and manipulate the drawings to make them feel more realistic.
Thanks for your help.
Scalable Vector Graphics (.svg) is a simple file format based in HTML. You would probaly generate a .svg element on the fly from user inputs, which you can then draw to the screen again (with appropriate libraries) and also can save / reload into your application very easily.
Here is a tutorial on svg paths, which sounds like what you would want; Bézier curves make for a good interpolation between your data points.
I was working on a vector based mobile app. First I've started using polygons to represent Curves. However, I was reaching quickly the Polygon Limit on mobile phones. To overcome this limit, I've started using a Texture and was coloring the pixels. Even though this was a pretty easy solution, I was limited by the max resolution of textures and operations.
The only promising thing I've found was OpenVG, but it seems like it is not very popular.
So how are vector drawing apps on mobile phones created? I was stunned by Adobe Illustrator mobile, which seems to be able to draw limitless curves/lines in vector graphics.
One possible method to allow vector based free form curve drawing is to use Bézier curves. Bezier curves are constructed by interpolating values defined by a start point, an end point and any number of control points. This results in the ability to construct a free form curve from a set of as little as 3 cartesian points.
A benefit of this is that by only storing tthe point data representing the curve, you can then render the same curve at any scale without having to store the curve in a texture. Therefore you do not need to store hundreds of intermediate points to form small line segments to represent the same curve.
Here are a number of sample code snippets that use the Path object in Android to construct free form vector curves.
If you have a very large number of curves to render to the canvas, you only need to store the point data defining the bezier curve. It is important that you only create the Path object once, and use the reset method to redefine the points each time you would like to draw a new curve. Sample code to achieve this can be found here.
I have a a drawing program where a user can trace with their finger, and in a manner similar to the FingerPaint program, a series of Path's are drawn to represent the lines.
Now, I am doing some collision detection to allow the user to enter an 'erase' mode and delete selected lines, and am trying to determine how to track the individual pixels of the Path. Essentially, I am tracking the RectF that encompasses the Path, and if the RectF is intersected when in erase mode, I'd like to then do pixel-by-pixel intersection tests. So, I need to create some structure for storing the pixels, likely a two dimensional array where each element will be a 1 or 0, based on whether or not the underlying pixel is occupied by the drawn Path.
It is this last part that I am struggling with. While the user is drawing the line, I am feeding the passed X/Y values in as control points for a quadratic bezier curve via Path.quadTo(). The problem is that while Path uses these points to represent a continuous line, I am only being fed discontinous X/Y points from the touch device. Essentially, I need a way to duplicate what the Path object itself is doing, and take the passed X/Y points and interpolate that into a continous curve, but as a set of X/Y coordinates rather than a Path object...
Any pointers to get started on this?
Thanks
EDIT/MORE:
Ok, so as I mentioned, each Path is created (roughly) using the method found in FingerPaint, which means that it is a series of segments, where each segment is a quadratic bezier curve. Given that I know P0, P1 and P2 when I add these curved segments to the larger Path, I can determine the X/Y coordinates along the curve with:
So, my only problem now is determining a 'continous' set of adjacent X/Y coordinates, such that there are no gaps in this set that a user's finger might pass through without hitting one. This would mean determining each X/Y point at 1 pixel intervals. As the above formula would yield points at an infinite number of intervals, given the values of T ranging from 0 through 1, any idea how to programmatically determine the right values of T that will yield points at 1 pixel intervals?
I would do all collision detection using not curves or pixels, but just lines, it's much easier to find intersecting lines, i.e. the intersection of two sequential x/y coordinates of the user's swipe, and the lines of existing lines
I am writing a mobile app (Android) that will allow the user to 'write' to a canvas using a single-touch device with 1 pixel accuracy. The app will be running on a tablet device that will be approximately standard 8 1/2" x 11" size. My strategy is to store the 'text' as vector data, in that each stroke of the input device will essentially be a vector consisting of a start point, and end point, and some number of intermediate points that help to define the shape of the vector (generated by the touchscreen/OS on touch movement). This should allow me to keep track of the order that the strokes were put down (to support undo, etc) and be flexible enough to allow this text to be re-sized, etc like any other vector graphic.
However, doing some very rough back of the envelope calculations, with a highly accurate input device and a large screen such that you can emulate on a one for one basis the standard paper notepad, that means you will have ~1,700 strokes per full page of text. Figuring, worst-case, that each stroke could be composed of up to ~20-30 individual points (a point for every pixel or so of the stroke), that means ~50,000 data points per page... WAY too big for SQLite/Android to handle with any expectation of reliability when a page is being reloaded and the vector strokes are being recreated (I have to imagine that pulling 50,000+ results from the SQLite db will exceed the CursorWindow limit of 1Mb)
I know I could break up the data retrieval into multiple queries, or could modify the stroke data such that I only add an intermediate point to help define the stroke vector shape if it is more than X pixels from a start, finish or other intermediate pixel, but I am wondering if I need to rethink this strategy from the ground up...
Any suggestions on how to tackle this problem in a more efficient way?
Thanks!
Paul
Is there any reason of using vector data in the first place? Without knowing your other requirements, it seems to me that you just need to store the data in raster / bitmap and compress it with regular compression methods such as PNG / zip / djvu (or if performance suffers, simple things like run-length-encoding / RLE).
EDIT: sorry I didn't read the question clearly. However if you only need things like "undo" and "resize", you can take a snapshot of the bitmap for every stroke (of course you only need to take a snapshot of the regions that change).
Also it might be possible to take a hybrid approach where you display a snapshot bitmap first while waiting for the (real) vector images to load.
Furthermore, I am not familiar about the android cursor limit, but SQL queries can always be rewritten to split the result in pieces (via LIMIT... OFFSET).
Solution I am using for now, although I would be open to any further suggestion!
Create a canvas View that can both convert SVG paths to Android paths, and can intercept motion events, converting them to android Paths while also storing them as SVG paths.
Display the Android Paths to the screen in onDraw()
Write the SVG Paths out to an .svg file
Paul
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) ?
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.
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.)
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?
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.