Skip to content

Commit 75af1fa

Browse files
authored
Merge pull request #42 from SDWebImage/feature_webp_encoding_max_pixel_size
Feature WebP Encoding max pixel size (Thumbnail Encoding)
2 parents 92e55d4 + 8b935e7 commit 75af1fa

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

SDWebImageWebPCoder/Classes/SDImageWebPCoder.m

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,15 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
609609
if (options[SDImageCoderEncodeCompressionQuality]) {
610610
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue];
611611
}
612+
CGSize maxPixelSize = CGSizeZero;
613+
NSValue *maxPixelSizeValue = options[SDImageCoderEncodeMaxPixelSize];
614+
if (maxPixelSizeValue != nil) {
615+
#if SD_MAC
616+
maxPixelSize = maxPixelSizeValue.sizeValue;
617+
#else
618+
maxPixelSize = maxPixelSizeValue.CGSizeValue;
619+
#endif
620+
}
612621
NSUInteger maxFileSize = 0;
613622
if (options[SDImageCoderEncodeMaxFileSize]) {
614623
maxFileSize = [options[SDImageCoderEncodeMaxFileSize] unsignedIntegerValue];
@@ -618,7 +627,7 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
618627
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
619628
if (encodeFirstFrame || frames.count == 0) {
620629
// for static single webp image
621-
data = [self sd_encodedWebpDataWithImage:image.CGImage quality:compressionQuality fileSize:maxFileSize];
630+
data = [self sd_encodedWebpDataWithImage:image.CGImage quality:compressionQuality maxPixelSize:maxPixelSize maxFileSize:maxFileSize];
622631
} else {
623632
// for animated webp image
624633
WebPMux *mux = WebPMuxNew();
@@ -627,7 +636,7 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
627636
}
628637
for (size_t i = 0; i < frames.count; i++) {
629638
SDImageFrame *currentFrame = frames[i];
630-
NSData *webpData = [self sd_encodedWebpDataWithImage:currentFrame.image.CGImage quality:compressionQuality fileSize:maxFileSize];
639+
NSData *webpData = [self sd_encodedWebpDataWithImage:currentFrame.image.CGImage quality:compressionQuality maxPixelSize:maxPixelSize maxFileSize:maxFileSize];
631640
int duration = currentFrame.duration * 1000;
632641
WebPMuxFrameInfo frame = { .bitstream.bytes = webpData.bytes,
633642
.bitstream.size = webpData.length,
@@ -664,7 +673,7 @@ - (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format o
664673
return data;
665674
}
666675

667-
- (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef quality:(double)quality fileSize:(NSUInteger)fileSize {
676+
- (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef quality:(double)quality maxPixelSize:(CGSize)maxPixelSize maxFileSize:(NSUInteger)maxFileSize {
668677
NSData *webpData;
669678
if (!imageRef) {
670679
return nil;
@@ -780,11 +789,11 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef q
780789
return nil;
781790
}
782791

783-
config.target_size = (int)fileSize; // Max filesize for output, 0 means use quality instead
784-
config.pass = fileSize > 0 ? 6 : 1; // Use 6 passes for file size limited encoding, which is the default value of `cwebp` command line
792+
config.target_size = (int)maxFileSize; // Max filesize for output, 0 means use quality instead
793+
config.pass = maxFileSize > 0 ? 6 : 1; // Use 6 passes for file size limited encoding, which is the default value of `cwebp` command line
785794
config.thread_level = 1; // Thread encoding for fast
786795
config.lossless = 0; // Disable lossless encoding (If we need, can add new Encoding Options in future version)
787-
picture.use_argb = config.lossless; // Lossy encoding use YUV for internel bitstream
796+
picture.use_argb = 0; // Lossy encoding use YUV for internel bitstream
788797
picture.width = (int)width;
789798
picture.height = (int)height;
790799
picture.writer = WebPMemoryWrite; // Output in memory data buffer
@@ -803,9 +812,21 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef q
803812
return nil;
804813
}
805814

815+
// Check if need to scale pixel size
816+
if (maxPixelSize.width > 0 && maxPixelSize.height > 0 && width > maxPixelSize.width && height > maxPixelSize.height) {
817+
CGSize scaledSize = SDCalculateThumbnailSize(CGSizeMake(width, height), YES, maxPixelSize);
818+
result = WebPPictureRescale(&picture, scaledSize.width, scaledSize.height);
819+
if (!result) {
820+
WebPMemoryWriterClear(&writer);
821+
WebPPictureFree(&picture);
822+
CFRelease(dataRef);
823+
return nil;
824+
}
825+
}
826+
806827
result = WebPEncode(&config, &picture);
807-
CFRelease(dataRef); // Free bitmap buffer
808828
WebPPictureFree(&picture);
829+
CFRelease(dataRef); // Free bitmap buffer
809830

810831
if (result) {
811832
// success

Tests/SDWebImageWebPCoderTests.m

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,8 @@ - (void)verifyCoder:(id<SDImageCoder>)coder
262262
thumbnailPixelSize = CGSizeMake(round(thumbnailHeight * ratio), thumbnailHeight);
263263
}
264264
// Image/IO's thumbnail API does not always use round to preserve precision, we check ABS <= 1
265-
expect(ABS(thumbImage.size.width - thumbnailPixelSize.width) <= 1);
266-
expect(ABS(thumbImage.size.height - thumbnailPixelSize.height) <= 1);
265+
expect(ABS(thumbImage.size.width - thumbnailPixelSize.width)).beLessThanOrEqualTo(1);
266+
expect(ABS(thumbImage.size.height - thumbnailPixelSize.height)).beLessThanOrEqualTo(1);
267267

268268

269269
if (supportsEncoding) {
@@ -282,6 +282,25 @@ - (void)verifyCoder:(id<SDImageCoder>)coder
282282
#if SD_UIKIT
283283
expect(outputImage.images.count).to.equal(inputImage.images.count);
284284
#endif
285+
286+
// check max pixel size encoding with scratch
287+
CGFloat maxWidth = 50;
288+
CGFloat maxHeight = 50;
289+
CGFloat maxRatio = maxWidth / maxHeight;
290+
CGSize maxPixelSize;
291+
if (ratio > maxRatio) {
292+
maxPixelSize = CGSizeMake(maxWidth, round(maxWidth / ratio));
293+
} else {
294+
maxPixelSize = CGSizeMake(round(maxHeight * ratio), maxHeight);
295+
}
296+
NSData *outputMaxImageData = [coder encodedDataWithImage:inputImage format:encodingFormat options:@{SDImageCoderEncodeMaxPixelSize : @(CGSizeMake(maxWidth, maxHeight))}];
297+
UIImage *outputMaxImage = [coder decodedImageWithData:outputMaxImageData options:nil];
298+
// Image/IO's thumbnail API does not always use round to preserve precision, we check ABS <= 1
299+
expect(ABS(outputMaxImage.size.width - maxPixelSize.width)).beLessThanOrEqualTo(1);
300+
expect(ABS(outputMaxImage.size.height - maxPixelSize.height)).beLessThanOrEqualTo(1);
301+
#if SD_UIKIT
302+
expect(outputMaxImage.images.count).to.equal(inputImage.images.count);
303+
#endif
285304
}
286305
}
287306

0 commit comments

Comments
 (0)