WebPCoder now supports the thumbnail decoding

This commit is contained in:
DreamPiggy 2020-01-16 22:12:43 +08:00
parent a936bdb6d1
commit 9db3358eb0
5 changed files with 88 additions and 17 deletions

View File

@ -1,2 +1,2 @@
github "SDWebImage/SDWebImage" ~> 5.0 github "SDWebImage/SDWebImage" ~> 5.5
github "SDWebImage/libwebp-Xcode" ~> 1.0 github "SDWebImage/libwebp-Xcode" ~> 1.0

View File

@ -1,2 +1,2 @@
github "SDWebImage/SDWebImage" "5.0.0" github "SDWebImage/SDWebImage" "5.5.0"
github "SDWebImage/libwebp-Xcode" "1.0.0" github "SDWebImage/libwebp-Xcode" "1.1.0"

View File

@ -35,7 +35,7 @@
NSURL *staticWebPURL = [NSURL URLWithString:@"https://www.gstatic.com/webp/gallery/2.webp"]; 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"]; NSURL *animatedWebPURL = [NSURL URLWithString:@"http://littlesvr.ca/apng/images/world-cup-2014-42.webp"];
[self.imageView1 sd_setImageWithURL:staticWebPURL completed:^(UIImage * _Nullable image, NSError * _Nullable error, SDImageCacheType cacheType, NSURL * _Nullable imageURL) { [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) {
if (image) { if (image) {
NSLog(@"%@", @"Static WebP load success"); NSLog(@"%@", @"Static WebP load success");
} }

View File

@ -27,7 +27,7 @@ This is a SDWebImage coder plugin to support WebP image.
'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SD_WEBP=1 WEBP_USE_INTRINSICS=1', 'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SD_WEBP=1 WEBP_USE_INTRINSICS=1',
'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src' 'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src'
} }
s.dependency 'SDWebImage/Core', '~> 5.0' s.dependency 'SDWebImage/Core', '~> 5.5'
s.dependency 'libwebp', '~> 1.0' s.dependency 'libwebp', '~> 1.0'
end end

View File

@ -68,6 +68,8 @@
CGFloat _canvasHeight; CGFloat _canvasHeight;
dispatch_semaphore_t _lock; dispatch_semaphore_t _lock;
NSUInteger _currentBlendIndex; NSUInteger _currentBlendIndex;
BOOL _preserveAspectRatio;
CGSize _thumbnailSize;
} }
- (void)dealloc { - (void)dealloc {
@ -133,6 +135,22 @@
} }
} }
CGSize thumbnailSize = CGSizeZero;
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
if (thumbnailSizeValue != nil) {
#if SD_MAC
thumbnailSize = thumbnailSizeValue.sizeValue;
#else
thumbnailSize = thumbnailSizeValue.CGSizeValue;
#endif
}
BOOL preserveAspectRatio = YES;
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
if (preserveAspectRatioValue != nil) {
preserveAspectRatio = preserveAspectRatioValue.boolValue;
}
// for animated webp image // for animated webp image
WebPIterator iter; WebPIterator iter;
// libwebp's index start with 1 // libwebp's index start with 1
@ -145,7 +163,7 @@
if (!hasAnimation || decodeFirstFrame) { if (!hasAnimation || decodeFirstFrame) {
// first frame for animated webp image // first frame for animated webp image
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpace]; CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpace preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize];
CGColorSpaceRelease(colorSpace); CGColorSpaceRelease(colorSpace);
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
UIImage *firstFrameImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; UIImage *firstFrameImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp];
@ -176,7 +194,7 @@
do { do {
@autoreleasepool { @autoreleasepool {
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:canvas iterator:iter colorSpace:colorSpace]; CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:canvas iterator:iter colorSpace:colorSpace preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize];
if (!imageRef) { if (!imageRef) {
continue; continue;
} }
@ -221,6 +239,22 @@
} }
} }
_scale = scale; _scale = scale;
CGSize thumbnailSize = CGSizeZero;
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
if (thumbnailSizeValue != nil) {
#if SD_MAC
thumbnailSize = thumbnailSizeValue.sizeValue;
#else
thumbnailSize = thumbnailSizeValue.CGSizeValue;
#endif
}
_thumbnailSize = thumbnailSize;
BOOL preserveAspectRatio = YES;
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
if (preserveAspectRatioValue != nil) {
preserveAspectRatio = preserveAspectRatioValue.boolValue;
}
_preserveAspectRatio = preserveAspectRatio;
} }
return self; return self;
} }
@ -308,7 +342,7 @@
return image; return image;
} }
- (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef { - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize {
size_t canvasHeight = CGBitmapContextGetHeight(canvas); size_t canvasHeight = CGBitmapContextGetHeight(canvas);
CGFloat tmpX = iter.x_offset; CGFloat tmpX = iter.x_offset;
CGFloat tmpY = canvasHeight - iter.height - iter.y_offset; CGFloat tmpY = canvasHeight - iter.height - iter.y_offset;
@ -317,7 +351,7 @@
if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
CGContextClearRect(canvas, imageRect); CGContextClearRect(canvas, imageRect);
} else { } else {
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef]; CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize];
if (!imageRef) { if (!imageRef) {
return; return;
} }
@ -331,8 +365,8 @@
} }
} }
- (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef CF_RETURNS_RETAINED { - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize CF_RETURNS_RETAINED {
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef]; CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize];
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }
@ -359,7 +393,7 @@
return newImageRef; return newImageRef;
} }
- (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef CF_RETURNS_RETAINED { - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize CF_RETURNS_RETAINED {
WebPDecoderConfig config; WebPDecoderConfig config;
if (!WebPInitDecoderConfig(&config)) { if (!WebPInitDecoderConfig(&config)) {
return nil; return nil;
@ -377,13 +411,34 @@
config.options.use_threads = 1; config.options.use_threads = 1;
config.output.colorspace = MODE_bgrA; config.output.colorspace = MODE_bgrA;
int width = config.input.width;
int height = config.input.height;
if (width == 0 || height == 0 || thumbnailSize.width == 0 || thumbnailSize.height == 0 || (width <= thumbnailSize.width && height <= thumbnailSize.height)) {
// Full Pixel
} else {
// Thumbnail
config.options.use_scaling = 1;
if (preserveAspectRatio) {
CGFloat pixelRatio = (CGFloat)width / (CGFloat)height;
CGFloat thumbnailRatio = thumbnailSize.width / thumbnailSize.height;
if (pixelRatio > thumbnailRatio) {
config.options.scaled_width = thumbnailSize.width;
config.options.scaled_height = thumbnailSize.width / pixelRatio;
} else {
config.options.scaled_height = thumbnailSize.height;
config.options.scaled_width = thumbnailSize.height * pixelRatio;
}
} else {
config.options.scaled_width = thumbnailSize.width;
config.options.scaled_height = thumbnailSize.height;
}
}
// Decode the WebP image data into a RGBA value array // Decode the WebP image data into a RGBA value array
if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) { if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) {
return nil; return nil;
} }
int width = config.input.width;
int height = config.input.height;
if (config.options.use_scaling) { if (config.options.use_scaling) {
width = config.options.scaled_width; width = config.options.scaled_width;
height = config.options.scaled_height; height = config.options.scaled_height;
@ -681,6 +736,22 @@ static void FreeImageData(void *info, const void *data, size_t size) {
scale = 1; scale = 1;
} }
} }
CGSize thumbnailSize = CGSizeZero;
NSValue *thumbnailSizeValue = options[SDImageCoderDecodeThumbnailPixelSize];
if (thumbnailSizeValue != nil) {
#if SD_MAC
thumbnailSize = thumbnailSizeValue.sizeValue;
#else
thumbnailSize = thumbnailSizeValue.CGSizeValue;
#endif
}
_thumbnailSize = thumbnailSize;
BOOL preserveAspectRatio = YES;
NSNumber *preserveAspectRatioValue = options[SDImageCoderDecodePreserveAspectRatio];
if (preserveAspectRatioValue != nil) {
preserveAspectRatio = preserveAspectRatioValue.boolValue;
}
_preserveAspectRatio = preserveAspectRatio;
_scale = scale; _scale = scale;
_demux = demuxer; _demux = demuxer;
_imageData = data; _imageData = data;
@ -812,7 +883,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
WebPDemuxReleaseIterator(&iter); WebPDemuxReleaseIterator(&iter);
return nil; return nil;
} }
CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:_colorSpace]; CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:_colorSpace preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize];
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }
@ -874,7 +945,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
if (endIndex > startIndex) { if (endIndex > startIndex) {
do { do {
@autoreleasepool { @autoreleasepool {
[self sd_blendWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace]; [self sd_blendWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize];
} }
} while ((size_t)iter.frame_num < endIndex && WebPDemuxNextFrame(&iter)); } while ((size_t)iter.frame_num < endIndex && WebPDemuxNextFrame(&iter));
} }
@ -887,7 +958,7 @@ static void FreeImageData(void *info, const void *data, size_t size) {
_currentBlendIndex = index; _currentBlendIndex = index;
// Now the canvas is ready, which respects of dispose method behavior. Just do normal decoding and produce image. // Now the canvas is ready, which respects of dispose method behavior. Just do normal decoding and produce image.
CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace]; CGImageRef imageRef = [self sd_drawnWebpImageWithCanvas:_canvas iterator:iter colorSpace:_colorSpace preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize];
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }