-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #16 from jinSasaki/support-webp-cocoapods
Support webp cocoapods
- Loading branch information
Showing
5 changed files
with
195 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// | ||
// UIImage+WebP.swift | ||
// SwiftWebP | ||
// | ||
// Created by Jin Sasaki on 2016/11/04. | ||
// Copyright © 2016年 Sasakky. All rights reserved. | ||
// | ||
|
||
import UIKit | ||
|
||
public extension UIImage { | ||
public class func image(fromWebPData webpData: Data) -> UIImage? { | ||
guard let image = WebPDecoder.decode(webpData) else { | ||
return nil | ||
} | ||
return image | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// | ||
// WebPDecoder.h | ||
// | ||
// Created by Jin Sasaki on 2016/11/04. | ||
// Copyright © 2016年 Sasakky. All rights reserved. | ||
// | ||
|
||
#import <UIKit/UIKit.h> | ||
|
||
@interface WebPDecoder : NSObject | ||
+ (nullable UIImage *)decode:(nullable NSData *)data; | ||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// | ||
// WebPDecoder.m | ||
// | ||
// Created by Jin Sasaki on 2016/11/04. | ||
// Copyright © 2016年 Sasakky. All rights reserved. | ||
// | ||
|
||
#import "WebPDecoder.h" | ||
|
||
#import "webp/decode.h" | ||
#import "webp/mux_types.h" | ||
#import "webp/demux.h" | ||
|
||
static void FreeImageData(void *info, const void *data, size_t size) { | ||
free((void *)data); | ||
} | ||
|
||
@implementation WebPDecoder | ||
+ (nullable UIImage *)decode:(nullable NSData *)data | ||
{ | ||
if (!data) { | ||
return nil; | ||
} | ||
|
||
WebPData webpData; | ||
WebPDataInit(&webpData); | ||
webpData.bytes = data.bytes; | ||
webpData.size = data.length; | ||
WebPDemuxer *demuxer = WebPDemux(&webpData); | ||
if (!demuxer) { | ||
return nil; | ||
} | ||
|
||
uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS); | ||
if (!(flags & ANIMATION_FLAG)) { | ||
// for static single webp image | ||
UIImage *staticImage = [self rawWepImageWithData:webpData]; | ||
WebPDemuxDelete(demuxer); | ||
return staticImage; | ||
} | ||
|
||
WebPIterator iter; | ||
if (!WebPDemuxGetFrame(demuxer, 1, &iter)) { | ||
WebPDemuxReleaseIterator(&iter); | ||
WebPDemuxDelete(demuxer); | ||
return nil; | ||
} | ||
|
||
NSMutableArray *images = [NSMutableArray array]; | ||
NSTimeInterval duration = 0; | ||
|
||
do { | ||
UIImage *image; | ||
if (iter.blend_method == WEBP_MUX_BLEND) { | ||
image = [self blendWebpImageWithOriginImage:[images lastObject] iterator:iter]; | ||
} else { | ||
image = [self rawWepImageWithData:iter.fragment]; | ||
} | ||
|
||
if (!image) { | ||
continue; | ||
} | ||
|
||
[images addObject:image]; | ||
duration += iter.duration / 1000.0f; | ||
|
||
} while (WebPDemuxNextFrame(&iter)); | ||
|
||
WebPDemuxReleaseIterator(&iter); | ||
WebPDemuxDelete(demuxer); | ||
|
||
UIImage *finalImage = nil; | ||
finalImage = [UIImage animatedImageWithImages:images duration:duration]; | ||
return finalImage; | ||
} | ||
|
||
|
||
+ (nullable UIImage *)blendWebpImageWithOriginImage:(nullable UIImage *)originImage iterator:(WebPIterator)iter { | ||
if (!originImage) { | ||
return nil; | ||
} | ||
|
||
CGSize size = originImage.size; | ||
CGFloat tmpX = iter.x_offset; | ||
CGFloat tmpY = size.height - iter.height - iter.y_offset; | ||
CGRect imageRect = CGRectMake(tmpX, tmpY, iter.width, iter.height); | ||
|
||
UIImage *image = [self rawWepImageWithData:iter.fragment]; | ||
if (!image) { | ||
return nil; | ||
} | ||
|
||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); | ||
uint32_t bitmapInfo = iter.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; | ||
CGContextRef blendCanvas = CGBitmapContextCreate(NULL, size.width, size.height, 8, 0, colorSpaceRef, bitmapInfo); | ||
CGContextDrawImage(blendCanvas, CGRectMake(0, 0, size.width, size.height), originImage.CGImage); | ||
CGContextDrawImage(blendCanvas, imageRect, image.CGImage); | ||
CGImageRef newImageRef = CGBitmapContextCreateImage(blendCanvas); | ||
|
||
image = [UIImage imageWithCGImage:newImageRef]; | ||
|
||
CGImageRelease(newImageRef); | ||
CGContextRelease(blendCanvas); | ||
CGColorSpaceRelease(colorSpaceRef); | ||
|
||
return image; | ||
} | ||
|
||
+ (nullable UIImage *)rawWepImageWithData:(WebPData)webpData { | ||
WebPDecoderConfig config; | ||
if (!WebPInitDecoderConfig(&config)) { | ||
return nil; | ||
} | ||
|
||
if (WebPGetFeatures(webpData.bytes, webpData.size, &config.input) != VP8_STATUS_OK) { | ||
return nil; | ||
} | ||
|
||
config.output.colorspace = config.input.has_alpha ? MODE_rgbA : MODE_RGB; | ||
config.options.use_threads = 1; | ||
|
||
// Decode the WebP image data into a RGBA value array. | ||
if (WebPDecode(webpData.bytes, webpData.size, &config) != VP8_STATUS_OK) { | ||
return nil; | ||
} | ||
|
||
int width = config.input.width; | ||
int height = config.input.height; | ||
if (config.options.use_scaling) { | ||
width = config.options.scaled_width; | ||
height = config.options.scaled_height; | ||
} | ||
|
||
// Construct a UIImage from the decoded RGBA value array. | ||
CGDataProviderRef provider = | ||
CGDataProviderCreateWithData(NULL, config.output.u.RGBA.rgba, config.output.u.RGBA.size, FreeImageData); | ||
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); | ||
CGBitmapInfo bitmapInfo = config.input.has_alpha ? kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast : 0; | ||
size_t components = config.input.has_alpha ? 4 : 3; | ||
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; | ||
CGImageRef imageRef = CGImageCreate(width, height, 8, components * 8, components * width, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); | ||
|
||
CGColorSpaceRelease(colorSpaceRef); | ||
CGDataProviderRelease(provider); | ||
|
||
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef]; | ||
CGImageRelease(imageRef); | ||
|
||
return image; | ||
} | ||
|
||
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters