Resize a large bitmap file to scaled output file on Android
No. I'd love for someone to correct me, but I accepted the load/resize approach you tried as a compromise.
Here are the steps for anyone browsing:
- Calculate the maximum possible
inSampleSize
that still yields an image larger than your target. - Load the image using
BitmapFactory.decodeFile(file, options)
, passing inSampleSize as an option. - Resize to the desired dimensions using
Bitmap.createScaledBitmap()
.
Justin answer translated to code (works perfect for me):
private Bitmap getBitmap(String path) {Uri uri = getImageUri(path);InputStream in = null;try { final int IMAGE_MAX_SIZE = 1200000; // 1.2MP in = mContentResolver.openInputStream(uri); // Decode image size BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); int scale = 1; while ((options.outWidth * options.outHeight) * (1 / Math.pow(scale, 2)) > IMAGE_MAX_SIZE) { scale++; } Log.d(TAG, "scale = " + scale + ", orig-width: " + options.outWidth + ", orig-height: " + options.outHeight); Bitmap resultBitmap = null; in = mContentResolver.openInputStream(uri); if (scale > 1) { scale--; // scale to max possible inSampleSize that still yields an image // larger than target options = new BitmapFactory.Options(); options.inSampleSize = scale; resultBitmap = BitmapFactory.decodeStream(in, null, options); // resize to desired dimensions int height = resultBitmap.getHeight(); int width = resultBitmap.getWidth(); Log.d(TAG, "1th scale operation dimenions - width: " + width + ", height: " + height); double y = Math.sqrt(IMAGE_MAX_SIZE / (((double) width) / height)); double x = (y / height) * width; Bitmap scaledBitmap = Bitmap.createScaledBitmap(resultBitmap, (int) x, (int) y, true); resultBitmap.recycle(); resultBitmap = scaledBitmap; System.gc(); } else { resultBitmap = BitmapFactory.decodeStream(in); } in.close(); Log.d(TAG, "bitmap size - width: " +resultBitmap.getWidth() + ", height: " + resultBitmap.getHeight()); return resultBitmap;} catch (IOException e) { Log.e(TAG, e.getMessage(),e); return null;}
This is 'Mojo Risin's and 'Ofir's solutions "combined". This will give you a proportionally resized image with the boundaries of max width and max height.
- It only reads meta data to get the original size (options.inJustDecodeBounds)
- It uses a rought resize to save memory (itmap.createScaledBitmap)
- It uses a precisely resized image based on the rough Bitamp created earlier.
For me it has been performing fine on 5 MegaPixel images an below.
try{ int inWidth = 0; int inHeight = 0; InputStream in = new FileInputStream(pathOfInputImage); // decode image size (decode metadata only, not the whole image) BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeStream(in, null, options); in.close(); in = null; // save width and height inWidth = options.outWidth; inHeight = options.outHeight; // decode full image pre-resized in = new FileInputStream(pathOfInputImage); options = new BitmapFactory.Options(); // calc rought re-size (this is no exact resize) options.inSampleSize = Math.max(inWidth/dstWidth, inHeight/dstHeight); // decode full image Bitmap roughBitmap = BitmapFactory.decodeStream(in, null, options); // calc exact destination size Matrix m = new Matrix(); RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight()); RectF outRect = new RectF(0, 0, dstWidth, dstHeight); m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER); float[] values = new float[9]; m.getValues(values); // resize bitmap Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true); // save image try { FileOutputStream out = new FileOutputStream(pathOfOutputImage); resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out); } catch (Exception e) { Log.e("Image", e.getMessage(), e); }}catch (IOException e){ Log.e("Image", e.getMessage(), e);}