From 6e88c64f4f410ecfe664e41aafcf32f307b2ea64 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 7 Jan 2020 14:54:33 +0800 Subject: [PATCH 1/2] Fix the issue when WebP is static webp, the `SDAnimatedImageCoder` protocol implementation still create CGContext, this can reduce RAM usage --- .../Classes/SDImageWebPCoder.m | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m index 6b06059..932eebb 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -711,6 +711,18 @@ static void FreeImageData(void *info, const void *data, size_t size) { uint32_t loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT); NSMutableArray *frames = [NSMutableArray array]; + _hasAnimation = hasAnimation; + _hasAlpha = hasAlpha; + _canvasWidth = canvasWidth; + _canvasHeight = canvasHeight; + _frameCount = frameCount; + _loopCount = loopCount; + + // If static WebP, does not need to parse the frame blend index + if (frameCount <= 1) { + return YES; + } + // We should loop all the frames and scan each frames' blendFromIndex for later decoding, this can also ensure all frames is valid do { SDWebPCoderFrame *frame = [[SDWebPCoderFrame alloc] init]; @@ -748,12 +760,6 @@ static void FreeImageData(void *info, const void *data, size_t size) { return NO; } _frames = [frames copy]; - _hasAnimation = hasAnimation; - _hasAlpha = hasAlpha; - _canvasWidth = canvasWidth; - _canvasHeight = canvasHeight; - _frameCount = frameCount; - _loopCount = loopCount; return YES; } @@ -774,6 +780,9 @@ static void FreeImageData(void *info, const void *data, size_t size) { if (index >= _frameCount) { return 0; } + if (_frameCount <= 1) { + return 0; + } return _frames[index].duration; } @@ -783,11 +792,40 @@ static void FreeImageData(void *info, const void *data, size_t size) { return nil; } SD_LOCK(_lock); - image = [self safeAnimatedImageFrameAtIndex:index]; + if (_frameCount <= 1) { + image = [self safeStaticImageFrame]; + } else { + image = [self safeAnimatedImageFrameAtIndex:index]; + } SD_UNLOCK(_lock); return image; } +- (UIImage *)safeStaticImageFrame { + UIImage *image; + if (!_colorSpace) { + _colorSpace = [self sd_colorSpaceWithDemuxer:_demux]; + } + // Static WebP image + WebPIterator iter; + if (!WebPDemuxGetFrame(_demux, 1, &iter)) { + WebPDemuxReleaseIterator(&iter); + return nil; + } + CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:_colorSpace]; + if (!imageRef) { + return nil; + } +#if SD_UIKIT || SD_WATCH + image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:UIImageOrientationUp]; +#else + image = [[UIImage alloc] initWithCGImage:imageRef scale:_scale orientation:kCGImagePropertyOrientationUp]; +#endif + CGImageRelease(imageRef); + WebPDemuxReleaseIterator(&iter); + return image; +} + - (UIImage *)safeAnimatedImageFrameAtIndex:(NSUInteger)index { if (!_canvas) { CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; From 7cd404cccc859740e2ed9502da9672ce9525c60d Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Tue, 7 Jan 2020 15:21:47 +0800 Subject: [PATCH 2/2] Add test case to ensure that Static WebP does not create CGContext --- SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m b/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m index f895071..d046396 100644 --- a/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m +++ b/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m @@ -10,6 +10,7 @@ @import XCTest; #import #import +#import const int64_t kAsyncTestTimeout = 5; @@ -172,6 +173,18 @@ const int64_t kAsyncTestTimeout = 5; } } +- (void)test34StaticImageNotCreateCGContext { + NSURL *staticWebPURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"TestImageStatic" withExtension:@"webp"]; + NSData *data = [NSData dataWithContentsOfURL:staticWebPURL]; + SDImageWebPCoder *coder = [[SDImageWebPCoder alloc] initWithAnimatedImageData:data options:nil]; + XCTAssertTrue(coder.animatedImageFrameCount == 1); + UIImage *image = [coder animatedImageFrameAtIndex:0]; + XCTAssertNotNil(image); + Ivar ivar = class_getInstanceVariable(coder.class, "_canvas"); + CGContextRef canvas = ((CGContextRef (*)(id, Ivar))object_getIvar)(coder, ivar); + XCTAssert(canvas == NULL); +} + @end @implementation SDWebImageWebPCoderTests (Helpers)