Issue 1:
Glide Version: 4.13.2

Integration libraries: None

Device/Android Version: Emulator (Pixel 2 - API 30 - Android 11) and Huawei P30 lite

Issue details / Repro steps / Use case background:

I'm trying to load images from the web, delivered by an API. Those images sometimes contain EXIF data with rotation information and Glide should automatically rotate those as requested. But for example in the case of the attached image (see below) the rotation isn't correctly applied.

If a URL (as string) or a file is used to request the image via Glide, it doesn't get rotated. 🛑

If I use the path of the file the rotation values are honored. ✅

Glide load line / GlideModule (if any) / list Adapter code (if any):

I just created a default Android example app, added three image views and loaded the same image in the three mentioned ways.

In onViewCreated() in FirstFragment

val url = "https://url-hosting-some-images.com/your-image.HEIC"
val result = runBlocking(Dispatchers.IO) {
val file = Glide.with(this@FirstFragment).downloadOnly().load(url).submit()
file.get()
}
firstImage(url)
secondImage(result)
thirdImage(result.absolutePath)

The actual methods in FirstFragment

// rotation is missing
private fun firstImage(url: String) {
Glide
.with(this)
.load(url)
.into(binding.imageFirst);
}

// rotation is missing
private fun secondImage(file: File) {
Glide
.with(this)
.load(file)
.into(binding.imageSecond);
}

// rotation is correctly displayed
private fun thirdImage(path: String) {
Glide
.with(this)
.load(path)
.into(binding.imageThird);
}

Layout XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".FirstFragment">

<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_first_fragment"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageView
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textview_first"
tools:src="@drawable/ic_launcher_foreground"
android:id="@+id/image_first"
android:contentDescription="first"
android:layout_width="0dp"
android:layout_height="150dp" />

<ImageView
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_first"
tools:src="@drawable/ic_launcher_foreground"
android:id="@+id/image_second"
android:contentDescription="second"
android:layout_width="0dp"
android:layout_height="150dp" />

<ImageView
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_second"
tools:src="@drawable/ic_launcher_foreground"
android:id="@+id/image_third"
android:contentDescription="third"
android:layout_width="0dp"
android:layout_height="150dp" />

<Button
android:id="@+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/image_third" />
</androidx.constraintlayout.widget.ConstraintLayout>

Stack trace / LogCat: No errors

Screenshot:

Example image:

As GitHub doesn't support HEIC attachments I had to zip it.

example_image.zip

Issue 2:
Invalid image: ExifInterface got an unsupported image format file(ExifInterface supports JPEG and some RAW image formats only) or a corrupted JPEG file to ExifInterface

W/ExifInterface: Invalid image: ExifInterface got an unsupported image format file(ExifInterface supports JPEG and some RAW image formats only) or a corrupted JPEG file to ExifInterface.

java.io.IOException: Invalid byte order: 4646

at androidx.exifinterface.media.ExifInterface.readByteOrder(ExifInterface.java:6581)

at androidx.exifinterface.media.ExifInterface.parseTiffHeaders(ExifInterface.java:6588)

at androidx.exifinterface.media.ExifInterface.getRawAttributes(ExifInterface.java:5617)

at androidx.exifinterface.media.ExifInterface.loadAttributes(ExifInterface.java:4583)

at androidx.exifinterface.media.ExifInterface.(ExifInterface.java:4032)

at androidx.exifinterface.media.ExifInterface.(ExifInterface.java:3983)

at com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser.getOrientation(ExifInterfaceImageHeaderParser.java:40)

at com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser.getOrientation(ExifInterfaceImageHeaderParser.java:53)

at com.bumptech.glide.load.ImageHeaderParserUtils$4.getOrientation(ImageHeaderParserUtils.java:147)

at com.bumptech.glide.load.ImageHeaderParserUtils.getOrientationInternal(ImageHeaderParserUtils.java:222)

at com.bumptech.glide.load.ImageHeaderParserUtils.getOrientation(ImageHeaderParserUtils.java:142)

at com.bumptech.glide.load.resource.bitmap.ImageReader$ByteBufferReader.getImageOrientation(ImageReader.java:166)

at com.bumptech.glide.load.resource.bitmap.Downsampler.decodeFromWrappedStreams(Downsampler.java:330)

at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:285)

at com.bumptech.glide.load.resource.bitmap.Downsampler.decode(Downsampler.java:187)

at com.bumptech.glide.load.resource.bitmap.ByteBufferBitmapDecoder.decode(ByteBufferBitmapDecoder.java:28)

at com.bumptech.glide.load.resource.bitmap.ByteBufferBitmapDecoder.decode(ByteBufferBitmapDecoder.java:12)

