Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package com.facebook.react.uimanager;

import android.graphics.Color;
import android.graphics.Matrix;
import android.os.Build;
import android.text.TextUtils;
import android.view.View;
Expand All @@ -29,6 +30,7 @@
import com.facebook.react.uimanager.ReactAccessibilityDelegate.Role;
import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.PointerEventHelper;
import com.facebook.react.uimanager.util.HiddenApiUtil;
import com.facebook.react.uimanager.util.ReactFindViewUtil;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -497,6 +499,11 @@ protected void setTransformProperty(
view.setCameraDistance(0);
return;
}
Matrix matrix = TransformHelper.tryProcessTransformBySkiaMatrix(transforms,
view.getWidth(), view.getHeight(), transformOrigin);
if (matrix != null && HiddenApiUtil.setAnimationMatrix(view, matrix)) {
return;
}

sMatrixDecompositionContext.reset();
TransformHelper.processTransform(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,10 @@ public static double degreesToRadians(double degrees) {
return degrees * Math.PI / 180;
}

public static double radiansToDegrees(double radians) {
return radians * 180 / Math.PI;
}

public static void resetIdentityMatrix(double[] matrix) {
matrix[1] =
matrix[2] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

package com.facebook.react.uimanager;

import android.graphics.Matrix;

import com.facebook.common.logging.FLog;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
Expand Down Expand Up @@ -45,6 +47,23 @@ private static double convertToRadians(ReadableMap transformMap, String key) {
return inRadians ? value : MatrixMathHelper.degreesToRadians(value);
}

private static double convertToDegrees(ReadableMap transformMap, String key) {
double value;
boolean inRadians = true;
if (transformMap.getType(key) == ReadableType.String) {
String stringValue = transformMap.getString(key);
if (stringValue.endsWith("deg")) {
inRadians = false;
}
if (stringValue.endsWith("rad") || stringValue.endsWith("deg")) {
stringValue = stringValue.substring(0, stringValue.length() - 3);
}
value = Float.parseFloat(stringValue);
} else {
value = transformMap.getDouble(key);
}
return inRadians ? MatrixMathHelper.radiansToDegrees(value) : value;
}
public static void processTransform(ReadableArray transforms, double[] result) {
processTransform(transforms, result, 0, 0, null);
}
Expand Down Expand Up @@ -130,6 +149,76 @@ public static void processTransform(
}
}

public static Matrix tryProcessTransformBySkiaMatrix(ReadableArray transforms,
float viewWidth,
float viewHeight,
ReadableArray transformOrigin) {
if (transforms.size() == 16 && transforms.getType(0) == ReadableType.Number) {
return null;
} else {
// Check for unsupported types.
for (int transformIdx = 0, size = transforms.size(); transformIdx < size; transformIdx++) {
ReadableMap transform = transforms.getMap(transformIdx);
String transformType = transform.keySetIterator().nextKey();

if ("matrix".equals(transformType) || "perspective".equals(transformType)
|| "rotateX".equals(transformType) || "rotateY".equals(transformType)) {
return null;
} else if ("translate".equals(transformType)) {
ReadableArray value = transform.getArray(transformType);
if (value.size() > 2 && value.getDouble(2) != 0d) {
return null;
}
}
}

Matrix matrix = new Matrix();
float[] offsets = getTranslateForTransformOrigin(viewWidth, viewHeight, transformOrigin);
float originX = viewWidth / 2, originY = viewHeight / 2;
if (offsets == null) {
offsets = new float[]{0,0};
}
matrix.postTranslate(offsets[0], offsets[1]);
for (int transformIdx = 0, size = transforms.size(); transformIdx < size; transformIdx++) {
ReadableMap transform = transforms.getMap(transformIdx);
String transformType = transform.keySetIterator().nextKey();

if ("rotate".equals(transformType) || "rotateZ".equals(transformType)) {
matrix.postRotate((float) convertToDegrees(transform, transformType), originX, originY);
} else if ("scale".equals(transformType)) {
float scale = (float) transform.getDouble(transformType);
matrix.postScale(scale, scale, originX, originY);
} else if ("scaleX".equals(transformType)) {
matrix.postScale((float) transform.getDouble(transformType), 1, originX, originY);
} else if ("scaleY".equals(transformType)) {
matrix.postScale(1, (float) transform.getDouble(transformType), originX, originY);
} else if ("translate".equals(transformType)) {
ReadableArray value = transform.getArray(transformType);
double x = value.getDouble(0);
double y = value.getDouble(1);
originX += x;
originY += y;
matrix.postTranslate(PixelUtil.toPixelFromDIP((float) x),
PixelUtil.toPixelFromDIP((float) y));
} else if ("translateX".equals(transformType)) {
double x = transform.getDouble(transformType);
originX += x;
matrix.postTranslate(PixelUtil.toPixelFromDIP(x), 0);
} else if ("translateY".equals(transformType)) {
double y = transform.getDouble(transformType);
originY += y;
matrix.postTranslate(0, PixelUtil.toPixelFromDIP(y));
} else if ("skewX".equals(transformType)) {
matrix.postSkew((float) convertToRadians(transform, transformType), 0, originX, originY);
} else if ("skewY".equals(transformType)) {
matrix.postSkew(0, (float) convertToRadians(transform, transformType), originX, originY);
}
}
matrix.postTranslate(-offsets[0], -offsets[1]);
return matrix;
}
}

private static float[] getTranslateForTransformOrigin(
float viewWidth, float viewHeight, ReadableArray transformOrigin) {
if (transformOrigin == null || (viewHeight == 0 && viewWidth == 0)) {
Expand Down Expand Up @@ -163,4 +252,5 @@ private static float[] getTranslateForTransformOrigin(

return new float[] {newTranslateX, newTranslateY, newTranslateZ};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.facebook.react.uimanager.util;

import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.os.Build;
import android.view.View;

import androidx.annotation.DoNotInline;
import androidx.annotation.RequiresApi;

/**
* @see <a href="https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:transition/transition/src/main/java/androidx/transition/ViewUtilsApi21.java">
* AdnroidX transition library</a>
*/
public class HiddenApiUtil {

/**
* False when linking of the hidden setAnimationMatrix method has previously failed.
*/
private static boolean sTryHiddenSetAnimationMatrix = true;

private HiddenApiUtil() {
// This class is not instantiable.
}

@SuppressLint("NewApi") // Lint doesn't know about the hidden method.
public static boolean setAnimationMatrix(View view, Matrix matrix) {
if (sTryHiddenSetAnimationMatrix) {
// Since this was an @hide method made public, we can link directly against it with
// a try/catch for its absence instead of doing the same through reflection.
try {
setAnimationMatrixApi29(view, matrix);
return true;
} catch (NoSuchMethodError e) {
sTryHiddenSetAnimationMatrix = false;
}
}
return false;
}

@DoNotInline
@RequiresApi(api = Build.VERSION_CODES.Q)
private static void setAnimationMatrixApi29(View view, Matrix matrix) {
view.setAnimationMatrix(matrix);
}
}