Create a file from drawable - android

I have a large number of resources in my drawable folder.All are having big size more than 500KB. I have to load all these 25 images all at once in a srollView. As usual I ran out of memory. Is there any way to reduce the size of the image programmatically.
I got this function but it's parameter is a File and I don't know how to create a File from a drawable.
private Bitmap decodeFile(File f){
Bitmap b = null;
try {
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
FileInputStream fis = new FileInputStream(f);
BitmapFactory.decodeStream(fis, null, o);
fis.close();
int scale = 1;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE / (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
//Decode with inSampleSize
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = scale;
fis = new FileInputStream(f);
b = BitmapFactory.decodeStream(fis, null, o2);
fis.close();
} catch (FileNotFoundException e) {
}
return b;
}
I have to go thorugh this screen cyclically many times after a number of traversal the system is showing Low memory and it is removing the other views in the back from the stack , but I actually need it.
Please help me.

You can open an InputStream from your drawable resource using following code:
InputStream is = getResources().openRawResource(id);
here id is the identifier of your drawable resource. for eg: R.drawable.abc
Now using this input stream you can create a file. If you also need help on how create a file using this input stream then tell me.
Update: to write data in a file:
try
{
File f=new File("your file name");
InputStream inputStream = getResources().openRawResource(id);
OutputStream out=new FileOutputStream(f);
byte buf[]=new byte[1024];
int len;
while((len=inputStream.read(buf))>0)
out.write(buf,0,len);
out.close();
inputStream.close();
}
catch (IOException e){}
}

I like shortcuts so I prefer using this.
For creatting file from drawable see this.
Put this in your build.gradle
compile 'id.zelory:compressor:1.0.4'
And wherever you want to compress the image put
Bitmap compressedImageFile = Compressor.getDefault(context).compressToBitmap(your_file);
README.md provides much more information. Sorry for bringing the answer after 6 years.

bro there are two methods
Easiest one Use some 3rd party library
Or use Bitmap class to scale drawable down
just use Bitmap.createScaledBitmap method to compress drawables
steps :
// Step 1 Load drawable and convert it to bitmap
Bitmap b = BitmapFactory.decodeResource( context , resId )
// Step 2 reScale your Bitmap
Bitmap nBitmap = b.createScaledBitmap( getResources() , newHieght , newWidth , true );
// Step 3 Create a drawable from bitmap
BitmapDrawable drawable = new BitmapDrawable(nBitmap);
I highly recommend you to use 3rd part library because this method is very expensive
like https://github.com/Tourenathan-G5organisation/SiliCompressor

I took cue from #mudit's answer and created the drawable from Input Stream. And later loaded the drawable to the ImageView in the Adapter.
InputStream inputStream = mContext.getResources().openRawResource(R.drawable.your_id);
Bitmap b = BitmapFactory.decodeStream(inputStream);
b.setDensity(Bitmap.DENSITY_NONE);
Drawable d = new BitmapDrawable(b);
mImageView.setImageDrawable(d);
Here is the detailed version of solution

Related

Image Compression before uploading Bitmap.CompressFormat

I'm trying to compress images selected by user from gallery for uploading. I saw that my camera pictures are over 5MB and I would like to compress them(same as facebook if possible). What I've been trying:
I let the user select the photo from gallery,get the uri and use this:
File file = new File(getRealPathFromURI(getActivity(), selectedImageUri));
long length = file.length();
Log.e("Filesize:", "Before: " + length);
if (file.getName().toLowerCase().endsWith("jpg")||file.getName().toLowerCase().endsWith("jpeg")){
Bitmap original;
try {
original = MediaStore.Images.Media.getBitmap(getActivity().getContentResolver(), selectedImageUri);
length = sizeOf(original);
Log.e("Filesize:", "BeforeCompression: " + length);
ByteArrayOutputStream out = new ByteArrayOutputStream();
original.compress(Bitmap.CompressFormat.JPEG, 50, out);
Bitmap decoded = BitmapFactory.decodeStream(new ByteArrayInputStream(out.toByteArray()));
length = sizeOf(decoded);
Log.e("Filesize:", "AfterCompression: " + length);
} catch (IOException e) {
e.printStackTrace();
Log.e("Filesize:", "Error: " + e);
}
I did this to test if it was working first, but what I get in the console is:
/name.company.newapp E/Filesize:: Before: 4970874
/name.company.newapp E/Filesize:: BeforeConversion: 63489024
/name.company.newapp E/Filesize:: AfterConversion: 63489024
The size doesn't change at all. Is this the right approach ?
This happens because you're actually getting the size of memory used by the Bitmap object by calling sizeOf(bitmap) and not the actual file size.
As you should know, a bitmap operates with the number of pixels in an image. Even though you compress the image using a JPEG compression, the image's width and height do not change. Thus the number of pixels do not change and thus the Bitmap's size (in memory) would not change too.
However, if you save the compressed bitmap to a location in your hard disk and use File.length() to calculate the size of the compressed image, then you will notice the difference.
Please check the size before decoding and after compression:
length = sizeOf(original);
Also i would recommend you to flush and close the outputstream:
out.flush();
out.close();
Hope i could help!
Edit:
Please try the following method to decode your bitmap:
public static Bitmap decodeFile(File f,int WIDTH,int HEIGHT){
try {
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(new FileInputStream(f),null,o);
final int REQUIRED_WIDTH=WIDTH;
final int REQUIRED_HEIGHT=HEIGHT;
//Find the correct scale value. It should be the power of 2.
int scale=1;
while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HEIGHT)
scale*=2;
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=scale;
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
} catch (FileNotFoundException e) {}
return null;
}
You can change the width and height of your picture to make it smaller.

Resizing bitmap from InputStream

I trying to resize one image from InputStream, so I use the code in Strange out of memory issue while loading an image to a Bitmap object but I don't know why this code always return Drawable without image.
This one works well:
private Drawable decodeFile(InputStream f){
try {
InputStream in2 = new BufferedInputStream(f);
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize=2;
return new BitmapDrawable(BitmapFactory.decodeStream(in2, null, o2));
} catch (Exception e) {
return null;
}
}
This one does not work:
private Drawable decodeFile(InputStream f){
try {
InputStream in1 = new BufferedInputStream(f);
InputStream in2 = new BufferedInputStream(f);
//Decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inJustDecodeBounds = true;
BitmapFactory.decodeStream(in1,null,o);
//The new size we want to scale to
final int IMAGE_MAX_SIZE=90;
//Find the correct scale value. It should be the power of 2.
int scale = 2;
if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
scale = (int)Math.pow(2, (int) Math.round(Math.log(IMAGE_MAX_SIZE /
(double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
}
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inJustDecodeBounds = false;
o2.inSampleSize=scale;
return new BitmapDrawable(BitmapFactory.decodeStream(in2, null, o2));
} catch (Exception e) {
return null;
}
}
why one option affect the other? how its possible if I use two different InputStream and Options?
Actually you have two different BufferedInputStream but they internally use the only one InputStream object because BufferedInputStream is only a wrapper for InputStream.
So you can't just call two times BitmapFactory.decodeStream method on the same stream, it will definitely fail because the second time it wouldn't start decoding from the beginning of the stream. You need to reset your stream if it is supported or reopen it.
this is my code that works well, I hope this will help
//Decode image size
BitmapFactory.Options optionsIn = new BitmapFactory.Options();
optionsIn.inJustDecodeBounds = true; // the trick is HERE, avoiding memory leaks
BitmapFactory.decodeFile(filePath, optionsIn);
BitmapFactory.Options optionsOut = new BitmapFactory.Options();
int requiredWidth = ECameraConfig.getEntryById(Preferences.I_CAMERA_IMAGE_RESOLUTION.get()).getyAxe();
float bitmapWidth = optionsIn.outWidth;
int scale = Math.round(bitmapWidth / requiredWidth);
optionsOut.inSampleSize = scale;
optionsOut.inPurgeable = true;//avoiding memory leaks
return BitmapFactory.decodeFile(filePath, optionsOut);
And I belive you dont need 2 InputStream.

Error:Bitmap size Exceed VM budget, when converting image to ARGB_8888

Here is my code:
File file = new File(Path to Jpeg File size is 700kb);
InputStream in = null;
try {
in = new BufferedInputStream(new FileInputStream(file));
}
catch (Exception e) {
// TODO: handle exception
}
bitmap =BitmapFactory.decodeStream(in);
bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
Please help i get error in this copy line i want to make its ARGB_8888 image.Need Help :(
You need to reduce the memory usage.
From you code, you first decode stream to one bitmap, and then copy it, which means you create two large bitmap objects.
You don't need to decode and then copy it, you can try
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888
// You can try value larger than 1
options.inSampleSize = 2 // If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
// Decode bitmap
bitmap = BitmapFactory.decodeStream(in, null, options)
In this case, there's only one bitmap created. And you set inSampleSize to large values to reduce the loaded bitmap size.

OutOfMemory when load image to bitmap from URL

I load image from url. it' ok, but when long time it error outofmemory: bitmap size exceeds vm budget. here my code
BitmapFactory.Options bmOptions;
bmOptions = new BitmapFactory.Options();
bmOptions.inJustDecodeBounds = true;
//from web
try {
Bitmap bitmap=null;
InputStream is=new URL(url).openStream();
BitmapFactory.decodeStream(is, null, bmOptions);
is.close();
BitmapFactory.Options o2 = new BitmapFactory.Options();
o2.inSampleSize = 1;
is = new URL(url).openStream();
bitmap = BitmapFactory.decodeStream(is, null, o2);
is.close();
return bitmap;
} catch (Exception ex){
ex.printStackTrace();
return null;
}
help me please!!
Try using the sampleSize of BitmapFactory.Options, this will reduce the size in memory of your image (and the quality)
But if your image is really too big, I think that's there no miracle solution, the image is simply too big ...
Your problem is exactly what stated in error message: your image is too big to be loaded into memory.
Try with any other image. As this image is exceding the size of ur folder.
R u using Emulator to test it?
Just a idea, it could be this piece of code throws sometime a exception. This will causes that the InputStream will not release rthe asigned resources (memory leak).
if you close the Input stream in a finaly block this issue should be solved

Load Large Image from server on Android

I am trying to display a jpg file from a server into an imageView. When I try to load a smaller image (300x400), there are no problems. But when I try to load a full size picture (2336x3504), the image will not load. The file size of the image is only 2mb. I do not get any errors in logcat and there are no exceptions thrown. It simply won't load the image. I also tried using this:
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap=BitmapFactory.decodeStream(is,null,options);
This doesn't do anything to help load the large files, but it does resize the smaller image (like it is suppose to). I did add the large picture to my resources and tested it as if it was embedded in the app and it worked fine, just won't work on the server. I have been working all day on this and can't seem to figure out how to load these large pictures. Can anyone help me out with this? Thanks for any info.
Here is the link where I found the above code and have been playing with the other examples but still not getting it to work.
EDIT:
Here is the code I'm using, to load the image:
public static Bitmap getBitmapFromURL(String src) {
Bitmap bmImg;
URL myFileUrl = null;
try {
myFileUrl = new URL(src);
HttpURLConnection conn= (HttpURLConnection)myFileUrl.openConnection();
conn.setDoInput(true);
conn.connect();
InputStream is = conn.getInputStream();
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize = 16;
bmImg = BitmapFactory.decodeStream(is, null, options);
return bmImg;
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d("Error", e.toString());
return null;
}
}
Here is the logcat screenshot (couldn't figure out how to copy the text appropriately in eclipse) I cleared the log right before I hit the button to load the image. So all you see is what happens when I hit that button. I erased the company and app names (where you see "com.", assume its "com.mycompany.myapp".
It is not uncommon for BitmapFactory.decodeFromStream() to give up and just return null when you connect it directly to the InputStream of a remote connection. Internally, if you did not provide a BufferedInputStream to the method, it will wrap the supplied stream in one with a buffer size of 16384. One option that sometimes works is to pass a BufferedInputStream with a larger buffer size like:
BufferedInputStream bis = new BufferedInputStream(is, 32 * 1024);
A more universally effective method is to download the file completely first, and then decode the data like this:
InputStream is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is, 8190);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
FYI, the buffer sizes in this example are somewhat arbitrary. As has been said in other answers, it's a fantastic idea not to keep an image that size in memory longer than you have to. You might consider writing it directly to a file and displaying a downsampled version.
Hope that helps!
Devunwired's answer is right but out of memory error can occur if image size is too large, in that case we will have to scale down image, here is the code to scale down image after DevunWired's download image code
final BitmapFactory.Options options = new BitmapFactory.Options();
BufferedInputStream bis = new BufferedInputStream(is, 4*1024);
ByteArrayBuffer baf = new ByteArrayBuffer(50);
int current = 0;
while ((current = bis.read()) != -1) {
baf.append((byte)current);
}
byte[] imageData = baf.toByteArray();
BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
options.inJustDecodeBounds = true;
options.inSampleSize = 2; //calculateInSampleSize(options, 128, 128);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
Does it silently fail, or does it throw an exception or OutOfMemory error? Btw, if a jpeg is 2MB that doesn't mean it'll take up 2MB of memory. 2MB is the compressed size, and since Android is working with a Bitmap, the 2336 x 3504 will take up approximately 2336 x 3504 x 4 bytes in memory. (2336 x 3504 x 4 = 32,741,376). Downsampling 8 times still might not be enough, especially if you have other bitmaps in memory at the time.

Categories

Resources