Fix WebP Encoding only works for RGBA8888 CGImage but not other color mode.
Detect the current CGImage color mode, covert the all other case to RGB888/RGBA8888 using vImage
This commit is contained in:
parent
12fe445498
commit
18ee2352b0
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue