Displaying images and managing memory in android - android

I wrote a program that at any time displays 8 user selected images on the screen. Each image is taken from its original form and scaled down to a uniform size. In order to do this I am using the code below:
Bitmap bitmapOrg = BitmapFactory.decodeFile(File Location Here);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 100;
int newHeight = 100;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width,
height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
//ImageView imageView = new ImageView(this);
ImageView iv = (ImageView) findViewById(R.id.imageView1);
// set the Drawable on the ImageView
iv.setImageDrawable(bmd);
// center the Image
iv.setScaleType(ScaleType.CENTER);
Even though my code works its not perfect. It seems like I'm using up a lot of memory especially calling this code possibly 8 times at once. Where in the code would I "recycle" the memory and how could I make this code possibly run better?
EDIT:
So I implemented the code in my project and it was working perfect and then I tried to add it to other sections and it just stopped working all together. My code looks like this: any idea what am I doing wrong?
BitmapFactory.Options options = new BitmapFactory.Options();
options.outWidth = 50;
options.outHeight = 50; Bitmap bitmap = BitmapFactory.decodeFile(path, options);
ImageView iv = (ImageView) findViewById(R.id.imageView7);
iv.setImageBitmap(bitmap);

You can use BitmapFactory.Options to scale the image as you decode it rather than reading in a full image and then scaling it.
BitmapFactory.Options options = new BitmapFactory.Options();
// You can play with this setting depending on how large your images are
// For example, to scale ~400x400 images to ~100x100, you can use 4.
options.inSampleSize = 4;
Bitmap bitmap = BitmapFactory.decodeFile(path, options);
Edit - George is correct. You should use inSampleSize to create a smaller image of the general size you need and then have it resized to the exact size you want using your ImageView. I've corrected my answer above to reflect this.
In any case, you should be much better off memory-wise if you are scaling the bitmaps during decode.

#matthew-willis I do not think you can use outWidth and outHeight to scale a bitmap. I believe they are output parameters only: they report the size of the bitmap created after the fact--setting them prior to decoding has no effect. You should use inSampleSize if you want to scale as you decode. George

Related

Reducing load image time and RAM

i am loading some bitmap from the gallery using the following code:
bitmap = (BitmapFactory.decodeFile(picturePath)).copy(Bitmap.Config.ARGB_8888, true);
bitmap = Bitmap.createScaledBitmap(bitmap, screenWidth, screenHeight, true);
bitmapCanvas = new Canvas(bitmap);
invalidate(); // refresh the screen
Question:
It seems that it takes so long time to load an image by first decode fully and copy, and then making scaling to fit for the screen width and height. It really actually does not need to load the pic with full density because I would not let the user to enlarge the imported image anyway.
In that way, are there any method to reduce the load time and RAM? (directly load a scaled-down image) How to further modify the above coding?
It may be worth trying RGB_565 instead of ARGB_8888 if you don't have transparency.
just have found the answer for this reducing RAM and load time and avoid outofmemory error from other similar questions.
//get importing bitmap dimension
Options op = new Options();
op.inJustDecodeBounds = true;
Bitmap pic_to_be_imported = BitmapFactory.decodeFile(picturePath, op);
final int x_pic = op.outWidth;
final int y_pic = op.outHeight;
//The new size we want to scale to
final int IMAGE_MAX_SIZE= (int) Math.max(DrawViewWidth, DrawViewHeight);
int scale = 1;
if (op.outHeight > IMAGE_MAX_SIZE || op.outWidth > IMAGE_MAX_SIZE)
{
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE /
(double) Math.max(op.outHeight, op.outWidth)) / Math.log(0.5)));
}
final BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
//Import the file using the o2 options: inSampleSized
bitmap = (BitmapFactory.decodeFile(picturePath, o2));
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);

Resize image generated by BitmapFactory.decodeByteArray()

I am creating audio player,I want to show the song cover to the player, it working with small image but if the mp3 file have large image then it goes out of layout view. I'm re-sizing image to 300x300 by using below code:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inDensity = 300;
opt.inTargetDensity = 300;
songCoverView.setImageBitmap(BitmapFactory.decodeByteArray(songCover, 0, songCover.length, opt));
But it still shows larger and goes out of layout.
Whats wrong with this code?
Turns out that there's a bug in Android: decodeByteArray somehow ignores some input options. A known workaround is using decodeStream with input array wrapped into ByteArrayInputStream instead, like this:
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inDensity = 300;
opt.inTargetDensity = 300;
songCoverView.setImageBitmap(BitmapFactory.decodeStream(new ByteArrayInputStream(songConver), null, opt));
try Bitmap.createScaledBitmap
bitmap = Bitmap.createScaledBitmap(songCover, 300, 300, true);
And you can keep the same aspect ratio for the old image... I use the following logic:
int width = songCover.getWidth();
int height = songCover.getHeight();
float scaleHeight = (float)height/(float)300;
float scaleWidth = (float)width /(float)300;
if (scaleWidth < scaleHeight) scale = scaleHeight;
else scale = scaleWidth;
bitmap = Bitmap.createScaledBitmap(songCover, (int)(width/scale), (int)(height/scale), true);
you can use the property the Bitmap
Bitmap bitmap = Bitmap.createScaledBitmap(image, (int)x, (int)y, true);

