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
Showing
1 changed file
with
95 additions
and
6 deletions
@@ -23,6 +23,7 @@ | @@ -23,6 +23,7 @@ | ||
23 | #import "webp/demux.h" | 23 | #import "webp/demux.h" |
24 | #import "webp/mux.h" | 24 | #import "webp/mux.h" |
25 | #endif | 25 | #endif |
26 | +#import <Accelerate/Accelerate.h> | ||
26 | 27 | ||
27 | @implementation SDWebImageWebPCoder { | 28 | @implementation SDWebImageWebPCoder { |
28 | WebPIDecoder *_idec; | 29 | WebPIDecoder *_idec; |
@@ -393,18 +394,106 @@ | @@ -393,18 +394,106 @@ | ||
393 | } | 394 | } |
394 | 395 | ||
395 | size_t bytesPerRow = CGImageGetBytesPerRow(imageRef); | 396 | size_t bytesPerRow = CGImageGetBytesPerRow(imageRef); |
397 | + CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef); | ||
398 | + CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask; | ||
399 | + CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask; | ||
400 | + BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone || | ||
401 | + alphaInfo == kCGImageAlphaNoneSkipFirst || | ||
402 | + alphaInfo == kCGImageAlphaNoneSkipLast); | ||
403 | + BOOL byteOrderNormal = NO; | ||
404 | + switch (byteOrderInfo) { | ||
405 | + case kCGBitmapByteOrderDefault: { | ||
406 | + byteOrderNormal = YES; | ||
407 | + } break; | ||
408 | + case kCGBitmapByteOrder32Little: { | ||
409 | + } break; | ||
410 | + case kCGBitmapByteOrder32Big: { | ||
411 | + byteOrderNormal = YES; | ||
412 | + } break; | ||
413 | + default: break; | ||
414 | + } | ||
415 | + // If we can not get bitmap buffer, early return | ||
396 | CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef); | 416 | CGDataProviderRef dataProvider = CGImageGetDataProvider(imageRef); |
397 | if (!dataProvider) { | 417 | if (!dataProvider) { |
398 | return nil; | 418 | return nil; |
399 | } | 419 | } |
400 | CFDataRef dataRef = CGDataProviderCopyData(dataProvider); | 420 | CFDataRef dataRef = CGDataProviderCopyData(dataProvider); |
401 | - uint8_t *rgba = (uint8_t *)CFDataGetBytePtr(dataRef); | 421 | + if (!dataRef) { |
422 | + return nil; | ||
423 | + } | ||
402 | 424 | ||
403 | - uint8_t *data = NULL; | ||
404 | - float quality = 100.0; | ||
405 | - size_t size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, quality, &data); | ||
406 | - CFRelease(dataRef); | ||
407 | - rgba = NULL; | 425 | + uint8_t *rgba = NULL; |
426 | + // We could not assume that input CGImage's color mode is always RGB888/RGBA8888. Convert all other cases to target color mode using vImage | ||
427 | + if (byteOrderNormal && ((alphaInfo == kCGImageAlphaNone) || (alphaInfo == kCGImageAlphaLast))) { | ||
428 | + // If the input CGImage is already RGB888/RGBA8888 | ||
429 | + rgba = (uint8_t *)CFDataGetBytePtr(dataRef); | ||
430 | + } else { | ||
431 | + // Convert all other cases to target color mode using vImage | ||
432 | + vImageConverterRef convertor = NULL; | ||
433 | + vImage_Error error = kvImageNoError; | ||
434 | + | ||
435 | + vImage_CGImageFormat srcFormat = { | ||
436 | + .bitsPerComponent = (uint32_t)CGImageGetBitsPerComponent(imageRef), | ||
437 | + .bitsPerPixel = (uint32_t)CGImageGetBitsPerPixel(imageRef), | ||
438 | + .colorSpace = CGImageGetColorSpace(imageRef), | ||
439 | + .bitmapInfo = bitmapInfo | ||
440 | + }; | ||
441 | + vImage_CGImageFormat destFormat = { | ||
442 | + .bitsPerComponent = 8, | ||
443 | + .bitsPerPixel = hasAlpha ? 32 : 24, | ||
444 | + .colorSpace = SDCGColorSpaceGetDeviceRGB(), | ||
445 | + .bitmapInfo = hasAlpha ? kCGImageAlphaLast | kCGBitmapByteOrderDefault : kCGImageAlphaNone | kCGBitmapByteOrderDefault // RGB888/RGBA8888 (Non-premultiplied to works for libwebp) | ||
446 | + }; | ||
447 | + | ||
448 | + convertor = vImageConverter_CreateWithCGImageFormat(&srcFormat, &destFormat, NULL, kvImageNoFlags, &error); | ||
449 | + if (error != kvImageNoError) { | ||
450 | + CFRelease(dataRef); | ||
451 | + return nil; | ||
452 | + } | ||
453 | + | ||
454 | + vImage_Buffer src = { | ||
455 | + .data = (uint8_t *)CFDataGetBytePtr(dataRef), | ||
456 | + .width = width, | ||
457 | + .height = height, | ||
458 | + .rowBytes = bytesPerRow | ||
459 | + }; | ||
460 | + vImage_Buffer dest; | ||
461 | + | ||
462 | + error = vImageBuffer_Init(&dest, height, width, destFormat.bitsPerPixel, kvImageNoFlags); | ||
463 | + if (error != kvImageNoError) { | ||
464 | + CFRelease(dataRef); | ||
465 | + return nil; | ||
466 | + } | ||
467 | + | ||
468 | + // Convert input color mode to RGB888/RGBA8888 | ||
469 | + error = vImageConvert_AnyToAny(convertor, &src, &dest, NULL, kvImageNoFlags); | ||
470 | + if (error != kvImageNoError) { | ||
471 | + CFRelease(dataRef); | ||
472 | + return nil; | ||
473 | + } | ||
474 | + | ||
475 | + rgba = dest.data; // Converted buffer | ||
476 | + bytesPerRow = dest.rowBytes; // Converted bytePerRow | ||
477 | + CFRelease(dataRef); | ||
478 | + dataRef = NULL; | ||
479 | + } | ||
480 | + | ||
481 | + uint8_t *data = NULL; // Output WebP data | ||
482 | + float qualityFactor = 100; // WebP quality is 0-100 | ||
483 | + // Encode RGB888/RGBA8888 buffer to WebP data | ||
484 | + size_t size; | ||
485 | + if (hasAlpha) { | ||
486 | + size = WebPEncodeRGBA(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data); | ||
487 | + } else { | ||
488 | + size = WebPEncodeRGB(rgba, (int)width, (int)height, (int)bytesPerRow, qualityFactor, &data); | ||
489 | + } | ||
490 | + if (dataRef) { | ||
491 | + CFRelease(dataRef); // free non-converted rgba buffer | ||
492 | + dataRef = NULL; | ||
493 | + } else { | ||
494 | + free(rgba); // free converted rgba buffer | ||
495 | + rgba = NULL; | ||
496 | + } | ||
408 | 497 | ||
409 | if (size) { | 498 | if (size) { |
410 | // success | 499 | // success |
-
Please register or login to post a comment