bumptech / glide

An image loading and caching library for Android focused on smooth scrolling
https://bumptech.github.io/glide/
Other
34.67k stars 6.13k forks source link

Load several images into one view (with some "combine" function) #514

Open alexoro opened 9 years ago

alexoro commented 9 years ago

Hi. I looked through this library and didn't find out such feature. Let me explain by example: 1) I have a ListView/RecyclerView. The content is similar to any messaging application. 2) There is an avatar. But we don't have only one person to talk with, we also have "group chat". 3) Each participant have the avatar. For example, there will be a chat of 3 people, and avatar must be a combination of 2 other participants (excluding "me"). 4) I want to start loading both 2 images, which are bounded to Target/ImageView. 5) I want to merge them into one someway.

Or I can simply show the pseudo-code:

Glide.with(this)
    .load(Arrays.asList("http://image1", "http://image2"))
    .merge(new ImageMerger() {
            public Bitmap merge(List<Bitmap>, <TargetView>) { /* merge by any way */ }
    })
    .into(<TargetView>);

Is there any possibility to do smth like that? If yes, let me know how and where to looking at. I can write such function and make pull-request.

sjudd commented 9 years ago

Interesting idea. This isn't totally straight forward to do. You could take a look at the ThumbnailRequestCoordinator and SingleRequest classes and see if you could find a way to get this to work. It would have to be called on the main thread, so the merge operation would have to be fast.

I'll see if I think of a simpler solution and let you know. There's a general problem of wanting to load multiple images into the same target that we haven't really solved, this may be another case of that (see #132 for another example).

jcelaviie commented 8 years ago

Just out of curiosity, is this issue been resolved? I have similar feature request as alexoro.

TWiStErRob commented 8 years ago

Revisiting this: we may be able to make a loader/fetcher for List.class where the fetcher does something like:

for(String url : list) {
    result.add(new FileInputStream(Glide.load(url).downloadOnly().get()));
}

and then write a decoder which supports decoding a list into list of bitmaps and then the transcoder merges them.

In v3 terms, something like: GenericRequestBuilder<List<String>, List<InputStream>, List<Bitmap>, Bitmap>. The big problem is that the source images may be too big to all fit in memory. Also what if the user wants to merge 4 images that have been centerCropped to 50x50? How would you tell that to Glide?

@alexoro this is also for you, what do you expect in the List<Bitmap> in the merge method? It could be resized, transformed, or just the original from the url; all of them could be simply wrong for some use cases. I guess a uniform handling and a call signature like:

.merge(Glide.with(context).fromString().fitCenter().override(50,50), new ImageMerger()...

may help in this regard?

jcelaviie commented 8 years ago

Thanks Rob!

Is that possible we use customied BitmapTransformation, which takes serveral number of loaded decoded images and then put them in a compositeBitmap in an certain way and then render it? Not sure if it is a possible solution though.

TWiStErRob commented 8 years ago

BitmapTransformation has a locked signature, the real problem is that we don't know how to push multiple requests through Glide into the same target, not even if we can modify the code of it.

jcelaviie commented 8 years ago

Revisiting this again. Currently Glide doesn't have the API to support loading multiple images into one single target simultaneously. To achieve this, we probably need to implement a ModelLoader that fetches multiple urls and returns some custom data type and a decoder that decodes multiple images, merges them, and returns a composite Bitmap. It may look something like:

public final class FetchMutilpeUrlsModelLoader implements ModelLoader<ImageUrls, List<InputStream>> {
   ....
   // ...buildLoadData 
}

public final class MultipleUrlsDataFetcher implements DataFetcher<List<InputStream>> {
    ...
   //  loadData
 }

public class StreamBitmapDecoder implements ResourceDecoder<List<InputStream>, Bitmap> {
  ...
  // decode multiple images and returns a composite bitmap
}

Is this a feasible solution? Can you think of anything that is required to implement in each class to make this work?

Thanks!

TWiStErRob commented 8 years ago

Everything that you need is already present in Glide, you can use the Downsampler class as decoder inside your decoder, the only logic you really need to write is the implementation of these classes that glue other classes together and the combining part. That can work for anyone, but it's probably not generic enough to be included in the API. The API would need to accept any load for each part image. There's also a problem of maybe not being able to fit all the part images in memory before merging. In that case your custom decoder may work better. Also note that if you fetch data like that you don't have Glide caches ready, so you might want to enable OkHttp or similar cache as well.

jcelaviie commented 8 years ago

Thanks Rob! Can you be more specific about classes I need to glue in the implementation. Also in the fetcher, should I call glide.load for each url to build the list of inputsteam or make http connection to load data? Besides that, can you explain more about how to make Glide cashes ready, do I need to implement cache for the case? I really appreciate if you can show me some examples!

TWiStErRob commented 8 years ago

You can try to call Glide inside a fetcher but you'll likely end up with a deadlock if all tasks in the queue are combining, because Glide can't execute the inner fetch to finish up the combination. This is why I said you won't be able to cache.

Here are two examples where you can ask Glide to fetch the data for you:, but there's no caching, just the hidden I/O. https://github.com/TWiStErRob/glide-support/blob/24dd8002922df166cbd4fb020de1781887e5b6df/src/glide3/java/com/bumptech/glide/supportapp/github/_606_fallback_in_fetcher/YouTubeModelLoader.java https://github.com/TWiStErRob/glide-support/tree/24dd8002922df166cbd4fb020de1781887e5b6df/src/glide3/java/com/bumptech/glide/supportapp/github/_670_delegate

Also take a look at the built-in implementations of the interfaces you mentioned above.

sjudd commented 8 years ago

Another option is to just use multiple Targets. You can't use multiple ViewTargets, but you can write your own implementation of the interface. You can then use a custom view or Drawable and draw each resource independently to create collage you're trying for.

Using multiple Targets is probably simpler than writing it into the fetchers and decoders and will also allow each image to be cached individually as normal.

The only real problem with multiple Targets is that if you're loading them in a ListView or RecyclerView, you would need to retain a reference to the Targets from the previous bindView calls and clear them manually in each subsequent bindView call.

ZTFtrue commented 5 years ago

I also have similar feature request ,I can only use multiple imageviews