bumptech / glide

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

GIFs animate slowly with Glide 4.2 #2471

Open moxie0 opened 7 years ago

moxie0 commented 7 years ago

Here is a screen capture of two gifs playing w/ Glide 3.7: fastgif

Here is a screen capture of the same two gifs playing w/ Glide 4.2: slowgif

      GlideApp.with(context)
              .load(url)
              .diskCacheStrategy(DiskCacheStrategy.ALL)
              .into(view);

This happens with any gif that I've tried, here's an example: https://media.giphy.com/media/3ohjV3cQ9lvPeCVLOg/giphy.gif

Glide Version: 4.2.0 Integration libraries: None Device/Android Version: Nexus 5, OnePlus One, Pixel

lbx2015 commented 7 years ago

same as me. Can you give me some advise about the hand on code to load gif with 4.2.0? call my 18221650910 from china? RequestOptions o = new RequestOptions(); Glide.with(h.imageView3.getContext()) .load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1508322038285&di=e0c474540228565f1271de3b4b2c56e7&imgtype=0&src=http%3A%2F%2Fwww.lia-edu.com%2Fupload%2Fimage%2F20170717%2F20170717151619_8233.gif").apply(o.diskCacheStrategy(DiskCacheStrategy.RESOURCE)).into(h.imageView3);

sjudd commented 7 years ago

I'm not able to reproduce this on a Nexus 5X on 7.1.1 if I download the gifs you've pointed to. The GIFs are however pretty small.

Can you attach the files you're having trouble with directly to the issue to make sure we're testing using the same exact data?

Can you also provide the XML or at least the dimensions of the view you're loading into?

moxie0 commented 7 years ago

Hmm, I think I must have made a mistake in my original issue report. I just tried building a sample app with a jarjar'd version of 3.7 and an unmodified 4.2 together in the same app which loads the same GIF into two adjacent ImageViews using two different versions of Glide, and I'm also not able to reproduce this.

What I am seeing, however, is that GIFs render in slow-motion with both Glide 3.7 and 4.2 on Android 5.0 and Android 6.0 devices (a Nexus 5 and OnePlus One, respectively), but render at normal speed on an Android 8.0 device (Pixel).

I tried a sample app with a single 3.7 or 4.2 production dependency, loading a single GIF, and the results are the same. Things render fine (with both 3.7 and 4.2) on an Android 8.0 device, but not an Android 5.0 or 6.0 device.

Here's a sample layout I'm using:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center">

        <ImageView
                android:id="@+id/image"
                android:layout_gravity="center"
                android:layout_width="210dp"
                android:layout_height="210dp"/>

</LinearLayout>

The Glide 4.2 code:

    Glide.with(this)
         .load(R.raw.giphy)
         .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.NONE))
         .into((ImageView)findViewById(R.id.image));

An attached GIF I've tested with: giphy

sjudd commented 7 years ago

Thanks for the follow up.

That makes sense. The GIF decoder we use isn't necessarily as fast or optimized as it could be. We've fixed a few cases where it was slower than it needed to be, but there may be more relatively simple changes we could make. Otherwise it might require a more fundamental restructuring.

One other idea we've been looking in to is adding support for rastermill/framesequence or another third party gif decoder as an integration library.

ssessa commented 6 years ago

I ran into this issue on a Nexus 5 running 6.0.1.

When running the debug Build Variant gifs perform poorly.

Once I switched to release, gifs began performing as expected. Hope this helps out...

sjudd commented 6 years ago

I haven't found anything huge so far but two things worth noting:

  1. In Glide 4.3.0, the number of threads available for GIFs decreased to 2 or 1. These threads are used only for GIF frames which avoids delays loading frames for network operations, but reduced the number of threads available for GIF frames, especially on high end devices. Limiting the number of concurrently playing GIFs will help. I'll also add an API in Glide 4.4.0 to allow you to modify the number of threads used for animations.
  2. In Glide 4.4.0, I'll add an (experimental) API called clearOnDetach (name may change, suggestions welcome). clearOnDetach will free up resources used by ViewTargets when the View is detached from it's window and automatically restart the request if the View is ever reattached. That method is primarily meant for reducing memory usage, but it will also help reduce the number of running GIFs by pausing those as soon as they go off screen. I've seen this help significantly in RecyclerView, which doesn't set visibility for off screen views and doesn't call its RecyclerListener for every view that's off screen. I may also try to just build this in to ViewTarget so that it happens automatically.

