From 602184ee665a0ddfcc3c37089bdd49c67c4e8b60 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 18 Jan 2020 18:09:41 +0800 Subject: [PATCH 1/2] Using the WebPDemux API to support progressive animated WebP. This is less performance but at least works --- .../ViewController.m | 4 +- .../Classes/SDImageWebPCoder.m | 49 +++++++++++++++++-- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Example/SDWebImageWebPCoderExample/ViewController.m b/Example/SDWebImageWebPCoderExample/ViewController.m index 0d31b16..ac91240 100644 --- a/Example/SDWebImageWebPCoderExample/ViewController.m +++ b/Example/SDWebImageWebPCoderExample/ViewController.m @@ -22,6 +22,8 @@ [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. + [SDImageCache.sharedImageCache clearDiskOnCompletion:nil]; + [[SDImageCodersManager sharedManager] addCoder:[SDImageWebPCoder sharedCoder]]; self.imageView1 = [UIImageView new]; @@ -46,7 +48,7 @@ } }); }]; - [self.imageView2 sd_setImageWithURL:animatedWebPURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + [self.imageView2 sd_setImageWithURL:animatedWebPURL placeholderImage:nil options:SDWebImageProgressiveLoad completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { if (image) { NSLog(@"%@", @"Animated WebP load success"); } diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m index 8b602aa..e068073 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -86,6 +86,7 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio @implementation SDImageWebPCoder { WebPIDecoder *_idec; WebPDemuxer *_demux; + WebPData *_webpdata; // Copied for progressive animation demuxer NSData *_imageData; CGFloat _scale; NSUInteger _loopCount; @@ -113,6 +114,10 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio WebPDemuxDelete(_demux); _demux = NULL; } + if (_webpdata) { + WebPDataClear(_webpdata); + _webpdata = NULL; + } if (_canvas) { CGContextRelease(_canvas); _canvas = NULL; @@ -290,6 +295,8 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio preserveAspectRatio = preserveAspectRatioValue.boolValue; } _preserveAspectRatio = preserveAspectRatio; + _currentBlendIndex = NSNotFound; + _lock = dispatch_semaphore_create(1); } return self; } @@ -300,11 +307,41 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio } _imageData = data; _finished = finished; - VP8StatusCode status = WebPIUpdate(_idec, data.bytes, data.length); - if (status != VP8_STATUS_OK && status != VP8_STATUS_SUSPENDED) { - return; + if (!_demux) { + VP8StatusCode status = WebPIUpdate(_idec, data.bytes, data.length); + if (status == VP8_STATUS_OK || status == VP8_STATUS_SUSPENDED) { + return; + } + // This case may be Animated WebP progressive decode + if (status == VP8_STATUS_UNSUPPORTED_FEATURE) { + WebPDemuxState state; + WebPData tmpData; + WebPDataInit(&tmpData); + tmpData.bytes = data.bytes; + tmpData.size = data.length; + // Copy to avoid the NSData dealloc and VP8 internal retain the pointer + _webpdata = malloc(sizeof(WebPData)); + WebPDataCopy(&tmpData, _webpdata); + _demux = WebPDemuxPartial(_webpdata, &state); + } + } else { + // libwebp current have no API to update demuxer, so we always delete and recreate demuxer + WebPDemuxDelete(_demux); + _demux = NULL; + WebPDemuxState state; + WebPData tmpData; + WebPDataInit(&tmpData); + tmpData.bytes = data.bytes; + tmpData.size = data.length; + // Copy to avoid the NSData dealloc and VP8 internal retain the pointer + WebPDataClear(_webpdata); + WebPDataCopy(&tmpData, _webpdata); + _demux = WebPDemuxPartial(_webpdata, &state); + } + + if (_demux) { + [self scanAndCheckFramesValidWithDemuxer:_demux]; } - // libwebp current does not support progressive decoding for animated image, so no need to scan and update the frame information } - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options { @@ -832,6 +869,10 @@ static void FreeImageData(void *info, const void *data, size_t size) { // We should loop all the frames and scan each frames' blendFromIndex for later decoding, this can also ensure all frames is valid do { + if (!iter.complete) { + // Skip partial frame + continue; + } SDWebPCoderFrame *frame = [[SDWebPCoderFrame alloc] init]; frame.index = iterIndex; frame.duration = [self sd_frameDurationWithIterator:iter]; From b6f4692439ffc8af19f0a792a5616c43ff8b2d68 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Sat, 18 Jan 2020 18:43:56 +0800 Subject: [PATCH 2/2] Supports the UIImageView to show Prorgessive Animated Image's first poster frame, match the behavior of built-in coders --- SDWebImageWebPCoder/Classes/SDImageWebPCoder.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m index e068073..5412ee4 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -347,6 +347,14 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options { UIImage *image; + // For Animated WebP Images, progressive decoding only return the first frame. + // If you want progressive animation, use the SDAniamtedCoder protocol method instead. + if (_demux) { + SD_LOCK(_lock); + image = [self safeStaticImageFrame]; + SD_UNLOCK(_lock); + } + // For Static WebP images int width = 0; int height = 0; int last_y = 0;