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:
parent
67f4479e86
commit
61f3f6c928
5 changed files with 73 additions and 63 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue