mirror of
https://github.com/zoriya/react-native-svg.git
synced 2026-06-04 23:54:53 +00:00
Implement useNativeDriver support for animation of transform style
This commit is contained in:
@@ -42,6 +42,7 @@ class GroupShadowNode extends RenderableShadowNode {
|
||||
void setupGlyphContext(Canvas canvas) {
|
||||
RectF clipBounds = new RectF(canvas.getClipBounds());
|
||||
mMatrix.mapRect(clipBounds);
|
||||
mTransform.mapRect(clipBounds);
|
||||
mGlyphContext = new GlyphContext(mScale, clipBounds.width(), clipBounds.height());
|
||||
}
|
||||
|
||||
|
||||
@@ -28,19 +28,19 @@ import com.facebook.imagepipeline.image.CloseableBitmap;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||
import com.facebook.react.views.imagehelper.ImageSource;
|
||||
import com.facebook.react.views.imagehelper.ResourceDrawableIdHelper;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
|
||||
/**
|
||||
* Shadow node for virtual Image view
|
||||
*/
|
||||
@@ -127,9 +127,10 @@ class ImageShadowNode extends RenderableShadowNode {
|
||||
}
|
||||
|
||||
@ReactProp(name = "matrix")
|
||||
public void setMatrix(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
public void setMatrix(Dynamic matrixArray) {
|
||||
ReadableType type = matrixArray.getType();
|
||||
if (!matrixArray.isNull() && type.equals(ReadableType.Array)) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray.asArray(), sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
|
||||
@@ -9,18 +9,35 @@
|
||||
|
||||
package com.horcrux.svg;
|
||||
|
||||
import android.graphics.Matrix;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
import com.facebook.react.uimanager.MatrixMathHelper;
|
||||
import com.facebook.react.uimanager.PixelUtil;
|
||||
import com.facebook.react.uimanager.ThemedReactContext;
|
||||
import com.facebook.react.uimanager.TransformHelper;
|
||||
import com.facebook.react.uimanager.ViewGroupManager;
|
||||
import com.facebook.react.uimanager.annotations.ReactProp;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.determinant;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.inverse;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.multiplyVectorByMatrix;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.roundTo3Places;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.transpose;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Combine;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Cross;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Dot;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Length;
|
||||
import static com.facebook.react.uimanager.MatrixMathHelper.v3Normalize;
|
||||
import static com.horcrux.svg.RenderableShadowNode.CAP_ROUND;
|
||||
import static com.horcrux.svg.RenderableShadowNode.FILL_RULE_NONZERO;
|
||||
import static com.horcrux.svg.RenderableShadowNode.JOIN_ROUND;
|
||||
@@ -53,9 +70,207 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
||||
|
||||
private final String mClassName;
|
||||
|
||||
public static class MatrixDecompositionContext extends MatrixMathHelper.MatrixDecompositionContext {
|
||||
double[] perspective = new double[4];
|
||||
double[] scale = new double[3];
|
||||
double[] skew = new double[3];
|
||||
double[] translation = new double[3];
|
||||
double[] rotationDegrees = new double[3];
|
||||
}
|
||||
|
||||
private static MatrixDecompositionContext sMatrixDecompositionContext =
|
||||
new MatrixDecompositionContext();
|
||||
private static double[] sTransformDecompositionArray = new double[16];
|
||||
|
||||
private static final int PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX = 2;
|
||||
private static final float CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER = 5;
|
||||
|
||||
private static final double EPSILON = .00001d;
|
||||
|
||||
private static boolean isZero(double d) {
|
||||
if (Double.isNaN(d)) {
|
||||
return false;
|
||||
}
|
||||
return Math.abs(d) < EPSILON;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param transformMatrix 16-element array of numbers representing 4x4 transform matrix
|
||||
*/
|
||||
public static void decomposeMatrix(double[] transformMatrix, MatrixDecompositionContext ctx) {
|
||||
Assertions.assertCondition(transformMatrix.length == 16);
|
||||
|
||||
// output values
|
||||
final double[] perspective = ctx.perspective;
|
||||
final double[] scale = ctx.scale;
|
||||
final double[] skew = ctx.skew;
|
||||
final double[] translation = ctx.translation;
|
||||
final double[] rotationDegrees = ctx.rotationDegrees;
|
||||
|
||||
// create normalized, 2d array matrix
|
||||
// and normalized 1d array perspectiveMatrix with redefined 4th column
|
||||
if (isZero(transformMatrix[15])) {
|
||||
return;
|
||||
}
|
||||
double[][] matrix = new double[4][4];
|
||||
double[] perspectiveMatrix = new double[16];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
double value = transformMatrix[(i * 4) + j] / transformMatrix[15];
|
||||
matrix[i][j] = value;
|
||||
perspectiveMatrix[(i * 4) + j] = j == 3 ? 0 : value;
|
||||
}
|
||||
}
|
||||
perspectiveMatrix[15] = 1;
|
||||
|
||||
// test for singularity of upper 3x3 part of the perspective matrix
|
||||
if (isZero(determinant(perspectiveMatrix))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// isolate perspective
|
||||
if (!isZero(matrix[0][3]) || !isZero(matrix[1][3]) || !isZero(matrix[2][3])) {
|
||||
// rightHandSide is the right hand side of the equation.
|
||||
// rightHandSide is a vector, or point in 3d space relative to the origin.
|
||||
double[] rightHandSide = { matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3] };
|
||||
|
||||
// Solve the equation by inverting perspectiveMatrix and multiplying
|
||||
// rightHandSide by the inverse.
|
||||
double[] inversePerspectiveMatrix = inverse(
|
||||
perspectiveMatrix
|
||||
);
|
||||
double[] transposedInversePerspectiveMatrix = transpose(
|
||||
inversePerspectiveMatrix
|
||||
);
|
||||
multiplyVectorByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspective);
|
||||
} else {
|
||||
// no perspective
|
||||
perspective[0] = perspective[1] = perspective[2] = 0d;
|
||||
perspective[3] = 1d;
|
||||
}
|
||||
|
||||
// translation is simple
|
||||
for (int i = 0; i < 3; i++) {
|
||||
translation[i] = matrix[3][i];
|
||||
}
|
||||
|
||||
// Now get scale and shear.
|
||||
// 'row' is a 3 element array of 3 component vectors
|
||||
double[][] row = new double[3][3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
row[i][0] = matrix[i][0];
|
||||
row[i][1] = matrix[i][1];
|
||||
row[i][2] = matrix[i][2];
|
||||
}
|
||||
|
||||
// Compute X scale factor and normalize first row.
|
||||
scale[0] = v3Length(row[0]);
|
||||
row[0] = v3Normalize(row[0], scale[0]);
|
||||
|
||||
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||
skew[0] = v3Dot(row[0], row[1]);
|
||||
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||
|
||||
// Compute XY shear factor and make 2nd row orthogonal to 1st.
|
||||
skew[0] = v3Dot(row[0], row[1]);
|
||||
row[1] = v3Combine(row[1], row[0], 1.0, -skew[0]);
|
||||
|
||||
// Now, compute Y scale and normalize 2nd row.
|
||||
scale[1] = v3Length(row[1]);
|
||||
row[1] = v3Normalize(row[1], scale[1]);
|
||||
skew[0] /= scale[1];
|
||||
|
||||
// Compute XZ and YZ shears, orthogonalize 3rd row
|
||||
skew[1] = v3Dot(row[0], row[2]);
|
||||
row[2] = v3Combine(row[2], row[0], 1.0, -skew[1]);
|
||||
skew[2] = v3Dot(row[1], row[2]);
|
||||
row[2] = v3Combine(row[2], row[1], 1.0, -skew[2]);
|
||||
|
||||
// Next, get Z scale and normalize 3rd row.
|
||||
scale[2] = v3Length(row[2]);
|
||||
row[2] = v3Normalize(row[2], scale[2]);
|
||||
skew[1] /= scale[2];
|
||||
skew[2] /= scale[2];
|
||||
|
||||
// At this point, the matrix (in rows) is orthonormal.
|
||||
// Check for a coordinate system flip. If the determinant
|
||||
// is -1, then negate the matrix and the scaling factors.
|
||||
double[] pdum3 = v3Cross(row[1], row[2]);
|
||||
if (v3Dot(row[0], pdum3) < 0) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
scale[i] *= -1;
|
||||
row[i][0] *= -1;
|
||||
row[i][1] *= -1;
|
||||
row[i][2] *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now, get the rotations out
|
||||
// Based on: http://nghiaho.com/?page_id=846
|
||||
double conv = 180 / Math.PI;
|
||||
rotationDegrees[0] = roundTo3Places(-Math.atan2(row[2][1], row[2][2]) * conv);
|
||||
rotationDegrees[1] = roundTo3Places(-Math.atan2(-row[2][0], Math.sqrt(row[2][1] * row[2][1] + row[2][2] * row[2][2])) * conv);
|
||||
rotationDegrees[2] = roundTo3Places(-Math.atan2(row[1][0], row[0][0]) * conv);
|
||||
}
|
||||
|
||||
private static void setTransformProperty(View view, ReadableArray transforms) {
|
||||
TransformHelper.processTransform(transforms, sTransformDecompositionArray);
|
||||
decomposeMatrix(sTransformDecompositionArray, sMatrixDecompositionContext);
|
||||
view.setTranslationX(
|
||||
PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[0]));
|
||||
view.setTranslationY(
|
||||
PixelUtil.toPixelFromDIP((float) sMatrixDecompositionContext.translation[1]));
|
||||
view.setRotation((float) sMatrixDecompositionContext.rotationDegrees[2]);
|
||||
view.setRotationX((float) sMatrixDecompositionContext.rotationDegrees[0]);
|
||||
view.setRotationY((float) sMatrixDecompositionContext.rotationDegrees[1]);
|
||||
view.setScaleX((float) sMatrixDecompositionContext.scale[0]);
|
||||
view.setScaleY((float) sMatrixDecompositionContext.scale[1]);
|
||||
|
||||
double[] perspectiveArray = sMatrixDecompositionContext.perspective;
|
||||
|
||||
if (perspectiveArray.length > PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX) {
|
||||
float invertedCameraDistance = (float) perspectiveArray[PERSPECTIVE_ARRAY_INVERTED_CAMERA_DISTANCE_INDEX];
|
||||
if (invertedCameraDistance == 0) {
|
||||
// Default camera distance, before scale multiplier (1280)
|
||||
invertedCameraDistance = 0.00078125f;
|
||||
}
|
||||
float cameraDistance = -1 / invertedCameraDistance;
|
||||
float scale = DisplayMetricsHolder.getScreenDisplayMetrics().density;
|
||||
|
||||
// The following converts the matrix's perspective to a camera distance
|
||||
// such that the camera perspective looks the same on Android and iOS
|
||||
float normalizedCameraDistance = scale * cameraDistance * CAMERA_DISTANCE_NORMALIZATION_MULTIPLIER;
|
||||
view.setCameraDistance(normalizedCameraDistance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void resetTransformProperty(View view) {
|
||||
view.setTranslationX(PixelUtil.toPixelFromDIP(0));
|
||||
view.setTranslationY(PixelUtil.toPixelFromDIP(0));
|
||||
view.setRotation(0);
|
||||
view.setRotationX(0);
|
||||
view.setRotationY(0);
|
||||
view.setScaleX(1);
|
||||
view.setScaleY(1);
|
||||
view.setCameraDistance(0);
|
||||
}
|
||||
|
||||
static RenderableViewManager<GroupShadowNode> createGroupViewManager() {
|
||||
return new RenderableViewManager<>(CLASS_GROUP);
|
||||
return new RenderableViewManager<GroupShadowNode>(CLASS_GROUP){
|
||||
|
||||
@ReactProp(name = "transform")
|
||||
public void setTransform(RenderableView<GroupShadowNode> node, ReadableArray matrix) {
|
||||
|
||||
if (matrix == null) {
|
||||
resetTransformProperty(node);
|
||||
} else {
|
||||
setTransformProperty(node, matrix);
|
||||
Matrix m = node.getMatrix();
|
||||
node.shadowNode.mTransform = m;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static RenderableViewManager<PathShadowNode> createPathViewManager() {
|
||||
@@ -221,11 +436,6 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
||||
public void setMeetOrSlice(RenderableView<ImageShadowNode> node, int meetOrSlice) {
|
||||
node.shadowNode.setMeetOrSlice(meetOrSlice);
|
||||
}
|
||||
|
||||
@ReactProp(name = "matrix")
|
||||
public void setMatrix(RenderableView<ImageShadowNode> node, @Nullable ReadableArray matrixArray) {
|
||||
node.shadowNode.setMatrix(matrixArray);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -768,6 +978,11 @@ class RenderableViewManager<T extends VirtualNode> extends ViewGroupManager<Rend
|
||||
node.shadowNode.setStrokeLinejoin(strokeLinejoin);
|
||||
}
|
||||
|
||||
@ReactProp(name = "matrix")
|
||||
public void setMatrix(RenderableView<RenderableShadowNode> node, Dynamic matrixArray) {
|
||||
node.shadowNode.setMatrix(matrixArray);
|
||||
}
|
||||
|
||||
@ReactProp(name = "propList")
|
||||
public void setPropList(RenderableView<RenderableShadowNode> node, @Nullable ReadableArray propList) {
|
||||
node.shadowNode.setPropList(propList);
|
||||
|
||||
@@ -17,7 +17,9 @@ import android.graphics.RectF;
|
||||
import android.graphics.Region;
|
||||
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.Dynamic;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableType;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.uimanager.DisplayMetricsHolder;
|
||||
import com.facebook.react.uimanager.LayoutShadowNode;
|
||||
@@ -54,6 +56,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
||||
};
|
||||
float mOpacity = 1f;
|
||||
Matrix mMatrix = new Matrix();
|
||||
Matrix mTransform = new Matrix();
|
||||
Matrix mInvMatrix = new Matrix();
|
||||
boolean mInvertible = true;
|
||||
private RectF mClientRect;
|
||||
@@ -188,6 +191,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
||||
int saveAndSetupCanvas(Canvas canvas) {
|
||||
int count = canvas.save();
|
||||
canvas.concat(mMatrix);
|
||||
canvas.concat(mTransform);
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -234,9 +238,10 @@ abstract class VirtualNode extends LayoutShadowNode {
|
||||
}
|
||||
|
||||
@ReactProp(name = "matrix")
|
||||
public void setMatrix(@Nullable ReadableArray matrixArray) {
|
||||
if (matrixArray != null) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray, sRawMatrix, mScale);
|
||||
public void setMatrix(Dynamic matrixArray) {
|
||||
ReadableType type = matrixArray.getType();
|
||||
if (!matrixArray.isNull() && type.equals(ReadableType.Array)) {
|
||||
int matrixSize = PropHelper.toMatrixData(matrixArray.asArray(), sRawMatrix, mScale);
|
||||
if (matrixSize == 6) {
|
||||
if (mMatrix == null) {
|
||||
mMatrix = new Matrix();
|
||||
@@ -250,6 +255,7 @@ abstract class VirtualNode extends LayoutShadowNode {
|
||||
} else {
|
||||
mMatrix = null;
|
||||
mInvMatrix = null;
|
||||
mInvertible = false;
|
||||
}
|
||||
|
||||
super.markUpdated();
|
||||
|
||||
Reference in New Issue
Block a user