react-native-fast-image 8.30.0 → 8.31.0
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.
- package/README.md +457 -210
- package/android/src/main/java/com/dylanvann/fastimage/FastImageApngRenderer.java +12 -1
- package/app.plugin.js +1 -1
- package/dist/index.d.ts +357 -63
- package/ios/FastImage/FFFAnimatedAVIFCoder.h +11 -0
- package/ios/FastImage/FFFAnimatedAVIFCoder.m +103 -0
- package/ios/FastImage/FFFastImageSource.h +4 -0
- package/ios/FastImage/FFFastImageSource.m +36 -2
- package/ios/FastImage/FFFastImageViewManager.m +1 -2
- package/package.json +4 -2
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#import "FFFAnimatedAVIFCoder.h"
|
|
2
|
+
#import <ImageIO/ImageIO.h>
|
|
3
|
+
|
|
4
|
+
// SDWebImage has no format for AVIF: this is the value SDWebImageAVIFCoder
|
|
5
|
+
// uses, so an image reports the same format with either coder.
|
|
6
|
+
static const SDImageFormat FFFImageFormatAVIF = 15;
|
|
7
|
+
|
|
8
|
+
// Whether the data is an animated AVIF: a file whose first box is `ftyp`
|
|
9
|
+
// with the `avis` (image sequence) brand, as its major brand or a compatible
|
|
10
|
+
// one. The header comes first, so a partial download can be checked too.
|
|
11
|
+
static BOOL FFFIsAnimatedAVIF(NSData* data) {
|
|
12
|
+
if (data.length < 16) {
|
|
13
|
+
return NO;
|
|
14
|
+
}
|
|
15
|
+
const uint8_t* bytes = data.bytes;
|
|
16
|
+
if (memcmp(bytes + 4, "ftyp", 4) != 0) {
|
|
17
|
+
return NO;
|
|
18
|
+
}
|
|
19
|
+
if (memcmp(bytes + 8, "avis", 4) == 0) {
|
|
20
|
+
return YES;
|
|
21
|
+
}
|
|
22
|
+
// The compatible brands, after the major brand and its version.
|
|
23
|
+
uint32_t size = (uint32_t)bytes[0] << 24 | (uint32_t)bytes[1] << 16 | (uint32_t)bytes[2] << 8 | bytes[3];
|
|
24
|
+
NSUInteger end = MIN((NSUInteger)size, data.length);
|
|
25
|
+
for (NSUInteger offset = 16; offset + 4 <= end; offset += 4) {
|
|
26
|
+
if (memcmp(bytes + offset, "avis", 4) == 0) {
|
|
27
|
+
return YES;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return NO;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Whether ImageIO reads AVIF image sequences on this version of iOS.
|
|
34
|
+
static BOOL FFFCanDecodeAVIFSequences(void) {
|
|
35
|
+
static BOOL supported;
|
|
36
|
+
static dispatch_once_t once;
|
|
37
|
+
dispatch_once(&once, ^{
|
|
38
|
+
NSArray* types = CFBridgingRelease(CGImageSourceCopyTypeIdentifiers());
|
|
39
|
+
supported = [types containsObject: @"public.avis"];
|
|
40
|
+
});
|
|
41
|
+
return supported;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@implementation FFFAnimatedAVIFCoder
|
|
45
|
+
|
|
46
|
+
+ (FFFAnimatedAVIFCoder*) sharedCoder {
|
|
47
|
+
static FFFAnimatedAVIFCoder* coder;
|
|
48
|
+
static dispatch_once_t once;
|
|
49
|
+
dispatch_once(&once, ^{
|
|
50
|
+
coder = [FFFAnimatedAVIFCoder new];
|
|
51
|
+
});
|
|
52
|
+
return coder;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
- (BOOL) canDecodeFromData: (NSData*)data {
|
|
56
|
+
return FFFIsAnimatedAVIF(data) && FFFCanDecodeAVIFSequences();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
- (BOOL) canIncrementalDecodeFromData: (NSData*)data {
|
|
60
|
+
return [self canDecodeFromData: data];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Only for decoding.
|
|
64
|
+
- (BOOL) canEncodeToFormat: (SDImageFormat)format {
|
|
65
|
+
return NO;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#pragma mark - SDImageIOAnimatedCoder
|
|
69
|
+
|
|
70
|
+
+ (SDImageFormat) imageFormat {
|
|
71
|
+
return FFFImageFormatAVIF;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
+ (NSString*) imageUTType {
|
|
75
|
+
return @"public.avis";
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ImageIO's keys for AVIF sequences (kCGImagePropertyAVIS…), as strings so
|
|
79
|
+
// they build with any SDK. LoopCount is the number of plays, 0 for forever,
|
|
80
|
+
// as for GIF and APNG.
|
|
81
|
+
+ (NSString*) dictionaryProperty {
|
|
82
|
+
return @"{AVIS}";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
+ (NSString*) unclampedDelayTimeProperty {
|
|
86
|
+
return @"UnclampedDelayTime";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
+ (NSString*) delayTimeProperty {
|
|
90
|
+
return @"DelayTime";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
+ (NSString*) loopCountProperty {
|
|
94
|
+
return @"LoopCount";
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// A sequence without a repetition count plays forever (ImageIO reports
|
|
98
|
+
// LoopCount 0 for one).
|
|
99
|
+
+ (NSUInteger) defaultLoopCount {
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
@end
|
|
@@ -72,6 +72,10 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
|
|
|
72
72
|
// a cache, where the size it was decoded from isn't recorded), or zero.
|
|
73
73
|
- (CGSize)photoPixelSize;
|
|
74
74
|
|
|
75
|
+
// Whether the data is an image FastImage can show: a format SDWebImage
|
|
76
|
+
// recognizes (an SVG only if its first element is <svg>), or one ImageIO
|
|
77
|
+
// reads that SDWebImage's check doesn't know (AVIF, ICO, ICNS, PSD).
|
|
78
|
+
+ (BOOL)isImageData:(NSData *)data;
|
|
75
79
|
// The HTTP cache of `cache: 'web'` images: their own rather than the app's
|
|
76
80
|
// shared NSURLCache, so clearDiskCache can empty it without the app's other
|
|
77
81
|
// responses. 50 MB, as on Android.
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
#import <SDWebImage/SDImageCodersManager.h>
|
|
9
9
|
#import <SDWebImage/SDImageAWebPCoder.h>
|
|
10
10
|
#import "FFFAnimatedWebPCoder.h"
|
|
11
|
+
#import "FFFAnimatedAVIFCoder.h"
|
|
11
12
|
#import <objc/message.h>
|
|
13
|
+
#import <ImageIO/ImageIO.h>
|
|
12
14
|
|
|
13
15
|
// In FFFastImageView.m.
|
|
14
16
|
FOUNDATION_EXTERN NSString *FFFErrorMessage(NSError *error);
|
|
@@ -224,8 +226,7 @@ static BOOL FFFLooksLikeSVG(NSData *data)
|
|
|
224
226
|
willCacheResponse:(NSCachedURLResponse *)proposedResponse
|
|
225
227
|
completionHandler:(void (^)(NSCachedURLResponse *cachedResponse))completionHandler
|
|
226
228
|
{
|
|
227
|
-
|
|
228
|
-
if (format == SDImageFormatUndefined || (format == SDImageFormatSVG && !FFFLooksLikeSVG(proposedResponse.data))) {
|
|
229
|
+
if (![FFFastImageSource isImageData:proposedResponse.data]) {
|
|
229
230
|
completionHandler(nil);
|
|
230
231
|
return;
|
|
231
232
|
}
|
|
@@ -260,6 +261,21 @@ static id<SDImageCoder> FFFSVGCoder;
|
|
|
260
261
|
[SDImageCodersManager.sharedManager addCoder:FFFAnimatedWebPCoder.sharedCoder];
|
|
261
262
|
}
|
|
262
263
|
|
|
264
|
+
// Animated AVIF: likewise, SDWebImage's own coders only decode an AVIF's
|
|
265
|
+
// first frame, so ImageIO's animated decoding does (iOS 16 and later).
|
|
266
|
+
// Not if the app has registered libavif's coder (SDWebImageAVIFCoder).
|
|
267
|
+
Class avifCoderClass = NSClassFromString(@"SDImageAVIFCoder");
|
|
268
|
+
BOOL hasAVIFCoder = NO;
|
|
269
|
+
for (id<SDImageCoder> registered in SDImageCodersManager.sharedManager.coders) {
|
|
270
|
+
if (avifCoderClass && [registered isKindOfClass:avifCoderClass]) {
|
|
271
|
+
hasAVIFCoder = YES;
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (!hasAVIFCoder) {
|
|
276
|
+
[SDImageCodersManager.sharedManager addCoder:FFFAnimatedAVIFCoder.sharedCoder];
|
|
277
|
+
}
|
|
278
|
+
|
|
263
279
|
// SVG: registered with SDWebImage's coders, as the pod's setup does (unless
|
|
264
280
|
// the app has already): SDWebImage then picks it for SVG data, including
|
|
265
281
|
// for FastImage's animated and downsampled image classes. Giving the
|
|
@@ -396,6 +412,24 @@ static id<SDImageCoder> FFFSVGCoder;
|
|
|
396
412
|
[[FFFastImageSource webURLCache] removeCachedResponseForRequest:[NSURLRequest requestWithURL:_url]];
|
|
397
413
|
}
|
|
398
414
|
|
|
415
|
+
+ (BOOL)isImageData:(NSData *)data
|
|
416
|
+
{
|
|
417
|
+
SDImageFormat format = [NSData sd_imageFormatForImageData:data];
|
|
418
|
+
if (format == SDImageFormatSVG) {
|
|
419
|
+
return FFFLooksLikeSVG(data);
|
|
420
|
+
}
|
|
421
|
+
if (format != SDImageFormatUndefined) {
|
|
422
|
+
return YES;
|
|
423
|
+
}
|
|
424
|
+
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
|
|
425
|
+
if (!source) {
|
|
426
|
+
return NO;
|
|
427
|
+
}
|
|
428
|
+
BOOL image = CGImageSourceGetType(source) != NULL && CGImageSourceGetCount(source) > 0;
|
|
429
|
+
CFRelease(source);
|
|
430
|
+
return image;
|
|
431
|
+
}
|
|
432
|
+
|
|
399
433
|
+ (NSURLCache *)webURLCache
|
|
400
434
|
{
|
|
401
435
|
static NSURLCache *cache;
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
#import <SDWebImage/SDWebImageManager.h>
|
|
7
7
|
#import <SDWebImage/SDWebImageError.h>
|
|
8
8
|
#import <SDWebImage/SDWebImagePrefetcher.h>
|
|
9
|
-
#import <SDWebImage/NSData+ImageContentType.h>
|
|
10
9
|
|
|
11
10
|
// configureCache's limits, by their names in JS. Each is set in the app's
|
|
12
11
|
// Info.plist (FastImageMaxDiskSize, ...) or changed at runtime, which is saved
|
|
@@ -405,7 +404,7 @@ RCT_EXPORT_METHOD(writeToCache:(FFFastImageSource *)source
|
|
|
405
404
|
resolve(FFFFailure(@"Can't read the file"));
|
|
406
405
|
return;
|
|
407
406
|
}
|
|
408
|
-
if ([
|
|
407
|
+
if (![FFFastImageSource isImageData:data]) {
|
|
409
408
|
resolve(FFFFailure(@"Not an image"));
|
|
410
409
|
return;
|
|
411
410
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-fast-image",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.31.0",
|
|
4
4
|
"description": "FastImage, performant React Native image component.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -38,7 +38,9 @@
|
|
|
38
38
|
"RNFastImage.podspec"
|
|
39
39
|
],
|
|
40
40
|
"scripts": {
|
|
41
|
-
"build": "
|
|
41
|
+
"build": "bun run build:package && bun run build:docs",
|
|
42
|
+
"build:package": "rm -rf dist && tsc -p tsconfig.build.json && rolldown -c",
|
|
43
|
+
"build:docs": "bun run --cwd website build",
|
|
42
44
|
"lint": "oxlint",
|
|
43
45
|
"release": "semantic-release",
|
|
44
46
|
"test": "bun test",
|