Skip to content

Commit

Permalink
Roll forward "Try using ParcelFileDescriptor for local files" with fixes
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 281546605
  • Loading branch information
sjudd authored and glide-copybara-robot committed Nov 20, 2019
1 parent 7ff5dc3 commit 9281d8e
Show file tree
Hide file tree
Showing 7 changed files with 448 additions and 69 deletions.
33 changes: 25 additions & 8 deletions library/src/main/java/com/bumptech/glide/Glide.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.bumptech.glide.load.ImageHeaderParser;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.data.InputStreamRewinder;
import com.bumptech.glide.load.data.ParcelFileDescriptorRewinder;
import com.bumptech.glide.load.engine.Engine;
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
Expand Down Expand Up @@ -63,6 +64,7 @@
import com.bumptech.glide.load.resource.bitmap.Downsampler;
import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser;
import com.bumptech.glide.load.resource.bitmap.InputStreamBitmapImageDecoderResourceDecoder;
import com.bumptech.glide.load.resource.bitmap.ParcelFileDescriptorBitmapDecoder;
import com.bumptech.glide.load.resource.bitmap.ResourceBitmapDecoder;
import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder;
import com.bumptech.glide.load.resource.bitmap.UnitBitmapDecoder;
Expand Down Expand Up @@ -386,18 +388,17 @@ private static void throwIncorrectGlideModule(Exception e) {
ResourceDecoder<ParcelFileDescriptor, Bitmap> parcelFileDescriptorVideoDecoder =
VideoDecoder.parcel(bitmapPool);

// TODO(judds): Make ParcelFileDescriptorBitmapDecoder work with ImageDecoder.
Downsampler downsampler =
new Downsampler(
registry.getImageHeaderParsers(), resources.getDisplayMetrics(), bitmapPool, arrayPool);

ResourceDecoder<ByteBuffer, Bitmap> byteBufferBitmapDecoder;
ResourceDecoder<InputStream, Bitmap> streamBitmapDecoder;
if (isImageDecoderEnabledForBitmaps && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
streamBitmapDecoder = new InputStreamBitmapImageDecoderResourceDecoder();
byteBufferBitmapDecoder = new ByteBufferBitmapImageDecoderResourceDecoder();
} else {
Downsampler downsampler =
new Downsampler(
registry.getImageHeaderParsers(),
resources.getDisplayMetrics(),
bitmapPool,
arrayPool);
byteBufferBitmapDecoder = new ByteBufferBitmapDecoder(downsampler);
streamBitmapDecoder = new StreamBitmapDecoder(downsampler, arrayPool);
}
Expand All @@ -422,7 +423,17 @@ private static void throwIncorrectGlideModule(Exception e) {
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class, byteBufferBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder)
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class, streamBitmapDecoder);

if (ParcelFileDescriptorRewinder.isSupported()) {
registry.append(
Registry.BUCKET_BITMAP,
ParcelFileDescriptor.class,
Bitmap.class,
new ParcelFileDescriptorBitmapDecoder(downsampler));
}

registry
.append(
Registry.BUCKET_BITMAP,
ParcelFileDescriptor.class,
Expand Down Expand Up @@ -483,7 +494,13 @@ Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitm
// Compilation with Gradle requires the type to be specified for UnitModelLoader here.
.append(File.class, File.class, UnitModelLoader.Factory.<File>getInstance())
/* Models */
.register(new InputStreamRewinder.Factory(arrayPool))
.register(new InputStreamRewinder.Factory(arrayPool));

if (ParcelFileDescriptorRewinder.isSupported()) {
registry.register(new ParcelFileDescriptorRewinder.Factory());
}

registry
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
.append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.bumptech.glide.load;

import android.os.Build;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.bumptech.glide.load.ImageHeaderParser.ImageType;
import com.bumptech.glide.load.data.ParcelFileDescriptorRewinder;
import com.bumptech.glide.load.engine.bitmap_recycle.ArrayPool;
import com.bumptech.glide.load.resource.bitmap.RecyclableBufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
Expand Down Expand Up @@ -34,34 +38,83 @@ public static ImageType getType(
}

is.mark(MARK_READ_LIMIT);
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = parsers.size(); i < size; i++) {
ImageHeaderParser parser = parsers.get(i);
try {
ImageType type = parser.getType(is);
if (type != ImageType.UNKNOWN) {
return type;
}
} finally {
is.reset();
}
}

