Commit 3dd9a3d8 authored by dmazzoni's avatar dmazzoni Committed by Commit bot

Add localToAbsoluteTransform and localToAncestorTransform.

These interfaces on LayoutObject will be used to more efficiently
represent object locations in the accessibility tree, rather than storing
everything using absolute coordinates.

BUG=551601

Review URL: https://codereview.chromium.org/1777613002

Cr-Commit-Position: refs/heads/master@{#381616}
parent ba362cd2
...@@ -2413,6 +2413,13 @@ void LayoutObject::localToAncestorRects(Vector<LayoutRect>& rects, const LayoutB ...@@ -2413,6 +2413,13 @@ void LayoutObject::localToAncestorRects(Vector<LayoutRect>& rects, const LayoutB
} }
} }
TransformationMatrix LayoutObject::localToAncestorTransform(const LayoutBoxModelObject* ancestor, MapCoordinatesFlags mode, bool* wasFixed) const
{
TransformState transformState(TransformState::ApplyTransformDirection);
mapLocalToAncestor(ancestor, transformState, mode | ApplyContainerFlip | UseTransforms, wasFixed);
return transformState.accumulatedTransform();
}
FloatPoint LayoutObject::localToInvalidationBackingPoint(const LayoutPoint& localPoint, PaintLayer** backingLayer) FloatPoint LayoutObject::localToInvalidationBackingPoint(const LayoutPoint& localPoint, PaintLayer** backingLayer)
{ {
const LayoutBoxModelObject& paintInvalidationContainer = containerForPaintInvalidation(); const LayoutBoxModelObject& paintInvalidationContainer = containerForPaintInvalidation();
......
...@@ -987,10 +987,22 @@ public: ...@@ -987,10 +987,22 @@ public:
} }
// Convert a local quad into the coordinate system of container, taking transforms into account. // Convert a local quad into the coordinate system of container, taking transforms into account.
// If the LayoutBoxModelObject ancestor is non-null, the result will be in the space of the ancestor.
// Otherwise:
// If TraverseDocumentBoundaries is specified, the result will be in the space of the local root frame.
// Otherwise, the result will be in the space of the containing frame.
FloatQuad localToAncestorQuad(const FloatQuad&, const LayoutBoxModelObject* ancestor, MapCoordinatesFlags = 0, bool* wasFixed = nullptr) const; FloatQuad localToAncestorQuad(const FloatQuad&, const LayoutBoxModelObject* ancestor, MapCoordinatesFlags = 0, bool* wasFixed = nullptr) const;
FloatPoint localToAncestorPoint(const FloatPoint&, const LayoutBoxModelObject* ancestor, MapCoordinatesFlags = 0, bool* wasFixed = nullptr, const PaintInvalidationState* = nullptr) const; FloatPoint localToAncestorPoint(const FloatPoint&, const LayoutBoxModelObject* ancestor, MapCoordinatesFlags = 0, bool* wasFixed = nullptr, const PaintInvalidationState* = nullptr) const;
void localToAncestorRects(Vector<LayoutRect>&, const LayoutBoxModelObject* ancestor, const LayoutPoint& preOffset, const LayoutPoint& postOffset) const; void localToAncestorRects(Vector<LayoutRect>&, const LayoutBoxModelObject* ancestor, const LayoutPoint& preOffset, const LayoutPoint& postOffset) const;
// Return the transformation matrix to map points from local to the coordinate system of a container, taking transforms into account.
// Passing null for |ancestor| behaves the same as localToAncestorQuad.
TransformationMatrix localToAncestorTransform(const LayoutBoxModelObject* ancestor, MapCoordinatesFlags = 0, bool* wasFixed = nullptr) const;
TransformationMatrix localToAbsoluteTransform(MapCoordinatesFlags mode = 0, bool* wasFixed = nullptr) const
{
return localToAncestorTransform(nullptr, mode, wasFixed);
}
// Convert a local point into the coordinate system of backing coordinates. Also returns the backing layer if needed. // Convert a local point into the coordinate system of backing coordinates. Also returns the backing layer if needed.
FloatPoint localToInvalidationBackingPoint(const LayoutPoint&, PaintLayer** backingLayer = nullptr); FloatPoint localToInvalidationBackingPoint(const LayoutPoint&, PaintLayer** backingLayer = nullptr);
......
...@@ -1045,4 +1045,96 @@ TEST_F(MapCoordinatesTest, SVGForeignObject) ...@@ -1045,4 +1045,96 @@ TEST_F(MapCoordinatesTest, SVGForeignObject)
EXPECT_EQ(FloatPoint(), mappedPoint); EXPECT_EQ(FloatPoint(), mappedPoint);
} }
TEST_F(MapCoordinatesTest, LocalToAbsoluteTransform)
{
setBodyInnerHTML(
"<div id='container' style='position: absolute; left: 0; top: 0;'>"
" <div id='scale' style='transform: scale(2.0); transform-origin: left top;'>"
" <div id='child'></div>"
" </div>"
"</div>");
LayoutBoxModelObject* container = toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
TransformationMatrix containerMatrix = container->localToAbsoluteTransform();
EXPECT_TRUE(containerMatrix.isIdentity());
LayoutObject* child = getLayoutObjectByElementId("child");
TransformationMatrix childMatrix = child->localToAbsoluteTransform();
EXPECT_FALSE(childMatrix.isIdentityOrTranslation());
EXPECT_TRUE(childMatrix.isAffine());
EXPECT_EQ(0.0, childMatrix.projectPoint(FloatPoint(0.0, 0.0)).x());
EXPECT_EQ(0.0, childMatrix.projectPoint(FloatPoint(0.0, 0.0)).y());
EXPECT_EQ(20.0, childMatrix.projectPoint(FloatPoint(10.0, 20.0)).x());
EXPECT_EQ(40.0, childMatrix.projectPoint(FloatPoint(10.0, 20.0)).y());
}
TEST_F(MapCoordinatesTest, LocalToAncestorTransform)
{
setBodyInnerHTML(
"<div id='container'>"
" <div id='rotate1' style='transform: rotate(45deg); transform-origin: left top;'>"
" <div id='rotate2' style='transform: rotate(90deg); transform-origin: left top;'>"
" <div id='child'></div>"
" </div>"
" </div>"
"</div>");
LayoutBoxModelObject* container = toLayoutBoxModelObject(getLayoutObjectByElementId("container"));
LayoutBoxModelObject* rotate1 = toLayoutBoxModelObject(getLayoutObjectByElementId("rotate1"));
LayoutBoxModelObject* rotate2 = toLayoutBoxModelObject(getLayoutObjectByElementId("rotate2"));
LayoutObject* child = getLayoutObjectByElementId("child");
TransformationMatrix matrix;
matrix = child->localToAncestorTransform(rotate2);
EXPECT_TRUE(matrix.isIdentity());
// Rotate (100, 0) 90 degrees to (0, 100)
matrix = child->localToAncestorTransform(rotate1);
EXPECT_FALSE(matrix.isIdentity());
EXPECT_TRUE(matrix.isAffine());
EXPECT_NEAR(0.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).y(), LayoutUnit::epsilon());
// Rotate (100, 0) 135 degrees to (-70.7, 70.7)
matrix = child->localToAncestorTransform(container);
EXPECT_FALSE(matrix.isIdentity());
EXPECT_TRUE(matrix.isAffine());
EXPECT_NEAR(-100.0 * sqrt(2.0) / 2.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(100.0 * sqrt(2.0) / 2.0, matrix.projectPoint(FloatPoint(100.0, 0.0)).y(), LayoutUnit::epsilon());
}
TEST_F(MapCoordinatesTest, LocalToAbsoluteTransformFlattens)
{
document().frame()->settings()->setAcceleratedCompositingEnabled(true);
setBodyInnerHTML(
"<div style='position: absolute; left: 0; top: 0;'>"
" <div style='transform: rotateY(45deg); -webkit-transform-style:preserve-3d;'>"
" <div style='transform: rotateY(-45deg); -webkit-transform-style:preserve-3d;'>"
" <div id='child1'></div>"
" </div>"
" </div>"
" <div style='transform: rotateY(45deg);'>"
" <div style='transform: rotateY(-45deg);'>"
" <div id='child2'></div>"
" </div>"
" </div>"
"</div>");
LayoutObject* child1 = getLayoutObjectByElementId("child1");
LayoutObject* child2 = getLayoutObjectByElementId("child2");
TransformationMatrix matrix;
matrix = child1->localToAbsoluteTransform();
// With child1, the rotations cancel and points should map basically back to themselves.
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).y(), LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).y(), LayoutUnit::epsilon());
// With child2, each rotation gets flattened and the end result is approximately a 90-degree rotation.
matrix = child2->localToAbsoluteTransform();
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(50.0, matrix.projectPoint(FloatPoint(100.0, 50.0)).y(), LayoutUnit::epsilon());
EXPECT_NEAR(25.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).x(), LayoutUnit::epsilon());
EXPECT_NEAR(100.0, matrix.projectPoint(FloatPoint(50.0, 100.0)).y(), LayoutUnit::epsilon());
}
} // namespace blink } // namespace blink
...@@ -39,6 +39,7 @@ TransformState& TransformState::operator=(const TransformState& other) ...@@ -39,6 +39,7 @@ TransformState& TransformState::operator=(const TransformState& other)
if (m_mapQuad) if (m_mapQuad)
m_lastPlanarQuad = other.m_lastPlanarQuad; m_lastPlanarQuad = other.m_lastPlanarQuad;
m_accumulatingTransform = other.m_accumulatingTransform; m_accumulatingTransform = other.m_accumulatingTransform;
m_forceAccumulatingTransform = other.m_forceAccumulatingTransform;
m_direction = other.m_direction; m_direction = other.m_direction;
m_accumulatedTransform.clear(); m_accumulatedTransform.clear();
...@@ -68,6 +69,9 @@ void TransformState::translateMappedCoordinates(const LayoutSize& offset) ...@@ -68,6 +69,9 @@ void TransformState::translateMappedCoordinates(const LayoutSize& offset)
void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate) void TransformState::move(const LayoutSize& offset, TransformAccumulation accumulate)
{ {
if (m_forceAccumulatingTransform)
accumulate = AccumulateTransform;
if (accumulate == FlattenTransform || !m_accumulatedTransform) { if (accumulate == FlattenTransform || !m_accumulatedTransform) {
m_accumulatedOffset += offset; m_accumulatedOffset += offset;
} else { } else {
...@@ -127,14 +131,19 @@ void TransformState::applyTransform(const TransformationMatrix& transformFromCon ...@@ -127,14 +131,19 @@ void TransformState::applyTransform(const TransformationMatrix& transformFromCon
} }
if (accumulate == FlattenTransform) { if (accumulate == FlattenTransform) {
const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer; if (m_forceAccumulatingTransform) {
flattenWithTransform(*finalTransform, wasClamped); m_accumulatedTransform->flattenTo2d();
} else {
const TransformationMatrix* finalTransform = m_accumulatedTransform ? m_accumulatedTransform.get() : &transformFromContainer;
flattenWithTransform(*finalTransform, wasClamped);
}
} }
m_accumulatingTransform = accumulate == AccumulateTransform; m_accumulatingTransform = accumulate == AccumulateTransform || m_forceAccumulatingTransform;
} }
void TransformState::flatten(bool* wasClamped) void TransformState::flatten(bool* wasClamped)
{ {
ASSERT(!m_forceAccumulatingTransform);
if (wasClamped) if (wasClamped)
*wasClamped = false; *wasClamped = false;
...@@ -180,6 +189,12 @@ FloatQuad TransformState::mappedQuad(bool* wasClamped) const ...@@ -180,6 +189,12 @@ FloatQuad TransformState::mappedQuad(bool* wasClamped) const
return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped); return m_accumulatedTransform->inverse().projectQuad(quad, wasClamped);
} }
const TransformationMatrix& TransformState::accumulatedTransform() const
{
ASSERT(m_forceAccumulatingTransform && m_accumulatingTransform);
return *m_accumulatedTransform;
}
void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped) void TransformState::flattenWithTransform(const TransformationMatrix& t, bool* wasClamped)
{ {
if (m_direction == ApplyTransformDirection) { if (m_direction == ApplyTransformDirection) {
......
...@@ -47,6 +47,7 @@ public: ...@@ -47,6 +47,7 @@ public:
: m_lastPlanarPoint(p) : m_lastPlanarPoint(p)
, m_lastPlanarQuad(quad) , m_lastPlanarQuad(quad)
, m_accumulatingTransform(false) , m_accumulatingTransform(false)
, m_forceAccumulatingTransform(false)
, m_mapPoint(true) , m_mapPoint(true)
, m_mapQuad(true) , m_mapQuad(true)
, m_direction(mappingDirection) , m_direction(mappingDirection)
...@@ -56,6 +57,7 @@ public: ...@@ -56,6 +57,7 @@ public:
TransformState(TransformDirection mappingDirection, const FloatPoint& p) TransformState(TransformDirection mappingDirection, const FloatPoint& p)
: m_lastPlanarPoint(p) : m_lastPlanarPoint(p)
, m_accumulatingTransform(false) , m_accumulatingTransform(false)
, m_forceAccumulatingTransform(false)
, m_mapPoint(true) , m_mapPoint(true)
, m_mapQuad(false) , m_mapQuad(false)
, m_direction(mappingDirection) , m_direction(mappingDirection)
...@@ -65,12 +67,24 @@ public: ...@@ -65,12 +67,24 @@ public:
TransformState(TransformDirection mappingDirection, const FloatQuad& quad) TransformState(TransformDirection mappingDirection, const FloatQuad& quad)
: m_lastPlanarQuad(quad) : m_lastPlanarQuad(quad)
, m_accumulatingTransform(false) , m_accumulatingTransform(false)
, m_forceAccumulatingTransform(false)
, m_mapPoint(false) , m_mapPoint(false)
, m_mapQuad(true) , m_mapQuad(true)
, m_direction(mappingDirection) , m_direction(mappingDirection)
{ {
} }
// Accumulate a transform but don't map any points directly.
TransformState(TransformDirection mappingDirection)
: m_accumulatedTransform(TransformationMatrix::create())
, m_accumulatingTransform(true)
, m_forceAccumulatingTransform(true)
, m_mapPoint(false)
, m_mapQuad(false)
, m_direction(mappingDirection)
{
}
TransformState(const TransformState& other) { *this = other; } TransformState(const TransformState& other) { *this = other; }
TransformState& operator=(const TransformState&); TransformState& operator=(const TransformState&);
...@@ -106,6 +120,9 @@ public: ...@@ -106,6 +120,9 @@ public:
FloatPoint mappedPoint(bool* wasClamped = 0) const; FloatPoint mappedPoint(bool* wasClamped = 0) const;
FloatQuad mappedQuad(bool* wasClamped = 0) const; FloatQuad mappedQuad(bool* wasClamped = 0) const;
// Return the accumulated transform.
const TransformationMatrix& accumulatedTransform() const;
private: private:
void translateTransform(const LayoutSize&); void translateTransform(const LayoutSize&);
void translateMappedCoordinates(const LayoutSize&); void translateMappedCoordinates(const LayoutSize&);
...@@ -119,6 +136,7 @@ private: ...@@ -119,6 +136,7 @@ private:
OwnPtr<TransformationMatrix> m_accumulatedTransform; OwnPtr<TransformationMatrix> m_accumulatedTransform;
LayoutSize m_accumulatedOffset; LayoutSize m_accumulatedOffset;
bool m_accumulatingTransform; bool m_accumulatingTransform;
bool m_forceAccumulatingTransform;
bool m_mapPoint, m_mapQuad; bool m_mapPoint, m_mapQuad;
TransformDirection m_direction; TransformDirection m_direction;
}; };
......
...@@ -1398,6 +1398,17 @@ AffineTransform TransformationMatrix::toAffineTransform() const ...@@ -1398,6 +1398,17 @@ AffineTransform TransformationMatrix::toAffineTransform() const
m_matrix[1][1], m_matrix[3][0], m_matrix[3][1]); m_matrix[1][1], m_matrix[3][0], m_matrix[3][1]);
} }
void TransformationMatrix::flattenTo2d()
{
m_matrix[2][0] = 0;
m_matrix[2][1] = 0;
m_matrix[0][2] = 0;
m_matrix[1][2] = 0;
m_matrix[2][2] = 1;
m_matrix[3][2] = 0;
m_matrix[2][3] = 0;
}
static inline void blendFloat(double& from, double to, double progress) static inline void blendFloat(double& from, double to, double progress)
{ {
if (from != to) if (from != to)
......
...@@ -319,6 +319,10 @@ public: ...@@ -319,6 +319,10 @@ public:
AffineTransform toAffineTransform() const; AffineTransform toAffineTransform() const;
// Flatten into a 2-D transformation (non-invertable).
// Same as gfx::Transform::FlattenTo2d(); see the docs for that function for details and discussion.
void flattenTo2d();
bool operator==(const TransformationMatrix& m2) const bool operator==(const TransformationMatrix& m2) const
{ {
return m_matrix[0][0] == m2.m_matrix[0][0] return m_matrix[0][0] == m2.m_matrix[0][0]
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment