When loading OpenGL texture data on Android, to cope with the difference in coordinate systems between Android and OpenGL the typical pattern I see is to flip the Bitmap before uploading it:
Bitmap original = BitmapFactory.decodeStream(...);
Matrix flip = new Matrix();
flip.postScale(1f, -1f);
Bitmap toUpload = Bitmap.createBitmap(original, ..., flip, true);
original.recyle();
Unfortunately for a brief amount of time the memory required for these bitmaps is doubled because both the original and flipped version are resident. This is problematic for very large images such as texture atlases.
Is there a clever way to avoid this doubling? e.g., manipulating the original in place, or loading and transforming in a single step? Of course I can always manipulate the source data (i.e., pre-flip the images) or texture mapping (i.e., invert the V coordinates) but I'd prefer to tackle this issue during image load time as a way of separating concerns.
As an extreme example, working with mobile VR, the image and video source content is usually in some arbitrary format, depending on what tool was used to produce it.
VR content is so large that transforming the image pixels (like flipping) is not practical. It is far more efficient to just insert transforms into the GL pipeline.
Transforming the vertices and UV mappings of the 3D objects, as you say does not preserve a nice clean separation of concerns, but it is by far the optimal way to resolve image formatting problems. Yes, it can get ugly inserting customized transforms into the pipeline.
Of course if you have the option, the images can be pre-transformed in an editor, specifically for your app, to keep your pipeline nice and clean.
I have an app that allows the user to draw grids of circles, rectangles, arrows, and paint. All of these are drawn on bitmaps which are in turn drawn on my main canvas. I have it structured this way because the users can zoom/pan on the main canvas (via a main bitmap) as well as move/scale/rotate each drawn object. The problem is that all these bitmaps eat up all of my memory, so I have been trying to find a better solution.
LRUcache is out of the question because I need all of the bitmaps every time the onDraw method is called, which is every time something new is drawn.
I have seen the Bitmap.getPixels() function and the Canvas.drawPoints(); function. Is there a way to use these together so I can get rid of my bitmaps? What I envision is creating a bitmap, drawing the object on it, extracting the pixels from that bitmap, and then recycling the bitmap to free up that memory. Is that feasible?
I am trying to construct a SurfaceView by reading in an array and using case switches to build the canvas.
so the question is: can I construct a canvas looping Y, by tracking X. loading bitmaps using BitmapFactory() into the canvas and then using 1 .show() to render the canvas to the screen? or will I need to call the canvas render for each of these (or will that through away the screen every time I do that)?
Not sure what you're getting at, but for one thing avoid using BitmapFactory in onDraw. You don't want to be doing bitmap decoding at the same time as rendering. You should load your bitmaps ahead of time and keep them around in memory for faster drawing later on.
Ok, I am going to be drawing a lot of "insects" on the screen. My question is, if I am drawing a spider for example, would it be faster to load a bitmap, matrix it to the correct angle, and draw it on screen (again and again), or draw an ant using the canvas.drawLine, drawCircle, etc? For a direct comparison:
Bitmap: 500 bytes w/ transparency
Drawn: 8 drawLines, 2 drawCircles
I already have a lot going on, so performance here is very important.
Thanks in advance!
It is much faster to draw a bitmap. What really matters is how many pixels you are going to draw (i.e. the overdraw, which will impact the maximum fillrate.) Using bitmaps also allows you to create richer graphics without performance penalties.
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.