react-native-fast-image 8.27.1 → 8.27.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -157,18 +157,73 @@ static CGSize FFFPhotoPixelSize(NSURL *url)
|
|
|
157
157
|
// body is an image: a page sent instead (e.g. a captive portal's, with status
|
|
158
158
|
// 200) would be served from there until it expired, and each load would fail.
|
|
159
159
|
// Removing it after a load failed (forgetResponseAfterError:) could still be
|
|
160
|
-
// in progress when the next load started.
|
|
160
|
+
// in progress when the next load started. A page with an inline <svg> isn't
|
|
161
|
+
// an SVG image.
|
|
161
162
|
@interface FFFWebDownloaderOperation : SDWebImageDownloaderOperation
|
|
162
163
|
@end
|
|
163
164
|
|
|
164
165
|
@implementation FFFWebDownloaderOperation
|
|
165
166
|
|
|
167
|
+
// Whether data is an SVG document: its first element (after an XML
|
|
168
|
+
// declaration, comments or a doctype) is <svg>, as on Android
|
|
169
|
+
// (FastImageSvg.looksLikeSvg). SDWebImage takes data that starts with '<' and
|
|
170
|
+
// has an <svg> element anywhere for SVG, such as a page with an icon.
|
|
171
|
+
static BOOL FFFHasPrefixAt(NSString *text, NSUInteger i, NSString *prefix)
|
|
172
|
+
{
|
|
173
|
+
return text.length - i >= prefix.length && [text compare:prefix options:0 range:NSMakeRange(i, prefix.length)] == NSOrderedSame;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// The index after the end marker from i, or NSNotFound if it isn't there.
|
|
177
|
+
static NSUInteger FFFSkipPast(NSString *text, NSUInteger i, NSString *end)
|
|
178
|
+
{
|
|
179
|
+
NSRange found = [text rangeOfString:end options:0 range:NSMakeRange(i, text.length - i)];
|
|
180
|
+
return found.location == NSNotFound ? NSNotFound : NSMaxRange(found);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
static BOOL FFFLooksLikeSVG(NSData *data)
|
|
184
|
+
{
|
|
185
|
+
// The markup is ASCII: Latin-1 reads any bytes (a UTF-8 character cut off
|
|
186
|
+
// at the end too).
|
|
187
|
+
NSData *head = [data subdataWithRange:NSMakeRange(0, MIN(data.length, (NSUInteger)1024))];
|
|
188
|
+
NSString *text = [[[NSString alloc] initWithData:head encoding:NSISOLatin1StringEncoding] lowercaseString];
|
|
189
|
+
NSCharacterSet *space = NSCharacterSet.whitespaceAndNewlineCharacterSet;
|
|
190
|
+
// A UTF-8 byte order mark, as Latin-1.
|
|
191
|
+
NSUInteger i = FFFHasPrefixAt(text, 0, @"\u00EF\u00BB\u00BF") ? 3 : 0;
|
|
192
|
+
while (i < text.length) {
|
|
193
|
+
if ([space characterIsMember:[text characterAtIndex:i]]) {
|
|
194
|
+
i++;
|
|
195
|
+
} else if (FFFHasPrefixAt(text, i, @"<?")) {
|
|
196
|
+
i = FFFSkipPast(text, i, @"?>");
|
|
197
|
+
} else if (FFFHasPrefixAt(text, i, @"<!--")) {
|
|
198
|
+
i = FFFSkipPast(text, i, @"-->");
|
|
199
|
+
} else if (FFFHasPrefixAt(text, i, @"<!")) {
|
|
200
|
+
// A doctype, whose internal subset ([...]) can have '>'s.
|
|
201
|
+
NSRange rest = NSMakeRange(i, text.length - i);
|
|
202
|
+
NSUInteger subset = [text rangeOfString:@"[" options:0 range:rest].location;
|
|
203
|
+
NSUInteger close = [text rangeOfString:@">" options:0 range:rest].location;
|
|
204
|
+
if (subset != NSNotFound && subset < close) {
|
|
205
|
+
i = FFFSkipPast(text, subset, @"]");
|
|
206
|
+
}
|
|
207
|
+
if (i != NSNotFound) {
|
|
208
|
+
i = FFFSkipPast(text, i, @">");
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
return FFFHasPrefixAt(text, i, @"<svg");
|
|
212
|
+
}
|
|
213
|
+
if (i == NSNotFound) {
|
|
214
|
+
return NO;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return NO;
|
|
218
|
+
}
|
|
219
|
+
|
|
166
220
|
- (void)URLSession:(NSURLSession *)session
|
|
167
221
|
dataTask:(NSURLSessionDataTask *)dataTask
|
|
168
222
|
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
|
169
223
|
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
|
|
170
224
|
{
|
|
171
|
-
|
|
225
|
+
SDImageFormat format = [NSData sd_imageFormatForImageData:proposedResponse.data];
|
|
226
|
+
if (format == SDImageFormatUndefined || (format == SDImageFormatSVG && !FFFLooksLikeSVG(proposedResponse.data))) {
|
|
172
227
|
completionHandler(nil);
|
|
173
228
|
return;
|
|
174
229
|
}
|
|
@@ -263,7 +263,12 @@ static UIImage* FFFBlurredImage(UIImage* image, CGFloat scale, CGFloat radius, B
|
|
|
263
263
|
[self blurAgainIfResized];
|
|
264
264
|
[self tileAgainIfResized];
|
|
265
265
|
if (self.waitsForSize) {
|
|
266
|
-
|
|
266
|
+
// An image in the memory cache shows in this frame. Others start
|
|
267
|
+
// loading from didSetProps's block, after layout, in the order the
|
|
268
|
+
// views got their props: UIKit lays views out in its own order (in a
|
|
269
|
+
// grid, the last one first), and downloads start in the order they're
|
|
270
|
+
// asked for, so a screen's first images would load last.
|
|
271
|
+
if ([self hasSize] && [self loadsDuringLayout]) {
|
|
267
272
|
[self reloadImage];
|
|
268
273
|
}
|
|
269
274
|
} else if (![self switchSourceIfResized]) {
|
|
@@ -770,9 +775,10 @@ NSString *FFFErrorMessage(NSError *error)
|
|
|
770
775
|
// With downsample on, the image is decoded for the view's size, and
|
|
771
776
|
// with several sources one is picked for it, so a view that hasn't
|
|
772
777
|
// been laid out yet loads once it has. Props and layout are applied
|
|
773
|
-
// in the same update, so that's
|
|
774
|
-
// layoutSubviews)
|
|
775
|
-
//
|
|
778
|
+
// in the same update, so that's here, just after it, or before the
|
|
779
|
+
// next frame (layoutSubviews) for an image in the memory cache. A
|
|
780
|
+
// view that still has no size then (e.g. one sized from onLoad) loads
|
|
781
|
+
// at full size, or the largest source.
|
|
776
782
|
if (([self downsamples] || [self picksSource]) && ![self hasSize]) {
|
|
777
783
|
if (!self.waitsForSize) {
|
|
778
784
|
self.waitsForSize = YES;
|
|
@@ -960,31 +966,59 @@ NSString *FFFErrorMessage(NSError *error)
|
|
|
960
966
|
// Headers, and the size to decode at (see FFFDownsampledImage), which it
|
|
961
967
|
// records as decodedBox.
|
|
962
968
|
- (SDWebImageContext*) loadContext {
|
|
963
|
-
SDWebImageMutableContext* context = [NSMutableDictionary dictionary];
|
|
964
|
-
context[SDWebImageContextDownloadRequestModifier] = _source.requestModifier;
|
|
965
|
-
context[SDWebImageContextImageLoader] = _source.imageLoader;
|
|
966
|
-
context[SDWebImageContextCacheKeyFilter] = _source.cacheKeyFilter;
|
|
967
969
|
CGSize box = [self decodeBox];
|
|
968
970
|
self.decodedBox = box;
|
|
969
971
|
self.decodedCover = [self decodeCovers];
|
|
972
|
+
return [self contextForSource: _source box: box cover: self.decodedCover];
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// Whether the view loads as it's laid out, so its image shows in this frame:
|
|
976
|
+
// one in the memory cache, where SDWebImage finds it as the load starts, or
|
|
977
|
+
// one that isn't downloaded (no source, which shows defaultSource, or a data
|
|
978
|
+
// uri). Also when that can't be told (an app's own image cache, or an
|
|
979
|
+
// SDWebImage without cacheKeyForURL:context:), as before.
|
|
980
|
+
- (BOOL) loadsDuringLayout {
|
|
981
|
+
FFFastImageSource* source = [self picksSource] ? [self sourceForSize] : _source;
|
|
982
|
+
if (!source.url || [source.url.scheme isEqualToString: @"data"]) {
|
|
983
|
+
return YES;
|
|
984
|
+
}
|
|
985
|
+
if (!source.memoryCache) {
|
|
986
|
+
return NO;
|
|
987
|
+
}
|
|
988
|
+
SDWebImageManager* manager = [SDWebImageManager sharedManager];
|
|
989
|
+
if (![manager.imageCache isKindOfClass: [SDImageCache class]] || ![manager respondsToSelector: @selector(cacheKeyForURL:context:)]) {
|
|
990
|
+
return YES;
|
|
991
|
+
}
|
|
992
|
+
CGSize box = [self decodeBox];
|
|
993
|
+
SDWebImageContext* context = [self contextForSource: source box: box cover: [self decodeCovers]];
|
|
994
|
+
NSURL* url = CGSizeEqualToSize(box, CGSizeZero) ? source.url : [FFFDownsampledImage loadURLForURL: source.url];
|
|
995
|
+
NSString* key = [manager cacheKeyForURL: url context: context];
|
|
996
|
+
return [(SDImageCache*) manager.imageCache imageFromMemoryCacheForKey: key] != nil;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
- (SDWebImageContext*) contextForSource: (FFFastImageSource*)source box: (CGSize)box cover: (BOOL)cover {
|
|
1000
|
+
SDWebImageMutableContext* context = [NSMutableDictionary dictionary];
|
|
1001
|
+
context[SDWebImageContextDownloadRequestModifier] = source.requestModifier;
|
|
1002
|
+
context[SDWebImageContextImageLoader] = source.imageLoader;
|
|
1003
|
+
context[SDWebImageContextCacheKeyFilter] = source.cacheKeyFilter;
|
|
970
1004
|
if (CGSizeEqualToSize(box, CGSizeZero)) {
|
|
971
1005
|
context[SDWebImageContextAnimatedImageClass] = [SDAnimatedImage class];
|
|
972
|
-
if (!
|
|
1006
|
+
if (!source.memoryCache) {
|
|
973
1007
|
// Only on disk, also when it comes from there.
|
|
974
1008
|
context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeDisk);
|
|
975
1009
|
}
|
|
976
1010
|
return context;
|
|
977
1011
|
}
|
|
978
|
-
NSString* key =
|
|
979
|
-
[FFFDownsampledImage addToContext: context forKey: key box: box cover:
|
|
980
|
-
if ([
|
|
1012
|
+
NSString* key = source.cacheKeyFilter ? source.cacheKey : source.url.absoluteString;
|
|
1013
|
+
[FFFDownsampledImage addToContext: context forKey: key box: box cover: cover];
|
|
1014
|
+
if ([source isPhotoLibrary]) {
|
|
981
1015
|
// Photos makes the photo at the size asked for, with no file
|
|
982
1016
|
// downloaded: SDWebImage would keep this smaller copy on disk as the
|
|
983
1017
|
// photo's original, and a larger view would get it from there.
|
|
984
1018
|
context[SDWebImageContextOriginalStoreCacheType] = @(SDImageCacheTypeNone);
|
|
985
1019
|
context[SDWebImageContextOriginalQueryCacheType] = @(SDImageCacheTypeNone);
|
|
986
1020
|
}
|
|
987
|
-
if (!
|
|
1021
|
+
if (!source.memoryCache) {
|
|
988
1022
|
// The smaller copy is only kept in memory, so it isn't kept (the
|
|
989
1023
|
// downloaded file is still on disk).
|
|
990
1024
|
context[SDWebImageContextStoreCacheType] = @(SDImageCacheTypeNone);
|