Is it possible to make BitmapFactory.decodeFile() respect EXIF?

前端 未结 3 406
走了就别回头了
走了就别回头了 2021-01-21 07:36

In my tests, a Bitmap created by BitmapFactory.decodeFile() doesn\'t respect EXIF header.

For example, with portrait images taken by a devices

相关标签:
3条回答
  • 2021-01-21 07:57

    You can do this with Glide now. See the "Background Threads" section here:

    https://bumptech.github.io/glide/doc/getting-started.html

    Bitmap bitmap = Glide.with(context).asBitmap().load(new File(fileName)).skipMemoryCache(true).submit().get();

    Glide takes into consideration EXIF. You'll need to load it on a background thread. I was using Glide 4.9.0

    0 讨论(0)
  • 2021-01-21 07:59

    Is there a way to make BitmapFactory.decodeFile() respect EXIF and produce a correct Bitmap?

    No, sorry.

    what is the recommended pattern to handle this issue?

    Use the support library's ExifInterface to determine the desired orientation. Then, depending upon your use of the Bitmap, either rotate the view (e.g., ImageView) or rotate the Bitmap. This sample project illustrates both approaches, though I use a separate set of EXIF code, as some things that I use in that sample are not supported by the support library's ExifInterface.

    0 讨论(0)
  • 2021-01-21 08:17

    As it turned out, resolving the initial issue turned into the set of new ones. Here is the complete tutorial for further readers.

    First of all, as @CommonsWare pointed in his answer, we have to handle orientation manually using EXIF orientation tag. To avoid buggy/security flawed android.media.ExifInterface as well as 3rd party dependencies, the best option seems to be a new com.android.support:exifinterface library, which we add to build.gradle as:

    dependencies {
        compile 'com.android.support:exifinterface:26.0.0'
    }
    

    However for me, it led to a Gradle sync error caused by that this specific newest version of the library is not found in Google repositories, though this version is the latest according to the Support Library Packages page.

    After an hour of trials, I figured out that jcenter() repository is not enough and Gradle sync was fixed by adding Google Maven repository:

    allprojects {
        repositories {
            jcenter()
            maven {
                url 'https://maven.google.com'
                // Alternative URL is 'https://dl.google.com/dl/android/maven2/'
            }
        }
    }
    

    Ok, now we're able to use android.support.media.ExifInterface.

    The next disappointment was that width and height stored in EXIF tags also do not respect orientation, i.e. for images taken in portrait mode you get the same width and height that are returned by a Bitmap created with BitmapFactory.decodeFile(). So the only way is to manually look at the EXIF ExifInterface.TAG_ORIENTATION tag and if it says that image is rotated 90 or 270 degree - swap width and height:

    ExifInterface exif = new ExifInterface(fileNameFull);
    orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
    
    BitmapFactory.Options optsForBounds = new BitmapFactory.Options();
    optsForBounds.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(fileName, optsForBounds);
    
    int width = optsForBounds.outWidth, height = optsForBounds.outHeight;
    if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270)
    {
        int temp = width;
        //noinspection SuspiciousNameCombination
        width = height;
        height = temp;
    }
    

    I'm not sure if this approach and code covers 100% of cases, so if you encounter any issues across various devices/image sources - feel free to comment.

    In general, dealing with EXIF doesn't seem to be a pleasant experience. Implementations of the EXIF standard even from big companies seem to treat nuances very differently. See extensive analysis here.

    0 讨论(0)
提交回复
热议问题