rotate bitmap and maintain size

I am creating a tile board game.
I want to rotate a bitmap tile piece by a few pre-determined degrees.
When I rotate my bitmap, the size changes.
For example, if I want a 75x75 triangle tile piece, on rotation I get a 68x68 back from this code. How can I keep things the same size so everything remains the size for the board?
Here's what I'm using to rotate:
public class RotatebitmapActivity extends Activity {
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout linLayout = new LinearLayout(this);
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.t123);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 75;
int newHeight = 75;
// calculate the scale - in this case = 0.4f
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// rotate the Bitmap
matrix.postRotate(120);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
ImageView imageView = new ImageView(this);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
// center the Image
imageView.setScaleType(ScaleType.CENTER);
// add ImageView to the Layout
linLayout.addView(imageView,
new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT
)
);
// set LinearLayout as ContentView
setContentView(linLayout);
}
What is happening in your code, is that the bitmap that is created is the correct size. However, all Bitmaps are automatically scaled to fit the density of the screen that is currently in use.
There are two ways (that I know of) to get the result you desire.
You can set the density of a bitmap after creating it and before wrapping it in a drawable. To do this, add code similar to:
resizedBitmap.setDensity(DisplayMetric.DENSITY_HIGH);
This code sets the density that the bitmap is designed for, and can prevent Android from auto-scaling the image.
The second solution is more of a methodology than a specific solution to this particular problem. I won't go into detail, for that see Supporting Multiple Screens | Android Developers
Hopefully someone will happen along that can answer your question better than I can.

Set wallpaper in Android

I have image 640*480 in sd card and i want to set it as home screen wallpaper. How Can I do this with small quality loss? With my code Wallpaper has a not good clarity. That's my code:
private void setWallpaper(String filePath) throws IOException{
if(filePath!=null){
// set options for decoding
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inScaled = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inDither = false;
options.inJustDecodeBounds = true;
//get bitmap from filepath
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
//get sizes of bitmap
int width = bitmap.getWidth();
int height = bitmap.getHeight();
//get sizes of screen and scale level
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int newWidth = display.getWidth()*2;
int newHeight = display.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
//set matrix of the scaling
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
//get new bitmap for wallpaper
Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
width, height, matrix, true);
//set bitmap as wallpaper
WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
wallpaperManager.clear();
wallpaperManager.setBitmap(resizedBitmap);
}
}
Any time you scale upwards you're going to suffer quality loss. What size screen are you trying to display this image one? Keep in mind the Android wallpaper dimensions are the height of the screen by 2 times the width of the screen (e.g. 800x480 screen needs a 800x960 image).
My recommendation would be to have a larger image and scale downwards. The quality loss from downsampling is far less noticeable than from upscaling. You could, if space is not an issue, include multiple sizes of the image for differing screen resolutions.

android drawable changes sizes on screen when reading image from file

I have an image on a private file.
I read the file, create the drawable, and assign it to an ImageView.
The ImageView has WRAP_CONTENT so the size is automatic.
On 320x480 screens, the image looks good
But on screens with more resolution and high density 480x800 or 480x854 (N1, droid) , when the image is for example 150x150, I see the image as 100x100.
Of course it has something to do with the density but not sure how should I resolve this.
This is my code:
FileInputStream fis = this.openFileInput("icon.png");
icon = Drawable.createFromStream(fis, "icon");
fis.close();
imageView.setImageDrawable(icon);
thanks
================================================================
update:
with the following code:
FileInputStream fis = this.openFileInput("icon.png");
icon = Drawable.createFromStream(fis, "icon");
if I then inspect the size of the icon, android thinks the size is 100x100, when really is 150x150.
Looks like its reducing the image by the density.
Can anybody explain this and how to avoid this.
Thanks
I am working on a similar issue, try this:
TypedValue typedValue = new TypedValue();
//density none divides by the density, density default keeps the original size
typedValue.density = TypedValue.DENSITY_DEFAULT;
Drawable d = Drawable.createFromResourceStream(null, typedValue, fis, "icon");
I am still working through how to develop a general solution to pick resolution/resize images from a generic stream instead of the drawable-* directories.
Rescale the image to desired dimension:
d = Drawable.createFromPath(filePath);
if (d != null) {
Bitmap bitmapOrg = ((BitmapDrawable) d).getBitmap();
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 170;
int newHeight = 170;
// calculate the scale
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);
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0,
width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
d = new BitmapDrawable(resizedBitmap);
}
Try BitmapDrawable#setTargetDensity:
Bitmap b = BitmapFactory.decodeFile(path)
BitmapDrawable bmd = new BitmapDrawable(b);
bmd.setTargetDensity(getResources().getDisplayMetrics());
Set some dimensions in device independent pixels (or DIPs).

Categories

Resources