diff --git a/app/src/main/java/com/limelight/grid/assets/DiskAssetLoader.java b/app/src/main/java/com/limelight/grid/assets/DiskAssetLoader.java index b22ca2eb..1582ebc4 100644 --- a/app/src/main/java/com/limelight/grid/assets/DiskAssetLoader.java +++ b/app/src/main/java/com/limelight/grid/assets/DiskAssetLoader.java @@ -13,7 +13,11 @@ import java.io.OutputStream; public class DiskAssetLoader { // 5 MB - private final long MAX_ASSET_SIZE = 5 * 1024 * 1024; + private static final long MAX_ASSET_SIZE = 5 * 1024 * 1024; + + // Standard box art is 300x400 + private static final int STANDARD_ASSET_WIDTH = 300; + private static final int STANDARD_ASSET_HEIGHT = 400; private final File cacheDir; @@ -25,33 +29,65 @@ public class DiskAssetLoader { return CacheHelper.cacheFileExists(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png"); } - public Bitmap loadBitmapFromCache(CachedAppAssetLoader.LoaderTuple tuple, int sampleSize) { - InputStream in = null; - Bitmap bmp = null; - try { - // Make sure the cached asset doesn't exceed the maximum size - if (CacheHelper.getFileSize(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png") > MAX_ASSET_SIZE) { - LimeLog.warning("Removing cached tuple exceeding size threshold: "+tuple); - CacheHelper.deleteCacheFile(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png"); - return null; - } + // https://developer.android.com/topic/performance/graphics/load-bitmap.html + 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; - in = CacheHelper.openCacheFileForInput(cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png"); - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inSampleSize = sampleSize; - options.inPreferredConfig = Bitmap.Config.RGB_565; - bmp = BitmapFactory.decodeStream(in, null, options); - } catch (IOException ignored) { - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ignored) {} + if (height > reqHeight || width > reqWidth) { + + final int halfHeight = height / 2; + final int halfWidth = width / 2; + + // Calculates 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 Bitmap loadBitmapFromCache(CachedAppAssetLoader.LoaderTuple tuple, int sampleSize) { + File file = CacheHelper.openPath(false, cacheDir, "boxart", tuple.computer.uuid.toString(), tuple.app.getAppId() + ".png"); + + // Don't bother with anything if it doesn't exist + if (!file.exists()) { + return null; + } + + // Make sure the cached asset doesn't exceed the maximum size + if (file.length() > MAX_ASSET_SIZE) { + LimeLog.warning("Removing cached tuple exceeding size threshold: "+tuple); + file.delete(); + return null; + } + + // Lookup bounds of the downloaded image + BitmapFactory.Options decodeOnlyOptions = new BitmapFactory.Options(); + decodeOnlyOptions.inJustDecodeBounds = true; + BitmapFactory.decodeFile(file.getAbsolutePath(), decodeOnlyOptions); + if (decodeOnlyOptions.outWidth <= 0 || decodeOnlyOptions.outHeight <= 0) { + // Dimensions set to -1 on error. Return value always null. + return null; + } + + LimeLog.info("Tuple "+tuple+" has cached art of size: "+decodeOnlyOptions.outWidth+"x"+decodeOnlyOptions.outHeight); + + // Load the image scaled to the appropriate size + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inSampleSize = calculateInSampleSize(decodeOnlyOptions, + STANDARD_ASSET_WIDTH / sampleSize, + STANDARD_ASSET_HEIGHT / sampleSize); + options.inPreferredConfig = Bitmap.Config.RGB_565; + options.inDither = true; + Bitmap bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); + if (bmp != null) { - LimeLog.info("Disk cache hit for tuple: "+tuple); + LimeLog.info("Tuple "+tuple+" decoded from disk cache with sample size: "+options.inSampleSize); } return bmp; diff --git a/app/src/main/java/com/limelight/utils/CacheHelper.java b/app/src/main/java/com/limelight/utils/CacheHelper.java index 52d42aec..4d265e18 100644 --- a/app/src/main/java/com/limelight/utils/CacheHelper.java +++ b/app/src/main/java/com/limelight/utils/CacheHelper.java @@ -13,7 +13,7 @@ import java.io.OutputStream; import java.io.Reader; public class CacheHelper { - private static File openPath(boolean createPath, File root, String... path) { + public static File openPath(boolean createPath, File root, String... path) { File f = root; for (int i = 0; i < path.length; i++) { String component = path[i];