diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt index b5bb9e91ce08..94fc219de9d9 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/SkewMatrixHelper.kt @@ -8,6 +8,7 @@ package com.facebook.react.uimanager import android.graphics.Matrix +import com.facebook.react.bridge.NativeArray import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.ReadableType @@ -28,6 +29,10 @@ internal object SkewMatrixHelper { */ @JvmStatic fun isAffine2DTransformWithSkew(transforms: ReadableArray): Boolean { + // Read Fabric's arrays in C++ rather than crossing JNI for every element and key. + if (transforms is NativeArray) { + return TransformHelper.isAffine2DTransformWithSkew(transforms) + } if (isRawMatrixShorthand(transforms)) { return false } diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt index 8c713b98fbec..62720a34a8c8 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/TransformHelper.kt @@ -237,6 +237,14 @@ public object TransformHelper { return doubleArrayOf(newTranslateX, newTranslateY, newTranslateZ) } + /** Native implementation of [SkewMatrixHelper.isAffine2DTransformWithSkew]. */ + @JvmStatic + internal fun isAffine2DTransformWithSkew(transforms: NativeArray): Boolean = + nativeIsAffine2DTransformWithSkew(transforms) + + @JvmStatic + private external fun nativeIsAffine2DTransformWithSkew(transforms: NativeArray): Boolean + @JvmStatic private external fun nativeProcessTransform( transforms: NativeArray, diff --git a/packages/react-native/ReactAndroid/src/main/jni/react/jni/TransformHelper.cpp b/packages/react-native/ReactAndroid/src/main/jni/react/jni/TransformHelper.cpp index 33d8ffaa9917..222023a337f6 100644 --- a/packages/react-native/ReactAndroid/src/main/jni/react/jni/TransformHelper.cpp +++ b/packages/react-native/ReactAndroid/src/main/jni/react/jni/TransformHelper.cpp @@ -51,11 +51,49 @@ void processTransform( jResult->setRegion(0, MatrixSize, doubleTransform.data()); } +// Same result as SkewMatrixHelper.isAffine2DTransformWithSkew, but reads the +// array in place instead of crossing JNI for each element and key. +jboolean isAffine2DTransformWithSkew( + jni::alias_ref /*unused*/, + NativeArray* jTransforms) { + const auto& transforms = jTransforms->getArray(); + // The raw 16-element matrix shorthand used by Fabric LayoutAnimations. + if (transforms.size() == 16 && transforms[0].isNumber()) { + return JNI_FALSE; + } + bool hasSkewTransform = false; + for (const auto& transform : transforms) { + if (!transform.isObject() || transform.empty()) { + continue; + } + const auto& [key, value] = *transform.items().begin(); + if (!key.isString()) { + continue; + } + const auto& type = key.getString(); + if (type == "matrix" || type == "perspective" || type == "rotateX" || + type == "rotateY") { + return JNI_FALSE; + } + if (type == "translate") { + if (value.isArray() && value.size() > 2 && value[2].isNumber() && + value[2].asDouble() != 0.0) { + return JNI_FALSE; + } + } else if (type == "skewX" || type == "skewY") { + hasSkewTransform = true; + } + } + return hasSkewTransform ? JNI_TRUE : JNI_FALSE; +} + } // namespace void TransformHelper::registerNatives() { javaClassLocal()->registerNatives({ makeNativeMethod("nativeProcessTransform", processTransform), + makeNativeMethod( + "nativeIsAffine2DTransformWithSkew", isAffine2DTransformWithSkew), }); }