Sdwebimage: How to decode animated WebP only first frame

Created on 15 Dec 2017  路  8Comments  路  Source: SDWebImage/SDWebImage

When creating animated WebP with image data, all image frames will be decoded. It may cost much memory. The WebP UIImage animation duration for each frame may be wrong. I need to use YYAnimatedImageView to display WebP, and image data is needed to create YYImage. I use SDWebImage to load image data. So, I should not decode all WebP image frames. This situation is like GIF and FLAnimatedImage. We load GIF data and decode only first frame. How can I get image data and decode animated WebP only first frame ?

WebP

All 8 comments

There is some reason related to this question, and I want spend some more time to talk about it.

The WebP UIImage animation duration for each frame may be wrong

SDWebImage from 4.1 fix the duration per frame bug in animated WebP. It use a calculation of GCD to each frame duration and repeat the UIImage several times and finally call [UIImage animatedImageWithImages:duration:]. See #1951 and #1952.

But to tell the truth, [UIImage animatedImageWithImages:duration:] method will keep all the instance into memory, this is suitable for small animted WebP images and have a really fast render performance. But when you decide to show many big animted WebP images, this may cause OOM.

YYImage, use another way to solve this issue. It create a UIImage subclass YYImage class, keep the image source into that instance. For Image/IO supported format, is CGImageSource. For WebP, is WebPDemuxer. And it also have one specify animted imageView YYAnimatedImageView. When you set a UIImage instance on it, it just show as normal UIImageView. But when you set a YYImage on it, it will use a CADisplay to render your image for each frame. It firstly decode, and then produce a CGImage through the inner image source when current frame's duration time reached.

YYImage's way can keep that only single frame instance alive at the sametime. So can reduce large memory usage for animted images. But it also cost extra CPU usage, because of that implementation, it need to re-decoding each frame everytime even you just leave your App without doing anything(For our application, it cause 50% CPU usage on a tableView showing 10 animted WebP cells). This is a time-space balance.

So, why SDWebImage currently use this implementation ? This may because of that SDWebImage historical design usage. It forcus on Simple and Category-friendly use case. So we do not need to including extra UIImageView subclass to showing the animated images.

But why we now have that FLAnimatedImageView ? This is because a huge refactory during 4.0 in #1575. We include FLAnimatedImage to support GIF rendering. Sadly, FLAnimatedImage does not do something like YYImage, it does not subclass from UIImage. And it just support GIF, but not general enough to support other animted image format such as APNG, WebP.

Maybe we can do another big refactor, to provide the same way like YYImage to support big animated images rendering. We need a new class SDAnimatedImageView and SDImage to do this. But this may cause a lot of time and we need contributers to support this feature. If someone need this, we can add a feature-request to future milestone. 馃憤

And also, for your last question:

How can I get image data and decode animated WebP only first frame ?

SDWebImage 4.2 introduce coder plugins. Which you can implement your own image decoder and encoder, see https://github.com/rs/SDWebImage/tree/4.2.0.

So for your case, you can just write a coder to produce a UIImage subclass instance like YYImage. Which just contains the first frame and keep that WebPDemuxer inside to decode other frames when you need.

In fact, since all our image decoding/encoding methods have been exposed as custom coders, you can even write a wrapper to use SDWebImage instead of YYWebImage to play with YYImage. (Sounds interesting ?). You just need to write a new category for YYAnimatedImageView, write a custom coder to directly call YYImageCoder methods, then combine them up :)

If you still have question about how to do this, I can write a demo provided for you 馃槄

If you're still interested in this issue, try to see #2140. We introduce a YYAnimatedImageView like new image view SDAnimatedImgeView and SDAnimatedImage, which also take benefit from our custom coder plugins and integrate with all our lib (cache, manager, downloader, category).

You have the choice to use normal UIImageView+UIAnimtedImage(small images, low CPU & high memory) or SDAnimatedImageView+SDAnimatedImage(big images, high CPU & low memory) by the same interface.

@Silence-GitHub We add a SDWebImageDecodeFirstFrameOnly options in 5.x branch. Currently our build-in coder always try to decode animated images if the original data is a animated format. You can try using image.images.firstObject to grab the first frame only. But in 4.x it seems that we can not break the current API to add a extra information to indicate that the custom coder SHOULD decode first frame only. :)

For more information, see #2140

Thank you! I solved the problem by implementing custom image coder.

:) I'll create a SDWebImage+YYImage CococaPods with SDWebImage 5.x dependency after #2140 merged (And also, we move our current GIF subspec to another SDWebImage+FLAnimatedImage Cocoapods). You can directlly use YYAnimatedImageView+WebCache category to load animated image conjunction SDWebImage and YYImage. All the API looks the same as UIImageView+WebCache

This is done by mark YYImage to adopt SDAnimatedImage protocol. This is also how our SDAnimatedImageView works(You can use SDAnimatedImageView separately). And you can even feed YYImage to SDAnimatedImageView because it conform SDAnimatedImage protocol. Hope this sounds good for you.

2140 Merged. If who are interested in that New Animated Image Solution for iOS & macOS with custom coder support. You can try 5.x branch or wait for 5.0.0-beta release

Was this page helpful?
0 / 5 - 0 ratings