in iOS/WAStickersThirdParty/YYImageCoder.m [1314:1436]
CGImageRef YYCGImageCreateWithWebPData(CFDataRef webpData,
BOOL decodeForDisplay,
BOOL useThreads,
BOOL bypassFiltering,
BOOL noFancyUpsampling) {
/*
Call WebPDecode() on a multi-frame webp data will get an error (VP8_STATUS_UNSUPPORTED_FEATURE).
Use WebPDemuxer to unpack it first.
*/
WebPData data = {0};
WebPDemuxer *demuxer = NULL;
int frameCount = 0, canvasWidth = 0, canvasHeight = 0;
WebPIterator iter = {0};
BOOL iterInited = NO;
const uint8_t *payload = NULL;
size_t payloadSize = 0;
WebPDecoderConfig config = {0};
BOOL hasAlpha = NO;
size_t bitsPerComponent = 0, bitsPerPixel = 0, bytesPerRow = 0, destLength = 0;
CGBitmapInfo bitmapInfo = 0;
WEBP_CSP_MODE colorspace = 0;
void *destBytes = NULL;
CGDataProviderRef provider = NULL;
CGImageRef imageRef = NULL;
if (!webpData || CFDataGetLength(webpData) == 0) return NULL;
data.bytes = CFDataGetBytePtr(webpData);
data.size = CFDataGetLength(webpData);
demuxer = WebPDemux(&data);
if (!demuxer) goto fail;
frameCount = WebPDemuxGetI(demuxer, WEBP_FF_FRAME_COUNT);
if (frameCount == 0) {
goto fail;
} else if (frameCount == 1) { // single-frame
payload = data.bytes;
payloadSize = data.size;
if (!WebPInitDecoderConfig(&config)) goto fail;
if (WebPGetFeatures(payload , payloadSize, &config.input) != VP8_STATUS_OK) goto fail;
canvasWidth = config.input.width;
canvasHeight = config.input.height;
} else { // multi-frame
canvasWidth = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_WIDTH);
canvasHeight = WebPDemuxGetI(demuxer, WEBP_FF_CANVAS_HEIGHT);
if (canvasWidth < 1 || canvasHeight < 1) goto fail;
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) goto fail;
iterInited = YES;
if (iter.width > canvasWidth || iter.height > canvasHeight) goto fail;
payload = iter.fragment.bytes;
payloadSize = iter.fragment.size;
if (!WebPInitDecoderConfig(&config)) goto fail;
if (WebPGetFeatures(payload , payloadSize, &config.input) != VP8_STATUS_OK) goto fail;
}
if (payload == NULL || payloadSize == 0) goto fail;
hasAlpha = config.input.has_alpha;
bitsPerComponent = 8;
bitsPerPixel = 32;
bytesPerRow = YYImageByteAlign(bitsPerPixel / 8 * canvasWidth, 32);
destLength = bytesPerRow * canvasHeight;
if (decodeForDisplay) {
bitmapInfo = kCGBitmapByteOrder32Host;
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
colorspace = MODE_bgrA; // small endian
} else {
bitmapInfo = kCGBitmapByteOrderDefault;
bitmapInfo |= hasAlpha ? kCGImageAlphaLast : kCGImageAlphaNoneSkipLast;
colorspace = MODE_RGBA;
}
destBytes = calloc(1, destLength);
if (!destBytes) goto fail;
config.options.use_threads = useThreads; //speed up 23%
config.options.bypass_filtering = bypassFiltering; //speed up 11%, cause some banding
config.options.no_fancy_upsampling = noFancyUpsampling; //speed down 16%, lose some details
config.output.colorspace = colorspace;
config.output.is_external_memory = 1;
config.output.u.RGBA.rgba = destBytes;
config.output.u.RGBA.stride = (int)bytesPerRow;
config.output.u.RGBA.size = destLength;
VP8StatusCode result = WebPDecode(payload, payloadSize, &config);
if ((result != VP8_STATUS_OK) && (result != VP8_STATUS_NOT_ENOUGH_DATA)) goto fail;
if (iter.x_offset != 0 || iter.y_offset != 0) {
void *tmp = calloc(1, destLength);
if (tmp) {
vImage_Buffer src = {destBytes, canvasHeight, canvasWidth, bytesPerRow};
vImage_Buffer dest = {tmp, canvasHeight, canvasWidth, bytesPerRow};
vImage_CGAffineTransform transform = {1, 0, 0, 1, iter.x_offset, -iter.y_offset};
uint8_t backColor[4] = {0};
vImageAffineWarpCG_ARGB8888(&src, &dest, NULL, &transform, backColor, kvImageBackgroundColorFill);
memcpy(destBytes, tmp, destLength);
free(tmp);
}
}
provider = CGDataProviderCreateWithData(destBytes, destBytes, destLength, YYCGDataProviderReleaseDataCallback);
if (!provider) goto fail;
destBytes = NULL; // hold by provider
imageRef = CGImageCreate(canvasWidth, canvasHeight, bitsPerComponent, bitsPerPixel, bytesPerRow, YYCGColorSpaceGetDeviceRGB(), bitmapInfo, provider, NULL, false, kCGRenderingIntentDefault);
CFRelease(provider);
if (iterInited) WebPDemuxReleaseIterator(&iter);
WebPDemuxDelete(demuxer);
return imageRef;
fail:
if (destBytes) free(destBytes);
if (provider) CFRelease(provider);
if (iterInited) WebPDemuxReleaseIterator(&iter);
if (demuxer) WebPDemuxDelete(demuxer);
return NULL;
}