Swiftgram/TelegramUI/UIImage+WebP.m
Ilya Laktyushin cc17271034 Send non opaque images from pasteboard as stickers
Language suggestion alert
Ability to create new contact from "tel:"-link context menu and chat search
Upload date display for profile pictures
Instant View improvements
2018-11-11 18:03:00 +04:00

151 lines
5.6 KiB
Objective-C

#import "UIImage+WebP.h"
#import "../third-party/libwebp/include/webp/decode.h"
#import "../third-party/libwebp/include/webp/encode.h"
@implementation UIImage (WebP)
+ (UIImage *)convertFromWebP:(NSData *)imgData {
if (imgData == nil) {
return nil;
}
// `WebPGetInfo` weill return image width and height
int width = 0, height = 0;
if(!WebPGetInfo([imgData bytes], [imgData length], &width, &height)) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"Header formatting error." forKey:NSLocalizedDescriptionKey];
return nil;
}
const struct { int width, height; } targetContextSize = { width, height};
size_t targetBytesPerRow = ((4 * (int)targetContextSize.width) + 15) & (~15);
void *targetMemory = malloc((int)(targetBytesPerRow * targetContextSize.height));
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
CGContextRef targetContext = CGBitmapContextCreate(targetMemory, (int)targetContextSize.width, (int)targetContextSize.height, 8, targetBytesPerRow, colorSpace, bitmapInfo);
UIGraphicsPushContext(targetContext);
CGColorSpaceRelease(colorSpace);
if (WebPDecodeBGRAInto(imgData.bytes, imgData.length, targetMemory, targetBytesPerRow * targetContextSize.height, (int)targetBytesPerRow) == NULL)
{
//[BridgingTrace objc_trace:@"WebP" what:@"error decoding webp"];
return nil;
}
for (int y = 0; y < targetContextSize.height; y++)
{
for (int x = 0; x < targetContextSize.width; x++)
{
uint32_t *color = ((uint32_t *)&targetMemory[y * targetBytesPerRow + x * 4]);
uint32_t a = (*color >> 24) & 0xff;
uint32_t r = ((*color >> 16) & 0xff) * a;
uint32_t g = ((*color >> 8) & 0xff) * a;
uint32_t b = (*color & 0xff) * a;
r = (r + 1 + (r >> 8)) >> 8;
g = (g + 1 + (g >> 8)) >> 8;
b = (b + 1 + (b >> 8)) >> 8;
*color = (a << 24) | (r << 16) | (g << 8) | b;
}
for (size_t i = y * targetBytesPerRow + targetContextSize.width * 4; i < (targetBytesPerRow >> 2); i++)
{
*((uint32_t *)&targetMemory[i]) = 0;
}
}
UIGraphicsPopContext();
CGImageRef bitmapImage = CGBitmapContextCreateImage(targetContext);
UIImage *image = [[UIImage alloc] initWithCGImage:bitmapImage scale:1.0f orientation:UIImageOrientationUp];
CGImageRelease(bitmapImage);
CGContextRelease(targetContext);
free(targetMemory);
return image;
}
+ (NSData *)convertToWebP:(UIImage *)image quality:(CGFloat)quality error:(NSError **)error {
WebPPreset preset = WEBP_PRESET_DEFAULT;
CGImageRef webPImageRef = image.CGImage;
size_t webPBytesPerRow = CGImageGetBytesPerRow(webPImageRef);
size_t webPImageWidth = CGImageGetWidth(webPImageRef);
size_t webPImageHeight = CGImageGetHeight(webPImageRef);
CGDataProviderRef webPDataProviderRef = CGImageGetDataProvider(webPImageRef);
CFDataRef webPImageDatRef = CGDataProviderCopyData(webPDataProviderRef);
uint8_t *webPImageData = (uint8_t *)CFDataGetBytePtr(webPImageDatRef);
WebPConfig config;
if (!WebPConfigPreset(&config, preset, quality)) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"Configuration preset failed to initialize." forKey:NSLocalizedDescriptionKey];
if(error != NULL)
*error = [NSError errorWithDomain:[NSString stringWithFormat:@"%@.errorDomain", [[NSBundle mainBundle] bundleIdentifier]] code:-101 userInfo:errorDetail];
CFRelease(webPImageDatRef);
return nil;
}
config.method = 6;
//if (configBlock) {
// configBlock(&config);
//}
if (!WebPValidateConfig(&config)) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"One or more configuration parameters are beyond their valid ranges." forKey:NSLocalizedDescriptionKey];
if(error != NULL)
*error = [NSError errorWithDomain:[NSString stringWithFormat:@"%@.errorDomain", [[NSBundle mainBundle] bundleIdentifier]] code:-101 userInfo:errorDetail];
CFRelease(webPImageDatRef);
return nil;
}
WebPPicture pic;
if (!WebPPictureInit(&pic)) {
NSMutableDictionary *errorDetail = [NSMutableDictionary dictionary];
[errorDetail setValue:@"Failed to initialize structure. Version mismatch." forKey:NSLocalizedDescriptionKey];
if(error != NULL)
*error = [NSError errorWithDomain:[NSString stringWithFormat:@"%@.errorDomain", [[NSBundle mainBundle] bundleIdentifier]] code:-101 userInfo:errorDetail];
CFRelease(webPImageDatRef);
return nil;
}
pic.width = (int)webPImageWidth;
pic.height = (int)webPImageHeight;
pic.colorspace = WEBP_YUV420;
WebPPictureImportRGBA(&pic, webPImageData, (int)webPBytesPerRow);
WebPPictureARGBToYUVA(&pic, WEBP_YUV420);
WebPCleanupTransparentArea(&pic);
WebPMemoryWriter writer;
WebPMemoryWriterInit(&writer);
pic.writer = WebPMemoryWrite;
pic.custom_ptr = &writer;
WebPEncode(&config, &pic);
NSData *webPFinalData = [NSData dataWithBytes:writer.mem length:writer.size];
free(writer.mem);
WebPPictureFree(&pic);
CFRelease(webPImageDatRef);
return webPFinalData;
}
@end