Skip to content

Commit

Permalink
Merge pull request #16 from jinSasaki/support-webp-cocoapods
Browse files Browse the repository at this point in the history
Support webp cocoapods
  • Loading branch information
jinSasaki authored Dec 7, 2016
2 parents 384997d + 2b98b4e commit 5f68f07
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 5 deletions.
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ Multi image downloader with priority in Swift
- Caching images
- Pure Swift
- Composable image
- Support webp
- Now supported by Carthage only. See [SwiftWebP](https://github.com/jinSasaki/SwiftWebP).
- Support WebP

Single download | Multi download with priority
--- | ---
Expand Down Expand Up @@ -83,11 +82,11 @@ imageView.vl_setImage(urls: [
```

### WebP image
Add `SwiftWebP.framework`.
If you installed via Carthage, add `SwiftWebP.framework`.

```swift
import Vulcan
import SwiftWebP
import SwiftWebP // Only installed via Carthage

extension WebPDecoder: ImageDecoder {
public func decode(data: Data, response: HTTPURLResponse, options: ImageDecodeOptions?) throws -> Image {
Expand Down
18 changes: 18 additions & 0 deletions Sources/WebP/UIImage+WebP.swift
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
}
}
12 changes: 12 additions & 0 deletions Sources/WebP/WebPDecoder.h
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
152 changes: 152 additions & 0 deletions Sources/WebP/WebPDecoder.m
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
11 changes: 10 additions & 1 deletion Vulcan.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Vulcan'
s.version = '0.1.2'
s.version = '0.2.0'
s.license = 'MIT'
s.summary = 'Multi image downloader with priority in Swift'
s.homepage = 'https://github.com/jinSasaki/Vulcan'
Expand All @@ -12,4 +12,13 @@ Pod::Spec.new do |s|
s.tvos.deployment_target = '9.0'

s.source_files = 'Sources/**/*.swift'

s.subspec 'WebP' do |webp|
webp.source_files = 'Sources/WebP/*.{h,m,swift}'
webp.xcconfig = {
'USER_HEADER_SEARCH_PATHS' => '$(inherited) $(SRCROOT)/libwebp/src'
}
webp.dependency 'libwebp'
end

end

0 comments on commit 5f68f07

Please sign in to comment.