Merge pull request #1985 from dreampiggy/performance_webp_decoding_optimize

Performance enhancement related to single WebP and animated WebP decoding...
This commit is contained in:
DreamPiggy 2017-09-29 12:01:18 +08:00 committed by GitHub
commit 250e1fc87f
3 changed files with 73 additions and 34 deletions

View File

@ -347,6 +347,12 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (data) { if (data) {
UIImage *image = [UIImage sd_imageWithData:data]; UIImage *image = [UIImage sd_imageWithData:data];
image = [self scaledImageForKey:key image:image]; image = [self scaledImageForKey:key image:image];
#ifdef SD_WEBP
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:data];
if (imageFormat == SDImageFormatWebP) {
return image;
}
#endif
if (self.config.shouldDecompressImages) { if (self.config.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image]; image = [UIImage decodedImageWithImage:image];
} }

View File

@ -446,8 +446,20 @@ didReceiveResponse:(NSURLResponse *)response
NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL]; NSString *key = [[SDWebImageManager sharedManager] cacheKeyForURL:self.request.URL];
image = [self scaledImageForKey:key image:image]; image = [self scaledImageForKey:key image:image];
// Do not force decoding animated GIFs BOOL shouldDecode = YES;
if (!image.images) { // Do not force decoding animated GIFs and WebPs
if (image.images) {
shouldDecode = NO;
} else {
#ifdef SD_WEBP
SDImageFormat imageFormat = [NSData sd_imageFormatForImageData:self.imageData];
if (imageFormat == SDImageFormatWebP) {
shouldDecode = NO;
}
#endif
}
if (shouldDecode) {
if (self.shouldDecompressImages) { if (self.shouldDecompressImages) {
if (self.options & SDWebImageDownloaderScaleDownLargeImages) { if (self.options & SDWebImageDownloaderScaleDownLargeImages) {
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH

View File

@ -43,14 +43,6 @@ static void FreeImageData(void *info, const void *data, size_t size) {
return nil; return nil;
} }
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
if (!(flags & ANIMATION_FLAG)) {
// for static single webp image
UIImage *staticImage = [self sd_rawWebpImageWithData:webpData];
WebPDemuxDelete(demuxer);
return staticImage;
}
WebPIterator iter; WebPIterator iter;
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) { if (!WebPDemuxGetFrame(demuxer, 1, &iter)) {
WebPDemuxReleaseIterator(&iter); WebPDemuxReleaseIterator(&iter);
@ -58,6 +50,8 @@ static void FreeImageData(void *info, const void *data, size_t size) {
return nil; return nil;
} }
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT); int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT);
int frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT); int frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
@ -77,6 +71,30 @@ static void FreeImageData(void *info, const void *data, size_t size) {
return nil; return nil;
} }
if (!(flags & ANIMATION_FLAG)) {
// for static single webp image
UIImage *staticImage = [self sd_rawWebpImageWithData:webpData];
if (staticImage) {
// draw on CGBitmapContext can reduce memory usage
CGImageRef imageRef = staticImage.CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGContextDrawImage(canvas, CGRectMake(0, 0, width, height), imageRef);
CGImageRef newImageRef = CGBitmapContextCreateImage(canvas);
#if SD_UIKIT || SD_WATCH
staticImage = [[UIImage alloc] initWithCGImage:newImageRef];
#else
staticImage = [[UIImage alloc] initWithCGImage:newImageRef size:NSZeroSize];
#endif
CGImageRelease(newImageRef);
}
WebPDemuxReleaseIterator(&iter);
WebPDemuxDelete(demuxer);
CGContextRelease(canvas);
return staticImage;
}
// for animated webp image
NSMutableArray<UIImage *> *images = [NSMutableArray array]; NSMutableArray<UIImage *> *images = [NSMutableArray array];
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
NSTimeInterval totalDuration = 0; NSTimeInterval totalDuration = 0;
@ -84,33 +102,36 @@ static void FreeImageData(void *info, const void *data, size_t size) {
#endif #endif
do { do {
UIImage *image; @autoreleasepool {
if (iter.blend_method == WEBP_MUX_BLEND) { UIImage *image;
image = [self sd_blendWebpImageWithCanvas:canvas iterator:iter]; if (iter.blend_method == WEBP_MUX_BLEND) {
} else { image = [self sd_blendWebpImageWithCanvas:canvas iterator:iter];
image = [self sd_nonblendWebpImageWithCanvas:canvas iterator:iter]; } else {
} image = [self sd_nonblendWebpImageWithCanvas:canvas iterator:iter];
}
if (!image) {
continue; if (!image) {
} continue;
}
[images addObject:image];
[images addObject:image];
#if SD_MAC #if SD_MAC
break; break;
#else #else
int duration = iter.duration; int duration = iter.duration;
if (duration <= 10) { if (duration <= 10) {
// WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms // WebP standard says 0 duration is used for canvas updating but not showing image, but actually Chrome and other implementations set it to 100ms if duration is lower or equal than 10ms
// Some animated WebP images also created without duration, we should keep compatibility // Some animated WebP images also created without duration, we should keep compatibility
duration = 100; duration = 100;
} }
totalDuration += duration; totalDuration += duration;
size_t count = images.count; size_t count = images.count;
durations[count - 1] = duration; durations[count - 1] = duration;
#endif #endif
}
} while (WebPDemuxNextFrame(&iter)); } while (WebPDemuxNextFrame(&iter));
WebPDemuxReleaseIterator(&iter); WebPDemuxReleaseIterator(&iter);