Improve media resizing (#722)

* improve MediaUtils.getImageThumbnail so it does not load the whole bitmap into memory

* load thumbnails in device specific sizes
This commit is contained in:
Konrad Pozniak 2018-07-23 21:55:09 +02:00 committed by GitHub
commit 61f3f6c928
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 73 additions and 63 deletions

View file

@ -131,21 +131,6 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
return orientation;
}
private static int calculateInSampleSize(int width, int height, int requiredScale) {
int inSampleSize = 1;
if (height > requiredScale || width > requiredScale) {
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 >= requiredScale
&& halfWidth / inSampleSize >= requiredScale) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
@Override
protected Boolean doInBackground(Uri... uris) {
resultList = new ArrayList<>();
@ -160,8 +145,6 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStream, null, options);
int beforeWidth = options.outWidth;
int beforeHeight = options.outHeight;
IOUtils.closeQuietly(inputStream);
// Get EXIF data, for orientation info.
int orientation = getOrientation(uri, contentResolver);
@ -180,8 +163,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
} catch (FileNotFoundException e) {
return false;
}
options.inSampleSize = calculateInSampleSize(beforeWidth, beforeHeight,
scaledImageSize);
options.inSampleSize = MediaUtils.calculateInSampleSize(options, scaledImageSize, scaledImageSize);
options.inJustDecodeBounds = false;
Bitmap scaledBitmap;
try {

View file

@ -27,6 +27,7 @@ import android.provider.OpenableColumns;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.Px;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
@ -34,11 +35,10 @@ import java.io.IOException;
import java.io.InputStream;
/**
* Class who will have all the code link with Media
* <p>
* Motivation : try to keep the ComposeActivity "smaller" and make modular method
* Class with helper methods for obtaining and resizing media files
*/
public class MediaUtils {
private static final String TAG = "MediaUtils";
public static final int MEDIA_SIZE_UNKNOWN = -1;
/**
@ -88,30 +88,51 @@ public class MediaUtils {
}
@Nullable
public static Bitmap getImageThumbnail(ContentResolver contentResolver, Uri uri,
@Px int thumbnailSize) {
public static Bitmap getSampledBitmap(ContentResolver contentResolver, Uri uri, @Px int reqWidth, @Px int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
InputStream stream;
try {
stream = contentResolver.openInputStream(uri);
} catch (FileNotFoundException e) {
Log.w(TAG, e);
return null;
}
Bitmap source = BitmapFactory.decodeStream(stream);
if (source == null) {
IOUtils.closeQuietly(stream);
return null;
}
Bitmap bitmap = ThumbnailUtils.extractThumbnail(source, thumbnailSize, thumbnailSize);
source.recycle();
BitmapFactory.decodeStream(stream, null, options);
IOUtils.closeQuietly(stream);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
bitmap.recycle();
stream = contentResolver.openInputStream(uri);
return BitmapFactory.decodeStream(stream, null, options);
} catch (FileNotFoundException e) {
Log.w(TAG, e);
return null;
} catch (OutOfMemoryError e) {
Log.e(TAG, "OutOfMemoryError while trying to get sampled Bitmap", e);
return null;
} finally {
IOUtils.closeQuietly(stream);
}
}
@Nullable
public static Bitmap getImageThumbnail(ContentResolver contentResolver, Uri uri,
@Px int thumbnailSize) {
Bitmap source = getSampledBitmap(contentResolver, uri, thumbnailSize, thumbnailSize);
if(source != null) {
return ThumbnailUtils.extractThumbnail(source, thumbnailSize, thumbnailSize, ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
} else {
return null;
}
return bitmap;
}
@Nullable
@ -128,8 +149,7 @@ public class MediaUtils {
}
public static long getImageSquarePixels(ContentResolver contentResolver, Uri uri) throws FileNotFoundException {
InputStream input;
input = contentResolver.openInputStream(uri);
InputStream input = contentResolver.openInputStream(uri);
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
@ -139,4 +159,25 @@ public class MediaUtils {
return (long) options.outWidth * options.outHeight;
}
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;
}
}