From 6dfcffbe61f87a1cdd943cc9062ae83acaac9003 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Mon, 7 Oct 2024 11:35:56 -0700 Subject: [PATCH] Add resizeMethod type 'never' to disable downsampling on Android Summary: ## Summary Adds a new `resizeMethod` called `never`. It disables downsampling on the image request and disregards the global image pipeline default. This has been a pain point raised when rendering large images on Android in issues like [this one](https://github.com/facebook/react-native/issues/21301) and [this one](https://github.com/facebook/fresco/issues/2397). ## Changelog [Android][Added] - Adds a new `resizeMethod`, `never`, which disables downsampling for an image Differential Revision: D62393211 --- packages/react-native/Libraries/Image/ImageProps.js | 2 +- .../__tests__/__snapshots__/public-api-test.js.snap | 2 +- .../react-native/ReactAndroid/api/ReactAndroid.api | 1 + .../facebook/react/modules/fresco/FrescoModule.kt | 2 +- .../facebook/react/views/image/ImageResizeMethod.kt | 1 + .../facebook/react/views/image/ReactImageManager.kt | 2 +- .../facebook/react/views/image/ReactImageView.kt | 13 ++++++++++--- 7 files changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/react-native/Libraries/Image/ImageProps.js b/packages/react-native/Libraries/Image/ImageProps.js index 3b62af9fcdcfec..1624e2f2312559 100644 --- a/packages/react-native/Libraries/Image/ImageProps.js +++ b/packages/react-native/Libraries/Image/ImageProps.js @@ -65,7 +65,7 @@ type AndroidImageProps = $ReadOnly<{| * dimensions differ from the image view's dimensions. Defaults to `'auto'`. * See https://reactnative.dev/docs/image#resizemethod-android */ - resizeMethod?: ?('auto' | 'resize' | 'scale'), + resizeMethod?: ?('auto' | 'resize' | 'scale' | 'never'), /** * When the `resizeMethod` is set to `resize`, the destination dimensions are diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 78185d4b309258..0d2b67b9bd3d3b 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -4825,7 +4825,7 @@ type AndroidImageProps = $ReadOnly<{| loadingIndicatorSource?: ?(number | $ReadOnly<{| uri: string |}>), progressiveRenderingEnabled?: ?boolean, fadeDuration?: ?number, - resizeMethod?: ?(\\"auto\\" | \\"resize\\" | \\"scale\\"), + resizeMethod?: ?(\\"auto\\" | \\"resize\\" | \\"scale\\" | \\"never\\"), resizeMultiplier?: ?number, |}>; export type ImageProps = {| diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index c84e8b87f72e8d..b3b877d1b2abd1 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -6632,6 +6632,7 @@ public final class com/facebook/react/views/image/ImageLoadEvent$Companion { public final class com/facebook/react/views/image/ImageResizeMethod : java/lang/Enum { public static final field AUTO Lcom/facebook/react/views/image/ImageResizeMethod; + public static final field NEVER Lcom/facebook/react/views/image/ImageResizeMethod; public static final field RESIZE Lcom/facebook/react/views/image/ImageResizeMethod; public static final field SCALE Lcom/facebook/react/views/image/ImageResizeMethod; public static fun getEntries ()Lkotlin/enums/EnumEntries; diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.kt index e8ba89a6d0029a..915d8830f34220 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/fresco/FrescoModule.kt @@ -114,7 +114,7 @@ constructor( // the 'last' ReactActivity is being destroyed, which effectively means the app is being // backgrounded. if (hasBeenInitialized() && clearOnDestroy) { - imagePipeline!!.clearMemoryCaches() + imagePipeline?.clearMemoryCaches() } } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.kt index 4cd3e8425a615d..9521c631b4aee7 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.kt @@ -11,4 +11,5 @@ public enum class ImageResizeMethod { AUTO, RESIZE, SCALE, + NEVER, } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt index b6e5a7bbcfc74d..fc24e3a3c85410 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.kt @@ -182,9 +182,9 @@ public constructor( when (resizeMethod) { null, "auto" -> view.setResizeMethod(ImageResizeMethod.AUTO) - "resize" -> view.setResizeMethod(ImageResizeMethod.RESIZE) "scale" -> view.setResizeMethod(ImageResizeMethod.SCALE) + "never" -> view.setResizeMethod(ImageResizeMethod.NEVER) else -> { view.setResizeMethod(ImageResizeMethod.AUTO) FLog.w(ReactConstants.TAG, "Invalid resize method: '$resizeMethod'") diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt index fdba665f52c667..d39dc9dee61fda 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.kt @@ -34,6 +34,7 @@ import com.facebook.drawee.generic.RoundingParams import com.facebook.drawee.view.GenericDraweeView import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory import com.facebook.imagepipeline.common.ResizeOptions +import com.facebook.imagepipeline.core.DownsampleMode import com.facebook.imagepipeline.image.CloseableImage import com.facebook.imagepipeline.image.ImageInfo import com.facebook.imagepipeline.postprocessors.IterativeBoxBlurPostProcessor @@ -415,6 +416,10 @@ public class ReactImageView( .setAutoRotateEnabled(true) .setProgressiveRenderingEnabled(progressiveRenderingEnabled) + if (resizeMethod == ImageResizeMethod.NEVER) { + imageRequestBuilder.setDownsampleOverride(DownsampleMode.NEVER) + } + val imageRequest: ImageRequest = ReactNetworkImageRequest.fromBuilderWithHeaders(imageRequestBuilder, headers) @@ -435,14 +440,16 @@ public class ReactImageView( callerContext?.let { builder.setCallerContext(it) } cachedImageSource?.let { cachedSource -> - val cachedImageRequest = + val cachedImageRequestBuilder = ImageRequestBuilder.newBuilderWithSource(cachedSource.uri) .setPostprocessor(postprocessor) .setResizeOptions(resizeOptions) .setAutoRotateEnabled(true) .setProgressiveRenderingEnabled(progressiveRenderingEnabled) - .build() - builder.setLowResImageRequest(cachedImageRequest) + if (resizeMethod == ImageResizeMethod.NEVER) { + cachedImageRequestBuilder.setDownsampleOverride(DownsampleMode.NEVER) + } + builder.setLowResImageRequest(cachedImageRequestBuilder.build()) } if (downloadListener != null && controllerForTesting != null) {