Drawing rotated Bitmaps on Canvas is slow: any other options? - android

I'm developing a live wallpaper app which will draw an analog clock. So far I reached the point where I draw a clock using three bitmaps: dial, hour hand, minute hand.
When I draw hours and minutes I do it like this (pseudo code):
// without this it looks weird after rotate
Paint smoothPaint = new Paint(FILTER_BITMAP_FLAG);
canvas.drawBitmap(dialBitmap, 0, 0, null);
canvas.rotate(ANGLE_FOR_HOUR_HAND, w/2, h/2);
canvas.drawBitmap(hourBitmap, 0, 0, smoothPaint);
canvas.rotate(ANGLE_FOR_MINUTE_HAND, w/2, h/2);
canvas.drawBitmap(minuteBitmap, 0, 0, smoothPaint);
This works okay except that I'm getting a really low frame rate: about 13 fps.
If I turn off bitmap filtering, it increases to about 24 fps, but still low for me.
I know that 2d drawing on Canvas is not accelerated in any way, but still want to ask are there any other options for me to speed up this code? Drawing to bitmap natively somehow? Something else maybe?
Using OpenGL is an option, but its not that simple since LiveWallpapers do not support GL drawing, so I need to use 3rd party hacks (which I know exist) for that which I would rather not do...

"Any other options?" you ask. You have two:
Don't use bitmaps. The second Cube SDK sample, for example, pops out a rotating 20-line dodecahedron at 25 fps.
As you've mentioned, you can sometimes use a "hack" for special-purpose graphics within live wallpaper. I suggest you at least take a look at the following three...you may find them useful one day: AndEngine (has a live wallpaper extension), LibGDX (allows you to prototype in a desktop environment; live wallpaper facilities still under development, methinks), and GLWallpaperService (designed specifically to bring GLSurfaceView to live wallpaper).
In your post you say that 24 fps is "still low" for you. It is my understanding that 24 fps is approximately the limit of human perception (it's what Flash uses by default, anyway). I suggest you go no higher, even if you can, in order to preserve battery life.
Edit: One other "hack" to consider: renderscript. That's how some of the sophisticated wallpapers (Galaxy, Grass, Water) that come with many phones work. See renderscript enabling java files here: https://android.googlesource.com/platform/packages/wallpapers/Basic/+/master/src/com/android/wallpaper

How about rendering your hand configuration to an intermediate bitmap each time the minute-hand changes, and then rendering that static bitmap to your canvas on each frame?

Found few things that helped to increase frame rate, will post here as an answer to myself :)
First is to use Canvas.drawBitmap(Bitmap, Matrix, Paint) overload to supply the matrix, instead of using canvas.save() + canvas.rotate() + canvas.restore() - more efficient.
Second: reducing the size of bitmaps. Earlier they were padded to near screen-size of hdpi (480pix) with transparent pixels added around them. After I cropped these transparent pixels away and modified the rendering code to account for that, rotating became much more efficient (as expected).

Related

Android: Low FPS drawing many bitmaps in a surfaceview

I'm trying to create a bullethell game and Ive run into a bit of a trouble. I can't get more than 17 fps after about 500 bullets. The update logic code takes around 1-4ms for all of them while the render code takes around 40ms
For now my code is
private void drawEntities(Canvas canvas) {
for (HashMap<UUID, Spatial> h: spatialList) {
for (Spatial spatial: h.values()) {
spatial.render(canvas);
if(spatial.life > 0)
spatial.life--;
else if (spatial.life == 0)
engine.deleteEntity(spatial.owner);
}
}
}
spatialList is an arrayList where each index is a zLevel
The spatial which displays the actual bullet is
public void render(Canvas canvas) {
float angle = (float) (vel.getAngle() * (180 / Math.PI));
matrix.reset();
matrix.setTranslate(pos.x - bullet.getWidth() / 2, pos.y - bullet.getHeight() / 2);
matrix.postRotate(angle + 90, pos.x, pos.y);
canvas.drawBitmap(bullet, matrix, paint);
canvas.drawCircle(pos.x, pos.y, col.getRadius(), paint);
}
I can provide more code but these seem to be the main issue. I've tried everything I can think of and can't find much else online. The only thing I can think of to fix this is to switch from a surfaceview to a GLSurfaceview but I really think there is a better way and I'm just using bad code.
Edit: I noticed my timer was off and removed the drawcircle and after running it again I get 40ms~ around 500 which is still a bit too low for reasonable performance.
TLDR; 500 entities = 17 fps.
You may be limited by pixel fill rate. How large (in pixels) is the display on your test device?
One simple thing to play with is to use setFixedSize() to reduce the size of the SurfaceView's Surface. That will reduce the number of pixels you're touching. Example here, video here, blog post here.
It's generally a good idea to do this, as newer devices seem to be racing toward absurd pixel counts. A full-screen game that performs all rendering in software is going to struggle on a 2560x1440 display, and flail badly at 4K. "Limiting" the game to 1080p and letting the display scaler do the heavy lifting should help. Depending on the nature of the game you could set the resolution even lower with no apparent loss of quality.
Another thing to try is to eliminate the drawBitmap() call, check your timings, then restore it and eliminate the drawCircle() call, to see if one or the other is chewing up the bulk of the time.
You may find switching to OpenGL ES not so bad. The "hardware scaler exerciser" activity in Grafika (from the video linked above) shows some simple bitmap rendering. Replace drawCircle() with a scaled bitmap and you might be most of the way done. (Note it uses SurfaceView, not GLSurfaceView, for GLES in that activity.)
I had the same issue. The problem will be solved to a large extent if you do all the drawing on a bitmap framebuffer and then draw the framebuffer to the canvas. If you are directly drawing on the canvas then there are several overheads. I looked around and found this tutorial
"http://www.kilobolt.com/day-6-the-android-game-framework-part-ii.html"
Look at the implementation for AndroidGraphics and AndroidFastRenderView and see how he uses AndroidGraphics to do all the actual drawing in a buffer and paints that buffer to the canvas in AndroidFastRenderView.
Try the performance when you remove the rotate and/or drawCircle parts from your render() method. Both of them might be quite time consuming as they probably contain sin() / cos() calculations.
If that helps, you'll have to figure out how to replace them with something faster, if not, well...

How to efficiently display images in Android

I'm making a simple 2d game in android. However, even with only a few images on screen, I'm running into problems with fps(its around 15-25 and quite stuttery).
Currently I use canvas, and simply set up an image each frame with everything I need to display on it.
Here's some of my render code:
public void render(Painter g) {
g.setColor(Color.rgb(208, 244, 247));
g.fillRect(0, 0, GameMainActivity.GAME_WIDTH, GameMainActivity.GAME_HEIGHT);
g.setColor(Color.rgb(139,69,19));
g.fillRect(0, 1400, GameMainActivity.GAME_WIDTH, GameMainActivity.GAME_HEIGHT - 1400);
g.drawImage(Assets.background3stone, currentBackground3X, 141);
g.drawImage(Assets.background3stone, currentBackground3X + 3459, 141);
g.drawImage(Assets.background2stone, currentBackground2X, 141);
g.drawImage(Assets.background2stone, currentBackground2X + 3459, 141);
g.drawImage(Assets.ground, currentBackground1X, 760);
g.drawImage(Assets.ground, currentBackground1X + 3000, 760);
renderNews(g);
renderButtons(g);
g.drawImage(Assets.leaderboard, 40, 180, 148, 148);
g.drawImage(Assets.achievements, 40, 354, 148, 148);
g.drawString("FPS: " + frameValue, 50, 200);
}
This code runs for every iteration of the game loop.
Does anyone know any ways to optimize my performance? Currently I redraw literally everything every frame, is there a way to not redraw static images? Would switching to openGl help a lot?
Thanks!
There's also a lot of good and efficient game engines available for Android (For example LibGDX). Game engines are usually well optimized for showing, moving and animating multiple images and they usually come with plenty other useful features.
However, if you prefer not to use game engines, I'd recommend using OpenGL to get some boost. You may also get some boost by optimizing your images. Try to reduce the sizes of images and don't use too large images. Also try to load images efficiently to the program.
Switching to openGL would always help a lot. But your problem is that you're drawing directly to the screen. Don't do that. Draw to a bitmap, then blit the bitmap to the screen in a single operation. The performance of that will be MUCH higher. It also would allow you to do your drawing on a 2nd thread and only draw to the screen on the main one.
Before switching to openGL you may want to try to not redraw the image itself, but just to move it. You just need to set new LayoutParams to your image. You may also want to look at TranslateAnimation in case you are moving stuff softly. Or you use a matrix transformation.

Android, fastest way to draw a bitmap to canvas

