Skip to content

Commit 3f98095

Browse files
author
Andy LaVoy
committed
support GIFs
1 parent 17d04b7 commit 3f98095

8 files changed

Lines changed: 211 additions & 4 deletions

File tree

Examples/SDWebImage Demo/MasterViewController.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
3030
target:self
3131
action:@selector(flushCache)];
3232
_objects = [NSArray arrayWithObjects:
33-
@"https://graph.facebook.com/olivier.poitrey/picture?height=200&width=200",
33+
@"http://assets.sbnation.com/assets/2512203/dogflops.gif",
3434
@"http://static2.dmcdn.net/static/video/656/177/44771656:jpeg_preview_small.jpg?20120509154705",
3535
@"http://static2.dmcdn.net/static/video/629/228/44822926:jpeg_preview_small.jpg?20120509181018",
3636
@"http://static2.dmcdn.net/static/video/116/367/44763611:jpeg_preview_small.jpg?20120509101749",

SDWebImage.xcodeproj/project.pbxproj

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,14 @@
6767
5376131E155AD0D5005750A4 /* SDWebImagePrefetcher.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D91148C56230056699D /* SDWebImagePrefetcher.h */; settings = {ATTRIBUTES = (Public, ); }; };
6868
5376131F155AD0D5005750A4 /* UIButton+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D93148C56230056699D /* UIButton+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
6969
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
70-
A99C65A116F9CA2600A73D75 /* SDWebImageCompat.m in Sources */ = {isa = PBXBuildFile; fileRef = 5340674F167780C40042B59E /* SDWebImageCompat.m */; };
70+
A18A6CC7172DC28500419892 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; };
71+
A18A6CC8172DC28500419892 /* UIImage+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CC5172DC28500419892 /* UIImage+GIF.h */; };
72+
A18A6CC9172DC28500419892 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CC6172DC28500419892 /* UIImage+GIF.m */; };
73+
A18A6CCA172DC28500419892 /* UIImage+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CC6172DC28500419892 /* UIImage+GIF.m */; };
74+
A18A6CCD172DC33A00419892 /* NSData+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CCB172DC33A00419892 /* NSData+GIF.h */; };
75+
A18A6CCE172DC33A00419892 /* NSData+GIF.h in Headers */ = {isa = PBXBuildFile; fileRef = A18A6CCB172DC33A00419892 /* NSData+GIF.h */; };
76+
A18A6CCF172DC33A00419892 /* NSData+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CCC172DC33A00419892 /* NSData+GIF.m */; };
77+
A18A6CD0172DC33A00419892 /* NSData+GIF.m in Sources */ = {isa = PBXBuildFile; fileRef = A18A6CCC172DC33A00419892 /* NSData+GIF.m */; };
7178
/* End PBXBuildFile section */
7279

7380
/* Begin PBXContainerItemProxy section */
@@ -107,6 +114,10 @@
107114
53922D96148C56230056699D /* UIImageView+WebCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImageView+WebCache.m"; path = "SDWebImage/UIImageView+WebCache.m"; sourceTree = SOURCE_ROOT; };
108115
53FB893F14D35D1A0020B787 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
109116
53FB894814D35E9E0020B787 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
117+
A18A6CC5172DC28500419892 /* UIImage+GIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+GIF.h"; sourceTree = "<group>"; };
118+
A18A6CC6172DC28500419892 /* UIImage+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+GIF.m"; sourceTree = "<group>"; };
119+
A18A6CCB172DC33A00419892 /* NSData+GIF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+GIF.h"; sourceTree = "<group>"; };
120+
A18A6CCC172DC33A00419892 /* NSData+GIF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+GIF.m"; sourceTree = "<group>"; };
110121
/* End PBXFileReference section */
111122

112123
/* Begin PBXFrameworksBuildPhase section */
@@ -181,8 +192,12 @@
181192
children = (
182193
535699B415113E7300A4C397 /* MKAnnotationView+WebCache.h */,
183194
535699B515113E7300A4C397 /* MKAnnotationView+WebCache.m */,
195+
A18A6CCB172DC33A00419892 /* NSData+GIF.h */,
196+
A18A6CCC172DC33A00419892 /* NSData+GIF.m */,
184197
53922D93148C56230056699D /* UIButton+WebCache.h */,
185198
53922D94148C56230056699D /* UIButton+WebCache.m */,
199+
A18A6CC5172DC28500419892 /* UIImage+GIF.h */,
200+
A18A6CC6172DC28500419892 /* UIImage+GIF.m */,
186201
53922D95148C56230056699D /* UIImageView+WebCache.h */,
187202
53922D96148C56230056699D /* UIImageView+WebCache.m */,
188203
);
@@ -240,6 +255,8 @@
240255
531041DC157EAFA400BBABC3 /* MKAnnotationView+WebCache.h in Headers */,
241256
530E49E916464C26002868E7 /* SDWebImageOperation.h in Headers */,
242257
530E49EB16464C7F002868E7 /* SDWebImageDownloaderOperation.h in Headers */,
258+
A18A6CC8172DC28500419892 /* UIImage+GIF.h in Headers */,
259+
A18A6CCE172DC33A00419892 /* NSData+GIF.h in Headers */,
243260
);
244261
runOnlyForDeploymentPostprocessing = 0;
245262
};
@@ -257,6 +274,8 @@
257274
53761320155AD0D5005750A4 /* UIImageView+WebCache.h in Headers */,
258275
530E49E816464C25002868E7 /* SDWebImageOperation.h in Headers */,
259276
530E49EA16464C7C002868E7 /* SDWebImageDownloaderOperation.h in Headers */,
277+
A18A6CC7172DC28500419892 /* UIImage+GIF.h in Headers */,
278+
A18A6CCD172DC33A00419892 /* NSData+GIF.h in Headers */,
260279
);
261280
runOnlyForDeploymentPostprocessing = 0;
262281
};
@@ -362,7 +381,6 @@
362381
isa = PBXSourcesBuildPhase;
363382
buildActionMask = 2147483647;
364383
files = (
365-
A99C65A116F9CA2600A73D75 /* SDWebImageCompat.m in Sources */,
366384
531041C4157EAFA400BBABC3 /* SDImageCache.m in Sources */,
367385
531041C5157EAFA400BBABC3 /* SDWebImageDecoder.m in Sources */,
368386
531041C6157EAFA400BBABC3 /* SDWebImageDownloader.m in Sources */,
@@ -372,6 +390,8 @@
372390
531041CA157EAFA400BBABC3 /* UIImageView+WebCache.m in Sources */,
373391
531041CB157EAFA400BBABC3 /* MKAnnotationView+WebCache.m in Sources */,
374392
530E49ED16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */,
393+
A18A6CCA172DC28500419892 /* UIImage+GIF.m in Sources */,
394+
A18A6CD0172DC33A00419892 /* NSData+GIF.m in Sources */,
375395
);
376396
runOnlyForDeploymentPostprocessing = 0;
377397
};
@@ -388,6 +408,8 @@
388408
5376130F155AD0D5005750A4 /* UIImageView+WebCache.m in Sources */,
389409
530E49EC16464C84002868E7 /* SDWebImageDownloaderOperation.m in Sources */,
390410
53406750167780C40042B59E /* SDWebImageCompat.m in Sources */,
411+
A18A6CC9172DC28500419892 /* UIImage+GIF.m in Sources */,
412+
A18A6CCF172DC33A00419892 /* NSData+GIF.m in Sources */,
391413
);
392414
runOnlyForDeploymentPostprocessing = 0;
393415
};

SDWebImage/NSData+GIF.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// NSData+GIF.h
3+
// SDWebImage
4+
//
5+
// Created by Andy LaVoy on 4/28/13.
6+
// Copyright (c) 2013 Dailymotion. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface NSData (GIF)
12+
13+
- (BOOL)isGIF;
14+
15+
@end

SDWebImage/NSData+GIF.m

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//
2+
// NSData+GIF.m
3+
// SDWebImage
4+
//
5+
// Created by Andy LaVoy on 4/28/13.
6+
// Copyright (c) 2013 Dailymotion. All rights reserved.
7+
//
8+
9+
#import "NSData+GIF.h"
10+
11+
@implementation NSData (GIF)
12+
13+
- (BOOL)isGIF {
14+
BOOL isGIF = NO;
15+
16+
uint8_t c;
17+
[self getBytes:&c length:1];
18+
19+
switch (c) {
20+
case 0x47: // probably a GIF
21+
isGIF = YES;
22+
break;
23+
default:
24+
break;
25+
}
26+
27+
return isGIF;
28+
}
29+
30+
@end

SDWebImage/SDWebImageCompat.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,4 @@
3737
#define SDDispatchQueueSetterSementics assign
3838
#endif
3939

40-
extern inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image);
40+
extern inline UIImage *SDScaledImageForKey(NSString *key, UIImage *image);

SDWebImage/SDWebImageManager.m

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*/
88

99
#import "SDWebImageManager.h"
10+
#import "UIImage+GIF.h"
1011
#import <objc/message.h>
1112

