From 9aed9f56f70569da055f8bf9d64ffd35cc124068 Mon Sep 17 00:00:00 2001 From: faifai21 Date: Tue, 26 Sep 2017 05:09:26 -0700 Subject: [PATCH] Use BitmapRegionDecoder to efficiently crop images on Android 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: https://github.com/facebook/react-native/issues/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 https://github.com/facebook/react-native/pull/15439 Differential Revision: D5628223 Pulled By: shergin fbshipit-source-id: bf314e76134cd015380968ec4533225e724c4b26 --- ImageEditingManager.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ImageEditingManager.java b/ImageEditingManager.java index 804b2e8..02ff3ec 100644 --- a/ImageEditingManager.java +++ b/ImageEditingManager.java @@ -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; @@ -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(); } }