From 68b9a368584716470ab1d581025e9e2d3904a0fa Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Fri, 5 Aug 2016 10:48:34 -0700 Subject: [PATCH] Accept transforms list instead of matrix for transform view parameter. Summary: In #7916 I moved transform matrix decomposition logic from JS to java. The next step is to accept list of transforms instead oftransform matrix as a transform ReactProp. This way there is no extra processing required on JS side for the transform param (at least for android now) and this on the other hand allow us to execute transform updates (through offloaded animation) solely on the UI thread. After this change there is a whole bunch of stuff from `Libraries/Utilities/MatrixMath.js` that can be deleted (methods like: determinant, inverse, transpose). Although astreet mentioned under one of my previous commits that the code is still being referenced internally at fb, so I decided not to delete it here. **Test plan (required)** Run UIExplorer Transform example before and after - compare the results Run android unit test: com.facebook.react.uimanager.MatrixMathHelperTest Closes https://github.com/facebook/react-native/pull/8892 Differential Revision: D3676017 Pulled By: astreet fbshipit-source-id: 5275e30805a85c12c89bea44e8b3a2b2ec7b33fa --- Libraries/StyleSheet/processTransform.js | 22 +++- .../react/uimanager/BaseViewManager.java | 13 +-- .../react/uimanager/MatrixMathHelper.java | 110 +++++++++++++++++- .../react/uimanager/TransformHelper.java | 99 ++++++++++++++++ .../react/uimanager/MatrixMathHelperTest.java | 15 +-- 5 files changed, 234 insertions(+), 25 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.java diff --git a/Libraries/StyleSheet/processTransform.js b/Libraries/StyleSheet/processTransform.js index fc44b5f8c..bddd54f54 100644 --- a/Libraries/StyleSheet/processTransform.js +++ b/Libraries/StyleSheet/processTransform.js @@ -26,14 +26,22 @@ var stringifySafe = require('stringifySafe'); * interface to native code. */ function processTransform(transform: Object): Object { + if (__DEV__) { + _validateTransforms(transform); + } + + // Android implementation of transform property accepts the list of transform + // properties as opposed to a transform Matrix. This is necessary to control + // transform property updates completely on the native thread. + if (Platform.OS === 'android') { + return transform; + } + var result = MatrixMath.createIdentityMatrix(); transform.forEach(transformation => { var key = Object.keys(transformation)[0]; var value = transformation[key]; - if (__DEV__) { - _validateTransform(key, value, transformation); - } switch (key) { case 'matrix': @@ -107,6 +115,14 @@ function _convertToRadians(value: string): number { return value.indexOf('rad') > -1 ? floatValue : floatValue * Math.PI / 180; } +function _validateTransforms(transform: Object): void { + transform.forEach(transformation => { + var key = Object.keys(transformation)[0]; + var value = transformation[key]; + _validateTransform(key, value, transformation); + }); +} + function _validateTransform(key, value, transformation) { invariant( !value.getValue, diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java index 4586bf78b..815bad0fa 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java @@ -8,7 +8,6 @@ import android.view.View; import android.view.ViewGroup; import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.uimanager.annotations.ReactProp; /** @@ -53,9 +52,9 @@ public abstract class BaseViewManager sHelperMatrix = new ThreadLocal() { + @Override + protected double[] initialValue() { + return new double[16]; + } + }; + + private static double convertToRadians(ReadableMap transformMap, String key) { + double value; + boolean inRadians = false; + if (transformMap.getType(key) == ReadableType.String) { + String stringValue = transformMap.getString(key); + if (stringValue.endsWith("rad")) { + inRadians = true; + stringValue = stringValue.substring(0, stringValue.length() - 3); + } else if (stringValue.endsWith("deg")) { + stringValue = stringValue.substring(0, stringValue.length() - 3); + } + value = Float.parseFloat(stringValue); + } else { + value = transformMap.getDouble(key); + } + return inRadians ? value : MatrixMathHelper.degreesToRadians(value); + } + + public static void processTransform(ReadableArray transforms, double[] result) { + double[] helperMatrix = sHelperMatrix.get(); + MatrixMathHelper.resetIdentityMatrix(result); + + for (int transformIdx = 0, size = transforms.size(); transformIdx < size; transformIdx++) { + ReadableMap transform = transforms.getMap(transformIdx); + String transformType = transform.keySetIterator().nextKey(); + + MatrixMathHelper.resetIdentityMatrix(helperMatrix); + if ("matrix".equals(transformType)) { + ReadableArray matrix = transform.getArray(transformType); + for (int i = 0; i < 16; i++) { + helperMatrix[i] = matrix.getDouble(i); + } + } else if ("perspective".equals(transformType)) { + MatrixMathHelper.applyPerspective(helperMatrix, transform.getDouble(transformType)); + } else if ("rotateX".equals(transformType)) { + MatrixMathHelper.applyRotateX( + helperMatrix, + convertToRadians(transform, transformType)); + } else if ("rotateY".equals(transformType)) { + MatrixMathHelper.applyRotateY( + helperMatrix, + convertToRadians(transform, transformType)); + } else if ("rotate".equals(transformType) || "rotateZ".equals(transformType)) { + MatrixMathHelper.applyRotateZ( + helperMatrix, + convertToRadians(transform, transformType)); + } else if ("scale".equals(transformType)) { + MatrixMathHelper.applyScaleZ(helperMatrix, transform.getDouble(transformType)); + } else if ("scaleX".equals(transformType)) { + MatrixMathHelper.applyScaleX(helperMatrix, transform.getDouble(transformType)); + } else if ("scaleY".equals(transformType)) { + MatrixMathHelper.applyScaleY(helperMatrix, transform.getDouble(transformType)); + } else if ("translate".equals(transformType)) { + ReadableArray value = transform.getArray(transformType); + double x = value.getDouble(0); + double y = value.getDouble(1); + double z = value.size() > 2 ? value.getDouble(2) : 0d; + MatrixMathHelper.applyTranslate3D(helperMatrix, x, y, z); + } else if ("translateX".equals(transformType)) { + MatrixMathHelper.applyTranslate2D(helperMatrix, transform.getDouble(transformType), 0d); + } else if ("translateY".equals(transformType)) { + MatrixMathHelper.applyTranslate2D(helperMatrix, 0d, transform.getDouble(transformType)); + } else if ("skewX".equals(transformType)) { + MatrixMathHelper.applySkewX( + helperMatrix, + convertToRadians(transform, transformType)); + } else if ("skewY".equals(transformType)) { + MatrixMathHelper.applySkewY( + helperMatrix, + convertToRadians(transform, transformType)); + } else { + throw new JSApplicationIllegalArgumentException("Unsupported transform type: " + + transformType); + } + + MatrixMathHelper.multiplyInto(result, result, helperMatrix); + } + } +} diff --git a/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.java b/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.java index 66e9a2144..c7d332813 100644 --- a/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.java +++ b/ReactAndroid/src/test/java/com/facebook/react/uimanager/MatrixMathHelperTest.java @@ -114,21 +114,12 @@ public class MatrixMathHelperTest { verifyXRotatedMatrix(360, 0d, 0d, 0d); } - private static double[] createIdentityMatrix() { - return new double[] { - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 - }; - } - private static double degreesToRadians(double degrees) { return degrees * Math.PI / 180; } private static double[] createRotateZ(double radians) { - double[] mat = createIdentityMatrix(); + double[] mat = MatrixMathHelper.createIdentityMatrix(); mat[0] = Math.cos(radians); mat[1] = Math.sin(radians); mat[4] = -Math.sin(radians); @@ -137,7 +128,7 @@ public class MatrixMathHelperTest { } private static double[] createRotateY(double radians) { - double[] mat = createIdentityMatrix(); + double[] mat = MatrixMathHelper.createIdentityMatrix(); mat[0] = Math.cos(radians); mat[2] = -Math.sin(radians); mat[8] = Math.sin(radians); @@ -146,7 +137,7 @@ public class MatrixMathHelperTest { } private static double[] createRotateX(double radians) { - double[] mat = createIdentityMatrix(); + double[] mat = MatrixMathHelper.createIdentityMatrix(); mat[5] = Math.cos(radians); mat[6] = Math.sin(radians); mat[9] = -Math.sin(radians);