diff --git a/Example/SDWebImageWebPCoderExample/ViewController.m b/Example/SDWebImageWebPCoderExample/ViewController.m index 66652f0..73275f8 100644 --- a/Example/SDWebImageWebPCoderExample/ViewController.m +++ b/Example/SDWebImageWebPCoderExample/ViewController.m @@ -37,7 +37,8 @@ NSURL *staticWebPURL = [NSURL URLWithString:@"https://www.gstatic.com/webp/gallery/2.webp"]; NSURL *animatedWebPURL = [NSURL URLWithString:@"http://littlesvr.ca/apng/images/world-cup-2014-42.webp"]; - [self.imageView1 sd_setImageWithURL:staticWebPURL placeholderImage:nil options:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(300, 300))} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + [self.imageView1 sd_setImageWithURL:staticWebPURL placeholderImage:nil options:0 context:@{SDWebImageContextImageScaleDownLimitBytes : @(1024 * 100)} progress:nil completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { + NSCAssert(image.size.width < 200, @"Limit Bytes should limit image size to 186"); if (image) { NSLog(@"%@", @"Static WebP load success"); } diff --git a/Package.resolved b/Package.resolved index 3452e00..1f5343b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/SDWebImage/SDWebImage.git", "state": { "branch": null, - "revision": "966e6c3ee4569227ce67434d890bb22073ead2d6", - "version": "5.5.0" + "revision": "fb50c1d20f24db5322b2f8f379de3618f75fe08e", + "version": "5.15.5" } } ] diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m index db308c7..bd158b3 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -88,6 +88,20 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv return canvas; } +// TODO, share this logic for multiple coders, or do refactory in v6.0 (The coder plugin should provide image information back to Core, like `CGImageSourceCopyPropertiesAtIndex`) +static inline CGSize SDCalculateScaleDownPixelSize(NSUInteger limitBytes, CGSize originalSize, NSUInteger frameCount, NSUInteger bytesPerPixel) { + if (CGSizeEqualToSize(originalSize, CGSizeZero)) return CGSizeMake(1, 1); + NSUInteger totalFramePixelSize = limitBytes / bytesPerPixel / (frameCount ?: 1); + CGFloat ratio = originalSize.height / originalSize.width; + CGFloat width = sqrt(totalFramePixelSize / ratio); + CGFloat height = width * ratio; + width = MAX(1, floor(width)); + height = MAX(1, floor(height)); + CGSize size = CGSizeMake(width, height); + + return size; +} + @interface SDWebPCoderFrame : NSObject @property (nonatomic, assign) NSUInteger index; // Frame index (zero based) @@ -126,6 +140,7 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv NSUInteger _currentBlendIndex; BOOL _preserveAspectRatio; CGSize _thumbnailSize; + BOOL _limitBytes; } - (void)dealloc { @@ -218,6 +233,24 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv CGColorSpaceRef colorSpace = [self sd_createColorSpaceWithDemuxer:demuxer]; int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); + uint32_t frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT); + int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT); + + NSUInteger limitBytes = 0; + NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes]; + if (limitBytesValue != nil) { + limitBytes = limitBytesValue.unsignedIntegerValue; + } + // Scale down to limit bytes if need + if (limitBytes > 0) { + // Hack 32 BitsPerPixel + CGSize imageSize = CGSizeMake(canvasWidth, canvasHeight); + CGSize framePixelSize = SDCalculateScaleDownPixelSize(limitBytes, imageSize, frameCount, 4); + // Override thumbnail size + thumbnailSize = framePixelSize; + preserveAspectRatio = YES; + } + // Check whether we need to use thumbnail CGSize scaledSize = [SDImageCoderHelper scaledSizeWithImageSize:CGSizeMake(canvasWidth, canvasHeight) scaleSize:thumbnailSize preserveAspectRatio:preserveAspectRatio shouldScaleUp:NO]; @@ -245,7 +278,6 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv return nil; } - int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT); NSMutableArray *frames = [NSMutableArray array]; do { @@ -312,6 +344,12 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv preserveAspectRatio = preserveAspectRatioValue.boolValue; } _preserveAspectRatio = preserveAspectRatio; + NSUInteger limitBytes = 0; + NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes]; + if (limitBytesValue != nil) { + limitBytes = limitBytesValue.unsignedIntegerValue; + } + _limitBytes = limitBytes; _currentBlendIndex = NSNotFound; SD_LOCK_INIT(_lock); } @@ -352,6 +390,15 @@ static inline CGContextRef _Nullable CreateWebPCanvas(BOOL hasAlpha, CGSize canv [self scanAndCheckFramesValidWithDemuxer:_demux]; } SD_UNLOCK(_lock); + // Scale down to limit bytes if need + if (_limitBytes > 0) { + // Hack 32 BitsPerPixel + CGSize imageSize = CGSizeMake(_canvasWidth, _canvasHeight); + CGSize framePixelSize = SDCalculateScaleDownPixelSize(_limitBytes, imageSize, _frameCount, 4); + // Override thumbnail size + _thumbnailSize = framePixelSize; + _preserveAspectRatio = YES; + } } - (UIImage *)incrementalDecodedImageWithOptions:(SDImageCoderOptions *)options { @@ -911,6 +958,21 @@ static float GetFloatValueForKey(NSDictionary * _Nonnull dictionary, NSString * preserveAspectRatio = preserveAspectRatioValue.boolValue; } _preserveAspectRatio = preserveAspectRatio; + NSUInteger limitBytes = 0; + NSNumber *limitBytesValue = options[SDImageCoderDecodeScaleDownLimitBytes]; + if (limitBytesValue != nil) { + limitBytes = limitBytesValue.unsignedIntegerValue; + } + _limitBytes = limitBytes; + // Scale down to limit bytes if need + if (_limitBytes > 0) { + // Hack 32 BitsPerPixel + CGSize imageSize = CGSizeMake(_canvasWidth, _canvasHeight); + CGSize framePixelSize = SDCalculateScaleDownPixelSize(_limitBytes, imageSize, _frameCount, 4); + // Override thumbnail size + _thumbnailSize = framePixelSize; + _preserveAspectRatio = YES; + } _scale = scale; _demux = demuxer; _imageData = data;