Skip to content

Commit

Permalink
Group resource decoders into buckets, where the user can specify a bu…
Browse files Browse the repository at this point in the history
…cket ordering so that prepending and appended is localized to the bucket.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=168779283
  • Loading branch information
asuszek authored and sjudd committed Sep 15, 2017
1 parent a84deb3 commit da90633
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 32 deletions.
24 changes: 13 additions & 11 deletions library/src/main/java/com/bumptech/glide/Glide.java
Original file line number Diff line number Diff line change
Expand Up @@ -302,37 +302,39 @@ private static GeneratedAppGlideModule getAnnotationGeneratedGlideModules() {
.append(ByteBuffer.class, new ByteBufferEncoder())
.append(InputStream.class, new StreamEncoder(arrayPool))
/* Bitmaps */
.append(ByteBuffer.class, Bitmap.class,
.append(Registry.BUCKET_BITMAP, ByteBuffer.class, Bitmap.class,
new ByteBufferBitmapDecoder(downsampler))
.append(InputStream.class, Bitmap.class,
.append(Registry.BUCKET_BITMAP, InputStream.class, Bitmap.class,
new StreamBitmapDecoder(downsampler, arrayPool))
.append(ParcelFileDescriptor.class, Bitmap.class, new VideoBitmapDecoder(bitmapPool))
.append(Registry.BUCKET_BITMAP, ParcelFileDescriptor.class, Bitmap.class,
new VideoBitmapDecoder(bitmapPool))
.append(Bitmap.class, new BitmapEncoder())
/* GlideBitmapDrawables */
.append(ByteBuffer.class, BitmapDrawable.class,
.append(Registry.BUCKET_BITMAP_DRAWABLE, ByteBuffer.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool,
new ByteBufferBitmapDecoder(downsampler)))
.append(InputStream.class, BitmapDrawable.class,
.append(Registry.BUCKET_BITMAP_DRAWABLE, InputStream.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool,
new StreamBitmapDecoder(downsampler, arrayPool)))
.append(ParcelFileDescriptor.class, BitmapDrawable.class,
.append(Registry.BUCKET_BITMAP_DRAWABLE, ParcelFileDescriptor.class, BitmapDrawable.class,
new BitmapDrawableDecoder<>(resources, bitmapPool, new VideoBitmapDecoder(bitmapPool)))
.append(BitmapDrawable.class, new BitmapDrawableEncoder(bitmapPool, new BitmapEncoder()))
/* GIFs */
.prepend(InputStream.class, GifDrawable.class,
.append(Registry.BUCKET_GIF, InputStream.class, GifDrawable.class,
new StreamGifDecoder(registry.getImageHeaderParsers(), byteBufferGifDecoder, arrayPool))
.prepend(ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.append(Registry.BUCKET_GIF, ByteBuffer.class, GifDrawable.class, byteBufferGifDecoder)
.append(GifDrawable.class, new GifDrawableEncoder())
/* GIF Frames */
.append(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory<GifDecoder>())
.append(GifDecoder.class, Bitmap.class, new GifFrameResourceDecoder(bitmapPool))
.append(GifDecoder.class, GifDecoder.class, new UnitModelLoader.Factory<>())
.append(Registry.BUCKET_BITMAP, GifDecoder.class, Bitmap.class,
new GifFrameResourceDecoder(bitmapPool))
/* Files */
.register(new ByteBufferRewinder.Factory())
.append(File.class, ByteBuffer.class, new ByteBufferFileLoader.Factory())
.append(File.class, InputStream.class, new FileLoader.StreamFactory())
.append(File.class, File.class, new FileDecoder())
.append(File.class, ParcelFileDescriptor.class, new FileLoader.FileDescriptorFactory())
.append(File.class, File.class, new UnitModelLoader.Factory<File>())
.append(File.class, File.class, new UnitModelLoader.Factory<>())
/* Models */
.register(new InputStreamRewinder.Factory(arrayPool))
.append(int.class, InputStream.class, new ResourceLoader.StreamFactory(resources))
Expand Down
110 changes: 102 additions & 8 deletions library/src/main/java/com/bumptech/glide/Registry.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.bumptech.glide.util.pool.FactoryPools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

Expand All @@ -33,6 +34,12 @@
* encoding logic.
*/
public class Registry {
public static final String BUCKET_GIF = "Gif";
public static final String BUCKET_BITMAP = "Bitmap";
public static final String BUCKET_BITMAP_DRAWABLE = "BitmapDrawable";
private static final String BUCKET_PREPEND_ALL = "legacy_prepend_all";
private static final String BUCKET_APPEND_ALL = "legacy_append";

private final ModelLoaderRegistry modelLoaderRegistry;
private final EncoderRegistry encoderRegistry;
private final ResourceDecoderRegistry decoderRegistry;
Expand All @@ -54,6 +61,8 @@ public Registry() {
this.dataRewinderRegistry = new DataRewinderRegistry();
this.transcoderRegistry = new TranscoderRegistry();
this.imageHeaderParserRegistry = new ImageHeaderParserRegistry();
setResourceDecoderBucketPriorityList(
Arrays.asList(BUCKET_GIF, BUCKET_BITMAP, BUCKET_BITMAP_DRAWABLE));
}

/**
Expand Down Expand Up @@ -113,16 +122,17 @@ public <Data> Registry prepend(Class<Data> dataClass, Encoder<Data> encoder) {
}

/**
* Appends the given {@link ResourceDecoder} onto the list of available {@link ResourceDecoder}s
* allowing it to be used if all earlier and default {@link ResourceDecoder}s for the given types
* fail (or if none are present).
* Appends the given {@link ResourceDecoder} onto the list of all available
* {@link ResourceDecoder}s allowing it to be used if all earlier and default
* {@link ResourceDecoder}s for the given types fail (or there are none).
*
* <p>If you're attempting to replace an existing {@link ResourceDecoder} or would like to ensure
* that your {@link ResourceDecoder} gets the chance to run before an existing
* {@link ResourceDecoder}, use {@link #prepend(Class, Class, ResourceDecoder)}. This method is
* best for new types of resources and data or as a way to add an additional fallback decoder
* for an existing type of data.
*
* @see #append(String, Class, Class, ResourceDecoder)
* @see #prepend(Class, Class, ResourceDecoder)
*
* @param dataClass The data that will be decoded from
Expand All @@ -135,21 +145,52 @@ public <Data, TResource> Registry append(
Class<Data> dataClass,
Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.append(decoder, dataClass, resourceClass);
append(BUCKET_APPEND_ALL, dataClass, resourceClass, decoder);
return this;
}

/**
* Prepends the given {@link ResourceDecoder} into the list of available {@link ResourceDecoder}s
* so that it is attempted before all later and default {@link ResourceDecoder}s for the given
* types.
* Appends the given {@link ResourceDecoder} onto the list of available {@link ResourceDecoder}s
* in this bucket, allowing it to be used if all earlier and default {@link ResourceDecoder}s for
* the given types in this bucket fail (or there are none).
*
* <p>If you're attempting to replace an existing {@link ResourceDecoder} or would like to ensure
* that your {@link ResourceDecoder} gets the chance to run before an existing
* {@link ResourceDecoder}, use {@link #prepend(Class, Class, ResourceDecoder)}. This method is
* best for new types of resources and data or as a way to add an additional fallback decoder
* for an existing type of data.
*
* @see #prepend(String, Class, Class, ResourceDecoder)
* @see #setResourceDecoderBucketPriorityList(List)
*
* @param bucket The bucket identifier to add this decoder to.
* @param dataClass The data that will be decoded from
* ({@link java.io.InputStream}, {@link java.io.FileDescriptor} etc).
* @param resourceClass The resource that will be decoded to ({@link android.graphics.Bitmap},
* {@link com.bumptech.glide.load.resource.gif.GifDrawable} etc).
* @param decoder The {@link ResourceDecoder} to register.
*/
public <Data, TResource> Registry append(
String bucket,
Class<Data> dataClass,
Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.append(bucket, decoder, dataClass, resourceClass);
return this;
}

/**
* Prepends the given {@link ResourceDecoder} into the list of all available
* {@link ResourceDecoder}s so that it is attempted before all later and default
* {@link ResourceDecoder}s for the given types.
*
* <p>This method allows you to replace the default {@link ResourceDecoder} because it ensures
* the registered {@link ResourceDecoder} will run first. You can use the
* {@link ResourceDecoder#handles(Object, Options)} to fall back to the default
* {@link ResourceDecoder}s if you only want to change the default functionality for certain
* types of data.
*
* @see #prepend(String, Class, Class, ResourceDecoder)
* @see #append(Class, Class, ResourceDecoder)
*
* @param dataClass The data that will be decoded from
Expand All @@ -162,7 +203,60 @@ public <Data, TResource> Registry prepend(
Class<Data> dataClass,
Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.prepend(decoder, dataClass, resourceClass);
prepend(BUCKET_PREPEND_ALL, dataClass, resourceClass, decoder);
return this;
}

/**
* Prepends the given {@link ResourceDecoder} into the list of available {@link ResourceDecoder}s
* in the same bucket so that it is attempted before all later and default
* {@link ResourceDecoder}s for the given types in that bucket.
*
* <p>This method allows you to replace the default {@link ResourceDecoder} for this bucket
* because it ensures the registered {@link ResourceDecoder} will run first. You can use the
* {@link ResourceDecoder#handles(Object, Options)} to fall back to the default
* {@link ResourceDecoder}s if you only want to change the default functionality for certain
* types of data.
*
* @see #append(String, Class, Class, ResourceDecoder)
* @see #setResourceDecoderBucketPriorityList(List)
*
* @param bucket The bucket identifier to add this decoder to.
* @param dataClass The data that will be decoded from
* ({@link java.io.InputStream}, {@link java.io.FileDescriptor} etc).
* @param resourceClass The resource that will be decoded to ({@link android.graphics.Bitmap},
* {@link com.bumptech.glide.load.resource.gif.GifDrawable} etc).
* @param decoder The {@link ResourceDecoder} to register.
*/
public <Data, TResource> Registry prepend(
String bucket,
Class<Data> dataClass,
Class<TResource> resourceClass,
ResourceDecoder<Data, TResource> decoder) {
decoderRegistry.prepend(bucket, decoder, dataClass, resourceClass);
return this;
}

/**
* Overrides the default ordering of resource decoder buckets. You may also add custom buckets
* which are identified as a unique string. Glide will attempt to decode using decoders in the
* highest priority bucket before moving on to the next one.
*
* <p>The default order is [{@link #BUCKET_GIF}, {@link #BUCKET_BITMAP},
* {@link #BUCKET_BITMAP_DRAWABLE}].
*
* <p>When registering decoders, you can use these buckets to specify the ordering relative only
* to other decoders in that bucket.
* @see #append(String, Class, Class, ResourceDecoder)
* @see #prepend(String, Class, Class, ResourceDecoder)
*
* @param buckets The list of bucket identifiers in order from highest priority to least priority.
*/
public Registry setResourceDecoderBucketPriorityList(List<String> buckets) {
List<String> modifiedBuckets = new ArrayList<>(buckets);
modifiedBuckets.add(0, BUCKET_PREPEND_ALL);
modifiedBuckets.add(BUCKET_APPEND_ALL);
decoderRegistry.setBucketPriorityList(modifiedBuckets);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,45 @@
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.util.Synthetic;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Contains an ordered list of {@link ResourceDecoder}s capable of decoding arbitrary data types
* into arbitrary resource types from highest priority decoders to lowest priority decoders.
*/
@SuppressWarnings("rawtypes")
public class ResourceDecoderRegistry {
private final List<Entry<?, ?>> decoders = new ArrayList<>();
private final List<String> bucketPriorityList = new ArrayList<>();
private final Map<String, List<Entry<?, ?>>> decoders = new HashMap<>();

public synchronized void setBucketPriorityList(List<String> buckets) {
List<String> previousBuckets = new ArrayList<>(bucketPriorityList);
bucketPriorityList.clear();
bucketPriorityList.addAll(buckets);
for (String previousBucket : previousBuckets) {
if (!buckets.contains(previousBucket)) {
// Keep any buckets from the previous list that aren't included here, but but them at the
// end.
bucketPriorityList.add(previousBucket);
}
}
}

@SuppressWarnings("unchecked")
public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(Class<T> dataClass,
Class<R> resourceClass) {
List<ResourceDecoder<T, R>> result = new ArrayList<>();
for (Entry<?, ?> entry : decoders) {
if (entry.handles(dataClass, resourceClass)) {
result.add((ResourceDecoder<T, R>) entry.decoder);
for (String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry<?, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)) {
result.add((ResourceDecoder<T, R>) entry.decoder);
}
}
}
// TODO: cache result list.
Expand All @@ -31,22 +53,41 @@ public synchronized <T, R> List<ResourceDecoder<T, R>> getDecoders(Class<T> data
public synchronized <T, R> List<Class<R>> getResourceClasses(Class<T> dataClass,
Class<R> resourceClass) {
List<Class<R>> result = new ArrayList<>();
for (Entry<?, ?> entry : decoders) {
if (entry.handles(dataClass, resourceClass)) {
result.add((Class<R>) entry.resourceClass);
for (String bucket : bucketPriorityList) {
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
continue;
}
for (Entry<?, ?> entry : entries) {
if (entry.handles(dataClass, resourceClass)) {
result.add((Class<R>) entry.resourceClass);
}
}
}
return result;
}

public synchronized <T, R> void append(ResourceDecoder<T, R> decoder, Class<T> dataClass,
Class<R> resourceClass) {
decoders.add(new Entry<>(dataClass, resourceClass, decoder));
public synchronized <T, R> void append(
String bucket, ResourceDecoder<T, R> decoder, Class<T> dataClass, Class<R> resourceClass) {
getOrAddEntryList(bucket).add(new Entry<>(dataClass, resourceClass, decoder));
}

public synchronized <T, R> void prepend(ResourceDecoder<T, R> decoder, Class<T> dataClass,
Class<R> resourceClass) {
decoders.add(0, new Entry<>(dataClass, resourceClass, decoder));
public synchronized <T, R> void prepend(
String bucket, ResourceDecoder<T, R> decoder, Class<T> dataClass, Class<R> resourceClass) {
getOrAddEntryList(bucket).add(0, new Entry<>(dataClass, resourceClass, decoder));
}

private synchronized List<Entry<?, ?>> getOrAddEntryList(String bucket) {
if (!bucketPriorityList.contains(bucket)) {
// Add this unspecified bucket as a low priority bucket.
bucketPriorityList.add(bucket);
}
List<Entry<?, ?>> entries = decoders.get(bucket);
if (entries == null) {
entries = new ArrayList<>();
decoders.put(bucket, entries);
}
return entries;
}

private static class Entry<T, R> {
Expand Down

0 comments on commit da90633

Please sign in to comment.