diff --git a/Example/SDWebImageWebPCoderExample/ViewController.m b/Example/SDWebImageWebPCoderExample/ViewController.m index 0d31b16..3dd8a77 100644 --- a/Example/SDWebImageWebPCoderExample/ViewController.m +++ b/Example/SDWebImageWebPCoderExample/ViewController.m @@ -28,12 +28,12 @@ - (void)viewDidLoad { self.imageView1.contentMode = UIViewContentModeScaleAspectFit; [self.view addSubview:self.imageView1]; - self.imageView2 = [SDAnimatedImageView new]; + self.imageView2 = [UIImageView new]; self.imageView2.contentMode = UIViewContentModeScaleAspectFit; [self.view addSubview:self.imageView2]; 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/SteamEngine.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) { if (image) { @@ -46,7 +46,7 @@ - (void)viewDidLoad { } }); }]; - [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:0 context:@{SDWebImageContextImageThumbnailPixelSize : @(CGSizeMake(200, 100))} progress:nil 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 104ee32..34d00ef 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -192,10 +192,13 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO return nil; } CGColorSpaceRef colorSpace = [self sd_createColorSpaceWithDemuxer:demuxer]; + int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); + int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); if (!hasAnimation || decodeFirstFrame) { // first frame for animated webp image - CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpace preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize]; + CGSize scaledSize = SDCalculateThumbnailSize(CGSizeMake(canvasWidth, canvasHeight), preserveAspectRatio, thumbnailSize); + CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpace scaledSize:scaledSize]; CGColorSpaceRelease(colorSpace); #if SD_UIKIT || SD_WATCH UIImage *firstFrameImage = [[UIImage alloc] initWithCGImage:imageRef scale:scale orientation:UIImageOrientationUp]; @@ -209,8 +212,6 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO return firstFrameImage; } - int canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH); - int canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT); BOOL hasAlpha = flags & ALPHA_FLAG; CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host; bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst; @@ -223,6 +224,10 @@ - (UIImage *)decodedImageWithData:(NSData *)data options:(nullable SDImageCoderO CGColorSpaceRelease(colorSpace); return nil; } + CGContextSetAllowsAntialiasing(canvas, YES); + CGContextSetShouldAntialias(canvas, YES); + CGContextSetInterpolationQuality(canvas, kCGInterpolationHigh); + int loopCount = WebPDemuxGetI(demuxer, WEBP_FF_LOOP_COUNT); NSMutableArray *frames = [NSMutableArray array]; @@ -383,16 +388,16 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator) CGFloat xScale = canvasWidth / canvasFullSize.width; CGFloat yScale = canvasHeight / canvasFullSize.height; - CGFloat tmpX = iter.x_offset * xScale; - CGFloat tmpY = (canvasFullSize.height - iter.height - iter.y_offset) * yScale; - CGFloat tmpWidth = iter.width * xScale; - CGFloat tmpHeight = iter.height * yScale; + CGFloat tmpX = ceil(iter.x_offset * xScale); + CGFloat tmpY = ceil((canvasFullSize.height - iter.height - iter.y_offset) * yScale); + CGFloat tmpWidth = ceil(iter.width * xScale); + CGFloat tmpHeight = ceil(iter.height * yScale); CGRect imageRect = CGRectMake(tmpX, tmpY, tmpWidth, tmpHeight); if (iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) { CGContextClearRect(canvas, imageRect); } else { - CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize]; + CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef scaledSize:imageRect.size]; if (!imageRef) { return; } @@ -407,23 +412,23 @@ - (void)sd_blendWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator) } - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator:(WebPIterator)iter colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize canvasFullSize:(CGSize)canvasFullSize CF_RETURNS_RETAINED { - CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef preserveAspectRatio:preserveAspectRatio thumbnailSize:thumbnailSize]; - if (!imageRef) { - return nil; - } - size_t canvasWidth = CGBitmapContextGetWidth(canvas); size_t canvasHeight = CGBitmapContextGetHeight(canvas); - CGFloat xScale = canvasWidth / canvasFullSize.width; - CGFloat yScale = canvasHeight / canvasFullSize.height; + CGFloat xScale = canvasFullSize.width / canvasWidth; + CGFloat yScale = canvasFullSize.height / canvasHeight; - CGFloat tmpX = iter.x_offset * xScale; - CGFloat tmpY = (canvasFullSize.height - iter.height - iter.y_offset) * yScale; - CGFloat tmpWidth = iter.width * xScale; - CGFloat tmpHeight = iter.height * yScale; + CGFloat tmpX = ceil(iter.x_offset / xScale); + CGFloat tmpY = ceil((canvasFullSize.height - iter.height - iter.y_offset) / yScale); + CGFloat tmpWidth = ceil(iter.width / xScale); + CGFloat tmpHeight = ceil(iter.height / yScale); CGRect imageRect = CGRectMake(tmpX, tmpY, tmpWidth, tmpHeight); - BOOL shouldBlend = iter.blend_method == WEBP_MUX_BLEND; + CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:colorSpaceRef scaledSize:imageRect.size]; + if (!imageRef) { + return nil; + } + + BOOL shouldBlend = iter.blend_method == WEBP_MUX_BLEND; // If not blend, cover the target image rect. (firstly clear then draw) if (!shouldBlend) { CGContextClearRect(canvas, imageRect); @@ -440,7 +445,7 @@ - (nullable CGImageRef)sd_drawnWebpImageWithCanvas:(CGContextRef)canvas iterator return newImageRef; } -- (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef preserveAspectRatio:(BOOL)preserveAspectRatio thumbnailSize:(CGSize)thumbnailSize CF_RETURNS_RETAINED { +- (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace:(nonnull CGColorSpaceRef)colorSpaceRef scaledSize:(CGSize)scaledSize CF_RETURNS_RETAINED { WebPDecoderConfig config; if (!WebPInitDecoderConfig(&config)) { return nil; @@ -458,14 +463,11 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace: config.options.use_threads = 1; config.output.colorspace = MODE_bgrA; - int width = config.input.width; - int height = config.input.height; - CGSize resultSize = SDCalculateThumbnailSize(CGSizeMake(width, height), preserveAspectRatio, thumbnailSize); - if (resultSize.width != width || resultSize.height != height) { - // Use scaling + // Check whether we should use thumbnail scale + if (scaledSize.width != 0 && scaledSize.height != 0) { + config.options.scaled_width = scaledSize.width; + config.options.scaled_height = scaledSize.height; config.options.use_scaling = 1; - config.options.scaled_width = resultSize.width; - config.options.scaled_height = resultSize.height; } // Decode the WebP image data into a RGBA value array @@ -473,19 +475,16 @@ - (nullable CGImageRef)sd_createWebpImageWithData:(WebPData)webpData colorSpace: return nil; } - if (config.options.use_scaling) { - width = config.options.scaled_width; - height = config.options.scaled_height; - } - // Construct a UIImage from the decoded RGBA value array CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); size_t bitsPerComponent = 8; size_t bitsPerPixel = 32; size_t bytesPerRow = config.output.u.RGBA.stride; + int width = config.output.width; + int height = config.output.height; CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; - CGImageRef imageRef = CGImageCreate(resultSize.width, resultSize.height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); + CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); CGDataProviderRelease(provider); @@ -917,7 +916,8 @@ - (UIImage *)safeStaticImageFrame { WebPDemuxReleaseIterator(&iter); return nil; } - CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:_colorSpace preserveAspectRatio:_preserveAspectRatio thumbnailSize:_thumbnailSize]; + CGSize scaledSize = SDCalculateThumbnailSize(CGSizeMake(_canvasWidth, _canvasHeight), _preserveAspectRatio, _thumbnailSize); + CGImageRef imageRef = [self sd_createWebpImageWithData:iter.fragment colorSpace:_colorSpace scaledSize:scaledSize]; if (!imageRef) { return nil; }