14
14
#import < mach/mach_host.h>
15
15
16
16
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
+ }
17
35
18
36
@interface SDImageCache ()
19
37
@@ -25,15 +43,20 @@ @interface SDImageCache ()
25
43
@end
26
44
27
45
28
- @implementation SDImageCache {
46
+ @implementation SDImageCache
47
+ {
29
48
NSFileManager *_fileManager;
30
49
}
31
50
32
51
+ (SDImageCache *)sharedImageCache
33
52
{
34
53
static dispatch_once_t once;
35
54
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
+ });
37
60
return instance;
38
61
}
39
62
@@ -66,7 +89,7 @@ - (id)initWithNamespace:(NSString *)ns
66
89
{
67
90
_fileManager = NSFileManager .new ;
68
91
});
69
-
92
+
70
93
#if TARGET_OS_IPHONE
71
94
// Subscribe to app events
72
95
[[NSNotificationCenter defaultCenter ] addObserver: self
@@ -78,7 +101,7 @@ - (id)initWithNamespace:(NSString *)ns
78
101
selector: @selector (cleanDisk )
79
102
name: UIApplicationWillTerminateNotification
80
103
object: nil ];
81
-
104
+
82
105
[[NSNotificationCenter defaultCenter ] addObserver: self
83
106
selector: @selector (backgroundCleanDisk )
84
107
name: UIApplicationDidEnterBackgroundNotification
@@ -138,7 +161,7 @@ - (NSString *)cachedFileNameForKey:(NSString *)key
138
161
139
162
#pragma mark ImageCache
140
163
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
142
165
{
143
166
if (!image || !key)
144
167
{
@@ -153,16 +176,35 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSStri
153
176
{
154
177
NSData *data = imageData;
155
178
156
- if (!data)
179
+ if (image && (recalculate || !data) )
157
180
{
158
- if (image)
159
- {
160
181
#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
+ {
161
199
data = UIImagePNGRepresentation (image);
200
+ }
201
+ else
202
+ {
203
+ data = UIImageJPEGRepresentation (image, (CGFloat)1.0 );
204
+ }
162
205
#else
163
- data = [NSBitmapImageRep representationOfImageRepsInArray: image.representations usingType: NSJPEGFileType properties: nil ];
206
+ data = [NSBitmapImageRep representationOfImageRepsInArray: image.representations usingType: NSJPEGFileType properties: nil ];
164
207
#endif
165
- }
166
208
}
167
209
168
210
if (data)
@@ -183,12 +225,12 @@ - (void)storeImage:(UIImage *)image imageData:(NSData *)imageData forKey:(NSStri
183
225
184
226
- (void )storeImage : (UIImage *)image forKey : (NSString *)key
185
227
{
186
- [self storeImage: image imageData: nil forKey: key toDisk: YES ];
228
+ [self storeImage: image recalculateFromImage: YES imageData: nil forKey: key toDisk: YES ];
187
229
}
188
230
189
231
- (void )storeImage : (UIImage *)image forKey : (NSString *)key toDisk : (BOOL )toDisk
190
232
{
191
- [self storeImage: image imageData: nil forKey: key toDisk: toDisk];
233
+ [self storeImage: image recalculateFromImage: YES imageData: nil forKey: key toDisk: toDisk];
192
234
}
193
235
194
236
- (BOOL )diskImageExistsWithKey : (NSString *)key
@@ -198,7 +240,7 @@ - (BOOL)diskImageExistsWithKey:(NSString *)key
198
240
{
199
241
exists = [_fileManager fileExistsAtPath: [self defaultCachePathForKey: key]];
200
242
});
201
-
243
+
202
244
return exists;
203
245
}
204
246
@@ -215,15 +257,15 @@ - (UIImage *)imageFromDiskCacheForKey:(NSString *)key
215
257
{
216
258
return image;
217
259
}
218
-
260
+
219
261
// Second check the disk cache...
220
262
UIImage *diskImage = [self diskImageForKey: key];
221
263
if (diskImage)
222
264
{
223
265
CGFloat cost = diskImage.size .height * diskImage.size .width * diskImage.scale ;
224
266
[self .memCache setObject: diskImage forKey: key cost: cost];
225
267
}
226
-
268
+
227
269
return diskImage;
228
270
}
229
271
@@ -272,7 +314,7 @@ - (UIImage *)scaledImageForKey:(NSString *)key image:(UIImage *)image
272
314
- (NSOperation *)queryDiskCacheForKey : (NSString *)key done : (void (^)(UIImage *image, SDImageCacheType cacheType))doneBlock
273
315
{
274
316
NSOperation *operation = NSOperation .new ;
275
-
317
+
276
318
if (!doneBlock) return nil ;
277
319
278
320
if (!key)
@@ -295,7 +337,7 @@ - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *im
295
337
{
296
338
return ;
297
339
}
298
-
340
+
299
341
@autoreleasepool
300
342
{
301
343
UIImage *diskImage = [self diskImageForKey: key];
@@ -311,7 +353,7 @@ - (NSOperation *)queryDiskCacheForKey:(NSString *)key done:(void (^)(UIImage *im
311
353
});
312
354
}
313
355
});
314
-
356
+
315
357
return operation;
316
358
}
317
359
@@ -454,13 +496,13 @@ - (void)backgroundCleanDisk
454
496
[application endBackgroundTask: bgTask];
455
497
bgTask = UIBackgroundTaskInvalid;
456
498
}];
457
-
499
+
458
500
// Start the long-running task and return immediately.
459
501
dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^
460
502
{
461
503
// Do the work associated with the task, preferably in chunks.
462
504
[self cleanDisk ];
463
-
505
+
464
506
[application endBackgroundTask: bgTask];
465
507
bgTask = UIBackgroundTaskInvalid;
466
508
});
@@ -487,7 +529,7 @@ - (int)getDiskCount
487
529
{
488
530
count += 1 ;
489
531
}
490
-
532
+
491
533
return count;
492
534
}
493
535
0 commit comments