From c935fd5e1067cf3e7e3543b8b5fb1ef53d7a0b62 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Tue, 13 May 2025 18:21:04 -0700 Subject: [PATCH] Resubmit: Expose Unsnapped Dimensions (#1811) Summary: Pull Request resolved: https://github.com/facebook/yoga/pull/1811 X-link: https://github.com/facebook/react-native/pull/51298 ## Resubmit This was backed out due to being up the stack from another change that was backed out, but should be safe by itself. ## Original We want to know if an artifact created during measurement can fully be reused after final layout, but the final layout is allowed to be slightly larger due to pixel grid rounding (while still allowing reuse). It's hard to tell after the fact, whether it is larger because of this rounding (though the measure is used), or if it may be a pixel larger for valid reasons. We can expose the unsnapped dimensions of a node to give us this information, and to correlate measurement artifacts. This is most of the time the same as the layout's measured dimension, though I don't think it's safe to use this, since anything else measuring the node after could clobber this (I think `YGNodeLayoutGetOverflow` may also be prone to this as a bug). Changelog: [Internal] Reviewed By: joevilches Differential Revision: D74673119 fbshipit-source-id: 06d2eb21e28b76458ec88f4dfcaec809707d0390 --- tests/YGRoundingFunctionTest.cpp | 20 ++++++++++++++++++++ yoga/YGNodeLayout.cpp | 8 ++++++++ yoga/YGNodeLayout.h | 10 ++++++++++ yoga/algorithm/PixelGrid.cpp | 12 ++++++------ yoga/node/LayoutResults.h | 9 +++++++++ yoga/node/Node.cpp | 1 + 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/tests/YGRoundingFunctionTest.cpp b/tests/YGRoundingFunctionTest.cpp index 55b5e86d..47efcbc4 100644 --- a/tests/YGRoundingFunctionTest.cpp +++ b/tests/YGRoundingFunctionTest.cpp @@ -161,3 +161,23 @@ TEST(YogaTest, per_node_point_scale_factor) { YGConfigFree(config2); YGConfigFree(config3); } + +TEST(YogaTest, raw_layout_dimensions) { + YGConfigRef config = YGConfigNew(); + YGConfigSetPointScaleFactor(config, 0.5f); + + YGNodeRef root = YGNodeNewWithConfig(config); + YGNodeStyleSetWidth(root, 11.5f); + YGNodeStyleSetHeight(root, 9.5f); + + YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR); + + ASSERT_EQ(YGNodeLayoutGetWidth(root), 12.0f); + ASSERT_EQ(YGNodeLayoutGetHeight(root), 10.0f); + ASSERT_EQ(YGNodeLayoutGetRawWidth(root), 11.5f); + ASSERT_EQ(YGNodeLayoutGetRawHeight(root), 9.5f); + + YGNodeFreeRecursive(root); + + YGConfigFree(config); +} diff --git a/yoga/YGNodeLayout.cpp b/yoga/YGNodeLayout.cpp index 93fcf0ad..50c8766e 100644 --- a/yoga/YGNodeLayout.cpp +++ b/yoga/YGNodeLayout.cpp @@ -90,3 +90,11 @@ float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge) { return getResolvedLayoutProperty<&LayoutResults::padding>( node, scopedEnum(edge)); } + +float YGNodeLayoutGetRawHeight(YGNodeConstRef node) { + return resolveRef(node)->getLayout().rawDimension(Dimension::Height); +} + +float YGNodeLayoutGetRawWidth(YGNodeConstRef node) { + return resolveRef(node)->getLayout().rawDimension(Dimension::Width); +} diff --git a/yoga/YGNodeLayout.h b/yoga/YGNodeLayout.h index 02ead588..4f25af21 100644 --- a/yoga/YGNodeLayout.h +++ b/yoga/YGNodeLayout.h @@ -32,4 +32,14 @@ YG_EXPORT float YGNodeLayoutGetMargin(YGNodeConstRef node, YGEdge edge); YG_EXPORT float YGNodeLayoutGetBorder(YGNodeConstRef node, YGEdge edge); YG_EXPORT float YGNodeLayoutGetPadding(YGNodeConstRef node, YGEdge edge); +/** + * Return the measured height of the node, before layout rounding + */ +YG_EXPORT float YGNodeLayoutGetRawHeight(YGNodeConstRef node); + +/** + * Return the measured width of the node, before layout rounding + */ +YG_EXPORT float YGNodeLayoutGetRawWidth(YGNodeConstRef node); + YG_EXTERN_C_END diff --git a/yoga/algorithm/PixelGrid.cpp b/yoga/algorithm/PixelGrid.cpp index 6083f0f2..61de2be2 100644 --- a/yoga/algorithm/PixelGrid.cpp +++ b/yoga/algorithm/PixelGrid.cpp @@ -106,25 +106,25 @@ void roundLayoutResultsToPixelGrid( const bool hasFractionalHeight = !yoga::inexactEquals(round(scaledNodeHeight), scaledNodeHeight); - node->setLayoutDimension( + node->getLayout().setDimension( + Dimension::Width, roundValueToPixelGrid( absoluteNodeRight, pointScaleFactor, (textRounding && hasFractionalWidth), (textRounding && !hasFractionalWidth)) - roundValueToPixelGrid( - absoluteNodeLeft, pointScaleFactor, false, textRounding), - Dimension::Width); + absoluteNodeLeft, pointScaleFactor, false, textRounding)); - node->setLayoutDimension( + node->getLayout().setDimension( + Dimension::Height, roundValueToPixelGrid( absoluteNodeBottom, pointScaleFactor, (textRounding && hasFractionalHeight), (textRounding && !hasFractionalHeight)) - roundValueToPixelGrid( - absoluteNodeTop, pointScaleFactor, false, textRounding), - Dimension::Height); + absoluteNodeTop, pointScaleFactor, false, textRounding)); } for (yoga::Node* child : node->getChildren()) { diff --git a/yoga/node/LayoutResults.h b/yoga/node/LayoutResults.h index 9f0aeaf7..6776a423 100644 --- a/yoga/node/LayoutResults.h +++ b/yoga/node/LayoutResults.h @@ -66,10 +66,18 @@ struct LayoutResults { return measuredDimensions_[yoga::to_underlying(axis)]; } + float rawDimension(Dimension axis) const { + return rawDimensions_[yoga::to_underlying(axis)]; + } + void setMeasuredDimension(Dimension axis, float dimension) { measuredDimensions_[yoga::to_underlying(axis)] = dimension; } + void setRawDimension(Dimension axis, float dimension) { + rawDimensions_[yoga::to_underlying(axis)] = dimension; + } + float position(PhysicalEdge physicalEdge) const { return position_[yoga::to_underlying(physicalEdge)]; } @@ -113,6 +121,7 @@ struct LayoutResults { std::array dimensions_ = {{YGUndefined, YGUndefined}}; std::array measuredDimensions_ = {{YGUndefined, YGUndefined}}; + std::array rawDimensions_ = {{YGUndefined, YGUndefined}}; std::array position_ = {}; std::array margin_ = {}; std::array border_ = {}; diff --git a/yoga/node/Node.cpp b/yoga/node/Node.cpp index dce42fb1..fbdf5c1b 100644 --- a/yoga/node/Node.cpp +++ b/yoga/node/Node.cpp @@ -247,6 +247,7 @@ void Node::setLayoutHadOverflow(bool hadOverflow) { void Node::setLayoutDimension(float lengthValue, Dimension dimension) { layout_.setDimension(dimension, lengthValue); + layout_.setRawDimension(dimension, lengthValue); } // If both left and right are defined, then use left. Otherwise return +left or