Android Code Snippet LoadBitmapResize conundrum

A current project involves manipulating images shared from the camera app or pictures gallery (or, presumably, any other
source capable of sharing images). The images are initially loaded and displayed in an imageview which is set to the
dimensions of the device. Since the images are expected to be large, LoadBitmapResize() is used. This insures
1) the entire image will be displayed nice and big, 2) the aspect ratio maintained, and 3) hopefully, OutOfMemoryError
exceptions avoided.

To my surprise, I discovered that LoadBitmapResize() not only resizes large images down to fill an imageview, but it also
resizes images up. Which, in the case of small images, can make them look really bad (unless total pixelation is the goal).

So one might say, for large images use a large imageview and LoadBitmapResize(), and for small images, use a small imageview
and plain LoadBitmap(). The problem is that you don't know the dimensions of the image until you load it, by which time
you may have experienced OutOfMemory. And even if there isn't a crash, you would still have to resize the large image after
having loaded it, a potentially 2-step operation for every image.

I suspected there was a way to determine an image's dimensions (I realize this is not the size of the image) without loading it,
otherwise LoadBitmapResize() would essentially have to perform a LoadBitmap() and then a bitmap.resize().
Android's BitmapFactory class does provide a way to determine the image's width and height without (apparently) creating a
bitmap, assuming I read the documentation correctly.

Based on that information, I cobbled together the attached inline Java-like snippet. Anyone who would care to turn it into
real Java is welcome to. I would imagine a purely B4A version is doable; when I see such examples in the forum, I can
picture myself writing one, but it was easier for me to do it the way I did it.

In the event this is simply a solution for which there was no problem, the circular file is always an option. No harm, no foul.
The name of the function peekImage() is a nod to the devilish twins of yore peek() and poke(), seen in the attached image.

B4X:
From B4A:
Dim in As JavaObject = Activity.GetStartingIntent
Dim uri as string = in.RunMethod("getParcelableExtra", Array("android.intent.extra.STREAM"))
           
Dim nativeme As JavaObject
nativeme.InitializeContext
       
Dim intarray(2) As Int = nativeme.RunMethod("peekImage",  Array As String(uri))
' make your decisions as to how to load based on width (intarray(0)) and height (intarray(1)) of image
' -----------------------------------------------------------------------------------------------------------

#If JAVA

import anywheresoftware.b4a.*;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.content.ContentResolver;
import java.io.File;
import java.io.InputStream;
import android.net.Uri;

public final int[] peekImage(String uri) {
    int[] ret = new int[2];
       
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;

    try {
        Uri thisuri = Uri.parse(uri);
        InputStream is = getContentResolver().openInputStream(thisuri);
        Bitmap bmp = BitmapFactory.decodeStream(is, null, options);       

        ret[0] = options.outWidth;
        ret[1] = options.outHeight;
           
        BA.Log(options.outWidth + " x " + options.outHeight);
    }
    catch(Exception e) {
           BA.Log("You've failed again: " + e.toString());
        ret[0] = -1;
        ret[1] = -1;
    }
           
    return(ret);
}
#End If
 

Attachments

  • peek_n_poke.jpg
    peek_n_poke.jpg
    77.4 KB · Views: 385

stevel05

Expert
Licensed User
Longtime User
I don't agree; even with small images you should use LoadBitmapResize, because of its last parameter, KeepAspectRatio.
Small images upsized will quickly become pixellated.
 

drgottjr

Expert
Licensed User
Longtime User
I don't agree; even with small images you should use LoadBitmapResize, because of its last parameter, KeepAspectRatio.
regardless of which loading method you use, you need to know the size of the imageview in order to avoid small images being pixelated in large imageviews. it gets back to the point i was trying to make that you need to know the dimensions of the image before creating the bitmap. (and bitmap.resize() also offers the same aspectratio flag).

i'm happy to use loadbitmapresize() all the time, but that doesn't work if you don't know the size you're resizing to, ie, the size of the imageview. and you can't know that unless you know the dimensions of the bitmap. and you can't know that unless you load the image first. my peek() tells me the height and width of the image. once i know that, i can size my imageview and use loadbitmapresize().
 

drgottjr

Expert
Licensed User
Longtime User
Small images upsized will quickly become pixellated.
that's what i'm saying. it was a shock to see a 64x64 image suddenly filling a 1000x2000 screen. had i know it was 64x64, i would have reduced the imageview to accomodate. by peek()'ing, i can see what's coming.
 

drgottjr

Expert
Licensed User
Longtime User

yeah, thanks. took a look. from 9 years ago with a lot of changes to android since then. so i think a fair amount of modification would be required (given that sharing is involved). my pathetic 5 lines of code are good to go with contentresolver and work for any image type, and the image is (apparently) not loaded, which is, of course, the key.

it is interesting that somebody was concerned about the issue so long ago, and that it hasn't seemed to have cropped up here, which is why i was wondering if it represented a solution for which no one (except me) had found a problem. and judging by the interest, that does seem to be the case.
 

drgottjr

Expert
Licensed User
Longtime User
Yes, I know; my comment was about ratio!

i just loaded (loadbitmap()) a small bitmap into my large imageview and aspect ratio was kept. had i used loadbitmapresize(), i would have to specify keeping the aspect ratio + i would have ended up with a gigantic pixelated bitmap filling the imageview. i just set the gravity to center (which is done in any case).
 

drgottjr

Expert
Licensed User
Longtime User
so we agree! a small image can be loaded into a large imageview without resize. that's what
i want to happen: bit image resized to fit, small image left alone (will fit naturally). you can't
have that with loadbitmapresize() because both the large image will be resized down to fit and
the small imaged will be resized up to fill the imageview.

solution? 1 large imageview and loadbitmapresize() for large images and loadbitmap() for small.
but to know which method to use, you need to know the dimensions of the image.

another solution? resize the imageview and use loadbitmapresize() for everything. problem:
you can't use loadbitmapresize() without knowing the dimensions of the imageview. in order
to know that, you have to peek() the dimensions of the image that's about to be loaded.
 
Top