Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Source/Classes/Categories/PINImage+DecodedImage.m
Original file line number Diff line number Diff line change
Expand Up @@ -122,12 +122,6 @@ + (PINImage *)pin_decodedImageWithData:(NSData *)data skipDecodeIfPossible:(BOOL
return nil;
}

#if PIN_WEBP
if ([data pin_isWebP]) {
return [PINImage pin_imageWithWebPData:data];
}
#endif

PINImage *decodedImage = nil;

CGImageSourceRef imageSourceRef = CGImageSourceCreateWithData((CFDataRef)data, NULL);
Expand Down Expand Up @@ -156,6 +150,12 @@ + (PINImage *)pin_decodedImageWithData:(NSData *)data skipDecodeIfPossible:(BOOL
CFRelease(imageSourceRef);
}

#if PIN_WEBP
if (!decodedImage && [data pin_isWebP]) {
return [PINImage pin_imageWithWebPData:data];
}
#endif

return decodedImage;
}

Expand Down
135 changes: 80 additions & 55 deletions Source/Classes/Categories/PINImage+WebP.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,70 +12,95 @@

#import "webp/decode.h"

static void releaseData(void *info, const void *data, size_t size)
{
WebPFree((void *)data);
@implementation PINImage (PINWebP)

static enum WEBP_CSP_MODE webp_cs_mode_from_cg_bitmap_info(CGBitmapInfo info, BOOL *fail) {
CGImageByteOrderInfo byteOrder = info & kCGBitmapByteOrderMask;
BOOL keepByteOrder;
switch (byteOrder) {
case kCGImageByteOrder32Big:
keepByteOrder = YES;
break;
case kCGImageByteOrder32Little:
keepByteOrder = NO;
break;
case kCGImageByteOrder16Big:
case kCGImageByteOrder16Little:
case kCGImageByteOrderDefault:
case kCGImageByteOrderMask:
*fail = YES;
return MODE_RGBA;
}

CGImageAlphaInfo ai = info & kCGBitmapAlphaInfoMask;
switch (ai) {
case kCGImageAlphaLast:
case kCGImageAlphaNoneSkipLast:
return keepByteOrder ? MODE_RGBA : MODE_ARGB;
case kCGImageAlphaNone:
return keepByteOrder ? MODE_RGB : MODE_BGR;
case kCGImageAlphaFirst:
case kCGImageAlphaNoneSkipFirst:
return keepByteOrder ? MODE_ARGB : MODE_BGRA;
case kCGImageAlphaPremultipliedLast:
return keepByteOrder ? MODE_rgbA : MODE_Argb;
case kCGImageAlphaPremultipliedFirst:
return keepByteOrder ? MODE_Argb : MODE_rgbA;
case kCGImageAlphaOnly:
*fail = YES;
return MODE_RGB;
}
}

@implementation PINImage (PINWebP)
#if PIN_TARGET_IOS
#define PIN_WEBP_DECODE_CLEANUP() UIGraphicsEndImageContext()
#elif PIN_TARGET_MAC
#define PIN_WEBP_DECODE_CLEANUP() [image unlockFocus]
#endif

// We let the system decide all the bitmap options for us, so Core Animation won't have
// to copy our images over to the render server. Use "Color Copied Images" in the iOS simulator to
// detect this case.
+ (PINImage *)pin_imageWithWebPData:(NSData *)webPData
{
WebPBitstreamFeatures features;
if (WebPGetFeatures([webPData bytes], [webPData length], &features) == VP8_STATUS_OK) {
// Decode the WebP image data into a RGBA value array
int height, width;
uint8_t *data = NULL;
int pixelLength = 0;

if (features.has_alpha) {
data = WebPDecodeRGBA([webPData bytes], [webPData length], &width, &height);
pixelLength = 4;
} else {
data = WebPDecodeRGB([webPData bytes], [webPData length], &width, &height);
pixelLength = 3;
}

if (data) {
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, data, width * height * pixelLength, releaseData);

CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;

if (features.has_alpha) {
bitmapInfo |= kCGImageAlphaLast;
} else {
bitmapInfo |= kCGImageAlphaNone;
}

CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
CGImageRef imageRef = CGImageCreate(width,
height,
8,
8 * pixelLength,
pixelLength * width,
colorSpaceRef,
bitmapInfo,
provider,
NULL,
NO,
renderingIntent);

PINImage *image = nil;
WebPDecoderConfig cfg;
WebPInitDecoderConfig(&cfg);
if (WebPGetFeatures(webPData.bytes, webPData.length, &cfg.input) != VP8_STATUS_OK) {
return nil;
}
CGSize size = CGSizeMake(cfg.input.width, cfg.input.height);
CGContextRef ctx = NULL;
PINImage *image;
#if PIN_TARGET_IOS
image = [UIImage imageWithCGImage:imageRef];
UIGraphicsBeginImageContextWithOptions(size, !cfg.input.has_alpha, 1.0);
ctx = UIGraphicsGetCurrentContext();
#elif PIN_TARGET_MAC
image = [[self alloc] initWithCGImage:imageRef size:CGSizeZero];
image = [[NSImage alloc] initWithSize:NSSizeFromCGSize(size)];
[image lockFocus];
ctx = NSGraphicsContext.currentContext.CGContext;
#endif
CGImageRelease(imageRef);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider);
return image;
}
NSAssert(ctx != NULL, @"Failed to get CG context.");
BOOL getColorspaceFailed = NO;
cfg.output.colorspace = webp_cs_mode_from_cg_bitmap_info(CGBitmapContextGetBitmapInfo(ctx),
&getColorspaceFailed);
if (getColorspaceFailed) {
PIN_WEBP_DECODE_CLEANUP();
return nil;
}
return nil;
cfg.output.width = cfg.input.width;
cfg.output.height = cfg.input.height;
cfg.output.is_external_memory = 1;
cfg.output.u.RGBA.rgba = (uint8_t *)CGBitmapContextGetData(ctx);
cfg.output.u.RGBA.stride = (int)CGBitmapContextGetBytesPerRow(ctx);
cfg.output.u.RGBA.size = cfg.output.u.RGBA.stride * cfg.input.height;
int status = WebPDecode(webPData.bytes, webPData.length, &cfg);
#if PIN_TARGET_IOS
if (status == VP8_STATUS_OK) {
image = UIGraphicsGetImageFromCurrentImageContext();
}
#endif
PIN_WEBP_DECODE_CLEANUP();
return status == VP8_STATUS_OK ? image : nil;
}

@end
Expand Down