Images

Drawable is a general abstraction for "something that can be drawn."

Bitmap represents a raw image data. Before Android 5 it is recommended to recycle the unused bitmap with method recycle().

BitmapFactory class allows to creates Bitmap objects from various sources, including files, streams, and byte-arrays. Supported formats are jpeg, png (argb8888, rgb888), webp (Android 4.4+ withot loss less compression). You can use it

  • to read header to get width and height of image
  • to load scaled image (thumbnail) to avoid the OutOfMemory exception
  • to load image with reusing other Bitmap object

ImageDecoder class (API 28+) allows to convert encoded images (like PNG, JPEG, WEBP, GIF, or HEIF) into Drawable or Bitmap objects. It is more powerful then BitmapFactory. It is support animated gif, post processing and etc.

Canvas class allows to draw in mutable Bitmap object.

ThumbnailUtils is utility class for generating visual thumbnails from files.

Images can be stored within app as assets or as drawable resources.

Most apps use libraries like Glide which allow to load remote images, save them in disk/mem cache and assign to an ImageView.

To avoid java.lang.OutOfMemory exceptions, check the dimensions of a bitmap before decoding it, unless you absolutely trust the source to provide you with predictably sized image data that comfortably fits within the available memory.

Read image size and type
val options = BitmapFactory.Options().apply {
    inJustDecodeBounds = true
}
BitmapFactory.decodeResource(resources, R.id.myimage, options)
val imageHeight: Int = options.outHeight
val imageWidth: Int = options.outWidth
val imageType: String = options.outMimeType
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

down scale image

To tell the decoder to subsample the image, loading a smaller version into memory, set inSampleSize in your BitmapFactory.Options object. For example, an image with resolution 2048x1536 that is decoded with an inSampleSize of 4 produces a bitmap of approximately 512x384.

Calculate sample size
fun calculateInSampleSize(
                options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
    // Raw height and width of image
    val (height: Int, width: Int) = options.run { outHeight to outWidth }
    var inSampleSize = 1

    if (height > reqHeight || width > reqWidth) {

        val halfHeight: Int = height / 2
        val halfWidth: Int = width / 2

        // Calculate the largest inSampleSize value 
        // that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
            inSampleSize *= 2
        }
    }

    return inSampleSize
}
public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value 
        // that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) >= reqHeight
                && (halfWidth / inSampleSize) >= reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}
Decode sample image

reusing bitmaps

You can decode a new image to the specified Bitmap object.

A destination bitmap must be mutable and satisfies the size. Prior Android 4.4 size must be exactly same.

Check image size

You can use set of SoftReference to store images for reusing. For example, let images are cached in the LRU cache. When the image is removed from the cache, you can store it in a set.

Save image for reusing
Search reusable image in set

A destination bitmap is specified in inBitmap field of BitmapFactory.Options class.

private fun addInBitmapOptions(options: BitmapFactory.Options, cache: ImageCache?) {
    // inBitmap only works with mutable bitmaps, so force the decoder to
    // return mutable bitmaps.
    options.inMutable = true

    // Try to find a bitmap to use for inBitmap.
    cache?.getBitmapFromReusableSet(options)?.also { inBitmap ->
        // If a suitable bitmap has been found, set it as the value of
        // inBitmap.
        options.inBitmap = inBitmap
    }
}

draw in Bitmap

You can draw in mutable Bitmap object using Canvas class.

Crop bitmap
/**
*  Extracts a part of a Bitmap defined by copyRect.
*/
fun getSubimage(b: Bitmap, copyRect: Rect): Bitmap? {

    val subImage = Bitmap.createBitmap(
         copyRect.width(),
         copyRect.height(), Bitmap.Config.ARGB_8888
    )

    Canvas(subImage).apply {
        drawBitmap(
            b, copyRect,
            Rect(0, 0, copyRect.width(), copyRect.height()), null
            )
    }
        
    return subImage
}