Upgrade the dependency to SDWebImage 5.7, supports the maxFileSize encoding options, as well as enabling the thread-encoding for speedup

This commit is contained in:
DreamPiggy 2020-04-07 16:59:59 +08:00
parent eb6e84f855
commit a62950d23f
5 changed files with 54 additions and 26 deletions

View File

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

View File

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

View File

@ -17,7 +17,7 @@ let package = Package(
dependencies: [ dependencies: [
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"), // .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.5.0"), .package(url: "https://github.com/SDWebImage/SDWebImage.git", from: "5.7.0"),
.package(url: "https://github.com/SDWebImage/libwebp-Xcode.git", from: "1.1.0") .package(url: "https://github.com/SDWebImage/libwebp-Xcode.git", from: "1.1.0")
], ],
targets: [ targets: [

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.5' s.dependency 'SDWebImage/Core', '~> 5.7'
s.dependency 'libwebp', '~> 1.0' s.dependency 'libwebp', '~> 1.0'
end end

View File

@ -609,12 +609,16 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio
if (options[SDImageCoderEncodeCompressionQuality]) { if (options[SDImageCoderEncodeCompressionQuality]) {
compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue]; compressionQuality = [options[SDImageCoderEncodeCompressionQuality] doubleValue];
} }
NSUInteger maxFileSize = 0;
if (options[SDImageCoderEncodeMaxFileSize]) {
maxFileSize = [options[SDImageCoderEncodeMaxFileSize] unsignedIntegerValue];
}
NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image]; NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image];
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue]; BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
if (encodeFirstFrame || frames.count == 0) { if (encodeFirstFrame || frames.count == 0) {
// for static single webp image // for static single webp image
data = [self sd_encodedWebpDataWithImage:image.CGImage quality:compressionQuality]; data = [self sd_encodedWebpDataWithImage:image.CGImage quality:compressionQuality fileSize:maxFileSize];
} else { } else {
// for animated webp image // for animated webp image
WebPMux *mux = WebPMuxNew(); WebPMux *mux = WebPMuxNew();
@ -623,7 +627,7 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio
} }
for (size_t i = 0; i < frames.count; i++) { for (size_t i = 0; i < frames.count; i++) {
SDImageFrame *currentFrame = frames[i]; SDImageFrame *currentFrame = frames[i];
NSData *webpData = [self sd_encodedWebpDataWithImage:currentFrame.image.CGImage quality:compressionQuality]; NSData *webpData = [self sd_encodedWebpDataWithImage:currentFrame.image.CGImage quality:compressionQuality fileSize:maxFileSize];
int duration = currentFrame.duration * 1000; int duration = currentFrame.duration * 1000;
WebPMuxFrameInfo frame = { .bitstream.bytes = webpData.bytes, WebPMuxFrameInfo frame = { .bitstream.bytes = webpData.bytes,
.bitstream.size = webpData.length, .bitstream.size = webpData.length,
@ -660,7 +664,7 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio
return data; return data;
} }
- (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef quality:(double)quality { - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef quality:(double)quality fileSize:(NSUInteger)fileSize {
NSData *webpData; NSData *webpData;
if (!imageRef) { if (!imageRef) {
return nil; return nil;
@ -704,7 +708,7 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio
return nil; return nil;
} }
uint8_t *rgba = NULL; uint8_t *rgba = NULL; // RGBA Buffer managed by CFData, don't call `free` on it, instead call `CFRelease` on `dataRef`
// We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage // We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage
if (byteOrderNormal && ((alphaInfo == kCGImageAlphaNone) || (alphaInfo == kCGImageAlphaLast))) { if (byteOrderNormal && ((alphaInfo == kCGImageAlphaNone) || (alphaInfo == kCGImageAlphaLast))) {
// If the input CGImage is already RGB888/RGBA8888 // If the input CGImage is already RGB888/RGBA8888
@ -758,34 +762,58 @@ static CGSize SDCalculateThumbnailSize(CGSize fullSize, BOOL preserveAspectRatio
rgba = dest.data; // Converted buffer rgba = dest.data; // Converted buffer
bytesPerRow = dest.rowBytes; // Converted bytePerRow bytesPerRow = dest.rowBytes; // Converted bytePerRow
CFRelease(dataRef); CFRelease(dataRef); // Use CFData to manage bytes for free, the same code path for error handling
dataRef = NULL; dataRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, rgba, bytesPerRow * height, kCFAllocatorDefault);
} }
uint8_t *data = NULL; // Output WebP data
float qualityFactor = quality * 100; // WebP quality is 0-100 float qualityFactor = quality * 100; // WebP quality is 0-100
// Encode RGB888/RGBA8888 buffer to WebP data // Encode RGB888/RGBA8888 buffer to WebP data
size_t size; // Using the libwebp advanced API: https://developers.google.com/speed/webp/docs/api#advanced_encoding_api
if (hasAlpha) { WebPConfig config;
size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data); WebPPicture picture;
} else { WebPMemoryWriter writer;
size = WebPEncodeRGB(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data);
if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, qualityFactor) ||
!WebPPictureInit(&picture)) {
// shouldn't happen, except if system installation is broken
CFRelease(dataRef);
return nil;
} }
if (dataRef) {
CFRelease(dataRef); // free non-converted rgba buffer config.target_size = (int)fileSize; // Max filesize for output, 0 means use quality instead
dataRef = NULL; config.thread_level = 1; // Thread encoding for fast
config.lossless = 0; // Disable lossless encoding (If we need, can add new Encoding Options in future version)
picture.use_argb = config.lossless; // Lossy encoding use YUV for internel bitstream
picture.width = (int)width;
picture.height = (int)height;
picture.writer = WebPMemoryWrite; // Output in memory data buffer
picture.custom_ptr = &writer;
WebPMemoryWriterInit(&writer);
int result;
if (hasAlpha) {
result = WebPPictureImportRGBA(&picture, rgba, (int)bytesPerRow);
} else { } else {
free(rgba); // free converted rgba buffer result = WebPPictureImportRGB(&picture, rgba, (int)bytesPerRow);
rgba = NULL; }
if (!result) {
WebPMemoryWriterClear(&writer);
CFRelease(dataRef);
return nil;
} }
if (size) { result = WebPEncode(&config, &picture);
CFRelease(dataRef); // Free bitmap buffer
WebPPictureFree(&picture);
if (result) {
// success // success
webpData = [NSData dataWithBytes:data length:size]; webpData = [NSData dataWithBytes:writer.mem length:writer.size];
} } else {
if (data) { // failed
WebPFree(data); webpData = nil;
} }
WebPMemoryWriterClear(&writer);
return webpData; return webpData;
} }