android how to scale down a bitmap and keep its aspect ration

239 Views Asked by At

using 3rd party library which returns a bitmap. in the app it would like to scale down the bitmap.

static  public Bitmap getResizedBitmap(Bitmap bm, int newWidth, int newHeight) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);

        Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
                matrix, false);

        return resizedBitmap;
    }
===
Bitmap doScaleDownBitmap() {

        Bitmap bitmap = libGetBitmap();  // got the bitmap from the lib

        int width = bitmap.getWidth();
        int height = bitmap.getHeight();

        if (width > 320 || height > 160) {
            bitmap = getResizedBitmap(bitmap, 320, 160);
        }

        System.out.println("+++ width;"+width+", height:"+height+ ", return bmp.w :"+bitmap.getWidth()+", bmp.h:"+bitmap.getHeight());

        return bitmap;
    }

the log for a test bitmap (348x96):

+++ width;348, height:96, return bmp.w :320, bmp.h:160

looks like the resized bitmap does not scale properly, shouldnt it be 320 x 88 to maintain the aspect ratio?

(it did from (348x96) ==> (320x160))

saw android sample

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

how to apply it if has the bitmap already?

or what is the correct way to scale down a bitmap?

EDIT:

this one could keep the aspect ration and one of the desired dimensions (either width or height) will be used for the generated bitmap. basically CENTER_FIT.

However it does not generate the bitmap with both desired width and height.

e.g. would like to have a new bitmap of (w:240 x h:120) from a src bitmap of (w:300 x h:600), it will map to (w:60 x h:120).

I guess it needs extra operation on top of this new bitmap if want the new bitmap has (w:240 x h:120).

is there a simpler way to do it?

  public static Bitmap scaleBitmapAndKeepRation(Bitmap srcBmp, int dstWidth, int dstHeight) {
        Matrix matrix = new Matrix();
        matrix.setRectToRect(new RectF(0, 0, srcBmp.getWidth(), srcBmp.getHeight()), 
                new RectF(0, 0, dstWidth, dstHeight), 
                Matrix.ScaleToFit.CENTER);
        Bitmap scaledBitmap = Bitmap.createBitmap(srcBmp, 0, 0, srcBmp.getWidth(), srcBmp.getHeight(), matrix, true);
        return scaledBitmap;
    }
2

There are 2 best solutions below

0
HFZ On

When you Scale-Down the bitmap, if width and height are not divisible by scale, you should expect tiny change in ratio. if you don't want that, first crop the image to be divisible and then scale.

float scale=0.5f;
scaledBitmap=Bitmap.createScaledBitmap(bitmap,
    (int)(bitmap.width()*scale),
    (int)(bitmap.height()*scale),
    true); //bilinear filtering 
0
lannyf On

Found a way, I am sure there is better one

public static Bitmap updated_scaleBitmapAndKeepRation(Bitmap srcBitmap, int targetBmpWidth,
                                            int targetBmpHeight) {

        int width = srcBitmap.getWidth();
        int height = srcBitmap.getHeight();
        if (targetBmpHeight > 0 && targetBmpWidth > 0 && (width != targetBmpWidth || height != targetBmpHeight))  {

            // create a canvas with the specified bitmap to draw into.
            Bitmap scaledImage = Bitmap.createBitmap(targetBmpWidth, targetBmpHeight, Bitmap.Config.ARGB_4444);
            Canvas canvas = new Canvas(scaledImage);

            // draw transparent background color
            Paint paint = new Paint();
            paint.setColor(Color.TRANSPARENT);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), paint);

            // draw the source bitmap on canvas and scale the image with center_fit (the source image's larger side is mapped to the corresponding desired dimensions, and the other side scaled with aspect ration)
            Matrix matrix = new Matrix();
            matrix.setRectToRect(new RectF(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight()),
                    new RectF(0, 0, targetBmpWidth, targetBmpHeight),
                    Matrix.ScaleToFit.CENTER);
            canvas.drawBitmap(srcBitmap, matrix, null);

            return scaledImage;

        } else {
            return srcBitmap;
        }
    }

The result screenshot:

The 1st image is the src (w:1680 x h:780),

2nd is from the scaleBitmapAndKeepRation() in the question part, which has scaled image but with dimensions (w:60 x h:120) not in desired dimensions (w: 240 x h:120),

3rd is the one does not keep the aspect ration, although has the dimension right.

4th is from the updated_scaleBitmapAndKeepRation() which has the desired dimensions and the image is center_fit and keep the aspect ratio.

enter image description here