react-native-fast-image 9.3.5 → 9.3.6

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 CHANGED
@@ -311,7 +311,7 @@ Changing it restarts the animation.
311
311
  How the image is filtered when it's drawn smaller or larger than its size (like CSS's `image-rendering`):
312
312
 
313
313
  - `'auto'`: the platform's usual filtering.
314
- - `'smooth'`: iOS only. Keeps a large image drawn much smaller than its size (e.g. a big photo as a thumbnail, or fine lines and text) from looking jagged or noisy. Such an image is already decoded at about the view's size by default (see `downsample`), so this is for images shown at less than half their size with `downsample={false}`, or a little smaller than their size. **It uses more memory:** the image is also kept at smaller sizes for drawing, about a third more than the decoded image. On Android it's the same as `'auto'` (images are always decoded at about the view's size there).
314
+ - `'smooth'`: iOS only. Keeps a large image drawn much smaller than its size (e.g. a big photo as a thumbnail, or fine lines and text) from looking jagged or noisy. Such an image is already decoded at about the view's size by default (see `downsample`), so this is for images shown at less than half their size with `downsample={false}`, or a little smaller than their size. **It uses more memory:** the image is also kept at smaller sizes for drawing, about a third more than the decoded image. On Android it's the same as `'auto'`.
315
315
  - `'pixelated'`: sharp pixels, without smoothing, e.g. for pixel art drawn larger than its size. On Android, animated images are still smoothed.
316
316
 
317
317
  ---
@@ -342,14 +342,14 @@ Downloads, local files (`file://`, `content://`) and bundled images (`require()`
342
342
 
343
343
  #### `downsample`
344
344
 
345
- **Type:** `boolean` · **Default:** `true` · iOS only
345
+ **Type:** `boolean` · **Default:** `true`
346
346
 
347
347
  Decodes a large image at about the size it's shown at, instead of at full size, so it takes much less memory.
348
348
 
349
- - `true`: an image at least twice the size its view needs is decoded at about the view's size. If the view grows, the image is decoded again for its new size (from the disk cache).
350
- - `false`: images are decoded at full size, e.g. for an image that's zoomed in on with a transform (a pinch-to-zoom viewer), which would otherwise show the smaller copy enlarged.
349
+ - `true`: an image at least twice the size its view needs is decoded at about the view's size (on Android, any image larger than its view). If the view grows, the image is decoded again for its new size (from the disk cache).
350
+ - `false`: images are decoded at full size, e.g. for an image that's zoomed in on with a transform (a pinch-to-zoom viewer), which would otherwise show the smaller copy enlarged. **It uses more memory:** a 12-megapixel photo takes about 48 MB decoded, however small it's shown. On Android, an image over 16 megapixels (4096 × 4096) is decoded at the largest size under that, which Android can still draw.
351
351
 
352
- It doesn't change `onLoad`'s width and height (the image's own size) or the cached file. Needs SDWebImage 5.19.7 or later: before 5.19 images are decoded at full size, and 5.19.0 to 5.19.6 show photos stored sideways with an EXIF orientation (most phone photos) sideways. Photo library images are always decoded this way, and on Android images are always decoded at about the view's size (but Android 16 and later decode animated WebP at full size, and scale it as they draw it).
352
+ It doesn't change `onLoad`'s width and height (the image's own size) or the cached file. On iOS it needs SDWebImage 5.19.7 or later: before 5.19 images are decoded at full size, and 5.19.0 to 5.19.6 show photos stored sideways with an EXIF orientation (most phone photos) sideways. Photo library images are always decoded at about the view's size. Android 16 and later decode animated WebP at full size either way, and scale it as they draw it.
353
353
 
354
354
  If you control the images, serve them at the size they're shown (resized on your server or by an image CDN), which also saves bandwidth.
355
355
 
@@ -34,8 +34,10 @@ final class FastImageGif {
34
34
  private FastImageGif() {}
35
35
 
36
36
  // Null if the GIF can't be decoded again (then the view shows Glide's).
37
+ // Full size (downsample false): at the GIF's own size, or sampled down to
38
+ // at most FastImageSourceSize.MAX_PIXELS pixels.
37
39
  @Nullable
38
- static GifDrawable copy(Context context, GifDrawable gif, int width, int height) {
40
+ static GifDrawable copy(Context context, GifDrawable gif, int width, int height, boolean fullSize) {
39
41
  if (width <= 0 || height <= 0) return null;
40
42
  ByteBuffer data = gif.getBuffer();
41
43
  GifHeader header = new GifHeaderParser().setData(data).parseHeader();
@@ -44,7 +46,7 @@ final class FastImageGif {
44
46
  BitmapPool bitmapPool = glide.getBitmapPool();
45
47
  GifDecoder decoder = new StandardGifDecoder(
46
48
  new GifBitmapProvider(bitmapPool, glide.getArrayPool()),
47
- header, gif.getBuffer(), sampleSize(header, width, height));
49
+ header, gif.getBuffer(), fullSize ? fullSizeSampleSize(header) : sampleSize(header, width, height));
48
50
  decoder.setDefaultBitmapConfig(Bitmap.Config.ARGB_8888);
49
51
  decoder.advance();
50
52
  Bitmap firstFrame = decoder.getNextFrame();
@@ -101,6 +103,15 @@ final class FastImageGif {
101
103
  }
102
104
  }
103
105
 
106
+ private static int fullSizeSampleSize(GifHeader header) {
107
+ int sampleSize = 1;
108
+ while ((float) (header.getWidth() / sampleSize) * (header.getHeight() / sampleSize)
109
+ > FastImageSourceSize.MAX_PIXELS) {
110
+ sampleSize *= 2;
111
+ }
112
+ return sampleSize;
113
+ }
114
+
104
115
  // ByteBufferGifDecoder's.
105
116
  private static int sampleSize(GifHeader header, int width, int height) {
106
117
  int exact = Math.min(header.getHeight() / height, header.getWidth() / width);
@@ -49,50 +49,72 @@ final class FastImageSourceSize {
49
49
  // options, so the size is recorded by image rather than by request.
50
50
  static final class Capture extends DownsampleStrategy {
51
51
  private final DownsampleStrategy strategy;
52
+ // What an SVG is drawn at (see FastImageSvgRenderer): the strategy for
53
+ // the view's scale type, also when images are decoded at full size
54
+ // (downsample false). An SVG has no size of its own to decode at, and
55
+ // drawn at the view's size it's already sharp.
56
+ private final DownsampleStrategy vectorStrategy;
52
57
  private final String key;
53
58
  private boolean recorded;
54
59
 
55
60
  Capture(DownsampleStrategy strategy, String key) {
61
+ this(strategy, strategy, key);
62
+ }
63
+
64
+ Capture(DownsampleStrategy strategy, DownsampleStrategy vectorStrategy, String key) {
56
65
  this.strategy = strategy;
66
+ this.vectorStrategy = vectorStrategy;
57
67
  this.key = key;
58
68
  }
59
69
 
60
- @Override
61
- public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
70
+ private void record(int sourceWidth, int sourceHeight) {
62
71
  // Called again with the sampled size; the first call has the image's.
63
72
  if (!recorded) {
64
73
  recorded = true;
65
74
  SIZES.put(key, new int[]{sourceWidth, sourceHeight});
66
75
  }
76
+ }
77
+
78
+ @Override
79
+ public float getScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
80
+ record(sourceWidth, sourceHeight);
67
81
  return strategy.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
68
82
  }
69
83
 
84
+ float getVectorScaleFactor(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
85
+ record(sourceWidth, sourceHeight);
86
+ return vectorStrategy.getScaleFactor(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
87
+ }
88
+
70
89
  @Override
71
90
  public SampleSizeRounding getSampleSizeRounding(int sourceWidth, int sourceHeight, int requestedWidth, int requestedHeight) {
72
91
  return strategy.getSampleSizeRounding(sourceWidth, sourceHeight, requestedWidth, requestedHeight);
73
92
  }
74
93
 
75
94
  // Glide's memory cache keys include the strategy, so match the wrapped
76
- // one to keep sharing cached images between views.
95
+ // ones to keep sharing cached images between views.
77
96
  @Override
78
97
  public boolean equals(Object o) {
79
- return o instanceof Capture && ((Capture) o).strategy.equals(strategy);
98
+ return o instanceof Capture && ((Capture) o).strategy.equals(strategy)
99
+ && ((Capture) o).vectorStrategy.equals(vectorStrategy);
80
100
  }
81
101
 
82
102
  @Override
83
103
  public int hashCode() {
84
- return strategy.hashCode();
104
+ return 31 * strategy.hashCode() + vectorStrategy.hashCode();
85
105
  }
86
106
  }
87
107
 
88
108
  // What RequestBuilder.into(ImageView) applies for the view's scale type
89
109
  // (it leaves a transformation that's already set alone), with the
90
110
  // downsample strategy it implies wrapped by capture, and then blur if it
91
- // isn't null.
92
- static RequestOptions scaleTypeOptions(@Nullable ImageView.ScaleType scaleType, Capture capture, @Nullable FastImageBlur blur) {
111
+ // isn't null. Not downsampled (downsample false), the image isn't cropped
112
+ // or fitted to the view's size either: the view scales it as it draws it.
113
+ static RequestOptions scaleTypeOptions(@Nullable ImageView.ScaleType scaleType, Capture capture, @Nullable FastImageBlur blur,
114
+ boolean downsample) {
93
115
  RequestOptions options = new RequestOptions();
94
116
  BitmapTransformation transformation = null;
95
- if (scaleType != null) {
117
+ if (scaleType != null && downsample) {
96
118
  switch (scaleType) {
97
119
  case CENTER_CROP:
98
120
  options = options.optionalCenterCrop();
@@ -121,8 +143,9 @@ final class FastImageSourceSize {
121
143
  return options.downsample(capture);
122
144
  }
123
145
 
124
- static Capture capture(@Nullable ImageView.ScaleType scaleType, Object model) {
125
- return new Capture(strategy(scaleType), String.valueOf(model));
146
+ static Capture capture(@Nullable ImageView.ScaleType scaleType, Object model, boolean downsample) {
147
+ DownsampleStrategy strategy = strategy(scaleType);
148
+ return new Capture(downsample ? strategy : FULL_SIZE, strategy, String.valueOf(model));
126
149
  }
127
150
 
128
151
  private static DownsampleStrategy strategy(@Nullable ImageView.ScaleType scaleType) {
@@ -145,11 +168,12 @@ final class FastImageSourceSize {
145
168
  }
146
169
 
147
170
  // At the image's own size (objectFit none shows it at that size, whatever
148
- // the view's), or scaled down to at most MAX_PIXELS pixels, which a
149
- // bitmap can be drawn at (Android won't draw one over 100 MB).
171
+ // the view's, and downsample false decodes every image this way), or
172
+ // scaled down to at most MAX_PIXELS pixels, which a bitmap can be drawn at
173
+ // (Android won't draw one over 100 MB).
150
174
  static final DownsampleStrategy FULL_SIZE = new FullSize();
151
175
 
152
- private static final float MAX_PIXELS = 4096f * 4096f;
176
+ static final float MAX_PIXELS = 4096f * 4096f;
153
177
 
154
178
  private static final class FullSize extends DownsampleStrategy {
155
179
  @Override
@@ -63,13 +63,17 @@ final class FastImageSvgRenderer {
63
63
  int sourceHeight = Math.max(1, Math.round(ownHeight));
64
64
 
65
65
  // The strategy also records the SVG's own size for onLoad
66
- // (FastImageSourceSize.Capture).
66
+ // (FastImageSourceSize.Capture), and has the one for the view when
67
+ // images are decoded at full size (downsample false).
67
68
  DownsampleStrategy strategy = options.get(DownsampleStrategy.OPTION);
68
69
  int targetWidth = width > 0 ? width : sourceWidth;
69
70
  int targetHeight = height > 0 ? height : sourceHeight;
70
- float scale = strategy != null
71
- ? strategy.getScaleFactor(sourceWidth, sourceHeight, targetWidth, targetHeight)
72
- : 1f;
71
+ float scale = strategy instanceof FastImageSourceSize.Capture
72
+ ? ((FastImageSourceSize.Capture) strategy).getVectorScaleFactor(
73
+ sourceWidth, sourceHeight, targetWidth, targetHeight)
74
+ : strategy != null
75
+ ? strategy.getScaleFactor(sourceWidth, sourceHeight, targetWidth, targetHeight)
76
+ : 1f;
73
77
  // objectFit scale-down and none: at its own size in dp (see
74
78
  // FastImageSvg.DENSITY), and for scale-down no larger than the view.
75
79
  Float density = options.get(FastImageSvg.DENSITY);
@@ -147,6 +147,11 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> {
147
147
  view.setImageRendering(imageRendering);
148
148
  }
149
149
 
150
+ @ReactProp(name = "downsample", defaultBoolean = true)
151
+ public void setDownsample(FastImageViewWithUrl view, boolean downsample) {
152
+ view.setDownsample(downsample);
153
+ }
154
+
150
155
  @ReactProp(name = "blurRadius")
151
156
  public void setBlurRadius(FastImageViewWithUrl view, float blurRadius) {
152
157
  view.setBlurRadius(PixelUtil.toPixelFromDIP(blurRadius));
@@ -659,7 +659,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
659
659
  }
660
660
  if (resource instanceof GifDrawable) {
661
661
  GifDrawable own = FastImageGif.copy(
662
- getContext(), (GifDrawable) resource, mLoadingWidth, mLoadingHeight);
662
+ getContext(), (GifDrawable) resource, mLoadingWidth, mLoadingHeight, !mDownsample);
663
663
  if (own != null) {
664
664
  applyLoopCount(own);
665
665
  super.onResourceReady(own, transition);
@@ -918,9 +918,9 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
918
918
 
919
919
  // imageRendering="pixelated": no resampling by Glide, and drawn without
920
920
  // filtering (sharp pixels). "smooth" is iOS only; here it's the same as
921
- // "auto": Glide already decodes a large image at about the view's size, and
922
- // averaging it properly would need a full-size decode. Part of the
923
- // request, so a change reloads.
921
+ // "auto": Glide decodes a large image at about the view's size (unless
922
+ // downsample is false), and averaging it properly would need mipmaps or
923
+ // a full-size decode. Part of the request, so a change reloads.
924
924
  private boolean mPixelated = false;
925
925
 
926
926
  public void setImageRendering(@Nullable String imageRendering) {
@@ -930,6 +930,18 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
930
930
  mNeedsReload = true;
931
931
  }
932
932
 
933
+ // downsample false: images are decoded at their own size (capped, see
934
+ // FastImageSourceSize.FULL_SIZE), not cropped or fitted to the view's
935
+ // size, so a view zoomed in on with a transform shows the image's detail.
936
+ // Part of the request, so a change reloads.
937
+ private boolean mDownsample = true;
938
+
939
+ public void setDownsample(boolean downsample) {
940
+ if (downsample == mDownsample) return;
941
+ mDownsample = downsample;
942
+ mNeedsReload = true;
943
+ }
944
+
933
945
  // blurRadius, in pixels (0 is no blur). Part of the request (see
934
946
  // reblur for a change).
935
947
  private float mBlurRadius = 0;
@@ -968,8 +980,8 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
968
980
  .dontTransform();
969
981
  if (blur != null) options = options.optionalTransform(blur);
970
982
  } else {
971
- options = FastImageSourceSize.scaleTypeOptions(
972
- mResizeScaleType, FastImageSourceSize.capture(mResizeScaleType, model), blur);
983
+ options = FastImageSourceSize.scaleTypeOptions(mResizeScaleType,
984
+ FastImageSourceSize.capture(mResizeScaleType, model, mDownsample), blur, mDownsample);
973
985
  boolean ownSize = mResizeScaleType == ScaleType.CENTER_INSIDE || mResizeScaleType == ScaleType.CENTER;
974
986
  // scale-down and none show it at its own size in dp: an SVG is
975
987
  // drawn at that size, so it's sharp (not with a blur, which hides
package/dist/index.d.ts CHANGED
@@ -376,8 +376,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
376
376
  * at less than half their size with `downsample={false}`, or a little
377
377
  * smaller than their size. **It uses more memory:** the image is also
378
378
  * kept at smaller sizes for drawing, about a third more than the decoded
379
- * image. On Android it's the same as `'auto'` (images are always decoded
380
- * at about the view's size there).
379
+ * image. On Android it's the same as `'auto'`.
381
380
  * - `'pixelated'`: sharp pixels, without smoothing, e.g. for pixel art
382
381
  * drawn larger than its size. On Android, animated images are still
383
382
  * smoothed.
@@ -416,25 +415,29 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
416
415
  * full size, so it takes much less memory.
417
416
  *
418
417
  * - `true`: an image at least twice the size its view needs is decoded
419
- * at about the view's size. If the view grows, the image is decoded
420
- * again for its new size (from the disk cache).
418
+ * at about the view's size (on Android, any image larger than its
419
+ * view). If the view grows, the image is decoded again for its new size
420
+ * (from the disk cache).
421
421
  * - `false`: images are decoded at full size, e.g. for an image that's
422
422
  * zoomed in on with a transform (a pinch-to-zoom viewer), which would
423
- * otherwise show the smaller copy enlarged.
423
+ * otherwise show the smaller copy enlarged. **It uses more memory:** a
424
+ * 12-megapixel photo takes about 48 MB decoded, however small it's
425
+ * shown. On Android, an image over 16 megapixels (4096 × 4096) is
426
+ * decoded at the largest size under that, which Android can still
427
+ * draw.
424
428
  *
425
429
  * It doesn't change `onLoad`'s width and height (the image's own size) or
426
- * the cached file. Needs SDWebImage 5.19.7 or later: before 5.19 images
427
- * are decoded at full size, and 5.19.0 to 5.19.6 show photos stored
428
- * sideways with an EXIF orientation (most phone photos) sideways. Photo
429
- * library images are always decoded this way, and on Android images are
430
- * always decoded at about the view's size (but Android 16 and later
431
- * decode animated WebP at full size, and scale it as they draw it).
430
+ * the cached file. On iOS it needs SDWebImage 5.19.7 or later: before
431
+ * 5.19 images are decoded at full size, and 5.19.0 to 5.19.6 show photos
432
+ * stored sideways with an EXIF orientation (most phone photos) sideways.
433
+ * Photo library images are always decoded at about the view's size.
434
+ * Android 16 and later decode animated WebP at full size either way, and
435
+ * scale it as they draw it.
432
436
  *
433
437
  * If you control the images, serve them at the size they're shown
434
438
  * (resized on your server or by an image CDN), which also saves
435
439
  * bandwidth.
436
440
  *
437
- * @platform ios
438
441
  * @default true
439
442
  */
440
443
  downsample?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "9.3.5",
3
+ "version": "9.3.6",
4
4
  "description": "FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",