Skip to content

Commit b201e13

Browse files
bpoplauschiOlivier Poitrey
authored and
Olivier Poitrey
committed
Added PNG detection to be able to determine at runtime if a downloaded image should be saved as PNG or as JPEG
1 parent 3380e56 commit b201e13

File tree

3 files changed

+68
-25
lines changed

3 files changed

+68
-25
lines changed

SDWebImage/SDImageCache.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,14 @@ typedef enum SDImageCacheType SDImageCacheType;
9090
* Store an image into memory and optionally disk cache at the given key.
9191
*
9292
* @param image The image to store
93+
* @param recalculate BOOL indicates if imageData can be used or a new data should be constructed from the UIImage
9394
* @param data The image data as returned by the server, this representation will be used for disk storage
9495
* instead of converting the given image object into a storable/compressed image format in order
9596
* to save quality and CPU
9697
* @param key The unique image cache key, usually it's image absolute URL
9798
* @param toDisk Store the image to disk cache if YES
9899
*/
99-
- (void)storeImage:(UIImage *)image imageData:(NSData *)data forKey:(NSString *)key toDisk:(BOOL)toDisk;
100+
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk;
100101

101102
/**
102103
* Query the disk cache asynchronously.

SDWebImage/SDImageCache.m

Lines changed: 63 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414
#import <mach/mach_host.h>
1515

1616
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24 * 7; // 1 week
17+
// PNG signature bytes and data (below)
18+
static unsigned char kPNGSignatureBytes[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
19+
static NSData *kPNGSignatureData = nil;
20+
21+
BOOL ImageDataHasPNGPreffix(NSData *data);
22+
BOOL ImageDataHasPNGPreffix(NSData *data)
23+
{
24+
NSUInteger pngSignatureLength = [kPNGSignatureData length];
25+
if ([data length] >= pngSignatureLength)
26+
{
27+
if ([[data subdataWithRange:NSMakeRange(0, pngSignatureLength)] isEqualToData:kPNGSignatureData])
28+
{
29+
return YES;
30+
}
31+
}
32+
33+
return NO;
34+
}
1735

1836
@interface SDImageCache ()
1937

@@ -25,15 +43,20 @@ @interface SDImageCache ()
2543
@end
2644

2745

28-
@implementation SDImageCache {
46+
@implementation SDImageCache
47+
{
2948
NSFileManager *_fileManager;
3049
}
3150

3251
+ (SDImageCache *)sharedImageCache
3352
{
3453
static dispatch_once_t once;
3554
static id instance;
36-
dispatch_once(&once, ^{instance = self.new;});
55+
dispatch_once(&once, ^
56+
{
57+
instance = self.new;
58+
kPNGSignatureData = [NSData dataWithBytes:kPNGSignatureBytes length:8];
59+
});
3760
return instance;
3861
}
3962

@@ -66,7 +89,7 @@ - (id)initWithNamespace:(NSString *)ns
6689
{
6790
_fileManager = NSFileManager.new;
6891
});
69-
92+
7093
#if TARGET_OS_IPHONE
7194
// Subscribe to app events
7295
[[NSNotificationCenter defaultCenter] addObserver:self
@@ -78,7 +101,7 @@ - (id)initWithNamespace:(NSString *)ns
78101
selector:@selector(cleanDisk)
79102
name:UIApplicationWillTerminateNotification
80103
object:nil];
81-
104+
82105
[[NSNotificationCenter defaultCenter] addObserver:self
83106
selector:@selector(backgroundCleanDisk)
84107
name:UIApplicationDidEnterBackgroundNotification
@@ -138,7 +161,7 @@ - (NSString *)cachedFileNameForKey:(NSString *)key
138161

139162
#pragma mark ImageCache
140163

141-
- (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk
164+
- (void)storeImage:(UIImage *)image recalculateFromImage:(BOOL)recalculate imageData:(NSData *)imageData forKey:(NSString *)key toDisk:(BOOL)toDisk
142165
{
143166
if (!image || !key)
144167
{
@@ -153,16 +176,35 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSStri
153176
{
154177
NSData *data = imageData;
155178

156-
if (!data)
179+
if (image && (recalculate || !data))
157180
{
158-
if (image)
159-
{
160181
#if TARGET_OS_IPHONE
182+
// We need to determine if the image is a PNG or a JPEG
183+
// PNGs are easier to detect because they have a unique signature (http://www.w3.org/TR/PNG-Structure.html)
184+
// The first eight bytes of a PNG file always contain the following (decimal) values:
185+
// 137 80 78 71 13 10 26 10
186+
187+
// We assume the image is PNG, in case the imageData is nil (i.e. if trying to save a UIImage directly),
188+
// we will consider it PNG to avoid loosing the transparency
189+
BOOL imageIsPng = YES;
190+
191+
// But if we have an image data, we will look at the preffix
192+
if ([imageData length] >= [kPNGSignatureData length])
193+
{
194+
imageIsPng = ImageDataHasPNGPreffix(imageData);
195+
}
196+
197+
if (imageIsPng)
198+
{
161199
data = UIImagePNGRepresentation(image);
200+
}
201+
else
202+
{
203+
data = UIImageJPEGRepresentation(image, (CGFloat)1.0);
204+
}
162205
#else
163-
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
206+
data = [NSBitmapImageRep representationOfImageRepsInArray:image.representations usingType: NSJPEGFileType properties:nil];
164207
#endif
165-
}
166208
}
167209

168210
if (data)
@@ -183,12 +225,12 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSStri
183225

184226
- (void)storeImage:(UIImage *)image forKey:(NSString *)key
185227
{
186-
[self storeImage:image imageData:nil forKey:key toDisk:YES];
228+
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:YES];
187229
}
188230

189231
- (void)storeImage:(UIImage *)image forKey:(NSString *)key toDisk:(BOOL)toDisk
190232
{
191-
[self storeImage:image imageData:nil forKey:key toDisk:toDisk];
233+
[self storeImage:image recalculateFromImage:YES imageData:nil forKey:key toDisk:toDisk];
192234
}
193235

194236
- (BOOL)diskImageExistsWithKey:(NSString *)key
@@ -198,7 +240,7 @@ - (BOOL)diskImageExistsWithKey:(NSString *)key
198240
{
199241
exists = [_fileManager fileExistsAtPath:[self defaultCachePathForKey:key]];
200242
});
201-
243+
202244
return exists;
203245
}
204246

@@ -215,15 +257,15 @@ - (UIImage *)imageFromDiskCacheForKey:(NSString *)key
215257
{
216258
return image;
217259
}
218-
260+
219261
// Second check the disk cache...
220262
UIImage *diskImage = [self diskImageForKey:key];
221263
if (diskImage)
222264
{
223265
CGFloat cost = diskImage.size.height * diskImage.size.width * diskImage.scale;
224266
[self.memCache setObject:diskImage forKey:key cost:cost];
225267
}
226-
268+
227269
return diskImage;
228270
}
229271

@@ -272,7 +314,7 @@ - (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image
272314
- (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock
273315
{
274316
NSOperation *operation = NSOperation.new;
275-
317+
276318
if (!doneBlock) return nil;
277319

278320
if (!key)
@@ -295,7 +337,7 @@ - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *im
295337
{
296338
return;
297339
}
298-
340+
299341
@autoreleasepool
300342
{
301343
UIImage *diskImage = [self diskImageForKey:key];
@@ -311,7 +353,7 @@ - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *im
311353
});
312354
}
313355
});
314-
356+
315357
return operation;
316358
}
317359

@@ -454,13 +496,13 @@ - (void)backgroundCleanDisk
454496
[application endBackgroundTask:bgTask];
455497
bgTask = UIBackgroundTaskInvalid;
456498
}];
457-
499+
458500
// Start the long-running task and return immediately.
459501
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^
460502
{
461503
// Do the work associated with the task, preferably in chunks.
462504
[self cleanDisk];
463-
505+
464506
[application endBackgroundTask:bgTask];
465507
bgTask = UIBackgroundTaskInvalid;
466508
});
@@ -487,7 +529,7 @@ - (int)getDiskCount
487529
{
488530
count += 1;
489531
}
490-
532+
491533
return count;
492534
}
493535

SDWebImage/SDWebImageManager.m

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ - (BOOL)diskImageExistsForURL:(NSURL *)url
199199

200200
if (transformedImage && finished)
201201
{
202-
NSData *dataToStore = [transformedImage isEqual:downloadedImage] ? data : nil;
203-
[self.imageCache storeImage:transformedImage imageData:dataToStore forKey:key toDisk:cacheOnDisk];
202+
BOOL imageWasTransformed = ![transformedImage isEqual:downloadedImage];
203+
[self.imageCache storeImage:transformedImage recalculateFromImage:imageWasTransformed imageData:data forKey:key toDisk:cacheOnDisk];
204204
}
205205
});
206206
}
@@ -213,7 +213,7 @@ - (BOOL)diskImageExistsForURL:(NSURL *)url
213213

214214
if (downloadedImage && finished)
215215
{
216-
[self.imageCache storeImage:downloadedImage imageData:data forKey:key toDisk:cacheOnDisk];
216+
[self.imageCache storeImage:downloadedImage recalculateFromImage:NO imageData:data forKey:key toDisk:cacheOnDisk];
217217
}
218218
}
219219
}

0 commit comments

Comments
 (0)