Reputation: 9599
I've got the following code.
this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 0, 100, 750), new Rect(0, 0, 100, 750), null);
this.getGame().getGraphics().drawBitmap(Assets.playScreen_LaserBeamAnimation, new Rect(0, 200, 10, 800), new Rect(0, 0, 200, 600), null);
The first render statement takes around 0.6 - 1 second
to render.
The second one around 1 millisecond
.
The Bitmap is large: 968 KB
and is loaded with the following code:
public Bitmap readAssetsBitmap(String filename) throws IOException {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPurgeable = true;
Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
if(bitmap == null) {
WSLog.d(Game.GAME_ENGINE_TAG, this,"File cannot be opened: It's value is null");
throw new IOException("File cannot be opened: It's value is null");
}
else {
WSLog.d(Game.GAME_ENGINE_TAG, this,"File successfully read: " + filename);
return bitmap;
}
}
catch (IOException e) {
WSLog.d(Game.GAME_ENGINE_TAG, this,"File cannot be opened: " + e.getMessage());
throw new IOException("File cannot be opened: " + e.getMessage());
}
}
I expect the reading of the image and setting the Bitmap to take some time, however rendering a small region of it should not take so long (and mysteriously it is only the first render call).
How do I avoid the first render to take so long?
NB: It does not have to do anything with the scaling (I believe).
Upvotes: 7
Views: 837
Reputation: 19651
TL;DR Loading and rendering graphics is actually a relatively expensive set of (internal) operations, first loading into memory then into the graphics module of the ARM CPU when it is first rendered to screen. Further, the animated GIF is expanded into a set of images, one for each animation frame making it quite a bit larger than you may think. To improve speed, you might consider decreasing the animation size (pixels and number of frames), or forcing the GIF onto the graphics memory earlier to avoid the first-frame pause.
There is one heck of a lot of things Android has to do internally to render game graphics, especially if you have a GIF animation:
Now we have an image (actually a set of frames) in the CPUs memory. Note that a very small GIF with a lot of frames can become a very large in memory - Android has to have a full-sized image for each frame in your GIF. Also the 256 bit colour model is expanded into RGBA internally, so you have 4 bytes per pixel.
Take for example a GIF (128x128 pixels) with a small shape animated on it (32x32 pixels). Let's further suppose you have 100 steps in the animation - this might be 10k or so depending on how simple the background is. However, expanded in memory, each frame in the animation is 4x128x128 = 64kB. You have 100 frames, so your in-memory size is 6MB.
Next:
Once the image (which is actually a bunch of frames) is loaded into the graphics module, then things get fast.
OK, so what can you do about it? Here are some suggestions:
Reducing the size of the GIF could reduce the memory usage significantly, and that will reduce the time Android takes to push the frames into the graphics module and the cost to compile into textures. The downside to that is you might not have the authority to modify the graphics.
Pre-loading pushes the image into the graphics module earlier by forcing a render during the game loading phase. The image might be off-screen (setting the window as hidden) or behind a top-layer by setting a low Z-order or in a 1 pixel region.
Tiling is discussed in my answer to a similar question). Take Google Earth as the most extreme example - this uses tiles (at any given resolution) for every area on earth. To render just the bit you are interested in, Google Earth has to load only the tiles that are to be shown. Because the tiles are small, they load fast. However, in your case you may have a GIF animation, so that's not going to work. Tiling an animation might not be practical or might lead to artefacts or delays in rendering portions of the image.
Upvotes: 1
Reputation: 13966
It looks like you are trying to draw an animation using a large bitmap that contains all the frames. If that is the case then scaling down the image is probably not going to work for you.
However, if you have n discrete frames in the image, then why not create the n bitmaps when you load the image and store them in an ArrayList<Bitmap>
when the app loads? That way you can prechop the image and don't have to worry about slicing the image when you render.
On render, you could either replace the the current bitmap with the one you want to draw, or you can draw the image in place without scaling (much faster than drawing with scaling).
This may take more memory, but will probably offer a noticeable speedup.
Update: On why it's so slow. Java graphics is really dumb. My guess is, in the first example (the long one), when it sees that the regions are the same scale, it iterates through the entire large image and whenever it finds a pixel that is in that given region, it draws it in the destination region.
On the fast draw, my guess is that it sees that the scales are different and then does it backwards: for every pixel in the destination region, it figures out which pixels to reference in the original image and blends accordingly into that square.
Since there are far fewer pixels in the destination region, the algorithm is much faster.
Java.Graphics is really bad. Have you looked into other, 3rd-party image processing libraries?
Upvotes: 0