0

I am trying to create an app that lets users click a button and select an image.

I used the second answer from this question to prompt the user for an image: How to pick an image from gallery (SD Card) for my app?

But sometimes if I choose an image that appears to be oriented correctly in the gallery, when I display it it will have a wrong orientation (I read that this usually happens with pictures taken by camera because they save their rotation in their ExifInterface)

So this code runs when the user chooses an image:

@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
        super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
        switch(requestCode) {
            case SELECT_PHOTO:
                if(resultCode == RESULT_OK){
                    Uri si = imageReturnedIntent.getData();
                    InputStream imageStream = null;
                    try {
                        imageStream = getContentResolver().openInputStream(si);
                        Bitmap image = Utils.decodeUri(this, si);
                        addPerson(selectedName, selectedDate, image);
                        imageStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
        }
    }

addPerson() adds the person to the list and calls saveData() which is here:

try {
     FileOutputStream out = new FileOutputStream(new File(folder, person.getName() + ".png"));
     person.getImage().compress(Bitmap.CompressFormat.PNG, 100, out);
     } catch (IOException e) {
         e.printStackTrace();
     };

And on onCreate() I run this code to load the items:

Bitmap notRotated = BitmapFactory.decodeFile(file.getPath());
Matrix matrix = new Matrix();

ExifInterface exif = new ExifInterface(file.getPath());
int rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
int rotationInDegrees = Utils.exifToDegrees(rotation);

if(rotation != 0f)
    matrix.preRotate(rotationInDegrees);
image = Bitmap.createBitmap(notRotated, 0, 0, notRotated.getWidth(), notRotated.getHeight(), matrix, true);
notRotated.recycle();

But when I run the above code it just ends up with black images and when I debug it says rotation is always 0 although there are obviously images with different rotation.

How can I fix this problem?

EDIT:

Here's my code to downsample my bitmaps to prevent OutOfMemoryExceptions:

public static Bitmap decodeUri(Context context, Uri selectedImage) throws FileNotFoundException {

        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(context.getContentResolver().openInputStream(selectedImage), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE = 140;

        // Find the correct scale value. It should be the power of 2.
        int width_tmp = o.outWidth, height_tmp = o.outHeight;
        int scale = 1;
        while (true) {
            if (width_tmp / 2 < REQUIRED_SIZE
                    || height_tmp / 2 < REQUIRED_SIZE) {
                break;
            }
            width_tmp /= 2;
            height_tmp /= 2;
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(selectedImage), null, o2);

    }

Could me downsampling affect the rotation?

Community
  • 1
  • 1
  • What, exactly, is `file` in your latter code snippet? It looks like it is a `Uri`, in which case `getPath()` is likely to be useless. `getPath()` only maps to a filesystem path if scheme of the `Uri` is `file`, and yours probably is not. – CommonsWare Nov 27 '16 at 17:37
  • @CommonsWare `file` is `File file = new File(folder, name + ".png");`. Is this wrong usage? – Gregory Skliar Nov 27 '16 at 17:40
  • I am going to interpret this as that your rotation code is working off of the results of `person.getImage().compress(Bitmap.CompressFormat.PNG, 100, out);`. In that case, you will have no EXIF headers. Partly, that is because you stripped them out when you created the `Bitmap`. Partly, that is because PNG files have no EXIF headers AFAIK. You need to deal with orientation *from the original `Uri`* using [a different `ExifInterface`](https://commonsware.com/blog/2016/05/31/tale-two-exifinterfaces.html). – CommonsWare Nov 27 '16 at 17:44
  • Also, [do not use the built-in `ExifInterface`, due to a security bug](https://commonsware.com/blog/2016/09/08/dealing-exifinterface-security-flaw.html). – CommonsWare Nov 27 '16 at 17:45
  • @CommonsWare Wait, so you're telling me .png doesn't have Exif headers at all? Well, I just checked and all the images I tested were .jpg but I am saving them as .png in the second code snippet. So lets say now I also save them as .jpg, will that solve my problem? Or do I need to use the "a different ExifInterface"? And if so, could you explain on how I should use it? (i.e. How to get a valid Uri to provide to it, and bla bla) – Gregory Skliar Nov 27 '16 at 17:56
  • "So lets say now I also save them as .jpg, will that solve my problem?" -- no, because you lost the EXIF headers when you created the `Bitmap`. "How to get a valid Uri to provide to it" -- you already have one. It is the one that you are getting the `InputStream` on, to create the `Bitmap`. You *also* use that `Uri` to get the EXIF headers. [This sample app](https://github.com/commonsguy/cw-omnibus/tree/master/Camera/EXIFRotater) shows using one of the alternative `ExifInterface` implementations, one that can use an `InputStream`. – CommonsWare Nov 27 '16 at 18:00
  • @CommonsWare Okay, so using the Uri will work in `onActivityResult()` but how will I get the correct rotation when I save it and then load it again? Or will it have the correct rotation because I already rotated it? – Gregory Skliar Nov 27 '16 at 18:06
  • Fix the image before saving it. Or, don't resample the image (just copy the bytes from the `InputStream`) so the EXIF headers remain intact. Or, stuff the EXIF headers back into the resampled image. Or, save the rotation information somewhere else, so you have it later. And there may be other possibilities as well. – CommonsWare Nov 27 '16 at 18:09
  • @CommonsWare Okay, I will try this in a bit and if it works I'll tell you to post an answer and I'll mark it as correct. – Gregory Skliar Nov 27 '16 at 18:17
  • @CommonsWare I tried the example you sent me, before I saved the images, and I get this error: `E/ExifParser: invalid jpeg header`. What does this mean and how should I fix it? – Gregory Skliar Nov 27 '16 at 20:08
  • I have no idea, sorry. However, do make sure that you get a fresh `InputStream` from the `ContentResolver`. You cannot use the same stream both for EXIF decoding and for `BitmapFactory.decodeStream()`. – CommonsWare Nov 27 '16 at 20:10
  • @CommonsWare I managed to fix it by downsamping the bitmap! Finally!! Now if you could post a question with a quick summary of everything we said here I'll mark it as correct (I would upvote if I had enough karma so sorry about that...) – Gregory Skliar Nov 27 '16 at 20:19
  • I would recommend that you post your own answer, showing how you used the stuff that I outlined in the comments. – CommonsWare Nov 27 '16 at 20:23

1 Answers1

0

Okay so thanks to @CommonsWare for the help. Some errors in my code that he pointed out were saving the bitmaps in PNG format, not rotating them before saving and using ExifInterface in a wrong way. I changed the saving format to JPEG and used this example of an ExifInterface use (with a custom ExifInterface).