Authored by Bogdan Poplauschi
Committed by GitHub

Merge pull request #2318 from dreampiggy/bugfix_WebP_encode_color_mode

Fix WebP Encoding only works for RGBA8888 CGImage but not other color mode
... ... @@ -23,6 +23,7 @@
#import "webp/demux.h"
#import "webp/mux.h"
#endif
#import <Accelerate/Accelerate.h>
@implementation SDWebImageWebPCoder {
WebPIDecoder *_idec;
... ... @@ -393,18 +394,106 @@
}
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
BOOL byteOrderNormal = NO;
switch (byteOrderInfo) {
case kCGBitmapByteOrderDefault: {
byteOrderNormal = YES;
} break;
case kCGBitmapByteOrder32Little: {
} break;
case kCGBitmapByteOrder32Big: {
byteOrderNormal = YES;
} break;
default: break;
}
// If we can not get bitmap buffer, early return
CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef);
if (!dataProvider) {
return nil;
}
CFDataRef dataRef = CGDataProviderCopyData(dataProvider);
uint8_t *rgba = (uint8_t *)CFDataGetBytePtr(dataRef);
if (!dataRef) {
return nil;
}
uint8_t *data = NULL;
float quality = 100.0;
size_t size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, quality, &data);
CFRelease(dataRef);
rgba = NULL;
uint8_t *rgba = NULL;
// 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 the input CGImage is already RGB888/RGBA8888
rgba = (uint8_t *)CFDataGetBytePtr(dataRef);
} else {
// Convert all other cases to target color mode using vImage
vImageConverterRef convertor = NULL;
vImage_Error error = kvImageNoError;
vImage_CGImageFormat srcFormat = {
.bitsPerComponent = (uint32_t)CGImageGetBitsPerComponent(imageRef),
.bitsPerPixel = (uint32_t)CGImageGetBitsPerPixel(imageRef),
.colorSpace = CGImageGetColorSpace(imageRef),
.bitmapInfo = bitmapInfo
};
vImage_CGImageFormat destFormat = {
.bitsPerComponent = 8,
.bitsPerPixel = hasAlpha ? 32 : 24,
.colorSpace = SDCGColorSpaceGetDeviceRGB(),
.bitmapInfo = hasAlpha ? kCGImageAlphaLast | kCGBitmapByteOrderDefault : kCGImageAlphaNone | kCGBitmapByteOrderDefault // RGB888/RGBA8888 (Non-premultiplied to works for libwebp)
};
convertor = vImageConverter_CreateWithCGImageFormat(&srcFormat, &destFormat, NULL, kvImageNoFlags, &error);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
vImage_Buffer src = {
.data = (uint8_t *)CFDataGetBytePtr(dataRef),
.width = width,
.height = height,
.rowBytes = bytesPerRow
};
vImage_Buffer dest;
error = vImageBuffer_Init(&dest, height, width, destFormat.bitsPerPixel, kvImageNoFlags);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
// Convert input color mode to RGB888/RGBA8888
error = vImageConvert_AnyToAny(convertor, &src, &dest, NULL, kvImageNoFlags);
if (error != kvImageNoError) {
CFRelease(dataRef);
return nil;
}
rgba = dest.data; // Converted buffer
bytesPerRow = dest.rowBytes; // Converted bytePerRow
CFRelease(dataRef);
dataRef = NULL;
}
uint8_t *data = NULL; // Output WebP data
float qualityFactor = 100; // WebP quality is 0-100
// Encode RGB888/RGBA8888 buffer to WebP data
size_t size;
if (hasAlpha) {
size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data);
} else {
size = WebPEncodeRGB(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data);
}
if (dataRef) {
CFRelease(dataRef); // free non-converted rgba buffer
dataRef = NULL;
} else {
free(rgba); // free converted rgba buffer
rgba = NULL;
}
if (size) {
// success
... ...