If you always have to draw the same rectangle, it is faster to do it with a static bitmap or with canvas.drawRect()?
For this example, the are four layered rectangles. So a boarder with a fill color, and then a boarder between a middle color and the fill color.
So four paint.setColor() commands and four canvas.drawRect commands or one canvas.drawBitmap().
I strongly recommend drawRect().
Bitmaps take up a huge chunk of memory, and can lead to Out Of Memory Exceptions if not used correctly.
Written by android:
Bitmaps take up a lot of memory, especially for rich images like photographs. For example, the camera on the Galaxy Nexus takes photos up to 2592x1936 pixels (5 megapixels). If the bitmap configuration used is ARGB_8888 (the default from the Android 2.3 onward) then loading this image into memory takes about 19MB of memory (2592*1936*4 bytes), immediately exhausting the per-app limit on some devices.
To prevent headache, and unexpected crashes. Use drawRect();
If you are doing these 4 draws on a regular basis for different objects, consider writing a method that does all 4 for you. So you are not causing massive repetition.
For example:
public void DrawMyRect(Canvas canvas, int x, int y)
{
canvas.drawRect(x, y ,x + 15, y + 40, paint);
// Draw its line etc etc..
}
Alternatively, if you do go for drawing bitmap, as it does have advantages:
See this epic Link by Android, on how to properly use Bitmaps
The performance difference is probably negligible. The bitmap will use more memory, the canvas draw calls will use slightly more CPU. You can probably use a ShapeDrawable if you want to reduce the calls without the overhead of a bitmap.
Related
Setup:
I have implemented a native (read JNI) mechanism to copy pixels from a Bitmap object, to native memory. This is done by malloc() uint23_t array in native memory and later using memcpy() to copy pixels to/from Bitmap's native pointer. This works well and have been tested. Pixels are successfully saved in native memory from a Bitmap object, and copied back to a Bitmap object, and visible on screen. Its pretty fast in copying, up to order of several milliseconds for fairly large bitmaps. But extremely slow in rendering it.
Intention:
The above was done to break free of heap limit on default android Bitmaps (refer to https://stackoverflow.com/a/1949205/1531054). There would be only 1 Java Bitmap object acting as buffer between native memory and target canvas.
Save a shape:
clear Buffer Bitmap.
Draw shape on Bitmap.
Copy pixels to native memory, and save the memory pointer.
Clear Buffer Bitmap.
So, any number of shapes can be saved to native memory, without running into heap size limits. This works.
Later when need to draw a shape (say in onDraw()):
clear Buffer Bitmap.
Copy pixels from native memory, to Buffer Bitmap, using the saved memory pointer.
Draw Buffer Bitmap on canvas.
Clear Buffer Bitmap.
Repeat again for next shape.
Problem When quickly drawing many shapes from memory, The Buffer Bitmap sorts of lags. Basically we're doing
clear bitmap -> load pixels from memory onto it -> draw it on view canvas
in Quick succession inside onDraw(), only the latest shape's pixels are drawn onto canvas. It appears as if:
The internal canvas.drawBitmap() is asynchronous and copies pixels off the bitmap later sometimes.
Android's Bitmaps have some hidden caching mechanism.
Has anyone run into such trouble before ? Or has some insight regarding this ?
I know one can get native skia lib's canvas instance in JNI and draw on it, but this is a non standard way.
In recent Android versions (3.0 and on, which is the majority of devices), pixels use regular Java memory heap. With the introduction of hardware acceleration, bitmaps are drawn asynchronously, and there is a caching system that manages bitmaps loaded as textures to the GPU. Therefore the hack you are trying to do will probably degrade performance on new devices. If you need more memory, try to use largeHeap="true" in your manifest.
On relatively new Androids (from 3.0 if I recall correctly) with hardware acceleration canvas.drawBitmap method does not actually draws anything (as well as dispatchDraw, draw, and onDraw). Instead, it creates record in display list which:
Might be cached for an indefinite amount of time.
Might (and will be) drawn in the future, not right away. It is not exactly asynchronous as for now, it just executed later in the same thread.
Those two points, I think are the answer to your question.
Alternatively, you can disable hardware acceleration for your view/window and see if your approach is working.
For further reading:
http://android-developers.blogspot.de/2011/03/android-30-hardware-acceleration.html
http://developer.android.com/guide/topics/graphics/hardware-accel.html#model
My question is about drawing performance. Let's say I have a Bitmap for image width=2400px and height=800px.
My Canvas is only 800px wide and 800px high. View containing the Canvas is a child of HorizontalScrollView so user can scroll to see whole image.
I load the Bitmap once and draw it every frame in onDraw method. Does the "offscreen" drawing cause performance hiccups in this scenario? If so, how to get it smoother?
Thanks.
Certainly the Bitmap size is a problem. In typical Android implementation, the texture with dimension over 4000px cannot be rendered. In some Android devices, this limit is 2000px.
Since your objective is to allow user to scroll to see the whole image, if I were you, I won't use HorizontalScrollView. Instead, you should implement a subclass of View and override the onDraw(Canvas canvas) method. You can then detect the touches and modify a Matrix. Such matrix will be used in calling Canvas.drawBitmap(Bitmap, Matrix,Paint)
Specifically for the oversize image problem, upon receiving the Bitmap object, you can slice it into 6 pieces. (2400 x 800 -> 800 x 800 x 6). Then you should control the viewport location and deduce the visible part of the image. In best case, you only need to draw 1 800x800 Bitmap. In worst case, you need to draw 4 800x800 Bitmap.
I'm working on a camera app, i'm showing the preview image luminosity histogram in a small rect (128x128 pix) overlying the live preview.
Sometimes an ANR happened, so i started using traceview to optimize my code (i'm doing some image manipulation on the fly, but it's very quick NEON asm & native code, no problem with it).
Using traceview i discovered that Canvas.drawLine() method is terribly slow. I have to update histogram 30 times per second in customView.onDraw(), drawing just 128 lines every frame. Incredibly, drawing 128 lines takes >8% cpu time (!!), when the entire native code to manipulate-convert the whole frame (720x480 yuv to ARGB_8888) takes <18%
I tried to draw the histogram on a new bitmap canvas then drawBitmap() it to the view's canvas but drawLine()s still take a lot of CPU.
I'm looking for an idea to avoid drawLine()...
I juts have to draw a small histogram from a int[128] normalized to 128
Here's my customView.onDraw (more or less...)
#Override
protected void onDraw(Canvas canvas) {
int size = 128;
int y = pos_y + size;
int x;
for(int i=0;i<size;i++) {
if(histogram_data[i]>1) {
x = pos_x+i;
// this is the slow call!!
canvas.drawLine(x, y, x, y-histogram_data[i], paint_histogram);
}
}
}
You could try to use Path instead of Lines.
http://developer.android.com/reference/android/graphics/Path.html
If you read HERE they say the following:
Animating large Paths
When Canvas.drawPath() is called on the hardware accelerated Canvas
passed to Views, Android draws these paths first on CPU, and uploads
them to the GPU. If you have large paths, avoid editing them from
frame to frame, so they can be cached and drawn efficiently.
drawPoints(), drawLines(), and drawRect/Circle/Oval/RoundRect() are
more efficient – it's better to use them even if you end up using more
draw calls.
So you can try calling drawLines() once for drawing multiple lines at the same time instead of calling drawLine() for each line separately.
For me personally I had to draw 500+ lines, and I tried all available method for drawing line in canvas: drawPath(), drawLine() and drawLines(), but with all of them I felt enormous lagging on my phone, so the only available alternative was to use OpenGL ES. Here is a example I wrote using OpenGL for drawing large amount of lines, I tested it on my phone and it is smooth as butter😄. You can check the source code it is written in Kotlin, it also support finger gestures, that way you can Scale, Translate and Rotate the whole scene with your fingers. And I have included all basic shapes Rectangle, Triangle, Circle, Line, Polygon, and you can set shape coordinates as values between [-1,1], or with the familiar way using pixel values. If you want to learn more about OpenGL you can check this post by the android team.
Here is the result of the OpenGL example I wrote:
maybe you can add new layer on top and draw these 128 lines only once while leaving other pixels transparent.
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.