Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / third_party / WebKit / Source / platform / graphics / paint / DisplayItemPropertyTreeBuilderTest.cpp
bloba1f76c70f36cecc81bf5029427f78f8d95d45ad0
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "config.h"
6 #include "platform/graphics/paint/DisplayItemPropertyTreeBuilder.h"
8 #include "platform/graphics/paint/DisplayItem.h"
9 #include "platform/graphics/paint/DisplayItemClient.h"
10 #include "platform/graphics/paint/DisplayItemClipTree.h"
11 #include "platform/graphics/paint/DisplayItemTransformTree.h"
12 #include "platform/graphics/paint/ScrollDisplayItem.h"
13 #include "platform/graphics/paint/Transform3DDisplayItem.h"
14 #include "platform/graphics/paint/TransformDisplayItem.h"
15 #include "platform/transforms/TransformTestHelper.h"
16 #include "platform/transforms/TransformationMatrix.h"
17 #include "public/platform/WebDisplayItemTransformTree.h"
18 #include "wtf/OwnPtr.h"
19 #include <gmock/gmock.h>
20 #include <gtest/gtest.h>
22 namespace blink {
23 namespace {
25 using ::testing::AllOf;
26 using ::testing::ElementsAre;
28 using RangeRecord = DisplayItemPropertyTreeBuilder::RangeRecord;
30 MATCHER_P2(hasRange, begin, end, "")
32 return arg.displayListBeginIndex == static_cast<size_t>(begin)
33 && arg.displayListEndIndex == static_cast<size_t>(end);
36 MATCHER_P(hasTransformNode, transformNode, "")
38 return arg.transformNodeIndex == static_cast<size_t>(transformNode);
41 MATCHER_P(hasOffset, offset, "")
43 return arg.offset == offset;
46 struct DummyClient {
47 DisplayItemClient displayItemClient() const { return toDisplayItemClient(this); }
48 String debugName() const { return "DummyClient"; }
51 class DummyDisplayItem final : public DisplayItem {
52 public:
53 DummyDisplayItem(const DummyClient& client) : DisplayItem(client, DisplayItem::DrawingFirst, sizeof(*this)) { }
56 class DisplayItemPropertyTreeBuilderTest : public ::testing::Test {
57 protected:
58 DisplayItemPropertyTreeBuilder& builder() { return m_builder; }
59 const DisplayItemTransformTree& transformTree() { return *m_transformTree; }
60 const DisplayItemClipTree& clipTree() { return *m_clipTree; }
61 const Vector<RangeRecord>& rangeRecords() { return m_rangeRecords; }
62 std::vector<RangeRecord> rangeRecordsAsStdVector() { return std::vector<RangeRecord>(m_rangeRecords.begin(), m_rangeRecords.end()); }
64 void processDisplayItem(const DisplayItem& displayItem) { m_builder.processDisplayItem(displayItem); }
65 void processDisplayItem(PassOwnPtr<DisplayItem> displayItem) { processDisplayItem(*displayItem); }
66 void processDummyDisplayItem() { processDisplayItem(DummyDisplayItem(newDummyClient())); }
67 const DummyClient& processBeginTransform3D(const TransformationMatrix& transform, const FloatPoint3D& transformOrigin = FloatPoint3D())
69 const DummyClient& client = newDummyClient();
70 processDisplayItem(BeginTransform3DDisplayItem(client, DisplayItem::Transform3DElementTransform, transform, transformOrigin));
71 return client;
73 void processEndTransform3D(const DummyClient& client)
75 processDisplayItem(EndTransform3DDisplayItem(client, DisplayItem::transform3DTypeToEndTransform3DType(DisplayItem::Transform3DElementTransform)));
77 const DummyClient& processBeginTransform(const AffineTransform& transform)
79 const DummyClient& client = newDummyClient();
80 processDisplayItem(BeginTransformDisplayItem(client, transform));
81 return client;
83 void processEndTransform(const DummyClient& client)
85 processDisplayItem(EndTransformDisplayItem(client));
87 const DummyClient& processBeginScroll(int offsetX, int offsetY)
89 const DummyClient& client = newDummyClient();
90 processDisplayItem(BeginScrollDisplayItem(client, DisplayItem::ScrollFirst, IntSize(offsetX, offsetY)));
91 return client;
93 void processEndScroll(const DummyClient& client)
95 processDisplayItem(EndScrollDisplayItem(client, DisplayItem::EndScrollFirst));
98 void finishPropertyTrees()
100 m_transformTree = m_builder.releaseTransformTree();
101 m_clipTree = m_builder.releaseClipTree();
102 m_rangeRecords = m_builder.releaseRangeRecords();
105 private:
106 // This makes empty objects which can be used as display item clients.
107 const DummyClient& newDummyClient()
109 m_dummyClients.append(adoptPtr(new DummyClient));
110 return *m_dummyClients.last();
113 DisplayItemPropertyTreeBuilder m_builder;
114 OwnPtr<DisplayItemTransformTree> m_transformTree;
115 OwnPtr<DisplayItemClipTree> m_clipTree;
116 Vector<RangeRecord> m_rangeRecords;
117 Vector<OwnPtr<DummyClient>> m_dummyClients;
120 TEST_F(DisplayItemPropertyTreeBuilderTest, NoDisplayItems)
122 finishPropertyTrees();
124 // There should be a root transform node.
125 ASSERT_EQ(1u, transformTree().nodeCount());
126 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
128 // There should be no range records, because there are no non-empty
129 // transformed ranges.
130 ASSERT_EQ(0u, rangeRecords().size());
133 TEST_F(DisplayItemPropertyTreeBuilderTest, NoTransforms)
135 // Three dummy display items.
136 processDummyDisplayItem();
137 processDummyDisplayItem();
138 processDummyDisplayItem();
139 finishPropertyTrees();
141 // There should only be a root transform node.
142 ASSERT_EQ(1u, transformTree().nodeCount());
143 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
145 // There should be one range record, for the entire list.
146 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
147 AllOf(hasRange(0, 3), hasTransformNode(0))));
150 TEST_F(DisplayItemPropertyTreeBuilderTest, IdentityTransform)
152 TransformationMatrix identity;
154 // There's an identity transform here, but we should not make a node for it.
155 processDummyDisplayItem();
156 auto transformClient = processBeginTransform3D(identity);
157 processDummyDisplayItem();
158 processEndTransform3D(transformClient);
159 processDummyDisplayItem();
160 finishPropertyTrees();
162 // There should only be a root transform node.
163 ASSERT_EQ(1u, transformTree().nodeCount());
164 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
166 // There should be three range records.
167 // Since the transform is the identity, these could be combined, but there
168 // is not currently a special path for this case.
169 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
170 AllOf(hasRange(0, 1), hasTransformNode(0)),
171 AllOf(hasRange(2, 3), hasTransformNode(0)),
172 AllOf(hasRange(4, 5), hasTransformNode(0))));
175 TEST_F(DisplayItemPropertyTreeBuilderTest, Only2DTranslation)
177 FloatSize offset(200.5, -100);
178 TransformationMatrix translation;
179 translation.translate(offset.width(), offset.height());
181 // There's a translation here, but we should not make a node for it.
182 processDummyDisplayItem();
183 auto transformClient = processBeginTransform3D(translation);
184 processDummyDisplayItem();
185 processEndTransform3D(transformClient);
186 processDummyDisplayItem();
187 finishPropertyTrees();
189 // There should only be a root transform node.
190 ASSERT_EQ(1u, transformTree().nodeCount());
191 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
193 // There should be three ranges, even though there's only one node.
194 // The middle one requires an offset.
195 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
196 AllOf(hasRange(0, 1), hasTransformNode(0)),
197 AllOf(hasRange(2, 3), hasTransformNode(0)),
198 AllOf(hasRange(4, 5), hasTransformNode(0))));
201 TEST_F(DisplayItemPropertyTreeBuilderTest, Nested2DTranslation)
203 FloatSize offset1(10, -40);
204 TransformationMatrix translation1;
205 translation1.translate(offset1.width(), offset1.height());
206 FloatSize offset2(80, 80);
207 TransformationMatrix translation2;
208 translation2.translate(offset2.width(), offset2.height());
210 // These drawings should share a transform node but have different range
211 // record offsets.
212 processDummyDisplayItem();
213 auto transform1 = processBeginTransform3D(translation1);
214 processDummyDisplayItem();
215 auto transform2 = processBeginTransform3D(translation2);
216 processDummyDisplayItem();
217 processEndTransform3D(transform2);
218 processEndTransform3D(transform1);
219 finishPropertyTrees();
221 // There should only be a root transform node.
222 ASSERT_EQ(1u, transformTree().nodeCount());
223 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
225 // Check that the range records have the right offsets.
226 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
227 AllOf(hasRange(0, 1), hasTransformNode(0), hasOffset(FloatSize())),
228 AllOf(hasRange(2, 3), hasTransformNode(0), hasOffset(offset1)),
229 AllOf(hasRange(4, 5), hasTransformNode(0), hasOffset(offset1 + offset2))));
232 TEST_F(DisplayItemPropertyTreeBuilderTest, ZTranslation)
234 TransformationMatrix zTranslation;
235 zTranslation.translate3d(0, 0, 1);
237 // Z translation: we expect another node.
238 processDummyDisplayItem();
239 auto transformClient = processBeginTransform3D(zTranslation);
240 processDummyDisplayItem();
241 processEndTransform3D(transformClient);
242 processDummyDisplayItem();
243 finishPropertyTrees();
245 // There should be two nodes here.
246 ASSERT_EQ(2u, transformTree().nodeCount());
247 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
248 EXPECT_EQ(0u, transformTree().nodeAt(1).parentNodeIndex);
250 // There should be three range records.
251 // The middle of these should be transformed, and the others should be
252 // attached to the root node.
253 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
254 AllOf(hasRange(0, 1), hasTransformNode(0)),
255 AllOf(hasRange(2, 3), hasTransformNode(1)),
256 AllOf(hasRange(4, 5), hasTransformNode(0))));
259 template <typename TreeType, typename NodeType>
260 size_t nodeDepth(const TreeType& tree, const NodeType& node)
262 const auto* currentNode = &node;
263 size_t depth = 0;
264 while (!currentNode->isRoot()) {
265 currentNode = &tree.nodeAt(currentNode->parentNodeIndex);
266 depth++;
268 return depth;
271 TEST_F(DisplayItemPropertyTreeBuilderTest, SkipUnnecessaryRangeRecords)
273 TransformationMatrix rotation;
274 rotation.rotate(1 /* degrees */);
276 // The only drawing is in the second transform.
277 auto transform1 = processBeginTransform3D(rotation);
278 auto transform2 = processBeginTransform3D(rotation);
279 processDummyDisplayItem();
280 auto transform3 = processBeginTransform3D(rotation);
281 processEndTransform3D(transform3);
282 processDummyDisplayItem();
283 processEndTransform3D(transform2);
284 processEndTransform3D(transform1);
285 finishPropertyTrees();
287 // There should be only two ranges.
288 // They must both belong to the same grandchild of the root node.
289 ASSERT_EQ(2u, rangeRecords().size());
290 size_t transformNodeIndex = rangeRecords()[0].transformNodeIndex;
291 EXPECT_EQ(2u, nodeDepth(transformTree(), transformTree().nodeAt(transformNodeIndex)));
292 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
293 AllOf(hasRange(2, 3), hasTransformNode(transformNodeIndex)),
294 AllOf(hasRange(5, 6), hasTransformNode(transformNodeIndex))));
297 TEST_F(DisplayItemPropertyTreeBuilderTest, RootTransformNodeHasIdentityTransform)
299 finishPropertyTrees();
300 ASSERT_EQ(1u, transformTree().nodeCount());
301 EXPECT_TRUE(transformTree().nodeAt(0).matrix.isIdentity());
302 EXPECT_TRANSFORMS_ALMOST_EQ(TransformationMatrix(), transformTree().nodeAt(0).matrix);
305 TEST_F(DisplayItemPropertyTreeBuilderTest, Transform3DMatrix)
307 TransformationMatrix matrix;
308 matrix.rotate3d(45, 45, 45);
310 auto transform1 = processBeginTransform3D(matrix);
311 processDummyDisplayItem();
312 processEndTransform3D(transform1);
313 finishPropertyTrees();
315 const auto& transformNode = transformTree().nodeAt(rangeRecords()[0].transformNodeIndex);
316 EXPECT_TRANSFORMS_ALMOST_EQ(matrix, transformNode.matrix);
319 TEST_F(DisplayItemPropertyTreeBuilderTest, NestedTransformsAreNotCombined)
321 // It's up the consumer of the tree to multiply transformation matrices.
323 TransformationMatrix matrix1;
324 matrix1.rotate3d(45, 45, 45);
325 TransformationMatrix matrix2;
326 matrix2.translate3d(0, 10, 20);
327 EXPECT_NE(matrix2, matrix1 * matrix2);
329 auto transform1 = processBeginTransform3D(matrix1);
330 auto transform2 = processBeginTransform3D(matrix2);
331 processDummyDisplayItem();
332 processEndTransform3D(transform2);
333 processDummyDisplayItem();
334 processEndTransform3D(transform1);
335 finishPropertyTrees();
337 const auto& transformNode = transformTree().nodeAt(rangeRecords()[0].transformNodeIndex);
338 ASSERT_FALSE(transformNode.isRoot());
339 EXPECT_TRANSFORMS_ALMOST_EQ(matrix2, transformNode.matrix);
340 const auto& parentNode = transformTree().nodeAt(transformNode.parentNodeIndex);
341 EXPECT_TRANSFORMS_ALMOST_EQ(matrix1, parentNode.matrix);
344 TEST_F(DisplayItemPropertyTreeBuilderTest, TransformDisplayItemCreatesTransformNode)
346 // 2D transform display items should create a transform node as well,
347 // unless the transform is a 2D translation only.
348 AffineTransform rotation;
349 rotation.rotate(45);
351 processDummyDisplayItem();
352 auto transformClient = processBeginTransform(rotation);
353 processDummyDisplayItem();
354 processEndTransform(transformClient);
355 processDummyDisplayItem();
356 finishPropertyTrees();
358 // There should be two transform nodes.
359 ASSERT_EQ(2u, transformTree().nodeCount());
360 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
361 EXPECT_EQ(0u, transformTree().nodeAt(1).parentNodeIndex);
362 EXPECT_TRANSFORMS_ALMOST_EQ(TransformationMatrix(rotation), transformTree().nodeAt(1).matrix);
364 // There should be three range records, the middle one affected by the
365 // rotation.
366 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
367 AllOf(hasRange(0, 1), hasTransformNode(0)),
368 AllOf(hasRange(2, 3), hasTransformNode(1)),
369 AllOf(hasRange(4, 5), hasTransformNode(0))));
372 TEST_F(DisplayItemPropertyTreeBuilderTest, TransformDisplayItemOnly2DTranslation)
374 // In this case no transform node should be created for the 2D translation.
375 AffineTransform translation = AffineTransform::translation(10, -40);
377 processDummyDisplayItem();
378 auto transformClient = processBeginTransform(translation);
379 processDummyDisplayItem();
380 processEndTransform(transformClient);
381 processDummyDisplayItem();
382 finishPropertyTrees();
384 // There should be only one transform node.
385 ASSERT_EQ(1u, transformTree().nodeCount());
386 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
388 // There should be three range records, the middle one affected by the
389 // translation.
390 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
391 AllOf(hasRange(0, 1), hasTransformNode(0), hasOffset(FloatSize(0, 0))),
392 AllOf(hasRange(2, 3), hasTransformNode(0), hasOffset(FloatSize(10, -40))),
393 AllOf(hasRange(4, 5), hasTransformNode(0), hasOffset(FloatSize(0, 0)))));
396 TEST_F(DisplayItemPropertyTreeBuilderTest, ScrollDisplayItemIs2DTranslation)
398 processDummyDisplayItem();
399 auto scrollClient = processBeginScroll(-90, 400);
400 processDummyDisplayItem();
401 processEndScroll(scrollClient);
402 processDummyDisplayItem();
403 finishPropertyTrees();
405 // There should be only one transform node.
406 ASSERT_EQ(1u, transformTree().nodeCount());
407 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
409 // There should be three range records, the middle one affected by the
410 // scroll. Note that the translation due to scroll is the negative of the
411 // scroll offset.
412 EXPECT_THAT(rangeRecordsAsStdVector(), ElementsAre(
413 AllOf(hasRange(0, 1), hasTransformNode(0), hasOffset(FloatSize(0, 0))),
414 AllOf(hasRange(2, 3), hasTransformNode(0), hasOffset(FloatSize(90, -400))),
415 AllOf(hasRange(4, 5), hasTransformNode(0), hasOffset(FloatSize(0, 0)))));
418 TEST_F(DisplayItemPropertyTreeBuilderTest, TransformTreeIncludesTransformOrigin)
420 FloatPoint3D transformOrigin(1, 2, 3);
421 TransformationMatrix matrix;
422 matrix.scale3d(2, 2, 2);
424 auto transformClient = processBeginTransform3D(matrix, transformOrigin);
425 processDummyDisplayItem();
426 processEndTransform3D(transformClient);
427 finishPropertyTrees();
429 // There should be two transform nodes.
430 ASSERT_EQ(2u, transformTree().nodeCount());
431 EXPECT_TRUE(transformTree().nodeAt(0).isRoot());
433 // And the non-root node should have both the matrix and the origin,
434 // separately.
435 const auto& transformNode = transformTree().nodeAt(1);
436 EXPECT_EQ(0u, transformNode.parentNodeIndex);
437 EXPECT_EQ(TransformationMatrix::toSkMatrix44(matrix), transformNode.matrix);
438 EXPECT_EQ(transformOrigin, transformNode.transformOrigin);
441 } // namespace
442 } // namespace blink