Breaking: per-node pointScaleFactor (#1379)

Summary:
Pull Request resolved: https://github.com/facebook/yoga/pull/1379

X-link: https://github.com/facebook/react-native/pull/39403

Right now we have a `pointScaleFactor` per-node, but only ever read the one off the root node. In most cases where config is global, these will be the same, but it is possible for these to differ.

This... doesn't make much sense from an API perspective, and there are edge cases where we may want to allow laying out a subtree with a different DPI then the rest of the tree (though I think there might be other solutions to that).

We should rethink some of what is currently on config being allowed per-node (do we really need each node to be able to have a separate logger?), but this makes the model consistent in the meantime.

This change is breaking to any users relying on setting `pointScaleFactor` on the config of the root node, but not other nodes.

Reviewed By: yungsters

Differential Revision: D49181131

fbshipit-source-id: f1363ca242094f04b995fd50c1e56834d5003425
This commit is contained in:
Nick Gerleman
2023-09-13 14:11:25 -07:00
committed by Facebook GitHub Bot
parent 0a90b16ac6
commit 66cc95f932
4 changed files with 86 additions and 49 deletions

View File

@@ -80,3 +80,45 @@ TEST(YogaTest, consistent_rounding_during_repeated_layouts) {
YGConfigFree(config);
}
TEST(YogaTest, per_node_point_scale_factor) {
const YGConfigRef config1 = YGConfigNew();
YGConfigSetPointScaleFactor(config1, 2);
const YGConfigRef config2 = YGConfigNew();
YGConfigSetPointScaleFactor(config2, 1);
const YGConfigRef config3 = YGConfigNew();
YGConfigSetPointScaleFactor(config3, 0.5f);
const YGNodeRef root = YGNodeNewWithConfig(config1);
YGNodeStyleSetWidth(root, 11.5);
YGNodeStyleSetHeight(root, 11.5);
const YGNodeRef node0 = YGNodeNewWithConfig(config2);
YGNodeStyleSetWidth(node0, 9.5);
YGNodeStyleSetHeight(node0, 9.5);
YGNodeInsertChild(root, node0, 0);
const YGNodeRef node1 = YGNodeNewWithConfig(config3);
YGNodeStyleSetWidth(node1, 7);
YGNodeStyleSetHeight(node1, 7);
YGNodeInsertChild(node0, node1, 0);
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
ASSERT_EQ(YGNodeLayoutGetWidth(root), 11.5);
ASSERT_EQ(YGNodeLayoutGetHeight(root), 11.5);
ASSERT_EQ(YGNodeLayoutGetWidth(node0), 10);
ASSERT_EQ(YGNodeLayoutGetHeight(node0), 10);
ASSERT_EQ(YGNodeLayoutGetWidth(node1), 8);
ASSERT_EQ(YGNodeLayoutGetHeight(node1), 8);
YGNodeFreeRecursive(root);
YGConfigFree(config1);
YGConfigFree(config2);
YGConfigFree(config3);
}

View File

@@ -2487,7 +2487,7 @@ bool calculateLayoutInternal(
layout->cachedLayout.computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
node->getConfig())) {
cachedResults = &layout->cachedLayout;
} else {
// Try to use the measurement cache.
@@ -2505,7 +2505,7 @@ bool calculateLayoutInternal(
layout->cachedMeasurements[i].computedHeight,
marginAxisRow,
marginAxisColumn,
config)) {
node->getConfig())) {
cachedResults = &layout->cachedMeasurements[i];
break;
}
@@ -2756,8 +2756,7 @@ void calculateLayout(
gCurrentGenerationCount.load(std::memory_order_relaxed))) {
node->setPosition(
node->getLayout().direction(), ownerWidth, ownerHeight, ownerWidth);
roundLayoutResultsToPixelGrid(
node, node->getConfig()->getPointScaleFactor(), 0.0f, 0.0f);
roundLayoutResultsToPixelGrid(node, 0.0f, 0.0f);
#ifdef DEBUG
if (node->getConfig()->shouldPrintTree()) {

View File

@@ -64,12 +64,9 @@ float roundValueToPixelGrid(
void roundLayoutResultsToPixelGrid(
yoga::Node* const node,
const double pointScaleFactor,
const double absoluteLeft,
const double absoluteTop) {
if (pointScaleFactor == 0.0f) {
return;
}
const auto pointScaleFactor = node->getConfig()->getPointScaleFactor();
const double nodeLeft = node->getLayout().position[YGEdgeLeft];
const double nodeTop = node->getLayout().position[YGEdgeTop];
@@ -83,52 +80,52 @@ void roundLayoutResultsToPixelGrid(
const double absoluteNodeRight = absoluteNodeLeft + nodeWidth;
const double absoluteNodeBottom = absoluteNodeTop + nodeHeight;
// If a node has a custom measure function we never want to round down its
// size as this could lead to unwanted text truncation.
const bool textRounding = node->getNodeType() == YGNodeTypeText;
if (pointScaleFactor != 0.0f) {
// If a node has a custom measure function we never want to round down its
// size as this could lead to unwanted text truncation.
const bool textRounding = node->getNodeType() == YGNodeTypeText;
node->setLayoutPosition(
roundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
YGEdgeLeft);
node->setLayoutPosition(
roundValueToPixelGrid(nodeLeft, pointScaleFactor, false, textRounding),
YGEdgeLeft);
node->setLayoutPosition(
roundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
YGEdgeTop);
node->setLayoutPosition(
roundValueToPixelGrid(nodeTop, pointScaleFactor, false, textRounding),
YGEdgeTop);
// We multiply dimension by scale factor and if the result is close to the
// whole number, we don't have any fraction To verify if the result is close
// to whole number we want to check both floor and ceil numbers
const bool hasFractionalWidth =
!yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 0) &&
!yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0);
const bool hasFractionalHeight =
!yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 0) &&
!yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0);
// We multiply dimension by scale factor and if the result is close to the
// whole number, we don't have any fraction To verify if the result is close
// to whole number we want to check both floor and ceil numbers
const bool hasFractionalWidth =
!yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 0) &&
!yoga::inexactEquals(fmod(nodeWidth * pointScaleFactor, 1.0), 1.0);
const bool hasFractionalHeight =
!yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 0) &&
!yoga::inexactEquals(fmod(nodeHeight * pointScaleFactor, 1.0), 1.0);
node->setLayoutDimension(
roundValueToPixelGrid(
absoluteNodeRight,
pointScaleFactor,
(textRounding && hasFractionalWidth),
(textRounding && !hasFractionalWidth)) -
roundValueToPixelGrid(
absoluteNodeLeft, pointScaleFactor, false, textRounding),
YGDimensionWidth);
node->setLayoutDimension(
roundValueToPixelGrid(
absoluteNodeRight,
pointScaleFactor,
(textRounding && hasFractionalWidth),
(textRounding && !hasFractionalWidth)) -
roundValueToPixelGrid(
absoluteNodeLeft, pointScaleFactor, false, textRounding),
YGDimensionWidth);
node->setLayoutDimension(
roundValueToPixelGrid(
absoluteNodeBottom,
pointScaleFactor,
(textRounding && hasFractionalHeight),
(textRounding && !hasFractionalHeight)) -
roundValueToPixelGrid(
absoluteNodeTop, pointScaleFactor, false, textRounding),
YGDimensionHeight);
node->setLayoutDimension(
roundValueToPixelGrid(
absoluteNodeBottom,
pointScaleFactor,
(textRounding && hasFractionalHeight),
(textRounding && !hasFractionalHeight)) -
roundValueToPixelGrid(
absoluteNodeTop, pointScaleFactor, false, textRounding),
YGDimensionHeight);
}
const size_t childCount = node->getChildCount();
for (size_t i = 0; i < childCount; i++) {
roundLayoutResultsToPixelGrid(
node->getChild(i), pointScaleFactor, absoluteNodeLeft, absoluteNodeTop);
for (yoga::Node* child : node->getChildren()) {
roundLayoutResultsToPixelGrid(child, absoluteNodeLeft, absoluteNodeTop);
}
}

View File

@@ -23,7 +23,6 @@ float roundValueToPixelGrid(
// Round the layout results of a node and its subtree to the pixel grid.
void roundLayoutResultsToPixelGrid(
yoga::Node* const node,
const double pointScaleFactor,
const double absoluteLeft,
const double absoluteTop);