Im currently developing a program that uses a scrollable/zoomable image as the main user interface. It uses a canvas which is manipulated by a matrix to traverse a large area. Instead of loading a super large bitmap into memory, i wanted to employ a tile system to load 256 by 256 squares. My problem is that the app will lag when the images are being loaded into memory. I use a simple outer and inner for loop to load the tiles if they are null, and if the user zooms out to a certain extent the tiles all disappear and a lower res version of the whole image is shown. Ive been steered into the direction of asynchronous image loading, which seems like it would prevent the lag while the image loads into memory but i have no idea how to start this, and was wondering if anyone had any advice on how to generally asynchronously load bitmaps into the canvas. Thanks!
I simply use a second thread to do ANYTHING that could take some time to make sure that the UI is always nice and responsive. You may even want to lower the priority of that thread if the UI contains animation (like scrolling) to get rid of stuttering.
You could use a second Thread object that sends a message via a Handler when it's done, or you can use AsyncTask.
Related
I want to make finger-draw app with vertical scroll and possibly unlimited space to draw. Scroll can be locked/unlocked.
I know 2 ways to do that:
Make a bitmap with given width and height, draw points/lines in onTouchEvent(MotionEvent) via Canvas(Bitmap) and draw that bitmap in onDraw(Canvas).
Save touch points to array and draw them directly in onDraw(Canvas).
But both ways have disadvantages:
OutOfMemoryException with big bitmap.
Very slow with numerous points while scrolling and OOM possible too.
I have an idea to use window and read/write regions of bitmap on demand. So there is a problem: how to write/append bitmap to another bitmap?
I cant find an appropriate way to append 200x200px bitmap to 8000x8000px bitmap for example. Is there a way to do that without native libs?
Or am I totally wrong and there is an easier method to do what I need to?
What about using a grid of small bitmaps, without appending them to a larger bitmap at all? When one of your bitmaps goes out of the screen you save it to the disk and destroy it or recycle it, a bit like how GridView recycles cells (it reuses the same 10 cells or so to display a potentially infinite grid of cells). Saving the whole drawing would just mean saving all bitmaps to the disk.
The main concern with this approach is the time it takes to save/load chunks from disk while the user scrolls through the view, which may cause the app to freeze for a short time, but there are ways to make it smooth (for example by preloading bitmaps in a background thread).
Storing touch points in a list is also completely possible, there are many ways to speed up the drawing part, for example by caching what's on the screen and drawing only what's missing when the user scrolls. Which one is best depends on what you will draw on your view.
I am creating an Android App that produces random images based on complex mathematical expressions. The color of a pixel depends on its location and the expression chosen. (I have seen many iPhone apps that produce "random art" but relatively few Android apps.)
It takes 5 to 15 seconds for the image to be drawn on a Nexus S dev phone.
To keep the UI thread responsive this seems like the purpose of the SurfaceView class. Almost all the examples of SurfaceView deal with animation, not a single complex image. Once the image is done being drawn / rendered I won't change it until the user
So, is SurfaceView the right component to use? If so, can I get a callback from the SurfaceView or its internal thread when it is done drawing when it is done rendering the image? The callback is so I know it is okay to switch the low resolution and blocky version of the image art with the high resolution one?
Is there an alternative to SurfaceView that is better for complex rendering of single images. (Not animation.)
Cheers!
If all you want to do is render a single complex image on another thread to keep the UI responsive, then after it's done rendering actually draw it, you might consider just doing this in the standard keep-work-off-the-UI-thread way by using something like an AsyncTask. It's not like you're doing compositing or anything that really is GPU-specific (unless as others have suggested you can offload the actual rendering calculations to the GPU).
I would at least experiment with simply building an array representing your pixels in an AsyncTask then when you're done, create a bitmap with it using setPixels and set the source of an ImageView to that bitmap.
If on the other hand you want your image to appear pixel by pixel, then maybe SurfaceView might be a reasonable choice, in which case it'll basically be an animation so you can follow other tutorials. There's some other setup, but the main thing would be to override onDraw and then you'll probably have to use Canvas.drawPoint to draw each pixel.
So I'm working on developing a game for a school project and so far its gone well, but I have been trying to use a bitmap image instead of just using drawColor for the background, but doing so has make the game sluggish, not respond to to Touch Events, and ultimately crash after a while. Here is my code so far for making that background that keeps crashing, I am using the getColor before the drawBitmap as to blank out the previous background, because without that the moving character threads were leaving a "trail" and not being erased after every movement.
canvas.drawColor(Color.BLACK);
canvas.drawBitmap
(BitmapFactory.decodeResource(getResources(),R.drawable.park),0,0,null);
So any more effective method to make a static background image for the thread to move over would be appreciated!
What you are doing is that you are loading your bitmap everytime you're rendering, which is why it runs slow and crashes after a while because of memory constraints. You should load your image once when you initialize everything:
// run once when you start the game
Bitmap background = BitmapFactory.decodeResource(getResources(),R.drawable.park);
In your render code:
canvas.drawBitmap(background);
And don't forget too free the image when you're done with it:
background.recycle();
The reason your application is slowing down is you are keeping too many references to the same bitmap and this is consuming the amount of memory that you have available, you need to release images as you dont need them. There a bunch of videos by Romain Guy on graphics and game graphics. Look on youtube and you'll learn how to handle this problem.
Been working on a custom tile overlay implementation for an android app.
I'm having trouble with threading the image downloads in the background.
Quick overview:
I have a class (MapOverlay) that extends Overlay. The draw method basically calculates the images (bitmaps) needed and the x,y position to draw them on the screen. I then have an AsyncTask that loops through each tiles and download the images if it doesn't exist on the storage card (local caching) then once the AsyncTask has completed I then loop through the images and draw them to the canvas object that is passed into the draw method of the MapOverlay. But the bitmaps are never getting drawn.
I have done some rudimentary debugging by logging the width and the height of the canvas element at the time the AsyncTask completes but it is returning 0 for them so I'm guessing that the canvas object is no longer available to the code running in AsyncTask's onPostExecute() after the draw method has completed
Without the threading I had this working pretty well, except for the "freezing" while panning while the image downloading blocks the UI thread. Now I'm totally suck trying to get threading to work
Have you tried calling postInvalidate() when the AsyncTask done downloading? That way the call in MapView.draw() will get and draw the downloaded overlay (that is if your caching system works).
I'm tried to determine the "best" way to scroll a background comprised of tiled Bitmaps on an Android SurfaceView. I've actually been successful in doing so, but wanted to determine if there is a more efficient technique, or if my technique might not work on all Android phones.
Basically, I create a new, mutable Bitmap to be slightly larger than the dimensions of my SurfaceView. Specifically, my Bitmap accomodates an extra line of tiles on the top, bottom, left, and right. I create a canvas around my new bitmap, and draw my bitmap tiles to it. Then, I can scroll up to a tile in any direction simply by drawing a "Surfaceview-sized" subset of my background Bitmap to the SurfaceHolder's canvas.
My questions are:
Is there a better bit blit technique than drawing a background bitmap to the canvas of my SurfaceHolder?
What is the best course of action when I scroll to the edge of my background bitmap, and wish to shift the map one tile length?
As I see it, my options are to:
a. Redraw all the tiles in my background individually, shifted a tile length in one direction. (This strikes me as being inefficient, as it would entail many small Bitmap draws).
b. Simply make the background bitmap so large that it will encompass the entire scrolling world. (This could require an extremely large bitmap, yet it would only need to be created once.)
c. Copy the background bitmap, draw it onto itself but shifted a tile length in the direction we are scrolling, and draw the newly revealed row or column of tiles with a few individual bitmap draws. (Here I am making the assumption that one large bitmap draw is more efficient than multiple small ones covering the same expanse.)
Thank you for reading all this, and I would be most grateful for any advice.
I originally used a similar technique to you in my 'Box Fox' platformer game and RTS, but found it caused quite noticeable delays if you scroll enough that the bitmap needs to be redrawn.
My current method these games is similar to your Option C. I draw my tiled map layers onto a grid of big bitmaps (about 7x7) taking up an area larger than the screen. When the user scrolls onto the edge of this grid, I shift all the bitmaps in the grid over (moving the end bitmaps to the front), change the offset of grid, and then just redraw the new edge.
I'm not quite sure which is faster with software rendering (your Option C or my current method). I think my method maybe faster if you ever change to OpenGL rendering as you wouldn't have to upload as much texture data to the graphics card as the user scrolls.
I wouldn't recommend Option A because, as you suggest, the hundreds small bitmap draws for a tiled map kills performance, and it gets pretty bad with larger screens. Option B may not even be possible with many devices, as it's quite easy to get a 'bitmap size exceeds VM budget' error as the heap space limit is set quite low on many phones.
Also if you don't need transparency on your map/background try to use RGB_565 bitmaps, as it's quite a lot faster to draw in software, and uses up less memory.
By the way, I get capped at 60fps on both my phone and 10" tablet in my RTS with the method above, rendered in software, and can scroll across the map smoothly. So you can definitely get some decent speed out of the android software renderer. I have a 2D OpenGL wrapper built for my game but haven't yet needed to switch to it.
My solution in a mapping app relies on a 2 level cache, first tile objects are created with a bitmap and a position, these are either stored on disk or in a Vector (synching is important for me, multithreaded HTTP comms all over the place).
When I need to draw the background I detect the visible area and get a list of all the tiles I need (this is heavily optimised as it gets called so often) then either pull the tiles from memory or load from disk. I get very reasonable performance even on slightly older phones and nice smooth scrolling with no hiccups.
As a caveat, I allow tiles not to be ready and swap them with a loading image, I don't know if this would work for you, but if you have all the tiles loaded in the APK you should be fine.
I think one efficent way to do this would be to use canvas.translate.
On the first drawing the entire canvas would have to be filled with tiles. New android phones can do this easily and quickly.
When the backround is scrolled I would use canvas.translate(scrollX, scrollY), then I would draw individualy one by one tile to fill the gaps, BUT, I would use
canvas.drawBitmap(tileImage[i], fromRect, toRect, null) which would only draw the parts of the tiles that are needed to be shown, by setting fromRect and toRect to correspond to scrollX and scrollY.
So all would be done by mathematics and no new bitmaps would be created for the background - save some memory.
EDIT:
However there is a problem using canvas.translate with surfaceView, because it is double buffered and canvas.translate will translate only one buffer but not the second one at the same time, so this alternating of buffers would have to be taken into account when depending on surfaceView to preserve the drawn image.
I am using your original method to draw a perspective scrolling background. I came up with this idea entirely by accident a few days ago while messing around with an easy technique to do a perspective scrolling star field simulation. The app can be found here: Aurora2D.apk
Just tilt your device or shake it to make the background scroll (excuse the 2 bouncing sprites - they are there to help me with an efficient method to display trails). Please let me know if you find a better way to do it, since I have coded several different methods over the years and this one seems to be superior. Simply mail me if you want to compare code.