Just wondering what the fastest way is to draw a bitmap to canvas?
Currently I have a bitmap (and canvas for drawing) which i use to double buffer drawing calls, and then when i draw to canvas have a scrolling effect by applying a 1px canvas translation. This alone will reduce the framerate from 60+ FPS to ~40, quite a hit. Im not using surfaceView (or GLSurfaceView) at the moment but just wondering if im missing anything that would improve the speed. onDraw() code below
#Override
public void onDraw(Canvas canvas)
{
//update fps text
mFpsTracker.frameTouch();
if(mBufferedBitmap == null)
{
mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444);
mBufferedCanvas = new Canvas(mBufferedBitmap);
}
paint.setColor(Color.BLUE);
mBufferedCanvas.drawLine(0, getHeight(), getWidth(), getHeight(), paint);
mBufferedCanvas.translate(0, -1);
canvas.drawBitmap(mBufferedBitmap, 0, 0, null);
//draw fps
mTextPaint.setColor(Color.WHITE);
canvas.drawText(mFpsTracker.getFPSString(), 40, 40, mTextPaint);
invalidate();
}
please see this blog post by Romain Guy.
A video version is available here.
Don't use ARGB_4444 anymore. It is deprecated. Each pixel is only allocated 4 bits per channel (hence the name). ARBG_8888 offers 16,777,216 colors instead of ARBG_4444's 4,096, but uses 4 bytes per pixel instead of 2.
In Gingerbread, Android made ARGB_8888 the standard format for Surface and increased memory allotment per process because of it.
It is more efficient to set your Window's and (assuming you are using streamlined SurfaceView) SurfaceHolder's format to RGBA_8888. This avoids format changes which are noticeably slower.
Other tips include:
Limit alpha compositing, as this requires comparatively expensive blending from Skia.
Request Bitmap Options that prefer the ARGB_8888 Config and disable dithering.
Remove the Window background if possible.
Enable hardware acceleration, but beware of unsupported operations.
On a 2.1 device, I am able to draw at least 300 bitmaps on-screen at 50 fps.
I got same problem as yours, please tell me when you found some thing new.
This is what I founded so far:
For android version > 3 better not to use double buffer, because you are getting hardware acceleration(need to set true in manifest)
set paint.setDither(true) it will work better on any device with different color then ARGB_4444, witch are most of the devices. Check this out for more info.
In onSizeChange you can resize or create bitmaps according to the canvas size, then the frame drawing will be much faster about 60fps, however using custom view in an endless loop slows down on some and becomes jumpy on some android devices hence I do not recommend it. Instead, it is better to use SurfaceView.
Check this example: How can I use the animation framework inside the canvas?
U need create your bitmap somewhere ( example onCreate or other place (constructor will be good)) because when u do scrolling effect you again and again created new bitmap. So just need create in constructor and than using this bitmap.
it's good solution for me when i have similar problems.
try created this mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444); in other place where no invalidate .
hope it's help you.
I think you can get good performance out of a canvas.. but it takes a lot of work..
if you start off with a good performance graphics library, then even if you get a lot of things wrong, you probably will still end up with good performance :) lol
there is a competition running for the fastest drawing library... libgdx is currently winning...
http://code.google.com/p/libgdx/wiki/SimpleApp#Project_Setup

Android: Canvas vs OpenGL

I have a drawing app where the user can draw lines with their finger, adjust the color, thickness, etc. As the user is drawing, I am converting the massed X/Y points from MotionEvent into SVG Paths, as well as creating Android Path's and then drawing the Android Path's to the screen via a Canvas, and committing the SVG Path's to the app's database.
I am following the model used in FingerPaint, in that the 'in progress' lines are drawn on the fly by repeated calls to invalidate() (and thus, onDraw()), and once the line is complete and a new line is started, the previous line(s) are drawn in onDraw() from the underlying Canvas Bitmap, with in progress lines again generating repeated re-draws.
This works fine in this application - until you start rotating the underlying Bitmap to compensate for device rotation, supporting the ability to 'zoom in' on the drawing surface and thus having to scale the underlying Bitmap, etc. So for example, with the device rotated and the drawing scaled in, when the user is drawing, we need to scale AND rotate our Bitmap in onDraw(), and this is absolutely crawling.
I've looked at a SurfaceView, but as this still uses the same Canvas mechanism, I'm not sure I'll see noticeable improvement... so my thoughts turn to OpenGL. I have read somewhere that OpenGL can do rotations and scaling essentially 'for free', and even seen rumors (third comment) that Canvas may be disappearing in future versions.
Essentially, I am a little stuck between the Canvas and OpenGL solutions... I have a 2D drawing app that seems to fit the Canvas model perfectly when in one state, as there are not constant re-draws going on like a game (for instance when the user is not drawing I don't need any re-drawing), but when the user IS drawing, I need the maximum performance necessary to do some increasingly complex things with the surface...
Would welcome any thoughts, pointers and suggestions.
OpenGL would be able to handle the rotations and scaling easily.
Honestly, you would probably need to learn a lot of OpenGL to do this, specifically related to the topics of:
Geometry
Lighting (or just disabling it)
Picking (selecting geometry to draw on it)
Pixel Maps
Texture Mapping
Mipmapping
Also, learning OpenGL for this might be overkill, and you would have to be pretty good at it to make it efficient.
Instead, I would recommend using the graphic components of a game library built on top of openGL, such as:
Cocos2d
libgdx
any of the engines listed here
Well, this question was asked 6 years ago. Maybe Android 4.0 has not come up?
Actually, after Android 4.0 the Canvas at android.view.View is a hardware accelerated canvas, which means it is implementd by OpenGL, so you do not need to use another way for performance.
You can see the https://github.com/ChillingVan/android-openGL-canvas/blob/master/canvasglsample/src/main/java/com/chillingvan/canvasglsample/comparePerformance/ComparePerformanceActivity.java to compare the performance of normal canvas in view with GLSurfaceView.
You are right that SurfaceView uses Canvas underneath the hood. The main difference is that SurfaceView uses another thread to do the actual drawing, which generally improves performance. It sounds like it would not help you a great deal, though.
You are correct that OpenGL can do rotations very quickly, so if you need more performance that is the way to go. You should probably use GLSurfaceView. The main drawback with using OpenGL is that it is a real pain to do text. Basically you have to (okay, don't have to, but seems to be the best option) render bitmaps of text.

Load bitmap or draw graphic? Performance Question

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.

Categories

Resources