react-native-fast-image 9.3.6 → 10.0.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 +87 -61
- package/RNFastImage.podspec +14 -9
- package/android/build.gradle +21 -47
- package/android/consumer-rules.pro +7 -7
- package/android/src/main/AndroidManifest.xml +1 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimatable.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageAnimated.java +2 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageApng.java +2 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageBlur.java +4 -6
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheControl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheLimits.java +4 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageCacheWrite.java +1 -1
- package/android/src/main/java/com/fastimage/FastImageCrop.java +92 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageEvent.java +2 -3
- package/android/src/main/java/com/fastimage/FastImageEvents.java +74 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGif.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlide.java +3 -3
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageGlideModule.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageKeyedGlideUrl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageOkHttpProgressGlideModule.java +36 -9
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageRequestListener.java +6 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSharedDownloads.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSource.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSourceSize.java +12 -2
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvg.java +3 -3
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageSvgRenderer.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageUrl.java +1 -1
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewConverter.java +7 -8
- package/android/src/main/java/com/fastimage/FastImageViewManager.java +591 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewModule.java +31 -28
- package/android/src/main/java/com/fastimage/FastImageViewPackage.java +51 -0
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewWithUrl.java +362 -129
- package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageWebGlideUrl.java +1 -1
- package/app.plugin.js +2 -7
- package/dist/index.cjs.js +276 -157
- package/dist/index.cjs.js.flow +33 -17
- package/dist/index.cjs.web.js +211 -61
- package/dist/index.cjs.web.js.flow +33 -17
- package/dist/index.d.ts +83 -58
- package/dist/index.js +278 -159
- package/dist/index.js.flow +33 -17
- package/dist/index.web.js +212 -62
- package/dist/index.web.js.flow +33 -17
- package/dist/objectFit.d.ts +2 -2
- package/dist/objectPosition.d.ts +42 -0
- package/dist/specs/FastImageViewNativeComponent.d.ts +48 -0
- package/dist/specs/NativeFastImageModule.d.ts +11 -0
- package/dist/warnings.d.ts +3 -0
- package/ios/FastImage/FFFDownsampledImage.h +4 -8
- package/ios/FastImage/FFFDownsampledImage.m +0 -34
- package/ios/FastImage/FFFastImageModule.h +8 -0
- package/ios/FastImage/{FFFastImageViewManager.m → FFFastImageModule.mm} +70 -59
- package/ios/FastImage/FFFastImageView.h +36 -7
- package/ios/FastImage/FFFastImageView.m +201 -164
- package/ios/FastImage/FFFastImageViewComponentView.h +11 -0
- package/ios/FastImage/FFFastImageViewComponentView.mm +325 -0
- package/ios/FastImage/RCTConvert+FFFastImage.h +1 -1
- package/ios/FastImage/RCTConvert+FFFastImage.m +7 -10
- package/package.json +28 -9
- package/src/constants.ts +28 -0
- package/src/index.js.flow +179 -0
- package/src/index.tsx +1103 -0
- package/src/index.web.tsx +548 -0
- package/src/objectFit.ts +48 -0
- package/src/objectPosition.ts +243 -0
- package/src/specs/FastImageViewNativeComponent.ts +83 -0
- package/src/specs/NativeFastImageModule.ts +35 -0
- package/src/warnings.ts +37 -0
- package/android/src/main/AndroidManifestNew.xml +0 -3
- package/android/src/main/java/com/dylanvann/fastimage/FastImageCookieHandler.java +0 -63
- package/android/src/main/java/com/dylanvann/fastimage/FastImageEvents.java +0 -49
- package/android/src/main/java/com/dylanvann/fastimage/FastImageProgressListener.java +0 -18
- package/android/src/main/java/com/dylanvann/fastimage/FastImageShadowNode.java +0 -21
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewManager.java +0 -379
- package/android/src/main/java/com/dylanvann/fastimage/FastImageViewPackage.java +0 -27
- package/ios/FastImage/FFFastImageViewManager.h +0 -5
package/android/src/main/java/com/{dylanvann/fastimage → fastimage}/FastImageViewWithUrl.java
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
package com.
|
|
1
|
+
package com.fastimage;
|
|
2
2
|
|
|
3
|
-
import static com.
|
|
3
|
+
import static com.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
|
|
4
4
|
|
|
5
5
|
import android.animation.Animator;
|
|
6
6
|
import android.animation.AnimatorListenerAdapter;
|
|
@@ -14,8 +14,8 @@ import android.graphics.Paint;
|
|
|
14
14
|
import android.graphics.PixelFormat;
|
|
15
15
|
import android.graphics.PorterDuff;
|
|
16
16
|
import android.graphics.PorterDuffXfermode;
|
|
17
|
+
import android.graphics.Rect;
|
|
17
18
|
import android.graphics.RectF;
|
|
18
|
-
import android.graphics.Shader;
|
|
19
19
|
import android.graphics.drawable.AnimatedImageDrawable;
|
|
20
20
|
import android.graphics.drawable.BitmapDrawable;
|
|
21
21
|
import android.graphics.drawable.Drawable;
|
|
@@ -28,7 +28,6 @@ import androidx.annotation.NonNull;
|
|
|
28
28
|
import androidx.annotation.Nullable;
|
|
29
29
|
import androidx.annotation.RequiresApi;
|
|
30
30
|
import androidx.appcompat.widget.AppCompatImageView;
|
|
31
|
-
import androidx.core.view.ViewCompat;
|
|
32
31
|
|
|
33
32
|
import com.bumptech.glide.GenericTransitionOptions;
|
|
34
33
|
import com.bumptech.glide.RequestBuilder;
|
|
@@ -49,12 +48,17 @@ import com.facebook.react.bridge.ReadableArray;
|
|
|
49
48
|
import com.facebook.react.bridge.ReadableMap;
|
|
50
49
|
import com.facebook.react.bridge.WritableMap;
|
|
51
50
|
import com.facebook.react.bridge.WritableNativeMap;
|
|
51
|
+
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
|
52
|
+
import com.facebook.react.touch.ReactHitSlopView;
|
|
53
|
+
import com.facebook.react.uimanager.BackgroundStyleApplicator;
|
|
54
|
+
import com.facebook.react.uimanager.PixelUtil;
|
|
52
55
|
import com.facebook.react.uimanager.PointerEvents;
|
|
53
56
|
import com.facebook.react.uimanager.ReactPointerEventsView;
|
|
57
|
+
import com.facebook.react.uimanager.style.LogicalEdge;
|
|
54
58
|
|
|
55
59
|
import javax.annotation.Nonnull;
|
|
56
60
|
|
|
57
|
-
class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEventsView {
|
|
61
|
+
class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEventsView, ReactHitSlopView {
|
|
58
62
|
private boolean mNeedsReload = false;
|
|
59
63
|
private ReadableMap mSource = null;
|
|
60
64
|
private Drawable mDefaultSource = null;
|
|
@@ -66,16 +70,93 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
66
70
|
public FastImageViewWithUrl(Context context, @Nullable RequestManager requestManager) {
|
|
67
71
|
super(context);
|
|
68
72
|
this.requestManager = requestManager;
|
|
69
|
-
//
|
|
70
|
-
//
|
|
73
|
+
// Borders inset the image: its padding is their widths (see
|
|
74
|
+
// updateBorderPadding). An image larger than that (objectFit none) is
|
|
75
|
+
// cropped to it, also when the view doesn't clip its content
|
|
76
|
+
// (overflow visible).
|
|
71
77
|
setCropToPadding(true);
|
|
72
78
|
}
|
|
73
79
|
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
80
|
+
// The image fills the view inside its borders, and padding doesn't inset
|
|
81
|
+
// it, as on iOS: the view's padding is the border widths, resolved as
|
|
82
|
+
// React Native draws the border (its BorderInsets, which isn't public).
|
|
83
|
+
// React Native passes the layout's padding and borders together, so
|
|
84
|
+
// FastImageViewManager.setPadding doesn't use them. Called when a border
|
|
85
|
+
// width or the layout direction changes.
|
|
86
|
+
void updateBorderPadding() {
|
|
87
|
+
LogicalEdge left = LogicalEdge.START;
|
|
88
|
+
LogicalEdge physicalLeft = LogicalEdge.LEFT;
|
|
89
|
+
LogicalEdge right = LogicalEdge.END;
|
|
90
|
+
LogicalEdge physicalRight = LogicalEdge.RIGHT;
|
|
91
|
+
if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
|
|
92
|
+
left = LogicalEdge.END;
|
|
93
|
+
right = LogicalEdge.START;
|
|
94
|
+
if (I18nUtil.getInstance().doLeftAndRightSwapInRTL(getContext())) {
|
|
95
|
+
physicalLeft = LogicalEdge.RIGHT;
|
|
96
|
+
physicalRight = LogicalEdge.LEFT;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
int paddingLeft = borderWidth(left, physicalLeft, LogicalEdge.HORIZONTAL);
|
|
100
|
+
int paddingTop = borderWidth(LogicalEdge.BLOCK_START, LogicalEdge.TOP, LogicalEdge.BLOCK, LogicalEdge.VERTICAL);
|
|
101
|
+
int paddingRight = borderWidth(right, physicalRight, LogicalEdge.HORIZONTAL);
|
|
102
|
+
int paddingBottom = borderWidth(LogicalEdge.BLOCK_END, LogicalEdge.BOTTOM, LogicalEdge.BLOCK, LogicalEdge.VERTICAL);
|
|
103
|
+
// Compared with the padding last set here: getPaddingLeft() and the
|
|
104
|
+
// others resolve the padding, which calls onRtlPropertiesChanged.
|
|
105
|
+
if (paddingLeft == mBorderPaddingLeft && paddingTop == mBorderPaddingTop
|
|
106
|
+
&& paddingRight == mBorderPaddingRight && paddingBottom == mBorderPaddingBottom) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
mBorderPaddingLeft = paddingLeft;
|
|
110
|
+
mBorderPaddingTop = paddingTop;
|
|
111
|
+
mBorderPaddingRight = paddingRight;
|
|
112
|
+
mBorderPaddingBottom = paddingBottom;
|
|
113
|
+
setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
|
|
114
|
+
// The image was loaded for the box inside the borders: for the new
|
|
115
|
+
// one, switch to the source that fits it (several sources) or load it
|
|
116
|
+
// again, as when the view's size changes (the view's own size
|
|
117
|
+
// doesn't).
|
|
118
|
+
if (!switchSourceIfResized()) reloadIfResized();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// The padding updateBorderPadding last set. No initial values: it runs
|
|
122
|
+
// during View's constructor, before field initializers would.
|
|
123
|
+
private int mBorderPaddingLeft;
|
|
124
|
+
private int mBorderPaddingTop;
|
|
125
|
+
private int mBorderPaddingRight;
|
|
126
|
+
private int mBorderPaddingBottom;
|
|
127
|
+
|
|
128
|
+
// The first of these edges' border width that's set, else the one for
|
|
129
|
+
// every edge, in pixels (rounded down, as React Native's layout does).
|
|
130
|
+
private int borderWidth(LogicalEdge... edges) {
|
|
131
|
+
Float width = null;
|
|
132
|
+
for (LogicalEdge edge : edges) {
|
|
133
|
+
width = BackgroundStyleApplicator.getBorderWidth(this, edge);
|
|
134
|
+
if (width != null) break;
|
|
135
|
+
}
|
|
136
|
+
if (width == null) width = BackgroundStyleApplicator.getBorderWidth(this, LogicalEdge.ALL);
|
|
137
|
+
return width == null ? 0 : (int) Math.floor(PixelUtil.toPixelFromDIP(width));
|
|
138
|
+
}
|
|
78
139
|
|
|
140
|
+
@Override
|
|
141
|
+
public void onRtlPropertiesChanged(int layoutDirection) {
|
|
142
|
+
super.onRtlPropertiesChanged(layoutDirection);
|
|
143
|
+
updateBorderPadding();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The View style props FastImage handles itself (FastImageViewManager):
|
|
147
|
+
// it's a single native view, so they're the image's.
|
|
148
|
+
private PointerEvents mPointerEvents = PointerEvents.AUTO;
|
|
149
|
+
@Nullable
|
|
150
|
+
private Rect mHitSlopRect;
|
|
151
|
+
// overflow: hidden (FastImage's default) or scroll clip the image to the
|
|
152
|
+
// padding box, with the border radius; visible only to the view.
|
|
153
|
+
private boolean mOverflowVisible = false;
|
|
154
|
+
// backfaceVisibility, as React Native's View does it: a view turned away
|
|
155
|
+
// (rotateX or rotateY past 90°) is hidden with an opacity of 0.
|
|
156
|
+
private boolean mBackfaceVisible = true;
|
|
157
|
+
private float mBackfaceOpacity = 1f;
|
|
158
|
+
|
|
159
|
+
@NonNull
|
|
79
160
|
@Override
|
|
80
161
|
public PointerEvents getPointerEvents() {
|
|
81
162
|
return mPointerEvents;
|
|
@@ -85,6 +166,50 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
85
166
|
mPointerEvents = pointerEvents;
|
|
86
167
|
}
|
|
87
168
|
|
|
169
|
+
@Nullable
|
|
170
|
+
@Override
|
|
171
|
+
public Rect getHitSlopRect() {
|
|
172
|
+
return mHitSlopRect;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
void setHitSlopRect(@Nullable Rect hitSlopRect) {
|
|
176
|
+
mHitSlopRect = hitSlopRect;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
void setOverflow(@Nullable String overflow) {
|
|
180
|
+
mOverflowVisible = "visible".equals(overflow);
|
|
181
|
+
invalidate();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
void setOpacityIfPossible(float opacity) {
|
|
185
|
+
mBackfaceOpacity = opacity;
|
|
186
|
+
setBackfaceVisibilityDependantOpacity();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
void setBackfaceVisibility(String backfaceVisibility) {
|
|
190
|
+
mBackfaceVisible = "visible".equals(backfaceVisibility);
|
|
191
|
+
setBackfaceVisibilityDependantOpacity();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
void setBackfaceVisibilityDependantOpacity() {
|
|
195
|
+
if (mBackfaceVisible) {
|
|
196
|
+
setAlpha(mBackfaceOpacity);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
float rotationX = getRotationX();
|
|
200
|
+
float rotationY = getRotationY();
|
|
201
|
+
boolean frontFaceVisible =
|
|
202
|
+
rotationX >= -90f && rotationX < 90f && rotationY >= -90f && rotationY < 90f;
|
|
203
|
+
setAlpha(frontFaceVisible ? mBackfaceOpacity : 0f);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// As React Native's Image: opacity applies to the view and its border and
|
|
207
|
+
// background as one, without an offscreen layer.
|
|
208
|
+
@Override
|
|
209
|
+
public boolean hasOverlappingRendering() {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
88
213
|
public void setSource(@Nullable ReadableMap source) {
|
|
89
214
|
mNeedsReload = true;
|
|
90
215
|
mPropSource = source;
|
|
@@ -119,13 +244,24 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
119
244
|
if (mSources == null) mSource = mPropSource;
|
|
120
245
|
}
|
|
121
246
|
|
|
247
|
+
// The box the image is drawn in: the view inside its padding, which is the
|
|
248
|
+
// border widths (see updateBorderPadding).
|
|
249
|
+
private int contentWidth() {
|
|
250
|
+
return getWidth() - getPaddingLeft() - getPaddingRight();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
private int contentHeight() {
|
|
254
|
+
return getHeight() - getPaddingTop() - getPaddingBottom();
|
|
255
|
+
}
|
|
256
|
+
|
|
122
257
|
// Of several sources, the index of the one whose size in pixels (width ×
|
|
123
|
-
// height × scale²) is closest to the
|
|
124
|
-
// largest while
|
|
258
|
+
// height × scale²) is closest to the box the image is drawn in (by pixel
|
|
259
|
+
// count), or of the largest while it has no size (e.g. a view sized from
|
|
260
|
+
// onLoad).
|
|
125
261
|
private int sourceIndexForSize() {
|
|
126
262
|
ReadableArray sources = mSources;
|
|
127
263
|
if (sources == null) return -1;
|
|
128
|
-
double viewPixels = (double)
|
|
264
|
+
double viewPixels = (double) Math.max(0, contentWidth()) * Math.max(0, contentHeight());
|
|
129
265
|
int best = -1;
|
|
130
266
|
double bestFit = Double.MAX_VALUE;
|
|
131
267
|
for (int i = 0; i < sources.size(); i++) {
|
|
@@ -152,10 +288,11 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
152
288
|
return map.hasKey(key) && !map.isNull(key) ? map.getDouble(key) : fallback;
|
|
153
289
|
}
|
|
154
290
|
|
|
155
|
-
// Several sources: another one fits the view's new size
|
|
156
|
-
// (after layout), keeping the image
|
|
291
|
+
// Several sources: another one fits the view's new size (or the box inside
|
|
292
|
+
// its new borders) better. Loads it (after layout), keeping the image
|
|
293
|
+
// showing until then, without a fade.
|
|
157
294
|
private boolean switchSourceIfResized() {
|
|
158
|
-
if (mSources == null || mNeedsReload || mWaitsForSize ||
|
|
295
|
+
if (mSources == null || mNeedsReload || mWaitsForSize || contentWidth() <= 0 || contentHeight() <= 0
|
|
159
296
|
|| mManager == null || sourceIndexForSize() == mSourceIndex) {
|
|
160
297
|
return false;
|
|
161
298
|
}
|
|
@@ -175,12 +312,12 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
175
312
|
mDefaultSource = source;
|
|
176
313
|
}
|
|
177
314
|
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
315
|
+
// The events JS has a handler for (the handledEvents prop): the view only
|
|
316
|
+
// sends those (see FastImageEvents).
|
|
317
|
+
int handledEvents;
|
|
318
|
+
|
|
319
|
+
boolean handles(int events) {
|
|
320
|
+
return (handledEvents & events) != 0;
|
|
184
321
|
}
|
|
185
322
|
|
|
186
323
|
// How many times GIFs play: -1 for the file's own loop count (the `loop`
|
|
@@ -215,13 +352,11 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
215
352
|
|
|
216
353
|
// Pauses GIFs on the frame they're showing (the view's own animation).
|
|
217
354
|
private boolean mPaused = false;
|
|
218
|
-
// Progress (see FastImageViewManager.onDownloadProgress):
|
|
219
|
-
//
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
// load has had.
|
|
224
|
-
private boolean mTrackProgress = false;
|
|
355
|
+
// Progress (see FastImageViewManager.onDownloadProgress): the url its
|
|
356
|
+
// current load downloads (null for a local image, and once the load has
|
|
357
|
+
// ended), the progress key the view gets progress for (the loading url's,
|
|
358
|
+
// while it has an onProgress), the last step downloads reported before the
|
|
359
|
+
// load started, and the fraction the load has had.
|
|
225
360
|
@Nullable
|
|
226
361
|
private GlideUrl mLoadingUrl;
|
|
227
362
|
@Nullable
|
|
@@ -232,8 +367,8 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
232
367
|
// posted to itself don't load anything, or track progress, any more.
|
|
233
368
|
private boolean mDropped = false;
|
|
234
369
|
|
|
235
|
-
void
|
|
236
|
-
|
|
370
|
+
void setHandledEvents(int events) {
|
|
371
|
+
handledEvents = events;
|
|
237
372
|
updateProgressTracking();
|
|
238
373
|
}
|
|
239
374
|
|
|
@@ -253,7 +388,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
253
388
|
|
|
254
389
|
// Whether a step a download reported is for the current load, and doesn't
|
|
255
390
|
// go back: one reported before it started is for an earlier load, e.g. of
|
|
256
|
-
// the same url before
|
|
391
|
+
// the same url before objectFit changed, posted while that load still
|
|
257
392
|
// ran, and one below what the load has had is from another download of
|
|
258
393
|
// the key (e.g. one that started after the first had ended).
|
|
259
394
|
boolean takesProgress(long step, long loaded, long total) {
|
|
@@ -265,7 +400,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
265
400
|
}
|
|
266
401
|
|
|
267
402
|
private void updateProgressTracking() {
|
|
268
|
-
String key =
|
|
403
|
+
String key = handles(FastImageEvents.PROGRESS) && mLoadingUrl != null && !mDropped
|
|
269
404
|
? FastImageSharedDownloads.key(mLoadingUrl)
|
|
270
405
|
: null;
|
|
271
406
|
if (key == null ? mTrackedKey == null : key.equals(mTrackedKey)) return;
|
|
@@ -462,9 +597,26 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
462
597
|
canvas.restoreToCount(count);
|
|
463
598
|
}
|
|
464
599
|
|
|
465
|
-
@SuppressWarnings("deprecation")
|
|
466
600
|
@Override
|
|
467
601
|
protected void onDraw(Canvas canvas) {
|
|
602
|
+
// Clipped as React Native's Image (and the View it used to be in): to
|
|
603
|
+
// the padding box, with the border radius, unless overflow is visible.
|
|
604
|
+
int saved = canvas.save();
|
|
605
|
+
if (mOverflowVisible) {
|
|
606
|
+
Rect bounds = new Rect();
|
|
607
|
+
getDrawingRect(bounds);
|
|
608
|
+
canvas.clipRect(bounds);
|
|
609
|
+
} else {
|
|
610
|
+
BackgroundStyleApplicator.clipToPaddingBox(this, canvas);
|
|
611
|
+
}
|
|
612
|
+
try {
|
|
613
|
+
drawImage(canvas);
|
|
614
|
+
} finally {
|
|
615
|
+
canvas.restoreToCount(saved);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private void drawImage(Canvas canvas) {
|
|
468
620
|
if (mFade == null) {
|
|
469
621
|
super.onDraw(canvas);
|
|
470
622
|
return;
|
|
@@ -475,15 +627,14 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
475
627
|
if (drawFadeWithoutLayers(canvas, alpha)) return;
|
|
476
628
|
if (mFadeFrom == null && mFadeFromDrawable == null) {
|
|
477
629
|
// Fading in over nothing: one layer.
|
|
478
|
-
int count = canvas.saveLayerAlpha(0, 0, width, height, alpha
|
|
630
|
+
int count = canvas.saveLayerAlpha(0, 0, width, height, alpha);
|
|
479
631
|
super.onDraw(canvas);
|
|
480
632
|
canvas.restoreToCount(count);
|
|
481
633
|
return;
|
|
482
634
|
}
|
|
483
635
|
// Two layers, which add up to the cross-dissolve whatever the image's
|
|
484
636
|
// transparency.
|
|
485
|
-
|
|
486
|
-
int count = canvas.saveLayer(0, 0, width, height, null, Canvas.ALL_SAVE_FLAG);
|
|
637
|
+
int count = canvas.saveLayer(0, 0, width, height, null);
|
|
487
638
|
if (mFadeFromDrawable != null) {
|
|
488
639
|
drawFadeFrom(canvas, mFadeFromDrawable, 255 - alpha);
|
|
489
640
|
} else {
|
|
@@ -491,7 +642,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
491
642
|
canvas.drawBitmap(mFadeFrom, 0, 0, mFadeFromPaint);
|
|
492
643
|
}
|
|
493
644
|
mFadeInPaint.setAlpha(alpha);
|
|
494
|
-
canvas.saveLayer(0, 0, width, height, mFadeInPaint
|
|
645
|
+
canvas.saveLayer(0, 0, width, height, mFadeInPaint);
|
|
495
646
|
super.onDraw(canvas);
|
|
496
647
|
canvas.restoreToCount(count);
|
|
497
648
|
}
|
|
@@ -543,9 +694,8 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
543
694
|
// an opacity can change during it.
|
|
544
695
|
// Not the transition alpha, which only Android's Transition fades
|
|
545
696
|
// (android.transition.Fade) set: getTransitionAlpha is public from API 29
|
|
546
|
-
// only (hidden on Android
|
|
547
|
-
//
|
|
548
|
-
// Animated, LayoutAnimation and Reanimated set the view's alpha, and
|
|
697
|
+
// only (hidden on Android 7 to 9). React Native's opacity, Animated,
|
|
698
|
+
// LayoutAnimation and Reanimated set the view's alpha, and
|
|
549
699
|
// react-native-screens' fragment animations run on a container with a
|
|
550
700
|
// layer of its own.
|
|
551
701
|
private boolean drawnWithAlpha() {
|
|
@@ -760,59 +910,129 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
760
910
|
animated.setRepeatCount(mLoopCount == 0 ? AnimatedImageDrawable.REPEAT_INFINITE : mLoopCount - 1);
|
|
761
911
|
}
|
|
762
912
|
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
//
|
|
766
|
-
private boolean mRepeat = false;
|
|
767
|
-
|
|
768
|
-
// The scale type for resizeMode, which Glide decodes the image for and the
|
|
769
|
-
// blur scales with. For scale-down (CENTER_INSIDE) and none (CENTER) the
|
|
913
|
+
// The scale type for objectFit, which Glide decodes the image for and the
|
|
914
|
+
// blur scales with. For scale-down (CENTER_INSIDE) and none (CENTER), and
|
|
915
|
+
// contain and cover with an objectPosition other than the center, the
|
|
770
916
|
// view is MATRIX: it lays the image out itself (see updateImageMatrix).
|
|
771
917
|
// Starts as ImageView's.
|
|
772
|
-
private ScaleType
|
|
773
|
-
|
|
774
|
-
// resizeMode center: CENTER_INSIDE as ImageView draws it, an image smaller
|
|
775
|
-
// than the view at its size in pixels on the screen (scale-down: in dp).
|
|
776
|
-
private boolean mInPixels = false;
|
|
918
|
+
private ScaleType mFitScaleType = ScaleType.FIT_CENTER;
|
|
777
919
|
|
|
778
|
-
// The scale type the image on screen is laid out for
|
|
779
|
-
//
|
|
920
|
+
// The scale type the image on screen is laid out for, and whether it was
|
|
921
|
+
// downsampled or pixelated (see applyObjectFit).
|
|
780
922
|
private ScaleType mLayoutScaleType = ScaleType.FIT_CENTER;
|
|
923
|
+
private boolean mLayoutDownsampled = true;
|
|
924
|
+
private boolean mLayoutPixelated = false;
|
|
781
925
|
|
|
782
926
|
// Glide crops or fits the bitmap for the scale type when it loads, so a
|
|
783
|
-
// new
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
927
|
+
// new objectFit needs a reload to take effect (#762): the image that's
|
|
928
|
+
// showing, loaded again quietly (see reblur), as for a new blurRadius,
|
|
929
|
+
// without the load events, as on iOS.
|
|
930
|
+
public void setObjectFit(ScaleType scaleType) {
|
|
931
|
+
if (scaleType == mFitScaleType) return;
|
|
932
|
+
mFitScaleType = scaleType;
|
|
933
|
+
mFitChanged = true;
|
|
790
934
|
// A loaded image on screen was cropped or fitted for the old scale
|
|
791
935
|
// type: it keeps its layout until the image for the new one is ready
|
|
792
936
|
// (onImageLoaded), or the load fails. Laid out for the new one, a
|
|
793
937
|
// cover crop would be stretched to the image's own size.
|
|
794
|
-
if (mShownRequest == null)
|
|
938
|
+
if (mShownRequest == null) applyObjectFit();
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Lays the view out for objectFit.
|
|
942
|
+
private void applyObjectFit() {
|
|
943
|
+
mLayoutScaleType = mFitScaleType;
|
|
944
|
+
mLayoutDownsampled = mDownsample;
|
|
945
|
+
mLayoutPixelated = mPixelated;
|
|
946
|
+
updateScaleType();
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// objectPosition: on each axis the image's left or top edge is at
|
|
950
|
+
// (view - image) * fraction + offset (offsets in pixels).
|
|
951
|
+
private float mPositionX = 0.5f;
|
|
952
|
+
private float mPositionXOffset = 0;
|
|
953
|
+
private float mPositionY = 0.5f;
|
|
954
|
+
private float mPositionYOffset = 0;
|
|
955
|
+
|
|
956
|
+
public void setObjectPositionX(float x) {
|
|
957
|
+
if (x == mPositionX) return;
|
|
958
|
+
mPositionX = x;
|
|
959
|
+
onPositionChanged();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
public void setObjectPositionXOffset(float xOffset) {
|
|
963
|
+
if (xOffset == mPositionXOffset) return;
|
|
964
|
+
mPositionXOffset = xOffset;
|
|
965
|
+
onPositionChanged();
|
|
795
966
|
}
|
|
796
967
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
968
|
+
public void setObjectPositionY(float y) {
|
|
969
|
+
if (y == mPositionY) return;
|
|
970
|
+
mPositionY = y;
|
|
971
|
+
onPositionChanged();
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
public void setObjectPositionYOffset(float yOffset) {
|
|
975
|
+
if (yOffset == mPositionYOffset) return;
|
|
976
|
+
mPositionYOffset = yOffset;
|
|
977
|
+
onPositionChanged();
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Glide crops a cover image at the position (FastImageCrop): the image on
|
|
981
|
+
// screen is loaded again for it, quietly (see reblur), and until that's
|
|
982
|
+
// ready it stays as it was cropped (see shownCropped). Others, and cover
|
|
983
|
+
// images Glide doesn't crop, are only laid out at it, right away.
|
|
984
|
+
private void onPositionChanged() {
|
|
985
|
+
if (mFitScaleType == ScaleType.CENTER_CROP && !mPixelated && mDownsample) {
|
|
986
|
+
mCropChanged = true;
|
|
987
|
+
}
|
|
988
|
+
updateScaleType();
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
private boolean isCentered() {
|
|
992
|
+
return mPositionX == 0.5f && mPositionY == 0.5f && mPositionXOffset == 0 && mPositionYOffset == 0;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Where Glide crops a cover image: CenterCrop at the center (as without
|
|
996
|
+
// objectPosition), FastImageCrop elsewhere.
|
|
997
|
+
@Nullable
|
|
998
|
+
private FastImageCrop positionedCrop() {
|
|
999
|
+
return mFitScaleType == ScaleType.CENTER_CROP && !isCentered() && !mPixelated && mDownsample
|
|
1000
|
+
? new FastImageCrop(mPositionX, mPositionXOffset, mPositionY, mPositionYOffset)
|
|
1001
|
+
: null;
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
// Whether the image on screen is a cover one Glide cropped to what the
|
|
1005
|
+
// view shows (CenterCrop or FastImageCrop, at the position it was loaded
|
|
1006
|
+
// for). Glide can't crop animated WebPs and AVIFs, APNGs and
|
|
1007
|
+
// defaultSource, and doesn't crop pixelated images or ones decoded at full
|
|
1008
|
+
// size (downsample false).
|
|
1009
|
+
private boolean shownCropped(Drawable drawable) {
|
|
1010
|
+
return mLayoutScaleType == ScaleType.CENTER_CROP && !mLayoutPixelated && mLayoutDownsampled
|
|
1011
|
+
&& drawable != mDefaultSource
|
|
1012
|
+
&& (drawable instanceof BitmapDrawable || drawable instanceof GifDrawable);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// ImageView's own scale type for the layout, or MATRIX where the view lays
|
|
1016
|
+
// the image out itself (updateImageMatrix): scale-down and none at their
|
|
1017
|
+
// size in dp, and contain and cover at a position other than the center.
|
|
1018
|
+
private void updateScaleType() {
|
|
1019
|
+
ScaleType type = mLayoutScaleType;
|
|
1020
|
+
boolean ownSize = type == ScaleType.CENTER_INSIDE || type == ScaleType.CENTER;
|
|
1021
|
+
boolean positioned = !isCentered()
|
|
1022
|
+
&& (ownSize || type == ScaleType.FIT_CENTER || type == ScaleType.CENTER_CROP);
|
|
1023
|
+
boolean laidOut = ownSize || positioned;
|
|
1024
|
+
setScaleType(laidOut ? ScaleType.MATRIX : type);
|
|
803
1025
|
updateImageMatrix();
|
|
804
1026
|
}
|
|
805
1027
|
|
|
806
|
-
// Whether the view shows its defaultSource
|
|
807
|
-
//
|
|
808
|
-
// startFade).
|
|
1028
|
+
// Whether the view shows its defaultSource: FastImage's own drawable,
|
|
1029
|
+
// which Glide doesn't recycle (see startFade).
|
|
809
1030
|
private boolean mShowsDefaultSource = false;
|
|
810
1031
|
|
|
811
|
-
// Repeats what the view shows (the loaded image, and defaultSource).
|
|
812
1032
|
@Override
|
|
813
1033
|
public void setImageDrawable(@Nullable Drawable drawable) {
|
|
814
1034
|
mShowsDefaultSource = drawable != null && drawable == mDefaultSource;
|
|
815
|
-
super.setImageDrawable(
|
|
1035
|
+
super.setImageDrawable(drawable);
|
|
816
1036
|
updateImageMatrix();
|
|
817
1037
|
}
|
|
818
1038
|
|
|
@@ -855,12 +1075,16 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
855
1075
|
return (float) (getResources().getDisplayMetrics().density / mLoadingScale);
|
|
856
1076
|
}
|
|
857
1077
|
|
|
858
|
-
//
|
|
859
|
-
//
|
|
860
|
-
//
|
|
861
|
-
//
|
|
862
|
-
//
|
|
863
|
-
// its size
|
|
1078
|
+
// The image laid out as CSS's object-fit and object-position do: its size
|
|
1079
|
+
// for the fit (cover and contain from its shape; none at its own size,
|
|
1080
|
+
// cropped by the view; scale-down at its own size, or scaled down
|
|
1081
|
+
// uniformly to fit if that's larger than the view), with its left and top
|
|
1082
|
+
// edges at (view - image) * fraction + offset. Glide decodes a scale-down
|
|
1083
|
+
// image no larger than that (CenterInside), a none one at its size, and
|
|
1084
|
+
// crops a cover one to what the view shows (FastImageCrop), so it's drawn
|
|
1085
|
+
// at this size. defaultSource (a resource) is already at its size on
|
|
1086
|
+
// screen, and so is a center image (its size in pixels, as ImageView's
|
|
1087
|
+
// CENTER_INSIDE draws it).
|
|
864
1088
|
private void updateImageMatrix() {
|
|
865
1089
|
if (getScaleType() != ScaleType.MATRIX) return;
|
|
866
1090
|
Drawable drawable = getDrawable();
|
|
@@ -878,7 +1102,27 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
878
1102
|
}
|
|
879
1103
|
float width;
|
|
880
1104
|
float height;
|
|
881
|
-
if (drawable
|
|
1105
|
+
if (shownCropped(drawable)) {
|
|
1106
|
+
// Glide cropped it to what the view shows (FastImageCrop), or for
|
|
1107
|
+
// the previous position or size until the image has loaded again
|
|
1108
|
+
// for this one: it covers the view, scaled uniformly (a crop for
|
|
1109
|
+
// another size isn't stretched), at the position's fractions (its
|
|
1110
|
+
// offsets are already in the crop).
|
|
1111
|
+
float cover = Math.max(viewWidth / drawableWidth, viewHeight / drawableHeight);
|
|
1112
|
+
matrix.setScale(cover, cover);
|
|
1113
|
+
matrix.postTranslate(
|
|
1114
|
+
Math.round((viewWidth - drawableWidth * cover) * mPositionX),
|
|
1115
|
+
Math.round((viewHeight - drawableHeight * cover) * mPositionY));
|
|
1116
|
+
setImageMatrix(matrix);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
1119
|
+
if (mLayoutScaleType == ScaleType.CENTER_CROP || mLayoutScaleType == ScaleType.FIT_CENTER) {
|
|
1120
|
+
float x = viewWidth / drawableWidth;
|
|
1121
|
+
float y = viewHeight / drawableHeight;
|
|
1122
|
+
float scale = mLayoutScaleType == ScaleType.CENTER_CROP ? Math.max(x, y) : Math.min(x, y);
|
|
1123
|
+
width = drawableWidth * scale;
|
|
1124
|
+
height = drawableHeight * scale;
|
|
1125
|
+
} else if (drawable == mDefaultSource) {
|
|
882
1126
|
width = drawableWidth;
|
|
883
1127
|
height = drawableHeight;
|
|
884
1128
|
} else if (mImageWidth > 0 && mImageHeight > 0) {
|
|
@@ -891,31 +1135,18 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
891
1135
|
width = drawableWidth * imageDensity();
|
|
892
1136
|
height = drawableHeight * imageDensity();
|
|
893
1137
|
}
|
|
894
|
-
float fit = mLayoutScaleType == ScaleType.
|
|
895
|
-
? 1
|
|
896
|
-
:
|
|
1138
|
+
float fit = mLayoutScaleType == ScaleType.CENTER_INSIDE
|
|
1139
|
+
? Math.min(1, Math.min(viewWidth / width, viewHeight / height))
|
|
1140
|
+
: 1;
|
|
897
1141
|
width *= fit;
|
|
898
1142
|
height *= fit;
|
|
899
1143
|
matrix.setScale(width / drawableWidth, height / drawableHeight);
|
|
900
|
-
matrix.postTranslate(
|
|
1144
|
+
matrix.postTranslate(
|
|
1145
|
+
Math.round((viewWidth - width) * mPositionX + mPositionXOffset),
|
|
1146
|
+
Math.round((viewHeight - height) * mPositionY + mPositionYOffset));
|
|
901
1147
|
setImageMatrix(matrix);
|
|
902
1148
|
}
|
|
903
1149
|
|
|
904
|
-
@Nullable
|
|
905
|
-
private Drawable tiled(@Nullable Drawable drawable) {
|
|
906
|
-
if (!(drawable instanceof BitmapDrawable)) return drawable;
|
|
907
|
-
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
|
|
908
|
-
if (bitmapDrawable.getTileModeX() == Shader.TileMode.REPEAT) return drawable;
|
|
909
|
-
Bitmap bitmap = bitmapDrawable.getBitmap();
|
|
910
|
-
if (bitmap == null) return drawable;
|
|
911
|
-
// A copy of the drawable (not of the bitmap): Glide's may be shown by
|
|
912
|
-
// other views too.
|
|
913
|
-
BitmapDrawable tiled = new BitmapDrawable(getResources(), bitmap);
|
|
914
|
-
tiled.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
|
|
915
|
-
if (mPixelated) tiled.setFilterBitmap(false);
|
|
916
|
-
return tiled;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
1150
|
// imageRendering="pixelated": no resampling by Glide, and drawn without
|
|
920
1151
|
// filtering (sharp pixels). "smooth" is iOS only; here it's the same as
|
|
921
1152
|
// "auto": Glide decodes a large image at about the view's size (unless
|
|
@@ -940,12 +1171,19 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
940
1171
|
if (downsample == mDownsample) return;
|
|
941
1172
|
mDownsample = downsample;
|
|
942
1173
|
mNeedsReload = true;
|
|
1174
|
+
// As for a new objectFit: an image on screen keeps the layout it was
|
|
1175
|
+
// loaded for until the new one is ready.
|
|
1176
|
+
if (mShownRequest == null) applyObjectFit();
|
|
943
1177
|
}
|
|
944
1178
|
|
|
945
1179
|
// blurRadius, in pixels (0 is no blur). Part of the request (see
|
|
946
1180
|
// reblur for a change).
|
|
947
1181
|
private float mBlurRadius = 0;
|
|
948
1182
|
private boolean mBlurChanged = false;
|
|
1183
|
+
// objectFit changed (setObjectFit).
|
|
1184
|
+
private boolean mFitChanged = false;
|
|
1185
|
+
// objectPosition changed for an image Glide crops (onPositionChanged).
|
|
1186
|
+
private boolean mCropChanged = false;
|
|
949
1187
|
|
|
950
1188
|
public void setBlurRadius(float blurRadius) {
|
|
951
1189
|
if (blurRadius == mBlurRadius) return;
|
|
@@ -958,21 +1196,10 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
958
1196
|
// as on iOS.
|
|
959
1197
|
private RequestOptions renderingOptions(Object model) {
|
|
960
1198
|
FastImageBlur blur = mBlurRadius > 0
|
|
961
|
-
? new FastImageBlur(mBlurRadius,
|
|
1199
|
+
? new FastImageBlur(mBlurRadius, mFitScaleType, loadingDensity())
|
|
962
1200
|
: null;
|
|
963
1201
|
RequestOptions options;
|
|
964
|
-
if (
|
|
965
|
-
// Decoded at its own size, or scaled down to fit the view if it's
|
|
966
|
-
// larger (not cropped or fitted for the scale type), and a GIF as
|
|
967
|
-
// its first frame (tiled by setImageDrawable).
|
|
968
|
-
options = new RequestOptions()
|
|
969
|
-
.downsample(new FastImageSourceSize.Capture(
|
|
970
|
-
mPixelated ? DownsampleStrategy.NONE : DownsampleStrategy.CENTER_INSIDE,
|
|
971
|
-
String.valueOf(model)))
|
|
972
|
-
.dontTransform()
|
|
973
|
-
.dontAnimate();
|
|
974
|
-
if (blur != null) options = options.optionalTransform(blur);
|
|
975
|
-
} else if (mPixelated) {
|
|
1202
|
+
if (mPixelated) {
|
|
976
1203
|
// Decoded as it is, then scaled by the view without filtering (see
|
|
977
1204
|
// OwnGifTarget).
|
|
978
1205
|
options = new RequestOptions()
|
|
@@ -980,15 +1207,16 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
980
1207
|
.dontTransform();
|
|
981
1208
|
if (blur != null) options = options.optionalTransform(blur);
|
|
982
1209
|
} else {
|
|
983
|
-
options = FastImageSourceSize.scaleTypeOptions(
|
|
984
|
-
FastImageSourceSize.capture(
|
|
985
|
-
|
|
1210
|
+
options = FastImageSourceSize.scaleTypeOptions(mFitScaleType,
|
|
1211
|
+
FastImageSourceSize.capture(mFitScaleType, model, mDownsample), positionedCrop(), blur,
|
|
1212
|
+
mDownsample);
|
|
1213
|
+
boolean ownSize = mFitScaleType == ScaleType.CENTER_INSIDE || mFitScaleType == ScaleType.CENTER;
|
|
986
1214
|
// scale-down and none show it at its own size in dp: an SVG is
|
|
987
1215
|
// drawn at that size, so it's sharp (not with a blur, which hides
|
|
988
1216
|
// the difference).
|
|
989
|
-
if (ownSize &&
|
|
1217
|
+
if (ownSize && blur == null) {
|
|
990
1218
|
options = options.set(FastImageSvg.DENSITY, loadingDensity())
|
|
991
|
-
.set(FastImageSvg.FITS,
|
|
1219
|
+
.set(FastImageSvg.FITS, mFitScaleType == ScaleType.CENTER_INSIDE);
|
|
992
1220
|
}
|
|
993
1221
|
}
|
|
994
1222
|
return blur != null ? options.dontAnimate() : options;
|
|
@@ -1055,7 +1283,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1055
1283
|
mLoadEnded = true;
|
|
1056
1284
|
endProgress();
|
|
1057
1285
|
// Before the view shows it (the target does once this returns).
|
|
1058
|
-
|
|
1286
|
+
applyObjectFit();
|
|
1059
1287
|
mShownRequest = mLoadingRequest;
|
|
1060
1288
|
mShownWidth = mLoadingWidth;
|
|
1061
1289
|
mShownHeight = mLoadingHeight;
|
|
@@ -1098,8 +1326,8 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1098
1326
|
|
|
1099
1327
|
// Whether the image loaded, at a size the view doesn't have now.
|
|
1100
1328
|
private boolean isResized() {
|
|
1101
|
-
int width =
|
|
1102
|
-
int height =
|
|
1329
|
+
int width = contentWidth();
|
|
1330
|
+
int height = contentHeight();
|
|
1103
1331
|
return requestManager != null && !mDropped && !mNeedsReload
|
|
1104
1332
|
&& mShownRequest != null && mShownRequest == mLoadingRequest
|
|
1105
1333
|
&& mShownWidth > 0 && mShownHeight > 0 && width > 0 && height > 0
|
|
@@ -1123,10 +1351,11 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1123
1351
|
into(shown, builder, meanwhile, true);
|
|
1124
1352
|
}
|
|
1125
1353
|
|
|
1126
|
-
// blurRadius changed, and nothing else that needs a reload:
|
|
1127
|
-
// that's showing, blurred again, with no events, as
|
|
1128
|
-
// decoded again from the cache and blurred
|
|
1129
|
-
//
|
|
1354
|
+
// blurRadius or objectFit changed, and nothing else that needs a reload:
|
|
1355
|
+
// the image that's showing, blurred or fitted again, with no events, as
|
|
1356
|
+
// reloadForSize does: it's decoded again from the cache and blurred or
|
|
1357
|
+
// fitted on Glide's threads, and the view keeps the current image (and its
|
|
1358
|
+
// layout, see onImageLoaded) until then. If that fails, it shows the image
|
|
1130
1359
|
// from Glide's caches; cache: 'web' skips them, so the view shows
|
|
1131
1360
|
// defaultSource (or nothing) then. Also while it's loading again that way
|
|
1132
1361
|
// (e.g. the blur changes every frame): that load is replaced. A load that
|
|
@@ -1221,7 +1450,7 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1221
1450
|
void onImageFailed(boolean hadThumbnail) {
|
|
1222
1451
|
mLoadEnded = true;
|
|
1223
1452
|
endProgress();
|
|
1224
|
-
|
|
1453
|
+
applyObjectFit();
|
|
1225
1454
|
mShownRequest = null;
|
|
1226
1455
|
if (!hadThumbnail) return;
|
|
1227
1456
|
final int load = mLoadCount;
|
|
@@ -1240,8 +1469,10 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1240
1469
|
@Nullable RequestManager requestManager) {
|
|
1241
1470
|
if (mDropped) return;
|
|
1242
1471
|
mManager = manager;
|
|
1243
|
-
if (mBlurChanged && !mNeedsReload) {
|
|
1472
|
+
if ((mBlurChanged || mFitChanged || mCropChanged) && !mNeedsReload) {
|
|
1244
1473
|
mBlurChanged = false;
|
|
1474
|
+
mFitChanged = false;
|
|
1475
|
+
mCropChanged = false;
|
|
1245
1476
|
reblur();
|
|
1246
1477
|
}
|
|
1247
1478
|
if (!mNeedsReload)
|
|
@@ -1269,9 +1500,11 @@ class FastImageViewWithUrl extends AppCompatImageView implements ReactPointerEve
|
|
|
1269
1500
|
pickSource();
|
|
1270
1501
|
}
|
|
1271
1502
|
// Only reload for changes that affect the request (source,
|
|
1272
|
-
// defaultSource,
|
|
1503
|
+
// defaultSource, objectFit), not for every prop update.
|
|
1273
1504
|
mNeedsReload = false;
|
|
1274
1505
|
mBlurChanged = false;
|
|
1506
|
+
mFitChanged = false;
|
|
1507
|
+
mCropChanged = false;
|
|
1275
1508
|
boolean switching = mSwitchesSource;
|
|
1276
1509
|
mSwitchesSource = false;
|
|
1277
1510
|
boolean restarting = mRestarting;
|