Home » Android » android – How to know a Bitmap size from InputStream before creating the Bitmap?

android – How to know a Bitmap size from InputStream before creating the Bitmap?

Posted by: admin June 15, 2020 Leave a comment

Questions:

I need to scale an image before creating it and I want to do it only if it exceeds 1024KB (for example).

By doing the following I can scale the image but I only need to scale the ones that are bigger than the given size.

Bitmap bmImg = null;
InputStream is = url.openStream();
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 10;
bmImg = BitmapFactory.decodeStream(is,null,opts);

How can I get the size of the Bitmap? (I’m happy knowing the amount of bytes, not the size after decompressing).

Edit:

I’m trying this:

BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bmImg=BitmapFactory.decodeStream(is,null,opts);
Log.e("optwidth",opts.outWidth+"");
Bitmap bmImg1 = BitmapFactory.decodeStream(is);

The first time I use the InputStream (is) to decode it with the “inJustDecodeBounds” works fine and I can get the Bitmap dimensions.
The problem is that the second time I use it to actually decode the image, no image is shown.

What am I doing wrong?

How to&Answers:

I’m a noob so I can’t comment on monkjack’s answer directly. The reason his answer is so slow is that it’s copying one byte at a time. Using a buffer of even 1K will significantly improve performance.

InputStream in = getContentResolver().openInputStream(docUri);

ByteArrayOutputStream bos = new ByteArrayOutputStream();
int i;
byte[] buffer = new byte[1024];
while ((i = in.read(buffer)) != -1) {
    bos.write(buffer);
}
byte[] docbuffer = bos.toByteArray();

Answer:

If you know the format of the file you might be able to read the header of the file. Habitually the header contains the size and format of the image. And luckily for you, this is always the first bytes of the file.

It might not be possible for certain types.

PNG, JPEG, BMP, TGA

Just read about the header and some file you might have to guess the width first. For exemple, the PNG doesn’t seem to include widht/height but you should be able to read the width and guess a height with the pixel size / ratio.

Edit: My bad I thought you wanted to scale using the dimension… Yeah ContentLenght should help but sometimes it will return 0 if the server doesn’t set the right dimension in the http header. In that case you need to know the returned size. But anyway, you can save a bytearray and once everything is loaded just check the lenght of the array. if it’s bigger than 1024kb then resize the image. And save it or do whatever you want with it.

Answer:

You can wrap is with a new BufferedInputStream(is); and use the mark(int readlimit) and reset() methods to re-read the stream. You’ll need to decide what to set readlimit to allow it to read the entire header, the links provided by Loïc should help here.

BufferedInputStream bis = new BufferedInputStream(is);
bis.mark(1024 * 1024);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
Bitmap bmImg=BitmapFactory.decodeStream(bis,null,opts);
Log.e("optwidth",opts.outWidth+"");
bis.reset();
Bitmap bmImg1 = BitmapFactory.decodeStream(bis);

I’ve left out appropriate error handling for the reset() method, it will throw an IOException if you’ve read more than readlimit bytes from the stream.

Answer:

If your’re trying to prevent OutOfMemory or something else…

you can create a buffer with your picture data,

BitmapFactory.Options buffer = new BitmapFactory.Options();
buffer.inJustDecodeBounds = true;
BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer);

Then you can get the size using buffer.outHeight, buffer.outWidth..

If you want to scale, you just use buffer.isSampleSize but i noticed you’re setting there a “10” value… wich i think is wrong… sampleSize should get a 2^ value .. like 2,4,8,16 etc.

Finally after you’re sampleSize setting, you can create the bitmap as always.

Bitmap bmp = BitmapFactory.decodeByteArray(photo, 0, photo.length, buffer);

hope to help you!

PS: SQS MY ENGLISH =(!!!

Answer:

You can’t get the size from a stream by its very definition. If you want, you could load the stream into a byte array, get the byte count from that. You can also use the BitmapFactory to decode a byte array directly, so that would work out quite nicely.

InputStream in = ...
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int i;
while ((i = in.read()) != -1) {
    bos.write(i);
}
byte[] byteArray = bos.toByteArray();
if (byteArray.length > SOME_VALUE) { 
    // do things here
}
BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);

Answer:

Most streams are consumed when you read from them. In your 2nd code block, you consume the stream the first time as a bitmap. When you invoke decodeString a second time, the stream is ’empty’ (if only in the sense that all of the data has already been read).

The best answer will probably be monkjack’s, but perhaps with just a little extra code to ensure you aren’t reading a gargantuan file into memory. I recommend just a check as you’re streaming it into a buffer for size (i.e. stop at 10MB); I’m not sure how well Android will handle an out of memory exception.

Answer:

If you are downscaling to prevent OutOfMemoryException like me, this solution may work better for you (building on answers provided here):

// <initialize stream>

// figure out number of bytes in stream
byte[] bytes = new byte[1024];
int i;
int byteCount = 0;
while ((i = stream.read(bytes)) != -1) {
    byteCount += i;
}

// <reset stream>

Bitmap bmImg;

// downsample if necessary
if (byteCount > MAX_SIZE) {
    // scale down photo if too large
    BitmapFactory.Options options = new BitmapFactory.Options();
    // scale to nearest power of 2 - faster
    options.inSampleSize = (int)Math.pow(2, (int)(Math.log10(Math.sqrt((double)byteCount / MAX_SIZE)) / Math.log10(2) + 1)); // 2 ^ (log2(lengthRatio)+1)
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    bmImg = BitmapFactory.decodeStream(stream, null, options);
}
else {
    bmImg = BitmapFactory.decodeStream(stream);
}

I avoided the use of mark(int) and reset() because in my case there is no upper limit on the size of the stream. So how to reset the stream may depend on your implementation. My stream was specified by a Uri, so I just used:

stream.close();
stream = this.getContentResolver().openInputStream(uri); // where this is an Activity

to reset the stream.

Answer:

Check logcat, if you have an error that says "Bitmap too large to be uploaded into a texture" then you need to do the following. My answer is similar to @Sergio A. but you can use BitmapFactory.decodeStream directly instead of decodeByteArray.

final BitmapFactory.options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;

// Pass in a Rect if you want to know the padding.
BitmapFactory.decodeStream(inputStream,null,options);

This will get just the size of the image (not the whole image itself). You can isolate the width and height using the following code.

final int height = options.outHeight;
final int width = options.outWidth;

You can use these values (along with your desired width and height – most likely based on the imageView or the screen) to compute options.inSampleSize. I’m not going to go into how to do that, but Google has supplied a tutorial here.

Hope that helps 🙂

Answer:

You can’t get the size of a bitmap, there just isn’t a built in way. You can however get the size of the content (a file), which is what you will probably need to do. Something like this should work.

int file_size = url.getContentLength();