Skip to content

Commit

Permalink
Use BitmapRegionDecoder to efficiently crop images on Android
Browse files Browse the repository at this point in the history
Summary:
The Android ImageEditingManager is inefficient and slow when cropping images. It loads the full resolution image into memory and then crops it. This leads to slow performance and occasional OutOfMemory Exceptions.
[BitmapRegionDecoder](https://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html) can be used to crop without needing to load the full resolution image into memory. Using it is much more efficient and much faster.

Relevant issue: #10470

Attempt to crop a very large image (2000x2000) on Android. With this change, the crop should happen almost instantly. On the master branch, it should take 2-3 seconds (and might run out of memory).

Please let me know if there's anything else I can provide.
Closes #15439

Differential Revision: D5628223

Pulled By: shergin

fbshipit-source-id: bf314e76134cd015380968ec4533225e724c4b26
  • Loading branch information
faifai21 authored and facebook-github-bot committed Sep 26, 2017
1 parent e6596dc commit 64ffe45
Showing 1 changed file with 8 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapRegionDecoder;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.AsyncTask;
Expand Down Expand Up @@ -298,17 +300,17 @@ protected void doInBackgroundGuarded(Void... params) {
*/
private Bitmap crop(BitmapFactory.Options outOptions) throws IOException {
InputStream inputStream = openBitmapInputStream();
// Effeciently crops image without loading full resolution into memory
// https://developer.android.com/reference/android/graphics/BitmapRegionDecoder.html
BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(inputStream, false);
try {
// This can use a lot of memory
Bitmap fullResolutionBitmap = BitmapFactory.decodeStream(inputStream, null, outOptions);
if (fullResolutionBitmap == null) {
throw new IOException("Cannot decode bitmap: " + mUri);
}
return Bitmap.createBitmap(fullResolutionBitmap, mX, mY, mWidth, mHeight);
Rect rect = new Rect(mX, mY, mX + mWidth, mY + mHeight);
return decoder.decodeRegion(rect, outOptions);
} finally {
if (inputStream != null) {
inputStream.close();
}
decoder.recycle();
}
}

Expand Down

0 comments on commit 64ffe45

Please sign in to comment.