From 0c97e75dfeec831abb6cb39889309d8299cdce9f Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Mon, 30 Jul 2018 09:30:51 -0700 Subject: [PATCH] Add `YogaNodeProperties` implementation based on `ByteBuffer` Summary: @public Adds an implementation of `YogaNodeProperties` that accesses style and layout properties using a `ByteBuffer` rather than JNI calls. We hope for a speed improvement. This needs further cleanup after experimenting, e.g. to codegen the offsets. Reviewed By: pasqualeanatriello Differential Revision: D8911723 fbshipit-source-id: 3c24b57eb545155878896ebb5d64d4553eb6bedc --- .../main/java/com/facebook/yoga/YogaNode.java | 41 +- .../facebook/yoga/YogaNodeMemoryLayout.java | 151 ++++++ .../com/facebook/yoga/YogaNodeProperties.java | 2 +- .../yoga/YogaNodePropertiesByteBuffer.java | 510 ++++++++++++++++++ .../facebook/yoga/YogaNodePropertiesJNI.java | 3 +- .../java/com/facebook/yoga/YogaValue.java | 4 +- .../jni/first-party/yogajni/jni/YGJNI.cpp | 57 +- 7 files changed, 749 insertions(+), 19 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeMemoryLayout.java create mode 100644 ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java index 18c49cfd57994b..1729701937b119 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNode.java @@ -18,12 +18,10 @@ public class YogaNode implements Cloneable { static { - SoLoader.loadLibrary("yoga"); + SoLoader.loadLibrary("yoga"); } - /** - * Get native instance count. Useful for testing only. - */ + /** Get native instance count. Useful for testing only. */ static native int jni_YGNodeGetInstanceCount(); private YogaNodeProperties mDelegate; @@ -41,6 +39,14 @@ public YogaNode(YogaConfig config) { mDelegate = new YogaNodePropertiesJNI(this, config); } + public YogaNode(boolean unsafeClownyUseByteBufferValueDoesNotMatter) { + mDelegate = new YogaNodePropertiesByteBuffer(this); + } + + public YogaNode(boolean unsafeClownyUseByteBufferValueDoesNotMatter, YogaConfig config) { + mDelegate = new YogaNodePropertiesByteBuffer(this, config); + } + public long getNativePointer() { return mDelegate.getNativePointer(); } @@ -69,6 +75,7 @@ public YogaNode getChildAt(int i) { } private native void jni_YGNodeInsertChild(long nativePointer, long childPointer, int index); + public void addChildAt(YogaNode child, int i) { if (child.mOwner != null) { throw new IllegalStateException("Child already has a parent, it must be removed first."); @@ -144,6 +151,7 @@ private void clearChildren() { } private native void jni_YGNodeRemoveChild(long nativePointer, long childPointer); + public YogaNode removeChildAt(int i) { if (mChildren == null) { throw new IllegalStateException( @@ -156,12 +164,10 @@ public YogaNode removeChildAt(int i) { } /** - * @returns the {@link YogaNode} that owns this {@link YogaNode}. - * The owner is used to identify the YogaTree that a {@link YogaNode} belongs - * to. - * This method will return the parent of the {@link YogaNode} when the - * {@link YogaNode} only belongs to one YogaTree or null when the - * {@link YogaNode} is shared between two or more YogaTrees. + * @returns the {@link YogaNode} that owns this {@link YogaNode}. The owner is used to identify + * the YogaTree that a {@link YogaNode} belongs to. This method will return the parent of the + * {@link YogaNode} when the {@link YogaNode} only belongs to one YogaTree or null when the + * {@link YogaNode} is shared between two or more YogaTrees. */ @Nullable public YogaNode getOwner() { @@ -179,10 +185,11 @@ public int indexOf(YogaNode child) { return mChildren == null ? -1 : mChildren.indexOf(child); } - private native void jni_YGNodeCalculateLayout(long nativePointer, float width, float height); + private native boolean jni_YGNodeCalculateLayout(long nativePointer, float width, float height); + public void calculateLayout(float width, float height) { - jni_YGNodeCalculateLayout(getNativePointer(), width, height); - mDelegate.onAfterCalculateLayout(); + boolean hasNewLayout = jni_YGNodeCalculateLayout(getNativePointer(), width, height); + mDelegate.onAfterCalculateLayout(hasNewLayout); } public boolean hasNewLayout() { @@ -190,6 +197,7 @@ public boolean hasNewLayout() { } private native void jni_YGNodeMarkDirty(long nativePointer); + public void dirty() { jni_YGNodeMarkDirty(getNativePointer()); } @@ -205,6 +213,7 @@ public boolean isDirty() { } private native void jni_YGNodeCopyStyle(long dstNativePointer, long srcNativePointer); + public void copyStyle(YogaNode srcNode) { jni_YGNodeCopyStyle(getNativePointer(), srcNode.getNativePointer()); } @@ -498,6 +507,7 @@ public YogaDirection getLayoutDirection() { } private native void jni_YGNodeSetHasMeasureFunc(long nativePointer, boolean hasMeasureFunc); + public void setMeasureFunction(YogaMeasureFunction measureFunction) { mMeasureFunction = measureFunction; jni_YGNodeSetHasMeasureFunc(getNativePointer(), measureFunction != null); @@ -523,6 +533,7 @@ public final long measure(float width, int widthMode, float height, int heightMo } private native void jni_YGNodeSetHasBaselineFunc(long nativePointer, boolean hasMeasureFunc); + public void setBaselineFunction(YogaBaselineFunction baselineFunction) { mBaselineFunction = baselineFunction; jni_YGNodeSetHasBaselineFunc(getNativePointer(), baselineFunction != null); @@ -548,8 +559,8 @@ public Object getData() { private native void jni_YGNodePrint(long nativePointer); /** - * Use the set logger (defaults to adb log) to print out the styles, children, and computed - * layout of the tree rooted at this node. + * Use the set logger (defaults to adb log) to print out the styles, children, and computed layout + * of the tree rooted at this node. */ public void print() { jni_YGNodePrint(getNativePointer()); diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeMemoryLayout.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeMemoryLayout.java new file mode 100644 index 00000000000000..212c27ec042978 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeMemoryLayout.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2014-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ + +package com.facebook.yoga; + +import com.facebook.proguard.annotations.DoNotStrip; +import java.nio.ByteBuffer; + +@DoNotStrip +/* package */ final class YogaNodeMemoryLayout { + + private static final int FLOAT_SIZE = 4; + private static final int INT_SIZE = 4; + private static final int VALUE_SIZE = FLOAT_SIZE + INT_SIZE; + private static final byte FALSE = 0; + private static final byte TRUE = 1; + private static final int AUTO = YogaUnit.AUTO.intValue(); + private static final int POINT = YogaUnit.POINT.intValue(); + private static final int PERCENT = YogaUnit.PERCENT.intValue(); + private static final int UNDEFINED = YogaUnit.UNDEFINED.intValue(); + + // TODO(davidaurelio) code-gen these values + static final int styleDirection = 0; + static final int styleFlexDirection = 4; + static final int styleJustifyContent = 8; + static final int styleAlignContent = 12; + static final int styleAlignItems = 16; + static final int styleAlignSelf = 20; + static final int stylePositionType = 24; + static final int styleFlexWrap = 28; + static final int styleOverflow = 32; + static final int styleDisplay = 36; + static final int styleFlex = 40; + static final int styleFlexGrow = 48; + static final int styleFlexShrink = 56; + static final int styleFlexBasis = 64; + static final int styleMargin = 72; + static final int stylePosition = 144; + static final int stylePadding = 216; + static final int styleBorder = 288; + static final int styleDimensions = 360; + static final int styleMinDimensions = 376; + static final int styleMaxDimensions = 392; + static final int styleAspectRatio = 408; + + static final int styleWidth = styleDimensions; + static final int styleHeight = styleDimensions + VALUE_SIZE; + static final int styleMinWidth = styleMinDimensions; + static final int styleMinHeight = styleMinDimensions + VALUE_SIZE; + static final int styleMaxWidth = styleMaxDimensions; + static final int styleMaxHeight = styleMaxDimensions + VALUE_SIZE; + + static final int layoutPosition = 0; + static final int layoutDimensions = 16; + static final int layoutMargin = 24; + static final int layoutBorder = 48; + static final int layoutPadding = 72; + static final int layoutDirection = 96; + static final int layoutComputedFlexBasisGeneration = 100; + static final int layoutComputedFlexBasis = 104; + static final int layoutHadOverflow = 112; + static final int layoutGenerationCount = 116; + static final int layoutLastOwnerDirection = 120; + static final int layoutNextCachedMeasurementsIndex = 124; + static final int layoutCachedMeasurements = 128; + static final int layoutMeasuredDimensions = 512; + static final int layoutCachedLayout = 520; + static final int layoutDidUseLegacyFlag = 544; + static final int layoutDoesLegacyStretchFlagAffectsLayout = 545; + + static final int layoutX = layoutPosition; + static final int layoutY = layoutPosition + FLOAT_SIZE; + static final int layoutWidth = layoutDimensions; + static final int layoutHeight = layoutDimensions + FLOAT_SIZE; + + static int stylePositionOffset(YogaEdge edge) { + return stylePosition + edge.intValue() * VALUE_SIZE; + } + + static int styleMarginOffset(YogaEdge edge) { + return styleMargin + edge.intValue() * VALUE_SIZE; + } + + static int layoutMarginOffset(YogaEdge edge) { + return layoutMargin + edge.intValue() * FLOAT_SIZE; + } + + static int stylePaddingOffset(YogaEdge edge) { + return stylePadding + edge.intValue() * VALUE_SIZE; + } + + static int layoutPaddingOffset(YogaEdge edge) { + return layoutPadding + edge.intValue() * FLOAT_SIZE; + } + + static int styleBorderOffset(YogaEdge edge) { + return styleBorder + edge.intValue() * VALUE_SIZE; + } + + static int layoutBorderOffset(YogaEdge edge) { + return layoutBorder + edge.intValue() * FLOAT_SIZE; + } + + static void putOptional(ByteBuffer buffer, int offset, float value) { + buffer.putFloat(offset, value); + buffer.put( + offset + FLOAT_SIZE, YogaConstants.isUndefined(value) ? TRUE : FALSE); // bool isUndefined_ + } + + static float getOptional(ByteBuffer buffer, int offset) { + return getBoolean(buffer, offset + FLOAT_SIZE) + ? YogaConstants.UNDEFINED + : buffer.getFloat(offset); + } + + private static void putValue(ByteBuffer buffer, int offset, float value, int unit) { + if (YogaConstants.isUndefined(value)) { + value = YogaConstants.UNDEFINED; + unit = UNDEFINED; + } + buffer.putFloat(offset, value); + buffer.putInt(offset + FLOAT_SIZE, unit); + } + + static void putAutoValue(ByteBuffer buffer, int offset) { + putValue(buffer, offset, 0, AUTO); + } + + static void putPointValue(ByteBuffer buffer, int offset, float value) { + putValue(buffer, offset, value, POINT); + } + + static void putPercentValue(ByteBuffer buffer, int offset, float value) { + putValue(buffer, offset, value, PERCENT); + } + + static YogaValue getValue(ByteBuffer buffer, int offset) { + float value = buffer.getFloat(offset); + int unit = buffer.getInt(offset + FLOAT_SIZE); + return new YogaValue(value, YogaUnit.fromInt(unit)); + } + + static boolean getBoolean(ByteBuffer buffer, int offset) { + return buffer.get(offset) != 0; + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeProperties.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeProperties.java index 834aa83394e6c0..c93145e49f8c2d 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeProperties.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodeProperties.java @@ -13,7 +13,7 @@ public interface YogaNodeProperties { long getNativePointer(); - void onAfterCalculateLayout(); + void onAfterCalculateLayout(boolean hasNewLayout); void reset(); diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java new file mode 100644 index 00000000000000..b792062bfe4b99 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesByteBuffer.java @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the LICENSE + * file in the root directory of this source tree. + * + */ +package com.facebook.yoga; + +import com.facebook.proguard.annotations.DoNotStrip; +import com.facebook.soloader.SoLoader; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +@DoNotStrip +public class YogaNodePropertiesByteBuffer implements YogaNodeProperties, Cloneable { + + static { + SoLoader.loadLibrary("yoga"); + } + + private static final int RTL = YogaDirection.RTL.intValue(); + private final ByteBuffer mStyleBuffer; + private final ByteBuffer mLayoutBuffer; + private final long mNativePointer; + private boolean mHasBorderSet = false; + private boolean mHasNewLayout = true; + + private static native ByteBuffer jni_getStyleBuffer(long nativePointer); + + private static native ByteBuffer jni_getLayoutBuffer(long nativePointer); + + private static native long jni_YGNodeNewByteBuffer(YogaNode node); + + public YogaNodePropertiesByteBuffer(YogaNode node) { + this(jni_YGNodeNewByteBuffer(node)); + } + + private static native long jni_YGNodeNewByteBufferWithConfig(YogaNode node, long configPointer); + + public YogaNodePropertiesByteBuffer(YogaNode node, YogaConfig config) { + this(jni_YGNodeNewByteBufferWithConfig(node, config.mNativePointer)); + } + + public YogaNodePropertiesByteBuffer(long nativePointer) { + mNativePointer = nativePointer; + mStyleBuffer = jni_getStyleBuffer(nativePointer).order(ByteOrder.LITTLE_ENDIAN); + mLayoutBuffer = jni_getLayoutBuffer(nativePointer).order(ByteOrder.LITTLE_ENDIAN); + } + + private static native void jni_YGNodeFree(long nativePointer); + + @Override + protected void finalize() throws Throwable { + try { + jni_YGNodeFree(getNativePointer()); + } finally { + super.finalize(); + } + } + + private static native long jni_YGNodeCloneNoProps(long nativePointer, YogaNode newNode); + + @Override + public YogaNodeProperties clone(YogaNode node) { + long clonedNativePointer = jni_YGNodeCloneNoProps(getNativePointer(), node); + YogaNodePropertiesByteBuffer clone = new YogaNodePropertiesByteBuffer(clonedNativePointer); + clone.mHasBorderSet = mHasBorderSet; + clone.mHasNewLayout = mHasNewLayout; + return clone; + } + + @Override + public long getNativePointer() { + return mNativePointer; + } + + @Override + public void onAfterCalculateLayout(boolean hasNewLayout) { + mHasNewLayout = hasNewLayout; + } + + private static native void jni_YGNodeReset(long nativePointer); + + @Override + public void reset() { + mHasBorderSet = false; + jni_YGNodeReset(getNativePointer()); + } + + @Override + public boolean hasNewLayout() { + return mHasNewLayout; + } + + private static native boolean jni_YGNodeIsDirty(long nativePointer); + + @Override + public boolean isDirty() { + return jni_YGNodeIsDirty(mNativePointer); + } + + @Override + public void markLayoutSeen() { + mHasNewLayout = false; + } + + @Override + public YogaDirection getStyleDirection() { + return YogaDirection.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleDirection)); + } + + @Override + public YogaValue getPosition(YogaEdge edge) { + return YogaNodeMemoryLayout.getValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePositionOffset(edge)); + } + + @Override + public YogaValue getMargin(YogaEdge edge) { + return YogaNodeMemoryLayout.getValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMarginOffset(edge)); + } + + @Override + public YogaValue getPadding(YogaEdge edge) { + return YogaNodeMemoryLayout.getValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePaddingOffset(edge)); + } + + @Override + public float getBorder(YogaEdge edge) { + return mHasBorderSet + ? mStyleBuffer.getFloat(YogaNodeMemoryLayout.styleBorderOffset(edge)) + : YogaConstants.UNDEFINED; + } + + @Override + public void setDirection(YogaDirection direction) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleDirection, direction.intValue()); + } + + @Override + public YogaFlexDirection getFlexDirection() { + return YogaFlexDirection.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleFlexDirection)); + } + + @Override + public void setFlexDirection(YogaFlexDirection flexDirection) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleFlexDirection, flexDirection.intValue()); + } + + @Override + public YogaJustify getJustifyContent() { + return YogaJustify.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleJustifyContent)); + } + + @Override + public void setJustifyContent(YogaJustify justifyContent) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleJustifyContent, justifyContent.intValue()); + } + + @Override + public YogaAlign getAlignItems() { + return YogaAlign.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleAlignItems)); + } + + @Override + public void setAlignItems(YogaAlign alignItems) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleAlignItems, alignItems.intValue()); + } + + @Override + public YogaAlign getAlignSelf() { + return YogaAlign.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleAlignSelf)); + } + + @Override + public void setAlignSelf(YogaAlign alignSelf) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleAlignSelf, alignSelf.intValue()); + } + + @Override + public YogaAlign getAlignContent() { + return YogaAlign.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleAlignContent)); + } + + @Override + public void setAlignContent(YogaAlign alignContent) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleAlignContent, alignContent.intValue()); + } + + @Override + public YogaPositionType getPositionType() { + return YogaPositionType.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.stylePositionType)); + } + + @Override + public void setPositionType(YogaPositionType positionType) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.stylePositionType, positionType.intValue()); + } + + @Override + public void setWrap(YogaWrap flexWrap) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleFlexWrap, flexWrap.intValue()); + } + + @Override + public YogaOverflow getOverflow() { + return YogaOverflow.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleOverflow)); + } + + @Override + public void setOverflow(YogaOverflow overflow) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleOverflow, overflow.intValue()); + } + + @Override + public YogaDisplay getDisplay() { + return YogaDisplay.fromInt(mStyleBuffer.getInt(YogaNodeMemoryLayout.styleDisplay)); + } + + @Override + public void setDisplay(YogaDisplay display) { + mStyleBuffer.putInt(YogaNodeMemoryLayout.styleDisplay, display.intValue()); + } + + @Override + public void setFlex(float flex) { + YogaNodeMemoryLayout.putOptional(mStyleBuffer, YogaNodeMemoryLayout.styleFlex, flex); + } + + @Override + public float getFlexGrow() { + return mStyleBuffer.getFloat(YogaNodeMemoryLayout.styleFlexGrow); + } + + @Override + public void setFlexGrow(float flexGrow) { + YogaNodeMemoryLayout.putOptional(mStyleBuffer, YogaNodeMemoryLayout.styleFlexGrow, flexGrow); + } + + @Override + public float getFlexShrink() { + return mStyleBuffer.getFloat(YogaNodeMemoryLayout.styleFlexShrink); + } + + @Override + public void setFlexShrink(float flexShrink) { + YogaNodeMemoryLayout.putOptional( + mStyleBuffer, YogaNodeMemoryLayout.styleFlexShrink, flexShrink); + } + + @Override + public YogaValue getFlexBasis() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleFlexBasis); + } + + @Override + public void setFlexBasis(float flexBasis) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.styleFlexBasis, flexBasis); + } + + @Override + public void setFlexBasisPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.styleFlexBasis, percent); + } + + @Override + public void setFlexBasisAuto() { + YogaNodeMemoryLayout.putAutoValue(mStyleBuffer, YogaNodeMemoryLayout.styleFlexBasis); + } + + @Override + public void setMargin(YogaEdge edge, float margin) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMarginOffset(edge), margin); + } + + @Override + public void setMarginPercent(YogaEdge edge, float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMarginOffset(edge), percent); + } + + @Override + public void setMarginAuto(YogaEdge edge) { + YogaNodeMemoryLayout.putAutoValue(mStyleBuffer, YogaNodeMemoryLayout.styleMarginOffset(edge)); + } + + @Override + public void setPadding(YogaEdge edge, float padding) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePaddingOffset(edge), padding); + } + + @Override + public void setPaddingPercent(YogaEdge edge, float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePaddingOffset(edge), percent); + } + + @Override + public void setBorder(YogaEdge edge, float border) { + mHasBorderSet = true; + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.styleBorderOffset(edge), border); + } + + @Override + public void setPosition(YogaEdge edge, float position) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePositionOffset(edge), position); + } + + @Override + public void setPositionPercent(YogaEdge edge, float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.stylePositionOffset(edge), percent); + } + + @Override + public YogaValue getWidth() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleWidth); + } + + @Override + public void setWidth(float width) { + YogaNodeMemoryLayout.putPointValue(mStyleBuffer, YogaNodeMemoryLayout.styleWidth, width); + } + + @Override + public void setWidthPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue(mStyleBuffer, YogaNodeMemoryLayout.styleWidth, percent); + } + + @Override + public void setWidthAuto() { + YogaNodeMemoryLayout.putAutoValue(mStyleBuffer, YogaNodeMemoryLayout.styleWidth); + } + + @Override + public YogaValue getHeight() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleHeight); + } + + @Override + public void setHeight(float height) { + YogaNodeMemoryLayout.putPointValue(mStyleBuffer, YogaNodeMemoryLayout.styleHeight, height); + } + + @Override + public void setHeightPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue(mStyleBuffer, YogaNodeMemoryLayout.styleHeight, percent); + } + + @Override + public void setHeightAuto() { + YogaNodeMemoryLayout.putAutoValue(mStyleBuffer, YogaNodeMemoryLayout.styleHeight); + } + + @Override + public YogaValue getMinWidth() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleMinWidth); + } + + @Override + public void setMinWidth(float minWidth) { + YogaNodeMemoryLayout.putPointValue(mStyleBuffer, YogaNodeMemoryLayout.styleMinWidth, minWidth); + } + + @Override + public void setMinWidthPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue(mStyleBuffer, YogaNodeMemoryLayout.styleMinWidth, percent); + } + + @Override + public YogaValue getMinHeight() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleMinHeight); + } + + @Override + public void setMinHeight(float minHeight) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMinHeight, minHeight); + } + + @Override + public void setMinHeightPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMinHeight, percent); + } + + @Override + public YogaValue getMaxWidth() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleMaxWidth); + } + + @Override + public void setMaxWidth(float maxWidth) { + YogaNodeMemoryLayout.putPointValue(mStyleBuffer, YogaNodeMemoryLayout.styleMaxWidth, maxWidth); + } + + @Override + public void setMaxWidthPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue(mStyleBuffer, YogaNodeMemoryLayout.styleMaxWidth, percent); + } + + @Override + public YogaValue getMaxHeight() { + return YogaNodeMemoryLayout.getValue(mStyleBuffer, YogaNodeMemoryLayout.styleMaxHeight); + } + + @Override + public void setMaxHeight(float maxHeight) { + YogaNodeMemoryLayout.putPointValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMaxHeight, maxHeight); + } + + @Override + public void setMaxHeightPercent(float percent) { + YogaNodeMemoryLayout.putPercentValue( + mStyleBuffer, YogaNodeMemoryLayout.styleMaxHeight, percent); + } + + @Override + public float getAspectRatio() { + return YogaNodeMemoryLayout.getOptional(mStyleBuffer, YogaNodeMemoryLayout.styleAspectRatio); + } + + @Override + public void setAspectRatio(float aspectRatio) { + YogaNodeMemoryLayout.putOptional( + mStyleBuffer, YogaNodeMemoryLayout.styleAspectRatio, aspectRatio); + } + + @Override + public float getLayoutX() { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutX); + } + + @Override + public float getLayoutY() { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutY); + } + + @Override + public float getLayoutWidth() { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutWidth); + } + + @Override + public float getLayoutHeight() { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutHeight); + } + + @Override + public boolean getDoesLegacyStretchFlagAffectsLayout() { + return YogaNodeMemoryLayout.getBoolean( + mLayoutBuffer, YogaNodeMemoryLayout.layoutDoesLegacyStretchFlagAffectsLayout); + } + + @Override + public float getLayoutMargin(YogaEdge edge) { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutMarginOffset(layoutEdge(edge))); + } + + @Override + public float getLayoutPadding(YogaEdge edge) { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutPaddingOffset(layoutEdge(edge))); + } + + @Override + public float getLayoutBorder(YogaEdge edge) { + return mLayoutBuffer.getFloat(YogaNodeMemoryLayout.layoutBorderOffset(layoutEdge(edge))); + } + + @Override + public YogaDirection getLayoutDirection() { + return YogaDirection.fromInt(getLayoutDirectionInt()); + } + + @Override + public void freeNatives() { + jni_YGNodeFree(mNativePointer); + } + + private int getLayoutDirectionInt() { + return mLayoutBuffer.getInt(YogaNodeMemoryLayout.layoutDirection); + } + + private YogaEdge layoutEdge(YogaEdge edge) { + int layoutDirection = getLayoutDirectionInt(); + switch (edge) { + case LEFT: + return layoutDirection == RTL ? YogaEdge.END : YogaEdge.START; + case RIGHT: + return layoutDirection == RTL ? YogaEdge.START : YogaEdge.END; + case TOP: + case BOTTOM: + case START: + case END: + return edge; + default: + throw new IllegalArgumentException("Cannot get layout properties of multi-edge shorthands"); + } + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesJNI.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesJNI.java index d44cd1d2b4de60..3ce7b493b9b804 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesJNI.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaNodePropertiesJNI.java @@ -10,6 +10,7 @@ import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.soloader.SoLoader; +@DoNotStrip public class YogaNodePropertiesJNI implements Cloneable, YogaNodeProperties { static { @@ -105,7 +106,7 @@ public long getNativePointer() { private static native void jni_YGTransferLayoutOutputsRecursive(long nativePointer); @Override - public void onAfterCalculateLayout() { + public void onAfterCalculateLayout(boolean hasNewLayoutIgnoredSetByNative) { jni_YGTransferLayoutOutputsRecursive(mNativePointer); } diff --git a/ReactAndroid/src/main/java/com/facebook/yoga/YogaValue.java b/ReactAndroid/src/main/java/com/facebook/yoga/YogaValue.java index c0bb6e224aeb13..27781a154f07db 100644 --- a/ReactAndroid/src/main/java/com/facebook/yoga/YogaValue.java +++ b/ReactAndroid/src/main/java/com/facebook/yoga/YogaValue.java @@ -33,7 +33,9 @@ public boolean equals(Object other) { if (other instanceof YogaValue) { final YogaValue otherValue = (YogaValue) other; if (unit == otherValue.unit) { - return unit == YogaUnit.UNDEFINED || Float.compare(value, otherValue.value) == 0; + return unit == YogaUnit.UNDEFINED + || unit == YogaUnit.AUTO + || Float.compare(value, otherValue.value) == 0; } } return false; diff --git a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp index dc38500e822dc8..d83f7034495ef0 100644 --- a/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp +++ b/ReactAndroid/src/main/jni/first-party/yogajni/jni/YGJNI.cpp @@ -6,6 +6,7 @@ * */ #include +#include #include #include #include @@ -26,6 +27,12 @@ struct JYogaNodePropertiesJNI : public JavaClass { "Lcom/facebook/yoga/YogaNodePropertiesJNI;"; }; +struct JYogaNodePropertiesByteBuffer + : public JavaClass { + static constexpr auto kJavaDescriptor = + "Lcom/facebook/yoga/YogaNodePropertiesByteBuffer"; +}; + struct YGConfigContext { global_ref* logger; global_ref* config; @@ -331,6 +338,19 @@ jlong jni_YGNodeNewWithConfig( return reinterpret_cast(node); } +jlong jni_YGNodeNewByteBuffer( + alias_ref, + alias_ref javaNode) { + return jni_YGNodeNew(nullptr, javaNode); +} + +jlong jni_YGNodeNewByteBufferWithConfig( + alias_ref, + alias_ref javaNode, + jlong configPointer) { + return jni_YGNodeNewWithConfig(nullptr, javaNode, configPointer); +} + void jni_YGNodeSetOwner( alias_ref thiz, jlong nativePointer, @@ -351,6 +371,13 @@ jlong jni_YGNodeClone( return reinterpret_cast(clonedYogaNode); } +jlong jni_YGNodeCloneNoProps( + alias_ref cls, + jlong nativePointer, + alias_ref clonedJavaObject) { + return jni_YGNodeClone(cls, nativePointer, clonedJavaObject, nullptr); +} + void jni_YGNodeFree(alias_ref thiz, jlong nativePointer) { const YGNodeRef node = _jlong2YGNodeRef(nativePointer); delete reinterpret_cast(node->getContext()); @@ -404,7 +431,7 @@ void jni_YGNodeRemoveChild( _jlong2YGNodeRef(nativePointer), _jlong2YGNodeRef(childPointer)); } -void jni_YGNodeCalculateLayout( +jboolean jni_YGNodeCalculateLayout( alias_ref, jlong nativePointer, jfloat width, @@ -415,6 +442,7 @@ void jni_YGNodeCalculateLayout( static_cast(width), static_cast(height), YGNodeStyleGetDirection(_jlong2YGNodeRef(nativePointer))); + return root->getHasNewLayout(); } static void jni_YGTransferLayoutOutputsRecursive( @@ -694,6 +722,21 @@ void jni_YGConfigSetLogger( jint jni_YGNodeGetInstanceCount(alias_ref clazz) { return YGNodeGetInstanceCount(); } +local_ref jni_getStyleBuffer( + alias_ref, + jlong nativePointer) { + YGStyle* style = &_jlong2YGNodeRef(nativePointer)->getStyle(); + return JByteBuffer::wrapBytes( + reinterpret_cast(style), sizeof(YGStyle)); +} + +local_ref jni_getLayoutBuffer( + alias_ref, + jlong nativePointer) { + YGLayout* layout = &_jlong2YGNodeRef(nativePointer)->getLayout(); + return JByteBuffer::wrapBytes( + reinterpret_cast(layout), sizeof(YGLayout)); +} #define YGMakeNativeMethod(name) makeNativeMethod(#name, name) @@ -803,5 +846,17 @@ jint JNI_OnLoad(JavaVM* vm, void*) { YGMakeNativeMethod( jni_YGConfigSetShouldDiffLayoutWithoutLegacyStretchBehaviour), }); + registerNatives( + "com/facebook/yoga/YogaNodePropertiesByteBuffer", + { + YGMakeNativeMethod(jni_YGNodeCloneNoProps), + YGMakeNativeMethod(jni_YGNodeFree), + YGMakeNativeMethod(jni_YGNodeNewByteBuffer), + YGMakeNativeMethod(jni_YGNodeNewByteBufferWithConfig), + YGMakeNativeMethod(jni_YGNodeReset), + YGMakeNativeMethod(jni_YGNodeIsDirty), + YGMakeNativeMethod(jni_getStyleBuffer), + YGMakeNativeMethod(jni_getLayoutBuffer), + }); }); }