More to come as I find it. If anyone else has any suggestions or can reproduce any particularly bad cases, I'd welcome the help.

sjudd commented 6 years ago

@ssessa Are you using proguard? Do you have optimizations enabled? If you disable proguard but otherwise leave you release variant the same, do you still see GIF performance improve on those builds?

sjudd commented 6 years ago

I've pushed a series of (hopefully) safe changes. Referencing the intermediate arrays in local variables seems to make a really large difference on API 23 devices, though I don't see the same difference on API 26. The rest of the changes are much smaller improvements.

I have a couple of larger fixes including automatic pausing of detached animatables and an option to cache color table indices for all GIF frames (one byte per changed pixel per frame vs four bytes per pixel for all pixels per frame if we cache the entire frame). I may hold off on those until 4.5 to give us some more time to test.

GIFs are difficult to test and there's lots of expected but not to spec behavior that can make things that seem like they ought to be safe unsafe.

delacrixmorgan commented 6 years ago

Realised that we are looking at the wrong place on this GIF issue. Problem isn't with Glide, but it how Android is recognising the.gif file. Try this.

Add this url into your Glide load, and it will load nicely. Although, in reality it's 7.2MB. https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif

In fact, because the API that Giphy uses for that image was specify the content-type as image/gif. Thus, somehow made Android recognise.gif file and handles them respectively.

TLDR Specifying content-type as image/gif will have your buttery smooth .gif. Simple indicator will be to enter your link in your browser and see whether it hosts the image on the page or prompt you to download.

KimiChiu commented 6 years ago

Hi,

I have the same problem too. In Glide 3, we fix this problem by this way.

builder.setDiskCacheService(new FifoPriorityThreadPoolExecutor(4));
builder.setResizeService(new FifoPriorityThreadPoolExecutor(4));

But we can't found these APIs in Glide 4.

sjudd commented 6 years ago

@KimiChiu http://bumptech.github.io/glide/javadocs/460/com/bumptech/glide/GlideBuilder.html#setAnimationExecutor-com.bumptech.glide.load.engine.executor.GlideExecutor-

Eggplant-Cui commented 6 years ago

For me I did some invesgate Obviously this is related to hardware I tested a high resolution gif (1920*1080 120 frames 867KB) on several devices Perfect performance on: XiaoMi 6 Qualcomm 845 published 2017 OnePlus 5 Qualcomm 845 published 2017 Normal performance on: Samsung S5 Qualcomm 800 published 2014 Bad performance on: Oppo R7 plus Qualcomm 615 published 2015

The test is not only on glide , i also try to play this gif with the default way the device offered.

But if i choose another gif (400*300, 7.2MB), it performance perfectly on all devices above.

So try to compress your gif or choose play it as movie will partly solve this problem.

Sorry for my poor English, I'll repeat my words in Chinese

gif动画帧率低的问题应该不只是glide的问题,各位如果能把设备信息和gif信息一起贴上来可能会更快的明确问题是关于设备还是这个库。 我的任务是为app添加开屏动画,动画尺寸是1080p,120帧,帧率60,文件大小867kb 在小米6,一加5上面完美播放 三星s5略有延迟,大概2.1-2.2秒才能播放完毕 oppo r7 plus 需要4-5秒才能播放完毕 而且调用系统相册播放gif也是相同的结果 而我换了一个400*300分辨率,7.2mb大小的gif文件,无论是用glide还是用系统的相册播放,都很流畅。所以我猜测这个问题应该是cpu解码gif的速度跟不上播放速度导致的

最终我的解决方案是用movie来播放gif,这样可能会导致丢帧,但是至少时间长度是一致的

KimiChiu commented 6 years ago

I tried this,

