react-native-fast-image 8.10.0 → 8.11.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 CHANGED
@@ -186,6 +186,16 @@ Changing it restarts the animation.
186
186
 
187
187
  ---
188
188
 
189
+ ### `imageRendering?: 'auto' | 'smooth' | 'pixelated'`
190
+
191
+ How the image is filtered when it's drawn smaller or larger than its size (like CSS's `image-rendering`):
192
+
193
+ - `'auto'` **(Default)** - The platform's usual filtering.
194
+ - `'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. **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 already decoded at about the view's size there).
195
+ - `'pixelated'` - Sharp pixels, without smoothing, e.g. for pixel art drawn larger than its size. On Android, animated images are still smoothed.
196
+
197
+ ---
198
+
189
199
  ### `onLoadStart?: () => void`
190
200
 
191
201
  Called when the image starts to load.
@@ -94,6 +94,11 @@ class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> imple
94
94
  view.setLoopCount(loopCount);
95
95
  }
96
96
 
97
+ @ReactProp(name = "imageRendering")
98
+ public void setImageRendering(FastImageViewWithUrl view, @Nullable String imageRendering) {
99
+ view.setImageRendering(imageRendering);
100
+ }
101
+
97
102
  @ReactProp(name = "resizeMode")
98
103
  public void setResizeMode(FastImageViewWithUrl view, String resizeMode) {
99
104
  final FastImageViewWithUrl.ScaleType scaleType = FastImageViewConverter.getScaleType(resizeMode);
@@ -5,6 +5,7 @@ import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_END
5
5
 
6
6
  import android.annotation.SuppressLint;
7
7
  import android.content.Context;
8
+ import android.graphics.drawable.BitmapDrawable;
8
9
  import android.graphics.drawable.Drawable;
9
10
 
10
11
  import androidx.annotation.NonNull;
@@ -15,8 +16,10 @@ import androidx.core.view.ViewCompat;
15
16
  import com.bumptech.glide.RequestBuilder;
16
17
  import com.bumptech.glide.RequestManager;
17
18
  import com.bumptech.glide.load.model.GlideUrl;
19
+ import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
18
20
  import com.bumptech.glide.load.resource.gif.GifDrawable;
19
21
  import com.bumptech.glide.request.Request;
22
+ import com.bumptech.glide.request.RequestOptions;
20
23
  import com.bumptech.glide.request.target.DrawableImageViewTarget;
21
24
  import com.bumptech.glide.request.target.SizeReadyCallback;
22
25
  import com.bumptech.glide.request.transition.Transition;
@@ -95,6 +98,13 @@ class FastImageViewWithUrl extends AppCompatImageView {
95
98
 
96
99
  @Override
97
100
  public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
101
+ if (resource instanceof BitmapDrawable) {
102
+ if (mPixelated) {
103
+ // Scaled by the view without filtering: sharp pixels.
104
+ resource = resource.mutate();
105
+ resource.setFilterBitmap(false);
106
+ }
107
+ }
98
108
  if (resource instanceof GifDrawable) {
99
109
  GifDrawable own = FastImageGif.copy(
100
110
  getContext(), (GifDrawable) resource, mLoadingWidth, mLoadingHeight);
@@ -134,6 +144,33 @@ class FastImageViewWithUrl extends AppCompatImageView {
134
144
  mNeedsReload = true;
135
145
  }
136
146
 
147
+ // imageRendering="pixelated": no resampling by Glide, and drawn without
148
+ // filtering (sharp pixels). "smooth" is iOS only; here it's the same as
149
+ // "auto": Glide already decodes a large image at about the view's size, and
150
+ // averaging it properly would need a full-size decode. Part of the
151
+ // request, so a change reloads.
152
+ private boolean mPixelated = false;
153
+
154
+ public void setImageRendering(@Nullable String imageRendering) {
155
+ boolean pixelated = "pixelated".equals(imageRendering);
156
+ if (pixelated == mPixelated) return;
157
+ mPixelated = pixelated;
158
+ mNeedsReload = true;
159
+ }
160
+
161
+ // The scale type's options (what into() would apply), or none when
162
+ // pixelated.
163
+ private RequestOptions renderingOptions(Object model) {
164
+ if (mPixelated) {
165
+ // Decoded as it is, then scaled by the view without filtering (see
166
+ // OwnGifTarget).
167
+ return new RequestOptions()
168
+ .downsample(new FastImageSourceSize.Capture(DownsampleStrategy.NONE, String.valueOf(model)))
169
+ .dontTransform();
170
+ }
171
+ return FastImageSourceSize.scaleTypeOptions(getScaleType(), FastImageSourceSize.capture(getScaleType(), model));
172
+ }
173
+
137
174
  // The request for the image the view shows once it has loaded, and the one
138
175
  // loading. A new source starts with the shown one as a thumbnail, from the
139
176
  // cache only, so the image stays until the new one has loaded instead of
@@ -353,7 +390,6 @@ class FastImageViewWithUrl extends AppCompatImageView {
353
390
  if (requestManager != null) {
354
391
  // Records the image's own size when Glide decodes it, for onLoad.
355
392
  Object model = imageSource == null ? null : imageSource.getSourceForLoad();
356
- FastImageSourceSize.Capture capture = FastImageSourceSize.capture(getScaleType(), model);
357
393
  RequestBuilder<Drawable> builder =
358
394
  requestManager
359
395
  // This will make this work for remote and local images. e.g.
@@ -368,8 +404,8 @@ class FastImageViewWithUrl extends AppCompatImageView {
368
404
  .placeholder(mDefaultSource) // show until loaded
369
405
  .fallback(mDefaultSource)) // null will not be treated as error
370
406
  // What into() would apply for the scale type, with
371
- // the size capture.
372
- .apply(FastImageSourceSize.scaleTypeOptions(getScaleType(), capture));
407
+ // the size capture, for imageRendering.
408
+ .apply(renderingOptions(model));
373
409
  RequestBuilder<Drawable> request = builder.clone();
374
410
 
375
411
  boolean thumbnail = shownRequest != null && model != null;
@@ -90,6 +90,7 @@ export type FastImageProps = $ReadOnly<{|
90
90
  fallback?: ?boolean,
91
91
  recyclingKey?: ?string,
92
92
  loop?: ?(boolean | number),
93
+ imageRendering?: ?('auto' | 'smooth' | 'pixelated'),
93
94
  testID?: ?string,
94
95
  |}>
95
96
 
package/dist/index.d.ts CHANGED
@@ -80,6 +80,14 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
80
80
  * or a number of times. Changing it restarts the animation.
81
81
  */
82
82
  loop?: boolean | number;
83
+ /**
84
+ * How the image is filtered when it's drawn smaller or larger than its
85
+ * size: `'auto'` (default, the platform's usual filtering), `'smooth'`
86
+ * (iOS only: keeps a large image drawn much smaller from looking jagged
87
+ * or noisy; uses more memory, see the README) or `'pixelated'` (sharp
88
+ * pixels, e.g. for pixel art).
89
+ */
90
+ imageRendering?: 'auto' | 'smooth' | 'pixelated';
83
91
  onLoadStart?(): void;
84
92
  onProgress?(event: OnProgressEvent): void;
85
93
  onLoad?(event: OnLoadEvent): void;
@@ -90,6 +90,7 @@ export type FastImageProps = $ReadOnly<{|
90
90
  fallback?: ?boolean,
91
91
  recyclingKey?: ?string,
92
92
  loop?: ?(boolean | number),
93
+ imageRendering?: ?('auto' | 'smooth' | 'pixelated'),
93
94
  testID?: ?string,
94
95
  |}>
95
96
 
@@ -25,6 +25,9 @@
25
25
  // How many times animated images play: -1 for the file's own loop count (the
26
26
  // `loop` prop not set), 0 for forever, or a number of times.
27
27
  @property (nonatomic, assign) NSInteger loopCount;
28
+ // How the image is filtered when drawn at another size: auto, smooth or
29
+ // pixelated.
30
+ @property (nonatomic, copy) NSString *imageRendering;
28
31
 
29
32
  @end
30
33
 
@@ -53,6 +53,24 @@ static CFTimeInterval FFFEnteredBackgroundAt = 0;
53
53
  return self;
54
54
  }
55
55
 
56
+ - (void) setImageRendering: (NSString*)imageRendering {
57
+ _imageRendering = [imageRendering copy];
58
+ if ([imageRendering isEqualToString: @"smooth"]) {
59
+ // Trilinear: drawn smaller from mipmaps, so a large image drawn much
60
+ // smaller is smoothed instead of aliased (#445). The mipmaps take
61
+ // about a third more memory than the decoded image.
62
+ self.layer.minificationFilter = kCAFilterTrilinear;
63
+ self.layer.magnificationFilter = kCAFilterLinear;
64
+ } else if ([imageRendering isEqualToString: @"pixelated"]) {
65
+ // Nearest neighbor: sharp pixels, e.g. for pixel art (#926).
66
+ self.layer.minificationFilter = kCAFilterNearest;
67
+ self.layer.magnificationFilter = kCAFilterNearest;
68
+ } else {
69
+ self.layer.minificationFilter = kCAFilterLinear;
70
+ self.layer.magnificationFilter = kCAFilterLinear;
71
+ }
72
+ }
73
+
56
74
  - (void) setLoopCount: (NSInteger)loopCount {
57
75
  if (_loopCount == loopCount) {
58
76
  return;
@@ -19,6 +19,7 @@ RCT_EXPORT_VIEW_PROPERTY(defaultSource, UIImage)
19
19
  RCT_EXPORT_VIEW_PROPERTY(resizeMode, RCTResizeMode)
20
20
  RCT_EXPORT_VIEW_PROPERTY(recyclingKey, NSString)
21
21
  RCT_EXPORT_VIEW_PROPERTY(loopCount, NSInteger)
22
+ RCT_EXPORT_VIEW_PROPERTY(imageRendering, NSString)
22
23
  RCT_EXPORT_VIEW_PROPERTY(onFastImageLoadStart, RCTDirectEventBlock)
23
24
  RCT_EXPORT_VIEW_PROPERTY(onFastImageProgress, RCTDirectEventBlock)
24
25
  RCT_EXPORT_VIEW_PROPERTY(onFastImageError, RCTDirectEventBlock)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-fast-image",
3
- "version": "8.10.0",
3
+ "version": "8.11.0",
4
4
  "description": "🚩 FastImage, performant React Native image component.",
5
5
  "keywords": [
6
6
  "cache",