react-native-fast-image 8.31.2 → 9.0.1

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.
Files changed (22) hide show
  1. package/README.md +24 -24
  2. package/RNFastImage.podspec +6 -4
  3. package/android/build.gradle +16 -24
  4. package/android/consumer-rules.pro +4 -15
  5. package/android/src/main/java/com/dylanvann/fastimage/FastImageAnimatable.java +1 -1
  6. package/android/src/{glide-4.15 → main}/java/com/dylanvann/fastimage/FastImageAnimated.java +0 -3
  7. package/android/src/main/java/com/dylanvann/fastimage/FastImageApng.java +139 -24
  8. package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +1 -22
  9. package/android/src/main/java/com/dylanvann/fastimage/FastImageEvent.java +3 -14
  10. package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +6 -35
  11. package/android/src/main/java/com/dylanvann/fastimage/FastImageOkHttpProgressGlideModule.java +1 -1
  12. package/android/src/main/java/com/dylanvann/fastimage/FastImageSvg.java +6 -6
  13. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +1 -7
  14. package/android/src/main/java/com/dylanvann/fastimage/FastImageViewWithUrl.java +1 -1
  15. package/dist/index.d.ts +10 -9
  16. package/ios/FastImage/FFFastImageSource.h +1 -2
  17. package/ios/FastImage/FFFastImageSource.m +28 -74
  18. package/package.json +2 -6
  19. package/android/src/glide-older/java/com/dylanvann/fastimage/FastImageAnimated.java +0 -36
  20. package/android/src/main/java/com/dylanvann/fastimage/FastImageApngRenderer.java +0 -152
  21. package/ios/FastImage.xcodeproj/project.pbxproj +0 -479
  22. package/ios/FastImage.xcodeproj/xcshareddata/xcschemes/FastImage-tvOS.xcscheme +0 -80
@@ -8,7 +8,6 @@ import android.app.Activity;
8
8
  import android.content.Context;
9
9
  import android.content.ContextWrapper;
10
10
  import android.graphics.PorterDuff;
11
- import android.os.Build;
12
11
 
13
12
  import androidx.annotation.NonNull;
14
13
 
@@ -240,12 +239,7 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
240
239
  }
241
240
 
242
241
  private static boolean isActivityDestroyed(Activity activity) {
243
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
244
- return activity.isDestroyed() || activity.isFinishing();
245
- } else {
246
- return activity.isFinishing() || activity.isChangingConfigurations();
247
- }
248
-
242
+ return activity.isDestroyed() || activity.isFinishing();
249
243
  }
250
244
 
251
245
  // Legacy architecture only; the New Architecture doesn't use shadow nodes.
@@ -443,7 +443,7 @@ class FastImageViewWithUrl extends AppCompatImageView {
443
443
  }
444
444
  }
