Skip final loop on absolute children, if possible

There's no need to go through all absolute children at the end of the
layout calculation if the node at hand doesn't have any. This also
ensures only absolutely positioned children are traversed in the final
loop.
This commit is contained in:
Lucas Rocha
2015-09-04 17:37:07 +01:00
parent 996f2a03d5
commit 793220faf8
6 changed files with 148 additions and 95 deletions

View File

@@ -604,6 +604,9 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
css_node_t* child;
css_flex_direction_t axis;
css_node_t* firstAbsoluteChild = NULL;
css_node_t* currentAbsoluteChild = NULL;
float definedMainDim = CSS_UNDEFINED;
if (isMainDimDefined) {
definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
@@ -637,6 +640,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
for (i = startLine; i < childCount; ++i) {
child = node->get_child(node->context, i);
child->next_absolute_child = NULL;
// Pre-fill cross axis dimensions when the child is using stretch before
// we call the recursive layout pass
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
@@ -650,6 +655,16 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
getPaddingAndBorderAxis(child, crossAxis)
);
} else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
// Store a private linked list of absolutely positioned children
// so that we can efficiently traverse them later.
if (firstAbsoluteChild == NULL) {
firstAbsoluteChild = child;
}
if (currentAbsoluteChild != NULL) {
currentAbsoluteChild->next_absolute_child = child;
}
currentAbsoluteChild = child;
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
@@ -1075,40 +1090,40 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction
}
// <Loop G> Calculate dimensions for absolutely positioned elements
for (i = 0; i < childCount; ++i) {
child = node->get_child(node->context, i);
if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child->layout.dimensions[dim[axis]] = fmaxf(
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(child, axis) -
getPosition(child, leading[axis]) -
getPosition(child, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, axis)
);
}
currentAbsoluteChild = firstAbsoluteChild;
while (currentAbsoluteChild != NULL) {
// Pre-fill dimensions when using absolute position and both offsets for
// the axis are defined (either both left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
!isDimDefined(currentAbsoluteChild, axis) &&
isPosDefined(currentAbsoluteChild, leading[axis]) &&
isPosDefined(currentAbsoluteChild, trailing[axis])) {
currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(currentAbsoluteChild, axis) -
getPosition(currentAbsoluteChild, leading[axis]) -
getPosition(currentAbsoluteChild, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
);
}
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (isPosDefined(child, trailing[axis]) &&
!isPosDefined(child, leading[axis])) {
child->layout.position[leading[axis]] =
node->layout.dimensions[dim[axis]] -
child->layout.dimensions[dim[axis]] -
getPosition(child, trailing[axis]);
}
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
!isPosDefined(currentAbsoluteChild, leading[axis])) {
currentAbsoluteChild->layout.position[leading[axis]] =
node->layout.dimensions[dim[axis]] -
currentAbsoluteChild->layout.dimensions[dim[axis]] -
getPosition(currentAbsoluteChild, trailing[axis]);
}
}
child = currentAbsoluteChild;
currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
child->next_absolute_child = NULL;
}
/** END_GENERATED **/
}

View File

@@ -129,18 +129,21 @@ typedef struct {
float maxDimensions[2];
} css_style_t;
typedef struct css_node {
typedef struct css_node css_node_t;
struct css_node {
css_style_t style;
css_layout_t layout;
int children_count;
int line_index;
css_node_t* next_absolute_child;
css_dim_t (*measure)(void *context, float width);
void (*print)(void *context);
struct css_node* (*get_child)(void *context, int i);
bool (*is_dirty)(void *context);
void *context;
} css_node_t;
};
// Lifecycle of nodes and children

View File