builder.setAnimationExecutor( GlideExecutor.newAnimationExecutor(4, GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );
builder.setDiskCacheExecutor( GlideExecutor.newDiskCacheExecutor(4, "glideDiskCacheExecutor", GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );
builder.setSourceExecutor( GlideExecutor.newSourceExecutor(4, "glideSourceExecutor", GlideExecutor.UncaughtThrowableStrategy.DEFAULT) );

But it still plays in slow motion in RecyclerView. The ViewPager doesn't have this problem. The test device is Nokia 6 with Android 8.

compileSdkVersion 27
buildToolsVersion '27.0.1'
minSdkVersion 19
targetSdkVersion 26

compile 'com.github.bumptech.glide:glide:4.6.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.6.1'
compile "com.github.bumptech.glide:okhttp3-integration:4.6.1"
compile 'com.squareup.okhttp3:okhttp:3.10.0'
Svoka commented 6 years ago

Any update on this?

perracodex commented 6 years ago

Any plans to add a third-party decoder to improve gif playback?

andrefrsousa commented 6 years ago

@sjudd Any update on this?

andrefrsousa commented 6 years ago

Here are my conclusions so far on this matter. I created a sample app that shows a list of gif links:

"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"
"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://media0.giphy.com/media/3o7WIFB3rd67FVuyOs/giphy.gif"
"https://media1.giphy.com/media/Sb7bVRgv5NJcjbLJce/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"
"https://media3.giphy.com/media/l4pT0Y5Zr6nsdFmZq/giphy.gif"
"https://media0.giphy.com/media/3o7WIFB3rd67FVuyOs/giphy.gif"
"https://media1.giphy.com/media/Sb7bVRgv5NJcjbLJce/giphy.gif"
"https://user-images.githubusercontent.com/512439/31472012-f0ca007a-aea0-11e7-882e-2b4ef259a954.gif"
"https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif"
"https://media.giphy.com/media/10A06pOVcpo6hW/giphy.gif"
"https://media.giphy.com/media/10D8j2EpNCXDA4/giphy.gif"

I made the test using Glide 4.7.1 and Fresco 1.9.0 on a Galaxy S6, and the results were as follow:

Glide: https://drive.google.com/open?id=1W8_JatGQ7-On3UVpiZw9ews30oc62fCf Fresco: https://drive.google.com/open?id=1puh6yClbeiUgTAwZviYkip7_T_klJBqo

There is a noticeable performance issue when processing GIF files. @sjudd any thoughts on why such a difference?

ghost commented 6 years ago

@andrefrsousa can you upload the code please? I can't get it working properly, the gif is too slow. Thanks

andrefrsousa commented 6 years ago

@lucasgriotto very simple: Glide

Glide.with(holder.binding.imageView)
                .asGif()
                .load(image[position])
                .into(holder.binding.imageView)
                .clearOnDetach()

Fresco

        val hierarchy = holder.binding.imageView.hierarchy
        hierarchy.fadeDuration = 0

        holder.binding.imageView.hierarchy = hierarchy
        holder.binding.imageView.controller = Fresco.newDraweeControllerBuilder()
                .setImageRequest(ImageRequest.fromUri(image[position]))
                .setAutoPlayAnimations(true)
                .build()
ghost commented 6 years ago

@andrefrsousa thanks. I don't know what is wrong I can't get it working properly.

I am using Glide.

 implementation 'com.github.bumptech.glide:glide:4.7.1'
 annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'

<ImageView
        android:id="@+id/fra_home_img_loading_burger"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

mImgLoading = view.findViewById(R.id.fra_home_img_loading_burger);

 Glide.with(mImgLoading).asGif().load("https://media.giphy.com/media/3o6Zt7YExBfE89p6Yo/giphy.gif")
.into(mImgLoading).clearOnDetach();
perracodex commented 6 years ago

The reason Fresco is faster is because it caches all the GIF frames, and in addition uses some native help for decoding. Source code here:

Fresco's GIF decoder

Notice how all the frames get cached. And for a fast decoding it uses the android.graphics.Movie class. If you check the source of Movie.decodeStream(), you’ll see how this one uses a native method for decoding: nativeDecodeStream

Glide doesn’t cache GIF frames to reduce memory usage, plus the decoding is done purely in Java.

I think an improvement would be to give an optional setting to cache GIF frames in memory. It makes sense to not cache them by default if the GIFs are seen in a RecyclerView with lots of other images. But once a user selects/opens one specific GIF, it would be better to use an optimized version with memory cached frames. Maybe as a caution, it could also have a parameter to specify the size for cached memory frames.

A second optimization would be to also use the Movie class, or the AnimatedImageDrawable one starting at API 28, as the Movie class has just been deprecated.

githubzjh commented 6 years ago

@sjudd Do you have some plans to fix the issue?

Zhuinden commented 5 years ago

Maybe this spares some time for people in the future, so what I did was ditch Glide in favor of Fresco because it loads GIFs fast.

So what I did is add Fresco:

    implementation 'com.facebook.fresco:fresco:1.9.0'
    // For animated GIF support
    implementation 'com.facebook.fresco:animated-gif:1.9.0'
    // For WebP support, including animated WebP
    implementation 'com.facebook.fresco:animated-webp:1.9.0'
    implementation 'com.facebook.fresco:webpsupport:1.9.0'

And the Fresco.initialize(context) call and stuff that it needs, and also

class GifDraweeView : SimpleDraweeView {
    private var drawableResource = 0

    constructor(context: Context) : super(context) {
        init(context, null, 0)
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        init(context, attrs, 0)
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(context, attrs, defStyleAttr)
    }

    private fun init(context: Context, attributeSet: AttributeSet?, defStyle: Int) {
        if (attributeSet != null) {
            val a: TypedArray? = when {
                defStyle != 0 -> context.obtainStyledAttributes(attributeSet, R.styleable.GifDraweeView, defStyle, 0)
                else -> context.obtainStyledAttributes(attributeSet, R.styleable.GifDraweeView)
            }
            drawableResource = a!!.getResourceId(R.styleable.GifDraweeView_gifResourceDrawable, 0)
            a.recycle()

            if (!isInEditMode) {
                if (drawableResource != 0) {
                    val controller = Fresco.newDraweeControllerBuilder()
                        .setUri(UriUtil.getUriForResourceId(drawableResource))
                        .setAutoPlayAnimations(true)
                        .build()
                    setController(controller)
                }
            }
        }
    }
}

and

<declare-styleable name="GifDraweeView">
        <attr name="gifResourceDrawable" format="reference"/>
</declare-styleable>

And Fresco GIF rendering is not slow, so it's ok

Make sure the actualImageScaleType is fitCenter.

AndroidDeveloperLB commented 5 years ago

Any news about this?

@Zhuinden Can you please share a full sample of just that? I also wonder how to go over each frame instead of just playing them directly into a View.

Zhuinden commented 5 years ago

I'm pretty sure you have to implement yourself what GifFrameLoader is doing in order to go through frames one by one instead of playing them directly into a View.

Check the GifDrawable source.

I didn't try using Glide 3.8.0 (or 3.7.0) to see if performance is better, but that might be an option too...

AndroidDeveloperLB commented 5 years ago

@Zhuinden I mean "can you please share a full sample of using Fresco?" And in the same context "I also wonder how to go over each frame instead of just playing them directly into a View, in Fresco"

Zhuinden commented 5 years ago

oh I think in Fresco you'd have even tougher luck going frame by frame, I have no idea.

AndroidDeveloperLB commented 5 years ago

@Zhuinden I see. Can you share your sample nevertheless? I had bad experience trying it myself. I want to compare with Glide myself...

delacrixmorgan commented 5 years ago

GIPHY wrote a blog post explaining the details of how Fresco handles GIF better and to quote the post.

Developers made it clear that performance can’t be improved, unless they rewrite their rendering engine from scratch.

https://engineering.giphy.com/giphy-android-app-and-fresco/

idish commented 5 years ago

Pretty major issue, this should be fixed somehow in the next release!

skyfit2 commented 5 years ago

I have the same problem , i recomended change to Fresco , its more fast and complete compared to Glide

takaoandrew commented 5 years ago

@skyfit2 Do you know if Fresco allow playing GIFs from a url like Glide does?

AndroidDeveloperLB commented 5 years ago

@takaoandrew For the size of this library, it better be....

nirukk52 commented 5 years ago

Adding crossfade on local image drawable worked for me.

Glide.with(context).load(stringImage).asGif()
   .crossFade().diskCacheStrategy(DiskCacheStrategy.SOURCE).into(postImage);
andrewcking commented 5 years ago

Also experiencing this on most devices I have tested it on, from Galaxy s10 to nexus 5. It is much more subtle on higher end devices but is still noticeable.

Zhuinden commented 5 years ago

Have you tried with Glide 3.7.2?

andrewcking commented 5 years ago

Wow..... just tried doing some rewrites to go back to 3.7.0 and gif performance dramatically improved (night and day difference). This makes me chuckle because they cited improved Gif performance as one of the reasons to upgrade to 4 (I started with 4). I have been sticking with Glide 4.2 because performance got a lot worse in the 4.3 update though I have been testing each version afterwards to see if it gets better and it hasn't.

Here is what I have found: Glide 4.3-4.9: Terrible gif performance on all devices Glide 4.2: Acceptable gif performance on most devices Glide 3.7.0: Great gif performance (so far in my tests)

AndroidDeveloperLB commented 5 years ago

@andrewcking Can you please show a sample, so that I could see the difference?

andrewcking commented 5 years ago

Visually? There are some examples above but here is what I am seeing in terms of performance difference between 3.7 and 4.9 download.zip

This is on a galaxy s10 displaying 4 low-res optimized gifs and 3.7 blows 4.9 out of the water.

AndroidDeveloperLB commented 5 years ago

@andrewcking I meant as projects, but visually I see on your sample that there is a difference... How could it be so slow? On which device and Android version have you tested it?

andrewcking commented 5 years ago

I've tested on nexus 4,5,5x and galaxy s8,s10 and then also on OnePlus 6. It is noticeable on all devices at least for my use case. I don't know the android version of each device but it ranges from android 5 on the nexus 4 to android 9 on the galaxy devices. Note these gifs are all local on the device (not loading from the internet).

I don't have a project implementation I can share but my glide calls between 3 and 4 are pretty similar

3.7

Glide.with(mActivity).load(images.get(position))
        .crossFade()
        .placeholder(R.drawable.placeholder)
        .into(holder.image);

4.+

RequestOptions options = new RequestOptions()
         .placeholder(R.drawable.placeholder);
Glide.with(mActivity).load(images.get(position))
        .transition(withCrossFade())
        .apply(options)
        .into(holder.image);
AndroidDeveloperLB commented 5 years ago

@andrewcking OK I've tested it myself on LG G5 with Android 8.

Attached sample project, using what you guys wrote.

MyApplication.zip

While I don't see much issue on how fluid it plays, here are the results in CPU usage via the profiler:

4.9.0:

image

3.7.0:

image

How did you perform the test? I think it can get slower in some cases, which I'm not sure what they are.

droidluv commented 5 years ago

Wow was just wondering how good Glide performance was with gif's and this thread just opened my eyes, guess I'll jump onto the Fresco bandwagon for smooth gif playback

AndroidDeveloperLB commented 5 years ago

@droidluv Where exactly? Please show a sample showing the issue

Zhuinden commented 5 years ago

@droidluv technically, Glide 3.7.2 was ok

droidluv commented 5 years ago

@droidluv Where exactly? Please show a sample showing the issue

I don't have a sample anymore, I encountered a stutter/slowdown when my backend started sending a gif accidentally instead of an image, on a Galaxy On6, and I am using the latest version of Glide (4.9.0), I am hesitant to add Fresco though because of it's much larger size, but saw above that Fresco performed better than Glide with its native code usage, I'll try to generate samples soon enough to compare because a new requirement to support gif playback is incoming.

AndroidDeveloperLB commented 5 years ago

Can you please put a sample of the best Glide , the newest one, and of Fresco?

droidluv commented 5 years ago

@AndroidDeveloperLB I am attaching the videos below differentiating Fresco and Glide, this slowdown seems specific to only certain devices like I have encountered the Glide slowdown in two Samsung phones but works fine on everything else including other Samsung phones like the Galaxy S7, even though Fresco gives a uniform GIF experience throughout every device, Glide has better performance advantages when dealing with complex images and recyclerviews, like fresco doesn't even support PNG resizing (we can do some downsampling but..) when dealing with complex lists of images and gifs Glide wins hands down, I just switched back to Glide even though I might have to deal with slowed down GIFs in some devices

This comparison is between the newest glide and the latest version of Fresco

Glide and Fresco comparison.zip

AndroidDeveloperLB commented 5 years ago

@droidluv This is of the newest Glide, right? And you also tried the version of Glide that works fine? Can you please share the sample code too?

droidluv commented 5 years ago

@AndroidDeveloperLB Its on the latest Glide 4.9.0, I haven't tried the 3.x glide because I didn't want to go back to an older version, the code is just a basic vertical Recyclerview with horizontal list's of GIF's and PNG's, Also please do note my point, the slowdown happens only on CERTAIN devices, one such model is the Galaxy J7 Pro, with Android Pie(Android version 9) and OneUI 1.1 and is the stock OS, if you want I can try and generate logs from that device, if I could but I suppose the video should talk for itself, the other device was also a Samsung, a Samsung Galaxy A series device, I can get more info about that device as well if needed, and I'm fairly sure the slowdown is happening on some other devices as well, Fresco seemingly works smoothly everywhere though regarding GIF playback.