/* * Copyright (C) 2014 Fastboot Mobile, LLC. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses>. */ package com.fastbootmobile.encore.utils; import android.annotation.TargetApi; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import com.fastbootmobile.encore.art.ImageCache; /** * Image processing utility methods */ public class ImageUtils { /** * Get the size in bytes of a bitmap in a BitmapDrawable. Note that from Android 4.4 (KitKat) * onward this returns the allocated memory size of the bitmap which can be larger than the * actual bitmap data byte count (in the case it was re-used). * * @param value The bitmap to measure * @return size in bytes */ @TargetApi(Build.VERSION_CODES.KITKAT) public static int getBitmapSize(BitmapDrawable value) { Bitmap bitmap = value.getBitmap(); // From KitKat onward use getAllocationByteCount() as allocated bytes can potentially be // larger than bitmap byte count. if (Utils.hasKitKat()) { return bitmap.getAllocationByteCount(); } return bitmap.getByteCount(); } /** * @param candidate - Bitmap to check * @param targetOptions - Options that have the out* value populated * @return true if <code>candidate</code> can be used for inBitmap re-use with * <code>targetOptions</code> */ @TargetApi(Build.VERSION_CODES.KITKAT) public static boolean canUseForInBitmap( Bitmap candidate, BitmapFactory.Options targetOptions) { if (!Utils.hasKitKat()) { // On earlier versions, the dimensions must match exactly and the inSampleSize must be 1 return candidate.getWidth() == targetOptions.outWidth && candidate.getHeight() == targetOptions.outHeight && targetOptions.inSampleSize == 1; } // From Android 4.4 (KitKat) onward we can re-use if the byte size of the new bitmap // is smaller than the reusable bitmap candidate allocation byte count. if (targetOptions.inSampleSize == 0) { targetOptions.inSampleSize = 1; } int width = targetOptions.outWidth / targetOptions.inSampleSize; int height = targetOptions.outHeight / targetOptions.inSampleSize; int byteCount = width * height * getBytesPerPixel(candidate.getConfig()); return byteCount <= candidate.getAllocationByteCount(); } /** * Return the byte usage per pixel of a bitmap based on its configuration. * @param config The bitmap configuration. * @return The byte usage per pixel. */ private static int getBytesPerPixel(Bitmap.Config config) { if (config == Bitmap.Config.ARGB_8888) { return 4; } else if (config == Bitmap.Config.RGB_565) { return 2; } else if (config == Bitmap.Config.ARGB_4444) { return 2; } else if (config == Bitmap.Config.ALPHA_8) { return 1; } return 1; } public static void addInBitmapOptions(BitmapFactory.Options options, ImageCache cache, int requestedSize, int width, int height) { // inBitmap only works with mutable bitmaps so force the decoder to // return mutable bitmaps. options.inMutable = true; // If no inSampleSize is forced, calculate a sampleSize to fit the requested size if (options.inSampleSize == 0 && requestedSize > 0) { final int originalLargestSide = Math.max(width, height); int largestSide = originalLargestSide; int factor = 1; while (largestSide > requestedSize) { factor = factor * 2; largestSide = originalLargestSide / factor; } // We re-divide the factor so that we have an image that is at least large enough. // Keeping the factor as is would mean the image can be smaller than the display area. if (largestSide == requestedSize) { options.inSampleSize = factor; } else { options.inSampleSize = factor / 2; } } else if (options.inSampleSize == 0) { options.inSampleSize = 1; } // Try to find a suitable input bitmap to reuse if (cache != null) { // Try and find a bitmap to use for inBitmap Bitmap inBitmap = cache.getBitmapFromReusableSet(options); if (inBitmap != null) { options.inBitmap = inBitmap; } } } }