Improved bitmaps with frame rate in canvas?

I shoot animation in live wallpaper. Here is the code. This pretty much follows the CubeWallpaper demo:

void drawFrame() { final SurfaceHolder holder = getSurfaceHolder(); final BufferedInputStream buf; final Bitmap bitmap, rbitmap; Canvas c = null; try { c = holder.lockCanvas(); if (c != null) { try { buf = new BufferedInputStream(assets. open(folder+"/" +imageList[ilen++]) ); bitmap = BitmapFactory. decodeStream(buf); rbitmap = Bitmap.createBitmap (bitmap, 0,0,imageWidth,imageHeight, transMatrix,false); c.drawBitmap(rbitmap, paddingX, paddingY, null); if ( ilen >= imageCount ) ilen=0; } catch (Exception e) { e.printStackTrace(); } } } finally { if (c != null) holder.unlockCanvasAndPost(c); } // Reschedule the next redraw mHandler.removeCallbacks(mDrawCube); if (mVisible) { mHandler.postDelayed(mDrawCube, fps); } } 

where "transMatrix" is a pre-scaled matrix of scaling and rotation.

It should be displayed at a speed of 30 frames per second, but, of course, it does not. My initial hunch is that BufferedInputStream is one of the factors. I probably should cache some of them when I go along with bitmaps. But any other ideas? Will I use OpenGL hack for live wallpapers?

+4
source share
2 answers

Yes, BufferedInputStream and BitmapFactory really shouldn't be in drawFrame() at all. You upload and create resources on each individual frame, and this is a huge waste. As you said, cache as much as you can in advance, and if you find it necessary to load more while drawing, use a separate stream so that it does not slow down the drawing.

+4
source

I had the same problem: slow rendering of the canvas in the context of live wallpapers.

I agree with others, saying that you should not make any / io processor heavy when rendering, for example. uploading images, especially in the user interface stream.

However, there is one more thing you should note. You request a redraw (mHandler.postDelayed (...)) AFTER the frame has been rendered. If you want 30 frames per second, and thus you request a redraw (1000/30) 33 ms, then this will NOT result in 30 frames per second. Why?

Suppose that it takes 28 ms to transfer all of your materials to the canvas. After that, you request a redraw after 33 milliseconds. That is, the period between redraws is 61 ms, which is 16 frames per second.

You have two options for solving it:

1) Put the mHandler.postDelayed (...) code at the beginning of the drawFrame (...) method. It seems OK, but it has some drawbacks: if your actual FPS is very close to the highest possible FPS on a real device - in other words, the UI thread is busy all the time using canvas rendering - then there will be no time for the UI thread to do other things. This does not mean that your LWP or home screen will lag, but you (your LWP) can start to skip some touch events (as my LWP did).

2) The best solution is to start a separate thread when creating a surface and pass it a link to SurfaceHolder. Render in this separate stream. The rendering method in this thread will look like this:

 private static final int DESIRED_FPS = 25; private static final int DESIRED_PERIOD_BETWEEN_FRAMES_MS = (int) (1000.0 / DESIRED_FPS + 0.5); @Override public void run() { while (mRunning) { long beforeRenderMs = SystemClock.currentThreadTimeMillis(); // do actual canvas rendering long afterRenderMs = SystemClock.currentThreadTimeMillis(); long renderLengthMs = afterRenderMs - beforeRenderMs; sleep(Math.max(DESIRED_PERIOD_BETWEEN_FRAMES_MS - renderLengthMs, 0)); } } 
+3
source

All Articles