Resize a large bitmap file to scaled output file on Android

前端 未结 21 921
执念已碎
执念已碎 2020-11-22 05:51

I have a large bitmap (say 3888x2592) in a file. Now, I want to resize that bitmap to 800x533 and save it to another file. I normally would scale the bitmap by calling

相关标签:
21条回答
  • 2020-11-22 06:06

    Above code made a little cleaner. InputStreams have finally close wrapping to ensure they get closed as well:

    *Note
    Input: InputStream is, int w, int h
    Output: Bitmap

        try
        {
    
            final int inWidth;
            final int inHeight;
    
            final File tempFile = new File(temp, System.currentTimeMillis() + is.toString() + ".temp");
    
            {
    
                final FileOutputStream tempOut = new FileOutputStream(tempFile);
    
                StreamUtil.copyTo(is, tempOut);
    
                tempOut.close();
    
            }
    
    
    
            {
    
                final InputStream in = new FileInputStream(tempFile);
                final BitmapFactory.Options options = new BitmapFactory.Options();
    
                try {
    
                    // decode image size (decode metadata only, not the whole image)
                    options.inJustDecodeBounds = true;
                    BitmapFactory.decodeStream(in, null, options);
    
                }
                finally {
                    in.close();
                }
    
                // save width and height
                inWidth = options.outWidth;
                inHeight = options.outHeight;
    
            }
    
            final Bitmap roughBitmap;
    
            {
    
                // decode full image pre-resized
                final InputStream in = new FileInputStream(tempFile);
    
                try {
    
                    final BitmapFactory.Options options = new BitmapFactory.Options();
                    // calc rought re-size (this is no exact resize)
                    options.inSampleSize = Math.max(inWidth/w, inHeight/h);
                    // decode full image
                    roughBitmap = BitmapFactory.decodeStream(in, null, options);
    
                }
                finally {
                    in.close();
                }
    
                tempFile.delete();
    
            }
    
            float[] values = new float[9];
    
            {
    
                // calc exact destination size
                Matrix m = new Matrix();
                RectF inRect = new RectF(0, 0, roughBitmap.getWidth(), roughBitmap.getHeight());
                RectF outRect = new RectF(0, 0, w, h);
                m.setRectToRect(inRect, outRect, Matrix.ScaleToFit.CENTER);
                m.getValues(values);
    
            }
    
            // resize bitmap
            final Bitmap resizedBitmap = Bitmap.createScaledBitmap(roughBitmap, (int) (roughBitmap.getWidth() * values[0]), (int) (roughBitmap.getHeight() * values[4]), true);
    
            return resizedBitmap;
    
        }
        catch (IOException e) {
    
            logger.error("Error:" , e);
            throw new ResourceException("could not create bitmap");
    
        }
    
    0 讨论(0)
  • 2020-11-22 06:08

    I don't know if my solution is best practice, but I achieved loading a bitmap with my desired scaling by using the inDensity and inTargetDensity options. inDensity is 0 initially when not loading a drawable resource, so this approach is for loading non resource images.

    The variables imageUri, maxImageSideLength and context are parameters of my method. I posted only the method implementation without the wrapping AsyncTask for clarity.

                ContentResolver resolver = context.getContentResolver();
                InputStream is;
                try {
                    is = resolver.openInputStream(imageUri);
                } catch (FileNotFoundException e) {
                    Log.e(TAG, "Image not found.", e);
                    return null;
                }
                Options opts = new Options();
                opts.inJustDecodeBounds = true;
                BitmapFactory.decodeStream(is, null, opts);
    
                // scale the image
                float maxSideLength = maxImageSideLength;
                float scaleFactor = Math.min(maxSideLength / opts.outWidth, maxSideLength / opts.outHeight);
                // do not upscale!
                if (scaleFactor < 1) {
                    opts.inDensity = 10000;
                    opts.inTargetDensity = (int) ((float) opts.inDensity * scaleFactor);
                }
                opts.inJustDecodeBounds = false;
    
                try {
                    is.close();
                } catch (IOException e) {
                    // ignore
                }
                try {
                    is = resolver.openInputStream(imageUri);
                } catch (FileNotFoundException e) {
                    Log.e(TAG, "Image not found.", e);
                    return null;
                }
                Bitmap bitmap = BitmapFactory.decodeStream(is, null, opts);
                try {
                    is.close();
                } catch (IOException e) {
                    // ignore
                }
    
                return bitmap;
    
    0 讨论(0)
  • 2020-11-22 06:09

    When i have large bitmaps and i want to decode them resized i use the following

    BitmapFactory.Options options = new BitmapFactory.Options();
    InputStream is = null;
    is = new FileInputStream(path_to_file);
    BitmapFactory.decodeStream(is,null,options);
    is.close();
    is = new FileInputStream(path_to_file);
    // here w and h are the desired width and height
    options.inSampleSize = Math.max(options.outWidth/w, options.outHeight/h);
    // bitmap is the resized bitmap
    Bitmap bitmap = BitmapFactory.decodeStream(is,null,options);
    
    0 讨论(0)
  • 2020-11-22 06:10

    This worked for me. The function gets a path to a file on the sd card and returns a Bitmap in the maximum displayable size. The code is from Ofir with some changes like image file on sd instead a Ressource and the witdth and heigth are get from the Display Object.

    private Bitmap makeBitmap(String path) {
    
        try {
            final int IMAGE_MAX_SIZE = 1200000; // 1.2MP
            //resource = getResources();
    
            // Decode image size
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(path, options);
    
            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 pic = null;
            if (scale > 1) {
                scale--;
                // scale to max possible inSampleSize that still yields an image
                // larger than target
                options = new BitmapFactory.Options();
                options.inSampleSize = scale;
                pic = BitmapFactory.decodeFile(path, options);
    
                // resize to desired dimensions
    
                Display display = getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);
                int width = size.y;
                int height = size.x;
    
                //int height = imageView.getHeight();
                //int width = imageView.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(pic, (int) x, (int) y, true);
                pic.recycle();
                pic = scaledBitmap;
    
                System.gc();
            } else {
                pic = BitmapFactory.decodeFile(path);
            }
    
            Log.d("TAG", "bitmap size - width: " +pic.getWidth() + ", height: " + pic.getHeight());
            return pic;
    
        } catch (Exception e) {
            Log.e("TAG", e.getMessage(),e);
            return null;
        }
    
    }
    
    0 讨论(0)
  • 2020-11-22 06:13

    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;
    }
    
    0 讨论(0)
  • 2020-11-22 06:13

    After reading these answers and android documentation here's the code to resize bitmap without loading it into memory:

    public Bitmap getResizedBitmap(int targetW, int targetH,  String imagePath) {
    
        // Get the dimensions of the bitmap
        BitmapFactory.Options bmOptions = new BitmapFactory.Options();
        //inJustDecodeBounds = true <-- will not load the bitmap into memory
        bmOptions.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(imagePath, bmOptions);
        int photoW = bmOptions.outWidth;
        int photoH = bmOptions.outHeight;
    
        // Determine how much to scale down the image
        int scaleFactor = Math.min(photoW/targetW, photoH/targetH);
    
        // Decode the image file into a Bitmap sized to fill the View
        bmOptions.inJustDecodeBounds = false;
        bmOptions.inSampleSize = scaleFactor;
        bmOptions.inPurgeable = true;
    
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);
        return(bitmap);
    }
    
    0 讨论(0)
提交回复
热议问题