I am loading an image with Universal Image Loader, and I'd like to scale it so that the width is the width of the screen and the height is scaled accordingly, meaning that before I load the image I know the width that I want, but not the height. So when I load the image I want to get the height and width of the image and use that to scale it according to the width of the screen. The code I use to do this is this:
try {
display.getSize(size);
scaledWidth = size.x;
} catch (java.lang.NoSuchMethodError ignore) {
scaledWidth = display.getWidth();
}
String filePath = "file://" + getBaseContext().getFilesDir().getPath().toString() + "/" + imagePath + ".png";
Bitmap bitmap = imageLoader.loadImageSync(filePath);
int height = bitmap.getHeight();
int width = bitmap.getWidth();
scaledHeight = (int) (((scaledWidth * 1.0) / width) * height);
//Code to resize Bitmap using scaledWidth and scaledHeight
What's the best way to resize the Bitmap using Universal Image Loader, or even better, is there a way that I can specify only the width and the Bitmap is scaled properly based on it's proportions?
You needn't do it yourself. Consider using ImageScaleType.EXACTLY
new DisplayImageOptions.Builder().imageScaleType(ImageScaleType.EXACTLY)
https://github.com/nostra13/Android-Universal-Image-Loader/blob/master/library/src/com/nostra13/universalimageloader/core/assist/ImageScaleType.java
use can use
// Load image, decode it to Bitmap and return Bitmap to callback
ImageSize targetSize = new ImageSize(120, 80); // result Bitmap will be fit to this size
imageLoader.loadImage(imageUri, targetSize, displayOptions, new SimpleImageLoadingListener() {
#Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// Do whatever you want with Bitmap
}
});
I haven't read your code but the calculations for determining the height are pretty easy. What we want is to keep the aspect ratio of the image intact. So first calculate the aspect_ratio i.e
imageHeight/imageWidth = aspt_ratio;
then take this aspect_ratio and multiply it with the current screen width to get the height.
scaledHeight = aspt_ratio*screen_width;
since we know that the scaled width of the image will always be equal to the screen width,according to your requirement.
This will work as you need
scaledWidth = size.x;
String filePath = "file://" + getBaseContext().getFilesDir().getPath().toString() + "/" + imagePath + ".png";
android.graphics.BitmapFactory.Options options= new Options();
options.inJustDecodeBounds=true;
//Just gets image size without allocating memory
BitmapFactory.decodeFile(filePath, options);
int height = options.outHeight;
int width = bitmap.outWidth;
scaledHeight = (int) (((scaledWidth * 1.0) / width) * height);
ImageSize targetSize = new ImageSize(scaledWidth, scaledHeight);
Bitmap bmp = imageLoader.loadImageSync(imageUri, targetSize, displayOptions);
Related
I've implemented the below code to scale an image down, with respect to the aspect ratio (by proportionally decreasing height/width with respect to one another).. This will definitely help to reduce the size of the images uploaded to my backend, but this does not take into account the resolution of the images. I want to set a hard image limit, say 800Kb, and if the image is greater than 800Kb after resizing, then compress to a point at which it is less than 800Kb.
Anyone have any experience doing something like this? I'm curious what relationship lies between the quality argument passed into the Bitmap.Compress method and how much file size is shaven off per percentage quality - If i could obtain this information, I believe I can reach my goal.
Thanks for any help ahead of time, my current code is below, maybe it will help others heading in this direction in the future.
public static void uploadImage(String url, File file, Callback callback, Context context,
IMAGE_PURPOSE purpose) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
int maxWidth = 0;
int maxHeight = 0;
int maxSize = 0;
switch(purpose){
case PROFILE:
maxWidth = Constants.MAX_PROFILE_IMAGE_WIDTH;
maxHeight = Constants.MAX_PROFILE_IMAGE_HEIGHT;
maxSize = Constants.MAX_PROFILE_IMAGE_SIZE;
break;
case UPLOAD:
maxWidth = Constants.MAX_UPLOAD_IMAGE_WIDTH;
maxHeight = Constants.MAX_UPLOAD_IMAGE_HEIGHT;
maxSize = Constants.MAX_UPLOAD_IMAGE_SIZE;
break;
}
int newWidth = bitmap.getWidth();
int newHeight = bitmap.getHeight();
// Make sure the width is OK
if(bitmap.getWidth() > maxWidth){
// Find out how much the picture had to shrink to get to our max defined width
float shrinkCoeff = ((float)(bitmap.getWidth() - maxWidth) / (float)bitmap.getWidth());
newWidth = maxWidth;
// Shrink the height by the same amount to maintain aspect ratio
newHeight = bitmap.getHeight() - (int)((float)bitmap.getHeight() * shrinkCoeff);
}
// Make sure the height is OK
if(newHeight > maxHeight){
// Find out how much the picture had to shrink to get to our max defined width
float shrinkCoeff = ((newHeight - maxHeight) / newHeight);
newHeight = maxHeight;
// Shrink the width by the same amount to maintain aspect ratio
newWidth = newWidth - (int)((float)newWidth * shrinkCoeff);
}
Bitmap resized = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
// Get the image in bytes
ByteArrayOutputStream bos = new ByteArrayOutputStream();
resized.compress(Bitmap.CompressFormat.JPEG, 100, bos);
byte[] imageBytes = bos.toByteArray();
// If the size on disk is too big, reduce the quality
if(imageBytes.length > maxSize){
// Compress image here to get to maxSize
}
I am using camera API to take picture i have to open camera in different sizes according to my Image view size. I am following the sample project which we get inside Android sdk/sample/adroid-18 at the name "ApiDemo" the thing i have changed is not set camera on setcontentview. I have set the camera on Frame Layout. at first my camera preview was starched so i got the camera OptimalPreviewSize and make FrameLayout parameter width and height as wrap-content.Now the camera preview is smaller then ImageView (The size i want). If i make the size of FrameLayout parameter as match-parent then camera View is stretch.How to resolve this issue.
find this link for more specification. Android camera preview look strange
UPDATE
My camera preview size is fine now i use the on Layout method the idea was i have the bigger layout then my ImageView and now camera preview is looking good.
Now the Problem I am facing is set the image of proper size for this I have to center crop and scale in same size in like my ImageView.this Image i get by TakePicture method and saved in sdcard.
For this I am using this method:-
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width, respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap will now
// be
RectF targetRect = new RectF(left+50, top, left + scaledWidth, top + scaledHeight+50);
// RectF targetRect = new RectF(0, 0, newWidth, newHeight/2);
// Finally, we create a new bitmap of the specified size and draw our new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
But the result image quality is not good.Height Corners are cutting from top and bottom, and result image quality is not good.Pixels are stretching.
Don't tell me to use scaleType=Center_crop i can't use it in my case,and don't want to show cropping frame to user,this all process should not show on UI.
UPDATE
I used blow method for crop image from center and scale according to my imageView size
Bitmap dstBmp = ThumbnailUtils.extractThumbnail(source, newWidth, newHeight);
But the bitmap i got is not looking same the camera preview shown on FrameLayout. because camera preview is big.I think these code cropped the large area.
I tried to reduce the width and change the height but not getting the same cropped image in which ratio i want.
One more idea i have after picture crop a last image frame set automatically on FrameLayout. can we get that set frame from Frame Layout. How is this possible?
Here is question like this How to retrieve the visible part of a SurfaceView in Android do any one have solution.
I want to achieve this by this line ThumbnailUtils.extractThumbnail(source, newWidth, newHeight);and by this line i am getting src like image described in diagram .
What to change in this line exactly ????
Center crop an image may be help you this.
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
// Compute the scaling factors to fit the new height and width, respectively.
// To cover the final image, the final scaling will be the bigger
// of these two.
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
// Now get the size of the source bitmap when scaled
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
// Let's find out the upper left coordinates if the scaled bitmap
// should be centered in the new size give by the parameters
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
// The target rectangle for the new, scaled version of the source bitmap will now
// be
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
// Finally, we create a new bitmap of the specified size and draw our new,
// scaled bitmap onto it.
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
#Akanksha Please use this below code, you just need to pass the path of the saved image, and the hight and width of our imageview. This code works perfectly for me.
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class ImageHandler {
/**
* Decode and sample down a bitmap from a file to the requested width and
* height.
*
* #param filename
* The full path of the file to decode
* #param reqWidth
* The requested width of the resulting bitmap
* #param reqHeight
* The requested height of the resulting bitmap
* #return A bitmap sampled down from the original with the same aspect
* ratio and dimensions that are equal to or greater than the
* requested width and height
*/
public static Bitmap decodeSampledBitmapFromFile(String filename,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filename, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filename, options);
}
public static int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
if (width > height) {
inSampleSize = Math.round((float) height / (float) reqHeight);
} else {
inSampleSize = Math.round((float) width / (float) reqWidth);
}
// This offers some additional logic in case the image has a
// strange
// aspect ratio. For example, a panorama may have a much larger
// width than height. In these cases the total pixels might
// still
// end up being too large to fit comfortably in memory, so we
// should
// be more aggressive with sample down the image (=larger
// inSampleSize).
final float totalPixels = width * height;
// Anything more than 2x the requested pixels we'll sample down
// further.
final float totalReqPixelsCap = reqWidth * reqHeight * 2;
while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
inSampleSize++;
}
}
return inSampleSize;
}
}
I call this method inside async task because it may take too much UImemory and time
Here is how I call it.
class Asyncing extends AsyncTask {
private int reqWidth;
private int reqHeight;
private ImageView iv;
private String fileName;
private ProgressDialog pd;
public Asyncing(int reqWidth, int reqHeight, ImageView iv,
String fileName) {
super();
this.reqWidth = reqWidth;
this.reqHeight = reqHeight;
this.fileName = fileName;
this.iv = iv;
}
#Override
protected Bitmap doInBackground(String... params) {
return ImageHandler.decodeSampledBitmapFromFile(params[0],
reqWidth, reqHeight);
}
#Override
protected void onPostExecute(Bitmap result) {
iv.setImageBitmap(result);
if (pd.isShowing()) {
pd.setMessage(getString(R.string.completed));
pd.dismiss();
}
super.onPostExecute(result);
}
#Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(CustomerDetailsActivity.this, "",
getString(R.string.processing_signature));
super.onPreExecute();
}
}
This is how you need to call the asynctask
signedImagePath = data.getExtras().getString("imagePath");
new Asyncing(signature_img.getWidth(), signature_img.getHeight(),
signature_img, "spenTest.png").execute(signedImagePath);
above code is written according to my requirements,you modify it according to yours.
How can I fit any size image coming in from a http request into a ImageView with the size of 100x100.
android:layout_width="100dp"
android:layout_height="100dp"
I want to avoid loosing accuracy and I'd like to see the exact same image but in 100x100 size.
I have tried with Bitmap.createScaledBitmap(bitmap, 100, 100, true); but failed.
What are the correct steps doing this?
Check out this link :
Fit image into ImageView, keep aspect ratio and then resize ImageView to image dimensions?
And ImageView that is a fixed size, regardless of image size
Try
android:scaleType="fitXY"
for larger images
Take a look at this:
"This cannot be done in xml only. It is not possible to scale both the image and the ImageView so that image's one dimension would always be 100dp and the ImageView would have the same dimensions as the image.
This code scales Drawable of an ImageView to stay in a square like 100dp x 100dp with one dimension exactly 100dp and keeping the aspect ratio. Then the ImageView is resized to match the dimensions of the scaled image. The code is used in an activity. I tested it via button click handler.
private void scaleImage()
{
// Get the ImageView and its bitmap
ImageView view = (ImageView) findViewById(R.id.image_box);
Drawable drawing = view.getDrawable();
if (drawing == null) {
return; // Checking for null & return, as suggested in comments
}
Bitmap bitmap = ((BitmapDrawable)drawing).getBitmap();
// Get current dimensions AND the desired bounding box
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int bounding = dpToPx(250);
Log.i("Test", "original width = " + Integer.toString(width));
Log.i("Test", "original height = " + Integer.toString(height));
Log.i("Test", "bounding = " + Integer.toString(bounding));
// Determine how much to scale: the dimension requiring less scaling is
// closer to the its side. This way the image always stays inside your
// bounding box AND either x/y axis touches it.
float xScale = ((float) bounding) / width;
float yScale = ((float) bounding) / height;
float scale = (xScale <= yScale) ? xScale : yScale;
Log.i("Test", "xScale = " + Float.toString(xScale));
Log.i("Test", "yScale = " + Float.toString(yScale));
Log.i("Test", "scale = " + Float.toString(scale));
// Create a matrix for the scaling and add the scaling data
Matrix matrix = new Matrix();
matrix.postScale(scale, scale);
// Create a new bitmap and convert it to a format understood by the ImageView
Bitmap scaledBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
width = scaledBitmap.getWidth(); // re-use
height = scaledBitmap.getHeight(); // re-use
BitmapDrawable result = new BitmapDrawable(scaledBitmap);
Log.i("Test", "scaled width = " + Integer.toString(width));
Log.i("Test", "scaled height = " + Integer.toString(height));
// Apply the scaled bitmap
view.setImageDrawable(result);
// Now change ImageView's dimensions to match the scaled image
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams();
params.width = width;
params.height = height;
view.setLayoutParams(params);
Log.i("Test", "done");
}
private int dpToPx(int dp)
{
float density = getApplicationContext().getResources().getDisplayMetrics().density;
return Math.round((float)dp * density);
}
The xml code for the ImageView:
<ImageView a:id="#+id/image_box"
a:background="#ff0000"
a:src="#drawable/star"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_marginTop="20dp"
a:layout_gravity="center_horizontal"/>"
source
I like to resize an bitmap,if it is big make it small and place it on an specific location in the surface view, I need to get device width and height and then bitmap size and place them in an surface view and then take another image resize it and place it in any location has i like.
How to know the location co ordinates first(to place second image - this should be device independent if larger/smaller screen layout should not change)
and how to scale an big bitmap and set as background so i can draw an image over it.
my code :
final int canvasWidth = getWidth();
final int canvasHeight = getHeight();
int imageWidth = img.getWidth();
int imageHeight = img.getHeight();
float scaleFactor = Math.min( (float)canvasWidth / imageWidth,
(float)canvasHeight / imageHeight );
Bitmap scaled = Bitmap.createScaledBitmap( img,
(int)(scaleFactor * imageWidth),
(int)(scaleFactor * imageHeight),
true );
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(scaled, 10, 10, null);
this scale the big image but it doesn't fit the whole screen "img - bitmap is like an background image "
Someone can help me to understand the resize basics (I'm new so finding hard to understand resizing) to resize image to fit screen and resize any image to smaller one and place it in any location i like .
Store your bitmap as a source for manipulation and display it using ImageView:
Bitmap realImage = BitmapFactory.decodeFile(filePathFromActivity.toString());
Bitmap newBitmap = scaleDown(realImage, MAX_IMAGE_SIZE, true);
imageView.setImageBitmap(newBitmap);
//scale down method
public static Bitmap scaleDown(Bitmap realImage, float maxImageSize,
boolean filter) {
float ratio = Math.min(
(float) maxImageSize / realImage.getWidth(),
(float) maxImageSize / realImage.getHeight());
int width = Math.round((float) ratio * realImage.getWidth());
int height = Math.round((float) ratio * realImage.getHeight());
Bitmap newBitmap = Bitmap.createScaledBitmap(realImage, width,
height, filter);
return newBitmap;
}
And set your ImageView's width and height to "fill_parent".
I have a large sized image. At runtime, I want to read the image from storage and scale it so that its weight and size gets reduced and I can use it as a thumbnail. When a user clicks on the thumbnail, I want to display the full-sized image.
Try this
Bitmap ThumbImage = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(imagePath), THUMBSIZE, THUMBSIZE);
This Utility is available from API_LEVEl 8. [Source]
My Solution
byte[] imageData = null;
try
{
final int THUMBNAIL_SIZE = 64;
FileInputStream fis = new FileInputStream(fileName);
Bitmap imageBitmap = BitmapFactory.decodeStream(fis);
imageBitmap = Bitmap.createScaledBitmap(imageBitmap, THUMBNAIL_SIZE, THUMBNAIL_SIZE, false);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
imageData = baos.toByteArray();
}
catch(Exception ex) {
}
The best solution I found is the following. Compared with the other solutions this one does not need to load the full image for creating a thumbnail, so it is more efficient!
Its limit is that you can not have a thumbnail with exact width and height but the solution as near as possible.
File file = ...; // the image file
Options bitmapOptions = new Options();
bitmapOptions.inJustDecodeBounds = true; // obtain the size of the image, without loading it in memory
BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);
// find the best scaling factor for the desired dimensions
int desiredWidth = 400;
int desiredHeight = 300;
float widthScale = (float)bitmapOptions.outWidth/desiredWidth;
float heightScale = (float)bitmapOptions.outHeight/desiredHeight;
float scale = Math.min(widthScale, heightScale);
int sampleSize = 1;
while (sampleSize < scale) {
sampleSize *= 2;
}
bitmapOptions.inSampleSize = sampleSize; // this value must be a power of 2,
// this is why you can not have an image scaled as you would like
bitmapOptions.inJustDecodeBounds = false; // now we want to load the image
// Let's load just the part of the image necessary for creating the thumbnail, not the whole image
Bitmap thumbnail = BitmapFactory.decodeFile(file.getAbsolutePath(), bitmapOptions);
// Save the thumbnail
File thumbnailFile = ...;
FileOutputStream fos = new FileOutputStream(thumbnailFile);
thumbnail.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
fos.close();
// Use the thumbail on an ImageView or recycle it!
thumbnail.recycle();
Here is a more complete solution to scaling down a Bitmap to thumbnail size. It expands on the Bitmap.createScaledBitmap solution by maintaining the aspect ratio of the images and also padding them to the same width so that they look good in a ListView.
Also, it would be best to do this scaling once and store the resulting Bitmap as a blob in your Sqlite database. I have included a snippet on how to convert the Bitmap to a byte array for this purpose.
public static final int THUMBNAIL_HEIGHT = 48;
public static final int THUMBNAIL_WIDTH = 66;
imageBitmap = BitmapFactory.decodeByteArray(mImageData, 0, mImageData.length);
Float width = new Float(imageBitmap.getWidth());
Float height = new Float(imageBitmap.getHeight());
Float ratio = width/height;
imageBitmap = Bitmap.createScaledBitmap(imageBitmap, (int)(THUMBNAIL_HEIGHT*ratio), THUMBNAIL_HEIGHT, false);
int padding = (THUMBNAIL_WIDTH - imageBitmap.getWidth())/2;
imageView.setPadding(padding, 0, padding, 0);
imageView.setImageBitmap(imageBitmap);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
imageBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
byte[] byteArray = baos.toByteArray();
Use BitmapFactory.decodeFile(...) to get your Bitmap object and set it to an ImageView with ImageView.setImageBitmap().
On the ImageView set the layout dimensions to something small, eg:
android:layout_width="66dip" android:layout_height="48dip"
Add an onClickListener to the ImageView and launch a new activity, where you display the image in full size with
android:layout_width="wrap_content" android:layout_height="wrap_content"
or specify some larger size.
/**
* Creates a centered bitmap of the desired size.
*
* #param source original bitmap source
* #param width targeted width
* #param height targeted height
* #param options options used during thumbnail extraction
*/
public static Bitmap extractThumbnail(
Bitmap source, int width, int height, int options) {
if (source == null) {
return null;
}
float scale;
if (source.getWidth() < source.getHeight()) {
scale = width / (float) source.getWidth();
} else {
scale = height / (float) source.getHeight();
}
Matrix matrix = new Matrix();
matrix.setScale(scale, scale);
Bitmap thumbnail = transform(matrix, source, width, height,
OPTIONS_SCALE_UP | options);
return thumbnail;
}
I found an easy way to do this
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(BitmapFactory.decodeFile(mPath),200,200)
Syntax
Bitmap thumbnail = ThumbnailUtils.extractThumbnail(Bitmap source,int width,int height)
OR
use Picasso dependancy
compile 'com.squareup.picasso:picasso:2.5.2'
Picasso.with(context)
.load("file:///android_asset/DvpvklR.png")
.resize(50, 50)
.into(imageView2);
Reference Picasso
If you want high quality result, so use [RapidDecoder][1] library. It is simple as follow:
import rapid.decoder.BitmapDecoder;
...
Bitmap bitmap = BitmapDecoder.from(getResources(), R.drawable.image)
.scale(width, height)
.useBuiltInDecoder(true)
.decode();
Don't forget to use builtin decoder if you want to scale down less than 50% and a HQ result.
This answer is based on the solution presented in https://developer.android.com/topic/performance/graphics/load-bitmap.html (without using of external libraries) with some changes by me to make its functionality better and more practical.
Some notes about this solution:
It is assumed that you want to keep the aspect ratio. In other words:
finalWidth / finalHeight == sourceBitmap.getWidth() / sourceBitmap.getWidth() (Regardless of casting and rounding issues)
It is assumed that you have two values (maxWidth & maxHeight) that you want any of the dimensions of your final bitmap doesn't exceed its corresponding value. In other words:
finalWidth <= maxWidth && finalHeight <= maxHeight
So minRatio has been placed as the basis of calculations (See the implementation). UNLIKE the basic solution that has placed maxRatio as the basis of calculations in actual. Also, the calculation of inSampleSize has been so much better (more logic, brief and efficient).
It is assumed that you want to (at least) one of the final dimensions has exactly the value of its corresponding maxValue (each one was possible, by considering the above assumptions). In other words:
finalWidth == maxWidth || finalHeight == maxHeight
The final additional step in compare to the basic solution (Bitmap.createScaledBitmap(...)) is for this "exactly" constraint. The very important note is you shouldn't take this step at first (like the accepted answer), because of its significant consumption of memory in case of huge images!
It is for decoding a file. You can change it like the basic solution to decode a resource (or everything that BitmapFactory supports).
The implementation:
public static Bitmap decodeSampledBitmap(String pathName, int maxWidth, int maxHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
final float wRatio_inv = (float) options.outWidth / maxWidth,
hRatio_inv = (float) options.outHeight / maxHeight; // Working with inverse ratios is more comfortable
final int finalW, finalH, minRatio_inv /* = max{Ratio_inv} */;
if (wRatio_inv > hRatio_inv) {
minRatio_inv = (int) wRatio_inv;
finalW = maxWidth;
finalH = Math.round(options.outHeight / wRatio_inv);
} else {
minRatio_inv = (int) hRatio_inv;
finalH = maxHeight;
finalW = Math.round(options.outWidth / hRatio_inv);
}
options.inSampleSize = pow2Ceil(minRatio_inv); // pow2Ceil: A utility function that comes later
options.inJustDecodeBounds = false; // Decode bitmap with inSampleSize set
return Bitmap.createScaledBitmap(BitmapFactory.decodeFile(pathName, options),
finalW, finalH, true);
}
/**
* #return the largest power of 2 that is smaller than or equal to number.
* WARNING: return {0b1000000...000} for ZERO input.
*/
public static int pow2Ceil(int number) {
return 1 << -(Integer.numberOfLeadingZeros(number) + 1); // is equivalent to:
// return Integer.rotateRight(1, Integer.numberOfLeadingZeros(number) + 1);
}
Sample Usage, in case of you have an imageView with a determined value for layout_width (match_parent or a explicit value) and a indeterminate value for layout_height (wrap_content) and instead a determined value for maxHeight:
imageView.setImageBitmap(decodeSampledBitmap(filePath,
imageView.getWidth(), imageView.getMaxHeight()));