im having problems with high resolution images.
Im using nodpi-drawable folder for 1280x720 images, and using this code to scale it.
public static Drawable scaleDrawable(Drawable d, int width, Activity cxt)
{
BitmapDrawable bd = (BitmapDrawable)d;
double oldWidth = bd.getBitmap().getWidth();
double scaleFactor = width / oldWidth;
int newHeight = (int) (d.getIntrinsicHeight() * scaleFactor);
int newWidth = (int) (oldWidth * scaleFactor);
Drawable drawable = new BitmapDrawable(cxt.getResources(),MainScreen.getResizedBitmap(bd.getBitmap(),newHeight,newWidth));
BitmapDrawable bd2 = (BitmapDrawable)drawable;
return drawable;
}
public static Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
I use that code to scale images to screen witdh so if the screen is 320x480 the image will scale to 320 and keep proportions, i dont care if image go out of screen from bottom.
All its working fine, but when trying in a xhdpi device specifically a Samsung Galaxy Note 2 with a screen of exactly 720x1280.
It crash with Out Of Memory Exception in the line:
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
I cant understand why, the image should be scaled from 720 to 720 but my code must be really bad optimized or something.
I havent tried on a 1080x1920 device but it seems it will crash too.
Someone can see something bad when looking at the code?
use this method to resize your bitmap-
Bitmap bm=decodeSampledBitmapFromPath(src, reqWidth, reqHeight);
use this Defination-
public Bitmap decodeSampledBitmapFromPath(String path, int reqWidth,
int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(path, options);
return bmp;
}
}
public int calculateInSampleSize(BitmapFactory.Options options,
int reqWidth, int reqHeight) {
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);
}
}
return inSampleSize;
}
If you are using resource then replace method with BitmapFactory's decodeResource method..
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
....
.....
return BitmapFactory.decodeResource(res, resId, options);
}
You should use the utility class BitmapFactory for image-processing-operations. Also use BitmapFactory.Options to adjust the input/output sizes of the bitmaps. After a bitmap is not needed anymore, you should free the related memory.
Related
I have confusion between android bitmap scaling and sampling here may have two code one for scaling and another for sampling can anyone help me for identifying working of this two code and what is main differences between them.
Scaling :
public static Bitmap getScaleBitmap(Bitmap bitmap, int newWidth, int newHeight) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
return Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
}
Sampling :
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),R.id.myimage, 100, 100));
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
while ((halfHeight / inSampleSize) >= reqHeight
&& (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
here both code perform image resizing but different way so how I can identify which one is good and simple.
First code, takes bitmap and creates new smaller bitmap - you would use memmory for the bigger bitmap.
Second code takes resource. inJustDecodeBounds Makes it so you not load the whole bitmap into memmory just information for it. Then calculate how it should be sized, and then again when set inJustDecodeBounds to false load into memmory reduced version of the image. So you would use memmory only for the decoded image
Oficial docs
Scaling: First, you decode the whole bitmap in memory, then you scale it.
Sampling: You get the required scaled bitmap without loading the whole bitmap in memory.
This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
From the logcat I understand that the Activity crashes because of NullPointerException causes by this line of code.
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
R.id.picture, imageWidth, imageHeight));
I have read articles about similar problem but still getting NullPointerException. I think that mistake right under my nose, but still I can't catch it.
Where should I put this line to avoid NullPointerException?
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
ImageView mImageView = (ImageView) findViewById(R.id.picture);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.picture, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(),
R.id.picture, imageWidth, imageHeight));
}
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);
}
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) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
and here is ImageView from layout.
<ImageView
android:layout_height="240dp"
android:layout_width="199dp"
android:src="#drawable/galaxy"
android:id="#+id/picture"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
android:layout_weight="0.19" />
There is a cleaner alternative to display a bitmap onto a imageview.
Try this:
private Bitmap bitmap_image;
Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//get image from resources
bitmap_image = BitmapFactory.decodeResource(getResources(), R.drawable.picture);
ImageView mImageView = (ImageView) findViewById(R.id.picture);
mImageView.setImageBitmap(bitmap_image);
}
-To retrieve a image from resources and store it into a bitmap, use BitmapFactory.decodeResource(getResources(), R.drawable.yourimagename);
-at this point you can directly set the image to a imageView. If you want to manipulate the size of the image here is a function that will resize a bitmap:
public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// CREATE A MATRIX FOR THE MANIPULATION
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// RECREATE THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
-If you need clarification on the re-sizing of the bitmap, refer to this stackoverflow post:
How to Resize a Bitmap in Android?
-if you need a full working example, i have a activity that utilizes retrieving a image from resources and storing it into a bitmap and then displaying it on screen. The only part I do not have that you request, is displaying the bitmap in a imageView.
Github class link:
https://github.com/Rifat-Rashid/thePlatformer/blob/master/MainActivity.java
I am displaying an image in full screen mode,but whenever going to display large image then nothing display in the image view. Below code try to resize bitmap but got same result blank imageview.
public static Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
// RESIZE THE BIT MAP
matrix.postScale(scaleWidth, scaleHeight);
// RECREATE THE NEW BITMAP
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
You solve your problem from two mehtods
Method 1
call this method(function)
public Bitmap decodeImage(int resourceId) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), resourceId, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 100; // you are free to modify size as your requirement
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeResource(getResources(), resourceId, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
add this before your adapter
picture.setImageBitmap((decodeImage(item.drawableId));
instead of
picture.setImageResource(item.drawableId);
Mehtod 2
You need to adjust your image size. The better way is to decode the image to a bitmap, and set the bitmap to the ImageView. For example:
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), item.drawableId, opts);
picture.setImageBitmap (bitmap);
I had the same problem. Got is solved through the following code.
Provide the filepath, desired width and desired height to the method. For more reference see here
public static Bitmap decodeScaledBitmapFromSdCard(String filePath, int reqWidth,
int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(filePath, 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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
Probably the image size is too large that the system cannot allocate enough memory to load it.
you can try loading your image after scaling it it with BitmapFactory.Options sampleSize
check this link for more info
http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
may be this will work for you
public static Bitmap decodeSampledBitmapFromResource(String pathName,
int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth,
reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(pathName, 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) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
You solve your problem in Fragment
Add method in adapter
public Bitmap decodeImage(int gridViewImageId) {
try {
// Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeResource(mContext.getResources(), gridViewImageId, o);
// The new size we want to scale to
final int REQUIRED_SIZE = 100; // you are free to modify size as your requirement
// Find the correct scale value. It should be the power of 2.
int scale = 1;
while (o.outWidth / scale / 2 >= REQUIRED_SIZE && o.outHeight / scale / 2 >= REQUIRED_SIZE)
scale *= 2;
// Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
return BitmapFactory.decodeResource(mContext.getResources(), gridViewImageId, o2);
} catch (Throwable e) {
e.printStackTrace();
}
return null;
}
and add this line
imageViewAndroid.setImageBitmap(decodeImage(gridViewImageId[i]));
after researching, i only find InSampleSize to resize a bitmap
but i am looking for something that'll allow me to resize the bitmap to large or smaller depends on the screen resolution
since bitmap.createBitmap will result OOM, i have to use something else...
please help
here is the code where i resize my bitmap which result 5mb~10mb bump each time i resize anybitmap
Bitmap createdBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
thanks
you can use this to get the screen that is currently running on:
#Override
public void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
screenW = w;
screenH = h;
}
and then use this to load the bitmap so you are loading it into memory reasonably:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(selectedImagePath, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
if(imageWidth > imageHeight){
options.inSampleSize = calculateInSampleSize(options,screenH,screenW);
}else{
options.inSampleSize = calculateInSampleSize(options,screenW,screenH);
}
options.inJustDecodeBounds = false;
photo = BitmapFactory.decodeFile(selectedImagePath,options);
method:
public 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) {
// Calculate ratios of height and width to requested height and width
final int heightRatio = Math.round((float) height / (float) reqHeight);
final int widthRatio = Math.round((float) width / (float) reqWidth);
// Choose the smallest ratio as inSampleSize value, this will guarantee
// a final image with both dimensions larger than or equal to the
// requested height and width.
inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
}
return inSampleSize;
}
I would like to reduce My Bitmap image size to maximum of 640px. For example I have Bitmap image of size 1200 x 1200 px .. How can I reduce it to 640px.
If you pass bitmap width and height then use:
public Bitmap getResizedBitmap(Bitmap image, int bitmapWidth, int bitmapHeight) {
return Bitmap.createScaledBitmap(image, bitmapWidth, bitmapHeight, true);
}
If you want to keep the bitmap ratio the same, but reduce it to a maximum side length, use:
public Bitmap getResizedBitmap(Bitmap image, int maxSize) {
int width = image.getWidth();
int height = image.getHeight();
float bitmapRatio = (float) width / (float) height;
if (bitmapRatio > 1) {
width = maxSize;
height = (int) (width / bitmapRatio);
} else {
height = maxSize;
width = (int) (height * bitmapRatio);
}
return Bitmap.createScaledBitmap(image, width, height, true);
}
Use this Method
/** getResizedBitmap method is used to Resized the Image according to custom width and height
* #param image
* #param newHeight (new desired height)
* #param newWidth (new desired Width)
* #return image (new resized image)
* */
public static Bitmap getResizedBitmap(Bitmap image, int newHeight, int newWidth) {
int width = image.getWidth();
int height = image.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(image, 0, 0, width, height,
matrix, false);
return resizedBitmap;
}
or you can do it like this:
Bitmap.createScaledBitmap(Bitmap src, int dstWidth, int dstHeight, boolean filter);
Passing filter = false will result in a blocky, pixellated image.
Passing filter = true will give you smoother edges.
Here is working code to reduce Bitmap image resolution (pixels) to desired value...
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.Toast;
public class ImageProcessActivity extends AppCompatActivity {
private static final String TAG = "ImageProcessActivity";
private static final String IMAGE_PATH = "/sdcard/DCIM/my_image.jpg";
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap decodeSampleDrawableFromFile(String file, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(file, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(file, options);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image_process);
new AsyncTask<Object, Object, Bitmap>() {
#Override
protected Bitmap doInBackground(Object... objects) {
try {
return decodeSampleDrawableFromFile(IMAGE_PATH, 640, 640);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
((ImageView) findViewById(R.id.img)).setImageBitmap(bitmap);
}
}.execute();
}
}
Steps:
Get the Bitmap.Options (image info).
Sample the size to desired sample size.
Load to Bitmap with given options (desired resolution) from the image file into a bitmap object. But do this operation in a background thread.
Load Bitmap image into ImageView on UI thread (onPostExecute()).