From b6735f33916429673fd65fb861ca963912bf2183 Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Fri, 9 Sep 2016 05:00:52 -0700 Subject: [PATCH] Enable developers to force Fresco to resize an image Summary: Here's a little background. Resizing is inferior to scaling. See http://frescolib.org/docs/resizing-rotating.html#_ Currently, React Native has a heuristic to use resize when the image is likely to be from the device's camera. However, there may be other cases where a developer wants to use resize. For example, when the developer knows they'll be downloading a large image from a service but the image will be rendered at a small size on the device. This change adds a `resizeMethod` prop to the `Image` component so developers can choose how Fresco resizes the image. The options are 'auto', 'resize', or 'scale'. When 'auto' is specified, a heuristic is used to choose between 'resize' and 'scale'. The default value is 'auto'. **Test plan (required)** In a small test app, verified that the `resizeMethod` prop properly influences the mechanism that is used to resize the image (e.g. resize or scale). Adam Comella Microsoft Corp. Closes https://github.com/facebook/react-native/pull/9652 Differential Revision: D3841322 Pulled By: foghina fbshipit-source-id: 6c78b5c75ea73053aa10386afd4cbff45f5b8ffe --- Libraries/Image/Image.android.js | 21 +++++++++++++++++++ Libraries/Image/Image.ios.js | 20 ++++++++++++++++++ .../facebook/react/uimanager/ViewProps.java | 1 + .../react/views/image/ImageResizeMethod.java | 16 ++++++++++++++ .../react/views/image/ReactImageManager.java | 14 +++++++++++++ .../react/views/image/ReactImageView.java | 20 ++++++++++++++---- 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.java diff --git a/Libraries/Image/Image.android.js b/Libraries/Image/Image.android.js index 4eff9533efd..9b4ea4153c7 100644 --- a/Libraries/Image/Image.android.js +++ b/Libraries/Image/Image.android.js @@ -65,6 +65,7 @@ function generateRequestId() { var ImageViewAttributes = merge(ReactNativeViewAttributes.UIView, { src: true, loadingIndicatorSrc: true, + resizeMethod: true, resizeMode: true, progressiveRenderingEnabled: true, fadeDuration: true, @@ -130,6 +131,26 @@ var Image = React.createClass({ * Used to locate this view in end-to-end tests. */ testID: PropTypes.string, + /** + * The mechanism that should be used to resize the image when the image's dimensions + * differ from the image view's dimensions. Defaults to `auto`. + * + * - `auto`: Use heuristics to pick between `resize` and `scale`. + * + * - `resize`: A software operation which changes the encoded image in memory before it + * gets decoded. This should be used instead of `scale` when the image is much larger + * than the view. + * + * - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is + * faster (usually hardware accelerated) and produces higher quality images. This + * should be used if the image is smaller than the view. It should also be used if the + * image is slightly bigger than the view. + * + * More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html. + * + * @platform android + */ + resizeMethod: PropTypes.oneOf(['auto', 'resize', 'scale']), /** * Determines how to resize the image when the frame doesn't match the raw * image dimensions. diff --git a/Libraries/Image/Image.ios.js b/Libraries/Image/Image.ios.js index 8b969ad21e5..1a11c646f6d 100644 --- a/Libraries/Image/Image.ios.js +++ b/Libraries/Image/Image.ios.js @@ -192,6 +192,26 @@ const Image = React.createClass({ * @platform ios */ capInsets: EdgeInsetsPropType, + /** + * The mechanism that should be used to resize the image when the image's dimensions + * differ from the image view's dimensions. Defaults to `auto`. + * + * - `auto`: Use heuristics to pick between `resize` and `scale`. + * + * - `resize`: A software operation which changes the encoded image in memory before it + * gets decoded. This should be used instead of `scale` when the image is much larger + * than the view. + * + * - `scale`: The image gets drawn downscaled or upscaled. Compared to `resize`, `scale` is + * faster (usually hardware accelerated) and produces higher quality images. This + * should be used if the image is smaller than the view. It should also be used if the + * image is slightly bigger than the view. + * + * More details about `resize` and `scale` can be found at http://frescolib.org/docs/resizing-rotating.html. + * + * @platform android + */ + resizeMethod: PropTypes.oneOf(['auto', 'resize', 'scale']), /** * Determines how to resize the image when the frame doesn't match the raw * image dimensions. diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java index b5d384bcf5f..469e0c708c8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewProps.java @@ -78,6 +78,7 @@ public class ViewProps { public static final String ELLIPSIZE_MODE = "ellipsizeMode"; public static final String ON = "on"; public static final String RESIZE_MODE = "resizeMode"; + public static final String RESIZE_METHOD = "resizeMethod"; public static final String TEXT_ALIGN = "textAlign"; public static final String TEXT_ALIGN_VERTICAL = "textAlignVertical"; public static final String TEXT_DECORATION_LINE = "textDecorationLine"; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.java new file mode 100644 index 00000000000..ff366b0c289 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ImageResizeMethod.java @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +package com.facebook.react.views.image; + +public enum ImageResizeMethod { + AUTO, + RESIZE, + SCALE +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java index 731da55d2fb..59e7617c9f9 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java @@ -19,6 +19,7 @@ import android.graphics.PorterDuff.Mode; import com.facebook.csslayout.CSSConstants; import com.facebook.drawee.backends.pipeline.Fresco; import com.facebook.drawee.controller.AbstractDraweeControllerBuilder; +import com.facebook.react.bridge.JSApplicationIllegalArgumentException; import com.facebook.react.bridge.ReadableArray; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.PixelUtil; @@ -131,6 +132,19 @@ public class ReactImageManager extends SimpleViewManager { view.setScaleType(ImageResizeMode.toScaleType(resizeMode)); } + @ReactProp(name = ViewProps.RESIZE_METHOD) + public void setResizeMethod(ReactImageView view, @Nullable String resizeMethod) { + if (resizeMethod == null || "auto".equals(resizeMethod)) { + view.setResizeMethod(ImageResizeMethod.AUTO); + } else if ("resize".equals(resizeMethod)) { + view.setResizeMethod(ImageResizeMethod.RESIZE); + } else if ("scale".equals(resizeMethod)) { + view.setResizeMethod(ImageResizeMethod.SCALE); + } else { + throw new JSApplicationIllegalArgumentException("Invalid resize method: '" + resizeMethod+ "'"); + } + } + @ReactProp(name = "tintColor", customType = "Color") public void setTintColor(ReactImageView view, @Nullable Integer tintColor) { if (tintColor == null) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java index 3c6214b60b6..f519627b634 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java @@ -82,6 +82,7 @@ public class ReactImageView extends GenericDraweeView { */ private static final Matrix sMatrix = new Matrix(); private static final Matrix sInverse = new Matrix(); + private ImageResizeMethod mResizeMethod = ImageResizeMethod.AUTO; private class RoundedCornerPostprocessor extends BasePostprocessor { @@ -260,6 +261,11 @@ public class ReactImageView extends GenericDraweeView { mIsDirty = true; } + public void setResizeMethod(ImageResizeMethod resizeMethod) { + mResizeMethod = resizeMethod; + mIsDirty = true; + } + public void setSource(@Nullable ReadableArray sources) { mSources.clear(); if (sources != null && sources.size() != 0) { @@ -451,12 +457,18 @@ public class ReactImageView extends GenericDraweeView { mImageSource = mSources.get(0); } - private static boolean shouldResize(ImageSource imageSource) { + private boolean shouldResize(ImageSource imageSource) { // Resizing is inferior to scaling. See http://frescolib.org/docs/resizing-rotating.html#_ // We resize here only for images likely to be from the device's camera, where the app developer // has no control over the original size - return - UriUtil.isLocalContentUri(imageSource.getUri()) || - UriUtil.isLocalFileUri(imageSource.getUri()); + if (mResizeMethod == ImageResizeMethod.AUTO) { + return + UriUtil.isLocalContentUri(imageSource.getUri()) || + UriUtil.isLocalFileUri(imageSource.getUri()); + } else if (mResizeMethod == ImageResizeMethod.RESIZE) { + return true; + } else { + return false; + } } }