react-native-fast-image 8.30.1 → 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.
@@ -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
- SDImageFormat format = [NSData sd_imageFormatForImageData:proposedResponse.data];
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 ([NSData sd_imageFormatForImageData:data] == SDImageFormatUndefined) {
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.30.1",
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": "rm -rf dist && tsc -p tsconfig.build.json && rolldown -c && for f in index index.cjs index.web index.cjs.web; do cp src/index.js.flow dist/$f.js.flow; done",
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",