at com.bumptech.glide.load.engine.DecodePath.decodeResourceWithList(DecodePath.java:92)

at com.bumptech.glide.load.engine.DecodePath.decodeResource(DecodePath.java:70)

at com.bumptech.glide.load.engine.DecodePath.decode(DecodePath.java:59)

at com.bumptech.glide.load.engine.LoadPath.loadWithExceptionList(LoadPath.java:76)

at com.bumptech.glide.load.engine.LoadPath.load(LoadPath.java:57)

at com.bumptech.glide.load.engine.DecodeJob.runLoadPath(DecodeJob.java:535)

at com.bumptech.glide.load.engine.DecodeJob.decodeFromFetcher(DecodeJob.java:499)

at com.bumptech.glide.load.engine.DecodeJob.decodeFromData(DecodeJob.java:485)

at com.bumptech.glide.load.engine.DecodeJob.decodeFromRetrievedData(DecodeJob.java:430)

at com.bumptech.glide.load.engine.DecodeJob.onDataFetcherReady(DecodeJob.java:394)

at com.bumptech.glide.load.engine.DataCacheGenerator.onDataReady(DataCacheGenerator.java:100)

at com.bumptech.glide.load.model.ByteBufferFileLoader$ByteBufferFetcher.loadData(ByteBufferFileLoader.java:62)

at com.bumptech.glide.load.engine.DataCacheGenerator.startNext(DataCacheGenerator.java:77)

at com.bumptech.glide.load.engine.DecodeJob.runGenerators(DecodeJob.java:311)

at com.bumptech.glide.load.engine.DecodeJob.runWrapped(DecodeJob.java:277)

at com.bumptech.glide.load.engine.DecodeJob.run(DecodeJob.java:235)

at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)

at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)

at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultThreadFactory$1.run(GlideExecutor.java:413)

at java.lang.Thread.run(Thread.java:764)

at com.bumptech.glide.load.engine.executor.GlideExecutor$DefaultPriorityThreadFactory$1.run(GlideExecutor.java:372)

Actually，It is not a bug from Android ExifInterface , but it is a bug of Glide, and happened in Webp image.

1, Why the exception happened ?

Before the exception happened, I debug the code and find that the image mime type check result is "unknown".

I found that , the ExifInterfaceImageHeaderParser get wrong header data which like this " [70, 70, -86, 8, 0, 0, 87, 69, 66, 80, 86, ......." .

Webp file starts with "RIFFxxxxWebpxxxx", must start with "RIFF" four characters , but the start characters begin with "FF", and "RI" is lost.

2, Why "FF" and not "RIFF"

First, I check the cache file, and the header start with "RIFF" , so it is not the problem of image file.

Then, check the code of getOrientation function , We know that there are two image header parsers do the work:

DefaultImageHeaderParser

ExifInterfaceImageHeaderParser

Two parsers share one image file input stream object.

The first parser DefaultImageHeaderParser call imageReader obj to get getUInt16, and check image number;

Because it is webp file, so it is not DefaultImageHeaderParser's work, so return unknown and the next parser continue the work.

While ExifInterfaceImageHeaderParser is working, but it does not do reset for the shared stream obj.

Because, the first parser read one UInt16, 2 bytes, so the second parser read header buffer from position = 2, not 0.

So, The begin 2 characters lost.

Based on webp header def:

RIFF Header - 32 bits representing the ASCII characters ‘R’ ‘I’ ‘F’ ‘F’

File Size - 32 bits (uint32) representing the size of the file in bytes starting at offset 8. The maximum value of this field is 2^32 minus 10 bytes and thus the size of the whole file is at most 4GiB minus 2 bytes.

‘WEBP’ - 32 bits representing the ASCII characters ‘W’ ‘E’ ‘B’ ‘P’

We know that, webp file type check, the first four characters are the key point. So the mime type check failed.

Then, exif info check run into unknown file type, and parser header based on JPEG, so throw the exception like :

"W/ExifInterface: Invalid image: ExifInterface got an unsupported image format file(ExifInterface supports JPEG and some RAW image formats only) or a corrupted JPEG file to ExifInterface. "

Glide provide wrong stream byte data is the root cause. It is not the problem of ExifInterface.

Of course, It is based on if you refer ExifInterface version = 1.2.0.

Because ExifInterface support webp header parse from 1.2.0, maybe, I found webp parsing code in 1.2.0 and not check earlier version.

So the suggestion is that adding reset code for input stream before ExifInterfaceImageHeaderParser do any parsing action in new version.

I did this check with glide 4.13.1 and ExifInterface 1.2.0.

Originally posted by @aaronKueng in #4451 (comment)