The Bitmap class will become our best friend. We load a bitmap from a file by using the BitmapFactory singleton. As we store our images in the form of assets, let's see how we can load an image from the assets/ directory:
InputStream inputStream = assetManager.open("bob.png"); Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
The Bitmap class itself has a couple of methods that are of interest to us. First we want to get to know its width and height in pixels:
int width = bitmap.getWidth(); int height = bitmap.getHeight();
The next thing we might want to know is what color format the Bitmap is stored in: Bitmap.Config config = bitmap.getConfig(); Bitmap.Config is an enumeration with the values: Config.ALPHA_8 Config.ARGB_4444 Config.ARGB_8888 Config.RGB_565
From Chapter 3, you should know what these values mean. If not I strongly suggest that you read the "Encoding Colors Digitally" section of Chapter 3 again.
Interestingly there's no RGB888 color format. PNG only supports ARGB8888, RGB888, and palettized colors. What color format would an RGB888 PNG be loaded to? BitmapConfig.RGB_565 is the answer. This happens automatically for any RGB888 PNG we load via the BitmapFactory. The reason for this is that the actual framebuffer of most Android devices works with that color format. It would be a waste of memory to load an image with a higher bit depth per pixel, as the pixels would need to be converted to RGB565 anyway for final rendering.
So why is there the Config.ARGB_8888 configuration then? Because image composition can be done on the CPU prior to actually drawing the final image to the framebuffer. In the case of the alpha component, we also have a lot more bit depth than with Config.ARGB_4444, which might be necessary for some high-quality image processing.
An ARGB8888 PNG image would be loaded to a Bitmap with a Config.ARGB_8888 configuration. The other two color formats are barely used. We can, however, tell the
BitmapFactory to try to load an image with a specific color format, even if its original format is different.
InputStream inputStream = assetManager.open("bob.png"); BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_4444; Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);
We use the overloaded BitmapFactory.decodeStream() method to pass a hint in the form of an instance of the BitmapFactory.Options class to the image decoder. We can specify the desired color format of the Bitmap instance via the BitmapFactory.Options.inPreferredConfig member, as shown previously. In this hypthotical example, the bob.png file would be a ARGB8888 PNG, and we want the BitmapFactory to load it and convert it to an ARGB4444 bitmap. The factory can ignore the hint, though.
This will free all the memory used by that Bitmap instance. Of course, you can't use the bitmap for rendering anymore after a call to this method.
You can also create an empty Bitmap with the following static method:
Bitmap bitmap = Bitmap.createBitmap(int width, int height, Bitmap.Config config);
This might come in handy if you want to do custom image compositing yourself on the fly. The Canvas class also works on bitmaps:
Canvas canvas = new Canvas(bitmap);
You can then modify your bitmaps in the same way you modify the contents of a View.
Was this article helpful?