return ImageType.UNKNOWN;
final InputStream finalIs = is;
return getTypeInternal(
parsers,
new TypeReader() {
@Override
public ImageType getType(ImageHeaderParser parser) throws IOException {
try {
return parser.getType(finalIs);
} finally {
finalIs.reset();
}
}
});
}

/** Returns the ImageType for the given ByteBuffer. */
@NonNull
public static ImageType getType(
@NonNull List<ImageHeaderParser> parsers, @Nullable ByteBuffer buffer) throws IOException {
@NonNull List<ImageHeaderParser> parsers, @Nullable final ByteBuffer buffer)
throws IOException {
if (buffer == null) {
return ImageType.UNKNOWN;
}

return getTypeInternal(
parsers,
new TypeReader() {
@Override
public ImageType getType(ImageHeaderParser parser) throws IOException {
return parser.getType(buffer);
}
});
}

@NonNull
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public static ImageType getType(
@NonNull List<ImageHeaderParser> parsers,
@NonNull final ParcelFileDescriptorRewinder parcelFileDescriptorRewinder,
@NonNull final ArrayPool byteArrayPool)
throws IOException {
return getTypeInternal(
parsers,
new TypeReader() {
@Override
public ImageType getType(ImageHeaderParser parser) throws IOException {
// Wrap the FileInputStream into a RecyclableBufferedInputStream to optimize I/O
// performance
InputStream is = null;
try {
is =
new RecyclableBufferedInputStream(
new FileInputStream(
parcelFileDescriptorRewinder.rewindAndGet().getFileDescriptor()),
byteArrayPool);
return parser.getType(is);
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// Ignored.
}
parcelFileDescriptorRewinder.rewindAndGet();
}
}
});
}

@NonNull
private static ImageType getTypeInternal(
@NonNull List<ImageHeaderParser> parsers, TypeReader reader) throws IOException {
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = parsers.size(); i < size; i++) {
ImageHeaderParser parser = parsers.get(i);
ImageType type = parser.getType(buffer);
ImageType type = reader.getType(parser);
if (type != ImageType.UNKNOWN) {
return type;
}
Expand All @@ -74,7 +127,7 @@ public static ImageType getType(
public static int getOrientation(
@NonNull List<ImageHeaderParser> parsers,
@Nullable InputStream is,
@NonNull ArrayPool byteArrayPool)
@NonNull final ArrayPool byteArrayPool)
throws IOException {
if (is == null) {
return ImageHeaderParser.UNKNOWN_ORIENTATION;
Expand All @@ -85,19 +138,75 @@ public static int getOrientation(
}

is.mark(MARK_READ_LIMIT);
final InputStream finalIs = is;
return getOrientationInternal(
parsers,
new OrientationReader() {
@Override
public int getOrientation(ImageHeaderParser parser) throws IOException {
try {
return parser.getOrientation(finalIs, byteArrayPool);
} finally {
finalIs.reset();
}
}
});
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public static int getOrientation(
@NonNull List<ImageHeaderParser> parsers,
@NonNull final ParcelFileDescriptorRewinder parcelFileDescriptorRewinder,
@NonNull final ArrayPool byteArrayPool)
throws IOException {
return getOrientationInternal(
parsers,
new OrientationReader() {
@Override
public int getOrientation(ImageHeaderParser parser) throws IOException {
// Wrap the FileInputStream into a RecyclableBufferedInputStream to optimize I/O
// performance
InputStream is = null;
try {
is =
new RecyclableBufferedInputStream(
new FileInputStream(
parcelFileDescriptorRewinder.rewindAndGet().getFileDescriptor()),
byteArrayPool);
return parser.getOrientation(is, byteArrayPool);
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
// Ignored.
}
parcelFileDescriptorRewinder.rewindAndGet();
}
}
});
}

private static int getOrientationInternal(
@NonNull List<ImageHeaderParser> parsers, OrientationReader reader) throws IOException {
//noinspection ForLoopReplaceableByForEach to improve perf
for (int i = 0, size = parsers.size(); i < size; i++) {
ImageHeaderParser parser = parsers.get(i);
try {
int orientation = parser.getOrientation(is, byteArrayPool);
if (orientation != ImageHeaderParser.UNKNOWN_ORIENTATION) {
return orientation;
}
} finally {
is.reset();
int orientation = reader.getOrientation(parser);
if (orientation != ImageHeaderParser.UNKNOWN_ORIENTATION) {
return orientation;
}
}

return ImageHeaderParser.UNKNOWN_ORIENTATION;
}

private interface TypeReader {
ImageType getType(ImageHeaderParser parser) throws IOException;
}

private interface OrientationReader {
int getOrientation(ImageHeaderParser parser) throws IOException;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public final class InputStreamRewinder implements DataRewinder<InputStream> {
private final RecyclableBufferedInputStream bufferedStream;

@Synthetic
InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
public InputStreamRewinder(InputStream is, ArrayPool byteArrayPool) {
// We don't check is.markSupported() here because RecyclableBufferedInputStream allows resetting
// after exceeding MARK_READ_LIMIT, which other InputStreams don't guarantee.
bufferedStream = new RecyclableBufferedInputStream(is, byteArrayPool);
Expand All @@ -37,6 +37,10 @@ public void cleanup() {
bufferedStream.release();
}

public void fixMarkLimits() {
bufferedStream.fixMarkLimit();
}

/**
* Factory for producing {@link com.bumptech.glide.load.data.InputStreamRewinder}s from {@link
* java.io.InputStream}s.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.bumptech.glide.load.data;

import static android.system.OsConstants.SEEK_SET;

import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.system.ErrnoException;
import android.system.Os;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.io.IOException;

/**
* Implementation for {@link ParcelFileDescriptor}s that rewinds file descriptors by seeking to 0.
*/
public final class ParcelFileDescriptorRewinder implements DataRewinder<ParcelFileDescriptor> {

private final InternalRewinder rewinder;

public static boolean isSupported() {
// Os.lseek() is only supported on API 21+.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
}

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public ParcelFileDescriptorRewinder(ParcelFileDescriptor parcelFileDescriptor) {
rewinder = new InternalRewinder(parcelFileDescriptor);
}

@NonNull
@Override
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public ParcelFileDescriptor rewindAndGet() throws IOException {
return rewinder.rewind();
}

@Override
public void cleanup() {
// Do nothing.
}

/**
* Factory for producing {@link ParcelFileDescriptorRewinder}s from {@link ParcelFileDescriptor}s.
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public static final class Factory implements DataRewinder.Factory<ParcelFileDescriptor> {

@NonNull
@Override
public DataRewinder<ParcelFileDescriptor> build(
@NonNull ParcelFileDescriptor parcelFileDescriptor) {
return new ParcelFileDescriptorRewinder(parcelFileDescriptor);
}

@NonNull
@Override
public Class<ParcelFileDescriptor> getDataClass() {
return ParcelFileDescriptor.class;
}
}

/**
* Catching ErrnoException cannot be done in classes that are loaded on APIs < Lollipop. To make
* sure that we do not do so, we catch inside this inner class instead of the outer class. The
* only reason this class exists is to avoid VerifyError on older APIs.
*/
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private static final class InternalRewinder {
private final ParcelFileDescriptor parcelFileDescriptor;

InternalRewinder(ParcelFileDescriptor parcelFileDescriptor) {
this.parcelFileDescriptor = parcelFileDescriptor;
}

ParcelFileDescriptor rewind() throws IOException {
try {
Os.lseek(parcelFileDescriptor.getFileDescriptor(), 0, SEEK_SET);
} catch (ErrnoException e) {
throw new IOException(e);
}
return parcelFileDescriptor;
}
}
}
Loading

0 comments on commit 9281d8e

Please sign in to comment.