445
445
  if (resource instanceof FastImageAnimatable) {
446
- // An APNG: each view's is its own (FastImageApngRenderer).
446
+ // An APNG: each view's is its own (FastImageApng).
447
447
  FastImageAnimatable animatable = (FastImageAnimatable) resource;
448
448
  animatable.setLoopCount(mLoopCount, false);
449
449
  // The target starts it; paused, it stays on its first frame.
package/dist/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
- import { StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
2
+ import { ColorValue, StyleProp, ViewStyle, ImageRequireSource, AccessibilityProps, ViewProps } from 'react-native';
3
3
  import { cacheControl, priority, resizeMode } from './constants';
4
- type ColorValue = NonNullable<ViewStyle['backgroundColor']>;
5
4
  /** How the image fits the view: see [`resizeMode`](#resizemode). */
6
5
  export type ResizeMode = 'contain' | 'cover' | 'stretch' | 'center' | 'repeat';
7
6
  /** An image's load priority: see [`source.priority`](#sourcepriority). */
@@ -15,9 +14,9 @@ export type Source = {
15
14
  *
16
15
  * Also loads local files (`file://`, and on Android `content://`), images
17
16
  * in the app by name (e.g. `'my_image'`: in its asset catalog on iOS, a
18
- * drawable on Android), photo library images on iOS (`ph://`, see
19
- * [Photo library images](#photo-library-images-ios)) and SVG images (see
20
- * [SVG images](docs/formats.md#svg-images)).
17
+ * drawable on Android), photo library images on iOS and tvOS (`ph://`,
18
+ * see [Photo library images](#photo-library-images-ios-and-tvos)) and SVG
19
+ * images (see [SVG images](docs/formats.md#svg-images)).
21
20
  */
22
21
  uri?: string;
23
22
  /** Headers to load the image with. e.g. `{ Authorization: 'someAuthToken' }`. */
@@ -221,8 +220,8 @@ export interface OnProgressEvent {
221
220
  };
222
221
  }
223
222
  export interface ImageStyle extends ViewStyle {
224
- overlayColor?: ViewStyle['backgroundColor'];
225
- tintColor?: ViewStyle['backgroundColor'];
223
+ overlayColor?: ColorValue;
224
+ tintColor?: ColorValue;
226
225
  }
227
226
  export interface FastImageProps extends AccessibilityProps, ViewProps {
228
227
  /**
@@ -493,8 +492,10 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
493
492
  */
494
493
  tintColor?: ColorValue;
495
494
  /**
496
- * Render children within the image. In the next major version,
497
- * `FastImage` won't render children: use `FastImageBackground`.
495
+ * Render children within the image.
496
+ *
497
+ * @deprecated In the next major version, `FastImage` won't render
498
+ * children: use `FastImageBackground`.
498
499
  */
499
500
  children?: React.ReactNode;
500
501
  }
@@ -64,8 +64,7 @@ typedef NS_ENUM(NSInteger, FFFCacheControl) {
64
64
  // which is decoded at about the view's size.
65
65
  - (BOOL)isPhotoLibrary;
66
66
 
67
- // The error's message, or for an SVG image that couldn't be decoded because
68
- // the app doesn't have SDWebImageSVGCoder, what to add.
67
+ // The error's message, for onError.
69
68
  - (NSString *)errorMessage:(NSError *)error;
70
69
 
71
70
  // A photo library image's own size in pixels (for onLoad when it comes from
@@ -9,7 +9,9 @@
9
9
  #import <SDWebImage/SDImageAWebPCoder.h>
10
10
  #import "FFFAnimatedWebPCoder.h"
11
11
  #import "FFFAnimatedAVIFCoder.h"
12
- #import <objc/message.h>
12
+ #import <SDWebImageSVGCoder/SDImageSVGCoder.h>
13
+ #import <SDWebImagePhotosPlugin/SDImagePhotosLoader.h>
14
+ #import <Photos/Photos.h>
13
15
  #import <ImageIO/ImageIO.h>
14
16
 
15
17
  // In FFFastImageView.m.
@@ -18,24 +20,16 @@ FOUNDATION_EXTERN NSString *FFFErrorMessage(NSError *error);
18
20
  static NSUInteger const FFFWebCacheSize = 50 * 1024 * 1024;
19
21
 
20
22
  // The photo's own size in pixels, looked up by its identifier (the url after
21
- // ph://), or zero. Through the runtime, as the Photos framework is only there
22
- // with SDWebImagePhotosPlugin (on iOS, always). Kept once looked up.
23
+ // ph://), or zero.
23
24
  static CGSize FFFLookUpPhotoPixelSize(NSURL *url)
24
25
  {
25
- Class assetClass = NSClassFromString(@"PHAsset");
26
- SEL fetch = NSSelectorFromString(@"fetchAssetsWithLocalIdentifiers:options:");
27
26
  NSString *prefix = @"ph://";
28
27
  NSString *string = url.absoluteString;
29
- if (!assetClass || ![assetClass respondsToSelector:fetch] || ![string hasPrefix:prefix]) {
28
+ if (![string hasPrefix:prefix]) {
30
29
  return CGSizeZero;
31
30
  }
32
- id (*fetchAssets)(id, SEL, NSArray *, id) = (void *)objc_msgSend;
33
- id result = fetchAssets(assetClass, fetch, @[[string substringFromIndex:prefix.length]], nil);
34
- id asset = [result respondsToSelector:@selector(firstObject)] ? [result firstObject] : nil;
35
- if (!asset) {
36
- return CGSizeZero;
37
- }
38
- return CGSizeMake([[asset valueForKey:@"pixelWidth"] doubleValue], [[asset valueForKey:@"pixelHeight"] doubleValue]);
31
+ PHAsset *asset = [PHAsset fetchAssetsWithLocalIdentifiers:@[[string substringFromIndex:prefix.length]] options:nil].firstObject;
32
+ return asset ? CGSizeMake(asset.pixelWidth, asset.pixelHeight) : CGSizeZero;
39
33
  }
40
34
 
41
35
  static NSCache<NSString *, NSValue *> *FFFPhotoSizes(void)
@@ -132,13 +126,11 @@ static NSString *FFFBundleAssetName(NSURL *url)
132
126
  @end
133
127
 
134
128
  // Loads photo library images (ph://<localIdentifier>) with
135
- // SDWebImagePhotosPlugin's loader: a dependency on iOS (the podspec), found
136
- // at runtime so that tvOS builds without it (an app can add it there). Its
137
- // own instance, so the app's use of the plugin is left as it is. Without the
138
- // plugin, or for a source it can't load (assets-library://), the load fails
139
- // with `failure`.
129
+ // SDWebImagePhotosPlugin's loader, its own instance, so the app's use of the
130
+ // plugin is left as it is. For a source it can't load (assets-library://),
131
+ // the load fails with `failure`.
140
132
  @interface FFFPhotosLoader : NSObject <SDImageLoader>
141
- @property (nonatomic, strong) id<SDImageLoader> plugin;
133
+ @property (nonatomic, strong) SDImagePhotosLoader *plugin;
142
134
  @property (nonatomic, copy) NSString *failure;
143
135
  @end
144
136
 
@@ -150,24 +142,12 @@ static NSString *FFFBundleAssetName(NSURL *url)
150
142
  static dispatch_once_t once;
151
143
  dispatch_once(&once, ^{
152
144
  loader = [FFFPhotosLoader new];
153
- Class pluginClass = NSClassFromString(@"SDImagePhotosLoader");
154
- id<SDImageLoader> plugin = pluginClass ? [pluginClass new] : nil;
155
- if ([plugin conformsToProtocol:@protocol(SDImageLoader)]) {
156
- // One image per load, at full quality. By default the plugin
157
- // first sends a quick low quality one, which a view would show
158
- // (and fade in) before the one it asked for.
159
- // PHImageRequestOptionsDeliveryModeHighQualityFormat. Skipped if
160
- // a later version of the plugin renames these.
161
- NSObject *requestOptions = [(NSObject *)plugin respondsToSelector:NSSelectorFromString(@"imageRequestOptions")]
162
- ? [(NSObject *)plugin valueForKey:@"imageRequestOptions"]
163
- : nil;
164
- if ([requestOptions respondsToSelector:NSSelectorFromString(@"setDeliveryMode:")]) {
165
- [requestOptions setValue:@1 forKey:@"deliveryMode"];
166
- }
167
- loader.plugin = plugin;
168
- } else {
169
- loader.failure = @"Photo library images (ph://) need SDWebImagePhotosPlugin: add pod 'SDWebImagePhotosPlugin' to the app's Podfile";
170
- }
145
+ SDImagePhotosLoader *plugin = [SDImagePhotosLoader new];
146
+ // One image per load, at full quality. By default the plugin first
147
+ // sends a quick low quality one, which a view would show (and fade
148
+ // in) before the one it asked for.
149
+ plugin.imageRequestOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
150
+ loader.plugin = plugin;
171
151
  });
172
152
  return loader;
173
153
  }
@@ -304,10 +284,6 @@ static BOOL FFFLooksLikeSVG(NSData *data)
304
284
 
305
285
  @end
306
286
 
307
- // SDWebImageSVGCoder's coder, when the app has the pod (SVG images): found at
308
- // runtime, so FastImage doesn't depend on it. Nil otherwise.
309
- static id<SDImageCoder> FFFSVGCoder;
310
-
311
287
  @implementation FFFastImageSource
312
288
 
313
289
  + (void)initialize
@@ -345,28 +321,17 @@ static id<SDImageCoder> FFFSVGCoder;
345
321
  [SDImageCodersManager.sharedManager addCoder:FFFAnimatedAVIFCoder.sharedCoder];
346
322
  }
347
323
 
348
- // SVG: registered with SDWebImage's coders, as the pod's setup does (unless
349
- // the app has already): SDWebImage then picks it for SVG data, including
350
- // for FastImage's animated and downsampled image classes. Giving the
351
- // coder per load instead (SDWebImageContextImageCoder) would make it the
352
- // only coder for everything in that load. So the app's own SDWebImage
353
- // use can decode SVG too, which is what the pod is for.
354
- Class coderClass = NSClassFromString(@"SDImageSVGCoder");
355
- SEL shared = NSSelectorFromString(@"sharedCoder");
356
- if (!coderClass || ![coderClass respondsToSelector:shared]) {
357
- return;
358
- }
359
- id coder = ((id (*)(id, SEL))objc_msgSend)(coderClass, shared);
360
- if (![coder conformsToProtocol:@protocol(SDImageCoder)]) {
361
- return;
362
- }
363
- FFFSVGCoder = coder;
324
+ // SVG (SDWebImageSVGCoder): registered with SDWebImage's coders, as the
325
+ // pod's setup does (unless the app has already): SDWebImage then picks it
326
+ // for SVG data, including for FastImage's animated and downsampled image
327
+ // classes. Giving the coder per load instead (SDWebImageContextImageCoder)
328
+ // would make it the only coder for everything in that load.
364
329
  for (id<SDImageCoder> registered in SDImageCodersManager.sharedManager.coders) {
365
- if ([registered isKindOfClass:coderClass]) {
330
+ if ([registered isKindOfClass:[SDImageSVGCoder class]]) {
366
331
  return;
367
332
  }
368
333
  }
369
- [SDImageCodersManager.sharedManager addCoder:coder];
334
+ [SDImageCodersManager.sharedManager addCoder:SDImageSVGCoder.sharedCoder];
370
335
  }
371
336
 
372
337
  - (instancetype)initWithURL:(NSURL *)url
@@ -412,10 +377,6 @@ static id<SDImageCoder> FFFSVGCoder;
412
377
 
413
378
  - (NSString *)errorMessage:(NSError *)error
414
379
  {
415
- if (!FFFSVGCoder && [error.domain isEqualToString:SDWebImageErrorDomain] && error.code == SDWebImageErrorBadImageData &&
416
- [_url.path.lowercaseString hasSuffix:@".svg"]) {
417
- return @"SVG images need SDWebImageSVGCoder: add pod 'SDWebImageSVGCoder' to the app's Podfile";
418
- }
419
380
  return FFFErrorMessage(error);
420
381
  }
421
382
 
@@ -508,17 +469,10 @@ static id<SDImageCoder> FFFSVGCoder;
508
469
  static dispatch_once_t once;
509
470
  dispatch_once(&once, ^{
510
471
  NSString *name = @"fast-image-http-cache";
511
- if (@available(iOS 13.0, tvOS 13.0, *)) {
512
- NSURL *caches = [NSFileManager.defaultManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask].firstObject;
513
- cache = [[NSURLCache alloc] initWithMemoryCapacity:0
514
- diskCapacity:FFFWebCacheSize
515
- directoryURL:[caches URLByAppendingPathComponent:name]];
516
- } else {
517
- // In the app's caches directory.
518
- cache = [[NSURLCache alloc] initWithMemoryCapacity:0
519
- diskCapacity:FFFWebCacheSize
520
- diskPath:name];
521
- }
472
+ NSURL *caches = [NSFileManager.defaultManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask].firstObject;
473
+ cache = [[NSURLCache alloc] initWithMemoryCapacity:0
474
+ diskCapacity:FFFWebCacheSize
475
+ directoryURL:[caches URLByAppendingPathComponent:name]];
522
476
  });
523
477
  return cache;
524
478
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "8.31.2",
3
+ "version": "9.0.1",
4
4
  "description": "FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",
@@ -28,11 +28,7 @@
28
28
  "android/build.gradle",
29
29
  "android/consumer-rules.pro",
30
30
  "android/src/main",
31
- "android/src/glide-4.15",
32
- "android/src/glide-older",
33
31
  "ios/FastImage",
34
- "ios/FastImage.xcodeproj/project.pbxproj",
35
- "ios/FastImage.xcodeproj/xcshareddata",
36
32
  "app.plugin.js",
37
33
  "dist",
38
34
  "RNFastImage.podspec"
@@ -73,6 +69,6 @@
73
69
  },
74
70
  "peerDependencies": {
75
71
  "react": "^17 || ^18 || ^19",
76
- "react-native": ">=0.60.0"
72
+ "react-native": "*"
77
73
  }
78
74
  }
@@ -1,36 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- import android.content.Context;
4
- import android.graphics.drawable.Drawable;
5
-
6
- import androidx.annotation.NonNull;
7
- import androidx.annotation.Nullable;
8
-
9
- import com.bumptech.glide.Glide;
10
- import com.bumptech.glide.Registry;
11
-
12
- // Built instead of src/glide-4.15's FastImageAnimated with a Glide before
13
- // 4.15 (build.gradle), which has no decoder for animated WebP or AVIF: they
14
- // show their first frame, so there's nothing to register, remember or copy.
15
- final class FastImageAnimated {
16
- private FastImageAnimated() {}
17
-
18
- interface CopyCallback {
19
- void onCopy(@Nullable Drawable copy);
20
- }
21
-
22
- static void register(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {}
23
-
24
- @Nullable
25
- static Integer repeatCount(@NonNull Drawable drawable) {
26
- return null;
27
- }
28
-
29
- static boolean shownElsewhere(@NonNull Drawable drawable, @NonNull Drawable.Callback view) {
30
- return false;
31
- }
32
-
33
- static void copy(@NonNull Drawable drawable, @NonNull CopyCallback callback) {
34
- callback.onCopy(null);
35
- }
36
- }
@@ -1,152 +0,0 @@
1
- package com.dylanvann.fastimage;
2
-
3
- import android.graphics.Rect;
4
- import android.graphics.drawable.Drawable;
5
-
6
- import androidx.annotation.NonNull;
7
- import androidx.annotation.Nullable;
8
-
9
- import com.bumptech.glide.load.engine.Resource;
10
- import com.github.penfeizhou.animation.apng.APNGDrawable;
11
- import com.github.penfeizhou.animation.apng.decode.APNGDecoder;
12
- import com.github.penfeizhou.animation.loader.ByteBufferLoader;
13
- import com.github.penfeizhou.animation.loader.Loader;
14
-
15
- import java.nio.ByteBuffer;
16
-
17
- // APNGs with APNG4Android (FastImageApng, which only uses this when it's
18
- // there). Glide keeps the image's data in its memory cache, and each view
19
- // gets a drawable of its own over it, so views don't share playback: making
20
- // one only reads the image's header, and it decodes its frames (at a sample
21
- // size from the size it's drawn at) on APNG4Android's threads as it plays.
22
- final class FastImageApngRenderer {
23
- private FastImageApngRenderer() {
24
- }
25
-
26
- // Throws NoClassDefFoundError without APNG4Android.
27
- static boolean available() {
28
- return APNGDrawable.class.getName() != null;
29
- }
30
-
31
- private static Loader loader(ByteBuffer data) {
32
- return new ByteBufferLoader() {
33
- @Override
34
- public ByteBuffer getByteBuffer() {
35
- ByteBuffer buffer = data.duplicate();
36
- buffer.position(0);
37
- return buffer;
38
- }
39
- };
40
- }
41
-
42
- // Null if APNG4Android can't read it (Glide then decodes it as a PNG).
43
- @Nullable
44
- static Resource<Drawable> decode(@NonNull ByteBuffer data, int plays) {
45
- // Reads the header, waiting for APNG4Android's thread.
46
- Rect bounds = new APNGDecoder(loader(data), null).getBounds();
47
- if (bounds.isEmpty()) return null;
48
- return new ApngResource(data, plays, bounds);
49
- }
50
-
51
- private static final class ApngResource implements Resource<Drawable> {
52
- private final ByteBuffer data;
53
- private final int plays;
54
- private final Rect bounds;
55
-
56
- ApngResource(ByteBuffer data, int plays, Rect bounds) {
57
- this.data = data;
58
- this.plays = plays;
59
- this.bounds = bounds;
60
- }
61
-
62
- @NonNull
63
- @Override
64
- public Class<Drawable> getResourceClass() {
65
- return Drawable.class;
66
- }
67
-
68
- @NonNull
69
- @Override
70
- public Drawable get() {
71
- return new ApngDrawable(loader(data), plays);
72
- }
73
-
74
- // The data, and a frame at full size (each view's drawable has its
75
- // own, at the size it's drawn at).
76
- @Override
77
- public int getSize() {
78
- return data.limit() + bounds.width() * bounds.height() * 4;
79
- }
80
-
81
- @Override
82
- public void recycle() {
83
- }
84
- }
85
-
86
- private static final class ApngDrawable extends APNGDrawable implements FastImageAnimatable {
87
- // How many times the file says it plays (0: forever).
88
- private final int plays;
89
- private volatile boolean paused = false;
90
- // Once a frame has been rendered (on the decoder's thread).
91
- private volatile boolean rendered = false;
92
-
93
- ApngDrawable(Loader loader, int plays) {
94
- super(loader);
95
- this.plays = plays;
96
- }
97
-
98
- @Override
99
- public void setLoopCount(int loopCount, boolean restart) {
100
- // Plays (0: forever), as APNG4Android counts them.
101
- setLoopLimit(loopCount == -1 ? plays : loopCount);
102
- if (restart) {
103
- reset();
104
- start();
105
- }
106
- }
107
-
108
- @Override
109
- public void setPaused(boolean paused) {
110
- this.paused = paused;
111
- // It starts and stops itself as it's shown and hidden, but not
112
- // while paused.
113
- setAutoPlay(!paused);
114
- if (paused) {
115
- // Before the first frame, onStart pauses once it's rendered:
116
- // the decoder's start clears a pause, then skips rendering if
117
- // one came in meanwhile, which left the view blank.
118
- if (rendered) pause();
119
- } else {
120
- resume();
121
- }
122
- }
123
-
124
- // From its first frame. The target starts it right after
125
- // setVisible(true) has (auto play): APNG4Android's start() stops and
126
- // starts a running decoder, and a stop made while the decoder is
127
- // still starting can win over that start, leaving it stopped on its
128
- // first frame. A running one is reset to its first frame instead.
129
- @Override
130
- public void start() {
131
- if (isRunning()) {
132
- getFrameSeqDecoder().reset();
133
- return;
134
- }
135
- super.start();
136
- }
137
-
138
- @Override
139
- public void onRender(ByteBuffer byteBuffer) {
140
- super.onRender(byteBuffer);
141
- rendered = true;
142
- }
143
-
144
- // On its decoder thread, once starting has drawn the first frame:
145
- // starting clears a pause made before it got there, so pause again.
146
- @Override
147
- public void onStart() {
148
- super.onStart();
149
- if (paused) getFrameSeqDecoder().pause();
150
- }
151
- }
152
- }