1213
@interface SDWebImageCombinedOperation : NSObject <SDWebImageOperation>
@@ -121,6 +122,10 @@ - (NSString *)cacheKeyForURL:(NSURL *)url
121122
}
122123
__block id<SDWebImageOperation> subOperation = [self.imageDownloader downloadImageWithURL:url options:downloaderOptions progress:progressBlock completed:^(UIImage *downloadedImage, NSData *data, NSError *error, BOOL finished)
123124
{
125+
if ([data isGIF]) {
126+
downloadedImage = [UIImage animatedGIFWithData:data];
127+
}
128+
124129
if (weakOperation.cancelled)
125130
{
126131
completedBlock(nil, nil, SDImageCacheTypeNone, finished);

SDWebImage/UIImage+GIF.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//
2+
// UIImage+GIF.h
3+
// LBGIFImage
4+
//
5+
// Created by Laurin Brandner on 06.01.12.
6+
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
7+
//
8+
9+
#import "NSData+GIF.h"
10+
#import <UIKit/UIKit.h>
11+
12+
@interface UIImage (GIF)
13+
14+
+(UIImage*)animatedGIFNamed:(NSString*)name;
15+
+(UIImage*)animatedGIFWithData:(NSData *)data;
16+
17+
-(UIImage*)animatedImageByScalingAndCroppingToSize:(CGSize)size;
18+
19+
@end

SDWebImage/UIImage+GIF.m

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
//
2+
// UIImage+GIF.m
3+
// LBGIFImage
4+
//
5+
// Created by Laurin Brandner on 06.01.12.
6+
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
7+
//
8+
9+
#import "UIImage+GIF.h"
10+
#import <ImageIO/ImageIO.h>
11+
12+
@implementation UIImage (GIF)
13+
14+
+(UIImage*)animatedGIFWithData:(NSData *)data {
15+
if (!data) {
16+
return nil;
17+
}
18+
19+
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
20+
21+
size_t count = CGImageSourceGetCount(source);
22+
NSMutableArray* images = [NSMutableArray array];
23+
24+
NSTimeInterval duration = 0.0f;
25+
26+
for (size_t i = 0; i < count; i++) {
27+
CGImageRef image = CGImageSourceCreateImageAtIndex(source, i, NULL);
28+
29+
NSDictionary* frameProperties = CFBridgingRelease(CGImageSourceCopyPropertiesAtIndex(source, i, NULL));
30+
duration += [[[frameProperties objectForKey:(NSString*)kCGImagePropertyGIFDictionary] objectForKey:(NSString*)kCGImagePropertyGIFDelayTime] doubleValue];
31+
32+
[images addObject:[UIImage imageWithCGImage:image scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
33+
34+
CGImageRelease(image);
35+
}
36+
37+
CFRelease(source);
38+
39+
if (!duration) {
40+
duration = (1.0f/10.0f)*count;
41+
}
42+
43+
return [UIImage animatedImageWithImages:images duration:duration];
44+
}
45+
46+
+(UIImage*)animatedGIFNamed:(NSString *)name {
47+
CGFloat scale = [UIScreen mainScreen].scale;
48+
49+
if (scale > 1.0f) {
50+
NSString* retinaPath = [[NSBundle mainBundle] pathForResource:[name stringByAppendingString:@"@2x"] ofType:@"gif"];
51+
52+
NSData* data = [NSData dataWithContentsOfFile:retinaPath];
53+
54+
if (data) {
55+
return [UIImage animatedGIFWithData:data];
56+
}
57+
58+
NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
59+
60+
data = [NSData dataWithContentsOfFile:path];
61+
62+
if (data) {
63+
return [UIImage animatedGIFWithData:data];
64+
}
65+
66+
return [UIImage imageNamed:name];
67+
}
68+
else {
69+
NSString* path = [[NSBundle mainBundle] pathForResource:name ofType:@"gif"];
70+
71+
NSData* data = [NSData dataWithContentsOfFile:path];
72+
73+
if (data) {
74+
return [UIImage animatedGIFWithData:data];
75+
}
76+
77+
return [UIImage imageNamed:name];
78+
}
79+
}
80+
81+
-(UIImage*)animatedImageByScalingAndCroppingToSize:(CGSize)size {
82+
if (CGSizeEqualToSize(self.size, size) || CGSizeEqualToSize(size, CGSizeZero)) {
83+
return self;
84+
}
85+
86+
CGSize scaledSize = size;
87+
CGPoint thumbnailPoint = CGPointZero;
88+
89+
CGFloat widthFactor = size.width / self.size.width;
90+
CGFloat heightFactor = size.height / self.size.height;
91+
CGFloat scaleFactor = (widthFactor > heightFactor) ? widthFactor :heightFactor;
92+
scaledSize.width = self.size.width * scaleFactor;
93+
scaledSize.height = self.size.height * scaleFactor;
94+
if (widthFactor > heightFactor) {
95+
thumbnailPoint.y = (size.height - scaledSize.height) * 0.5;
96+
} else if (widthFactor < heightFactor) {
97+
thumbnailPoint.x = (size.width - scaledSize.width) * 0.5;
98+
}
99+
100+
NSMutableArray* scaledImages = [NSMutableArray array];
101+
102+
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
103+
104+
for (UIImage* image in self.images) {
105+
[image drawInRect:CGRectMake(thumbnailPoint.x, thumbnailPoint.y, scaledSize.width, scaledSize.height)];
106+
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
107+
108+
[scaledImages addObject:newImage];
109+
}
110+
111+
UIGraphicsEndImageContext();
112+
113+
return [UIImage animatedImageWithImages:scaledImages duration:self.duration];
114+
}
115+
116+
@end

0 commit comments

Comments
 (0)