@@ -472,6 +472,9 @@ var computeLayout = (function() {
var/*css_node_t**/ child;
var/*(c)!css_flex_direction_t*//*(java)!int*/ axis;
var/*css_node_t**/ firstAbsoluteChild = null;
var/*css_node_t**/ currentAbsoluteChild = null;
var/*float*/ definedMainDim = CSS_UNDEFINED;
if (isMainDimDefined) {
definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain;
@@ -505,6 +508,8 @@ var computeLayout = (function() {
for (i = startLine; i < childCount; ++i) {
child = node.children[i];
child.nextAbsoluteChild = null;
// Pre-fill cross axis dimensions when the child is using stretch before
// we call the recursive layout pass
if (getAlignItem(node, child) === CSS_ALIGN_STRETCH &&
@@ -518,6 +523,16 @@ var computeLayout = (function() {
getPaddingAndBorderAxis(child, crossAxis)
);
} else if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {
// Store a private linked list of absolutely positioned children
// so that we can efficiently traverse them later.
if (firstAbsoluteChild === null) {
firstAbsoluteChild = child;
}
if (currentAbsoluteChild !== null) {
currentAbsoluteChild.nextAbsoluteChild = child;
}
currentAbsoluteChild = child;
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
@@ -943,40 +958,40 @@ var computeLayout = (function() {
}
// <Loop G> Calculate dimensions for absolutely positioned elements
for (i = 0; i < childCount; ++i) {
child = node.children[i];
if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node.layout[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child.layout[dim[axis]] = fmaxf(
boundAxis(child, axis, node.layout[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(child, axis) -
getPosition(child, leading[axis]) -
getPosition(child, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, axis)
);
}
currentAbsoluteChild = firstAbsoluteChild;
while (currentAbsoluteChild !== null) {
// Pre-fill dimensions when using absolute position and both offsets for
// the axis are defined (either both left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node.layout[dim[axis]]) &&
!isDimDefined(currentAbsoluteChild, axis) &&
isPosDefined(currentAbsoluteChild, leading[axis]) &&
isPosDefined(currentAbsoluteChild, trailing[axis])) {
currentAbsoluteChild.layout[dim[axis]] = fmaxf(
boundAxis(currentAbsoluteChild, axis, node.layout[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(currentAbsoluteChild, axis) -
getPosition(currentAbsoluteChild, leading[axis]) -
getPosition(currentAbsoluteChild, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
);
}
for (ii = 0; ii < 2; ii++) {
axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (isPosDefined(child, trailing[axis]) &&
!isPosDefined(child, leading[axis])) {
child.layout[leading[axis]] =
node.layout[dim[axis]] -
child.layout[dim[axis]] -
getPosition(child, trailing[axis]);
}
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
!isPosDefined(currentAbsoluteChild, leading[axis])) {
currentAbsoluteChild.layout[leading[axis]] =
node.layout[dim[axis]] -
currentAbsoluteChild.layout[dim[axis]] -
getPosition(currentAbsoluteChild, trailing[axis]);
}
}
child = currentAbsoluteChild;
currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
child.nextAbsoluteChild = null;
}
}

View File

@@ -63,6 +63,8 @@ public class CSSNode {
public int lineIndex = 0;
CSSNode nextAbsoluteChild;
private @Nullable ArrayList<CSSNode> mChildren;
private @Nullable CSSNode mParent;
private @Nullable MeasureFunction mMeasureFunction = null;

View File

@@ -421,6 +421,9 @@ public class LayoutEngine {
CSSNode child;
int axis;
CSSNode firstAbsoluteChild = null;
CSSNode currentAbsoluteChild = null;
float definedMainDim = CSSConstants.UNDEFINED;
if (isMainDimDefined) {
definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
@@ -454,6 +457,8 @@ public class LayoutEngine {
for (i = startLine; i < childCount; ++i) {
child = node.getChildAt(i);
child.nextAbsoluteChild = null;
// Pre-fill cross axis dimensions when the child is using stretch before
// we call the recursive layout pass
if (getAlignItem(node, child) == CSSAlign.STRETCH &&
@@ -467,6 +472,16 @@ public class LayoutEngine {
getPaddingAndBorderAxis(child, crossAxis)
);
} else if (child.style.positionType == CSSPositionType.ABSOLUTE) {
// Store a private linked list of absolutely positioned children
// so that we can efficiently traverse them later.
if (firstAbsoluteChild == null) {
firstAbsoluteChild = child;
}
if (currentAbsoluteChild != null) {
currentAbsoluteChild.nextAbsoluteChild = child;
}
currentAbsoluteChild = child;
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
@@ -892,40 +907,40 @@ public class LayoutEngine {
}
// <Loop G> Calculate dimensions for absolutely positioned elements
for (i = 0; i < childCount; ++i) {
child = node.getChildAt(i);
if (child.style.positionType == CSSPositionType.ABSOLUTE) {
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
// left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node.layout.dimensions[dim[axis]]) &&
!isDimDefined(child, axis) &&
isPosDefined(child, leading[axis]) &&
isPosDefined(child, trailing[axis])) {
child.layout.dimensions[dim[axis]] = Math.max(
boundAxis(child, axis, node.layout.dimensions[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(child, axis) -
getPosition(child, leading[axis]) -
getPosition(child, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(child, axis)
);
}
currentAbsoluteChild = firstAbsoluteChild;
while (currentAbsoluteChild != null) {
// Pre-fill dimensions when using absolute position and both offsets for
// the axis are defined (either both left and right or top and bottom).
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (!isUndefined(node.layout.dimensions[dim[axis]]) &&
!isDimDefined(currentAbsoluteChild, axis) &&
isPosDefined(currentAbsoluteChild, leading[axis]) &&
isPosDefined(currentAbsoluteChild, trailing[axis])) {
currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.max(
boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] -
getBorderAxis(node, axis) -
getMarginAxis(currentAbsoluteChild, axis) -
getPosition(currentAbsoluteChild, leading[axis]) -
getPosition(currentAbsoluteChild, trailing[axis])
),
// You never want to go smaller than padding
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
);
}
for (ii = 0; ii < 2; ii++) {
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
if (isPosDefined(child, trailing[axis]) &&
!isPosDefined(child, leading[axis])) {
child.layout.position[leading[axis]] =
node.layout.dimensions[dim[axis]] -
child.layout.dimensions[dim[axis]] -
getPosition(child, trailing[axis]);
}
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
!isPosDefined(currentAbsoluteChild, leading[axis])) {
currentAbsoluteChild.layout.position[leading[axis]] =
node.layout.dimensions[dim[axis]] -
currentAbsoluteChild.layout.dimensions[dim[axis]] -
getPosition(currentAbsoluteChild, trailing[axis]);
}
}
child = currentAbsoluteChild;
currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
child.nextAbsoluteChild = null;
}
}
/** END_GENERATED **/

View File

@@ -243,6 +243,7 @@ function printLayout(test) {
function transpileAnnotatedJStoC(jsCode) {
return jsCode
.replace('node.style.measure', 'node.measure')
.replace(/null/g, 'NULL')
.replace(/\.children\.length/g, '.children_count')
.replace(/\.width/g, '.dimensions[CSS_WIDTH]')
.replace(/\.height/g, '.dimensions[CSS_HEIGHT]')
@@ -251,6 +252,7 @@ function transpileAnnotatedJStoC(jsCode) {
.replace(/\.minWidth/g, '.minDimensions[CSS_WIDTH]')
.replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]')
.replace(/\.lineIndex/g, '.line_index')
.replace(/\.nextAbsoluteChild/g, '.next_absolute_child')
.replace(/layout\[dim/g, 'layout.dimensions[dim')
.replace(/layout\[pos/g, 'layout.position[pos')
.replace(/layout\[leading/g, 'layout.position[leading')
@@ -261,6 +263,7 @@ function transpileAnnotatedJStoC(jsCode) {
.replace(/node\./g, 'node->')
.replace(/child\./g, 'child->')
.replace(/parent\./g, 'parent->')
.replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->')
.replace(/getPositionType\((.+?)\)/g, '$1->style.position_type')
.replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content')
.replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content')