OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "config.h" | |
6 #include "platform/graphics/paint/DisplayItemTransformTreeBuilder.h" | |
7 | |
8 #include "platform/graphics/paint/DisplayItem.h" | |
9 #include "platform/graphics/paint/DisplayItemClient.h" | |
10 #include "platform/graphics/paint/DisplayItemTransformTree.h" | |
11 #include "platform/graphics/paint/Transform3DDisplayItem.h" | |
12 #include "platform/transforms/TransformTestHelper.h" | |
13 #include "platform/transforms/TransformationMatrix.h" | |
14 #include "public/platform/WebDisplayItemTransformTree.h" | |
15 #include <gtest/gtest.h> | |
16 | |
17 namespace blink { | |
18 namespace { | |
19 | |
20 using RangeRecord = WebDisplayItemTransformTree::RangeRecord; | |
21 | |
22 struct DummyClient { | |
23 DisplayItemClient displayItemClient() const { return toDisplayItemClient(thi
s); } | |
24 String debugName() const { return "DummyClient"; } | |
25 }; | |
26 | |
27 class DummyDisplayItem final : public DisplayItem { | |
28 public: | |
29 DummyDisplayItem(const DummyClient& client) : DisplayItem(client, DisplayIte
m::DrawingFirst, sizeof(*this)) { } | |
30 }; | |
31 | |
32 class DisplayItemTransformTreeBuilderTest : public ::testing::Test { | |
33 protected: | |
34 DisplayItemTransformTreeBuilder& builder() { return m_builder; } | |
35 | |
36 void processDisplayItem(const DisplayItem& displayItem) { m_builder.processD
isplayItem(displayItem); } | |
37 void processDisplayItem(PassOwnPtr<DisplayItem> displayItem) { processDispla
yItem(*displayItem); } | |
38 void processDummyDisplayItem() { processDisplayItem(DummyDisplayItem(newDumm
yClient())); } | |
39 const DummyClient& processBeginTransform3D(const TransformationMatrix& trans
form) | |
40 { | |
41 const DummyClient& client = newDummyClient(); | |
42 processDisplayItem(BeginTransform3DDisplayItem(client, DisplayItem::Tran
sform3DElementTransform, transform)); | |
43 return client; | |
44 } | |
45 void processEndTransform3D(const DummyClient& client) | |
46 { | |
47 processDisplayItem(EndTransform3DDisplayItem(client, DisplayItem::transf
orm3DTypeToEndTransform3DType(DisplayItem::Transform3DElementTransform))); | |
48 } | |
49 | |
50 private: | |
51 // This makes empty objects which can be used as display item clients. | |
52 const DummyClient& newDummyClient() | |
53 { | |
54 m_dummyClients.append(adoptPtr(new DummyClient)); | |
55 return *m_dummyClients.last(); | |
56 } | |
57 | |
58 DisplayItemTransformTreeBuilder m_builder; | |
59 Vector<OwnPtr<DummyClient>> m_dummyClients; | |
60 }; | |
61 | |
62 TEST_F(DisplayItemTransformTreeBuilderTest, NoDisplayItems) | |
63 { | |
64 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
65 | |
66 // There should be a root transform node. | |
67 ASSERT_EQ(1u, tree.nodeCount()); | |
68 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
69 EXPECT_EQ(WebDisplayItemTransformTree::kInvalidIndex, tree.nodeAt(0).parentN
odeIndex); | |
70 | |
71 // There should be no range records, because there are no non-empty | |
72 // transformed ranges. | |
73 ASSERT_EQ(0u, tree.rangeRecordCount()); | |
74 } | |
75 | |
76 TEST_F(DisplayItemTransformTreeBuilderTest, NoTransforms) | |
77 { | |
78 // Three dummy display items. | |
79 processDummyDisplayItem(); | |
80 processDummyDisplayItem(); | |
81 processDummyDisplayItem(); | |
82 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
83 | |
84 // There should only be a root transform node. | |
85 ASSERT_EQ(1u, tree.nodeCount()); | |
86 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
87 EXPECT_EQ(WebDisplayItemTransformTree::kInvalidIndex, tree.nodeAt(0).parentN
odeIndex); | |
88 | |
89 // There should be one range record, for the entire list. | |
90 ASSERT_EQ(1u, tree.rangeRecordCount()); | |
91 EXPECT_EQ(RangeRecord(0, 3, 0), tree.rangeRecordAt(0)); | |
92 } | |
93 | |
94 TEST_F(DisplayItemTransformTreeBuilderTest, IdentityTransform) | |
95 { | |
96 TransformationMatrix identity; | |
97 | |
98 // There's an identity transform here, but we should not make a node for it. | |
99 processDummyDisplayItem(); | |
100 auto transformClient = processBeginTransform3D(identity); | |
101 processDummyDisplayItem(); | |
102 processEndTransform3D(transformClient); | |
103 processDummyDisplayItem(); | |
104 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
105 | |
106 // There should only be a root transform node. | |
107 ASSERT_EQ(1u, tree.nodeCount()); | |
108 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
109 EXPECT_EQ(WebDisplayItemTransformTree::kInvalidIndex, tree.nodeAt(0).parentN
odeIndex); | |
110 | |
111 // There should be three range records. | |
112 // Since the transform is the identity, these could be combined, but there | |
113 // is not currently a special path for this case. | |
114 ASSERT_EQ(3u, tree.rangeRecordCount()); | |
115 EXPECT_EQ(RangeRecord(0, 1, 0), tree.rangeRecordAt(0)); | |
116 EXPECT_EQ(RangeRecord(2, 3, 0), tree.rangeRecordAt(1)); | |
117 EXPECT_EQ(RangeRecord(4, 5, 0), tree.rangeRecordAt(2)); | |
118 } | |
119 | |
120 TEST_F(DisplayItemTransformTreeBuilderTest, Only2DTranslation) | |
121 { | |
122 FloatSize offset(200.5, -100); | |
123 TransformationMatrix translation; | |
124 translation.translate(offset.width(), offset.height()); | |
125 | |
126 // There's a translation here, but we should not make a node for it. | |
127 processDummyDisplayItem(); | |
128 auto transformClient = processBeginTransform3D(translation); | |
129 processDummyDisplayItem(); | |
130 processEndTransform3D(transformClient); | |
131 processDummyDisplayItem(); | |
132 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
133 | |
134 // There should only be a root transform node. | |
135 ASSERT_EQ(1u, tree.nodeCount()); | |
136 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
137 EXPECT_EQ(WebDisplayItemTransformTree::kInvalidIndex, tree.nodeAt(0).parentN
odeIndex); | |
138 | |
139 // There should be three ranges, even though there's only one node. | |
140 // The middle one requires an offset. | |
141 ASSERT_EQ(3u, tree.rangeRecordCount()); | |
142 EXPECT_EQ(RangeRecord(0, 1, 0, FloatSize()), tree.rangeRecordAt(0)); | |
143 EXPECT_EQ(RangeRecord(2, 3, 0, offset), tree.rangeRecordAt(1)); | |
144 EXPECT_EQ(RangeRecord(4, 5, 0, FloatSize()), tree.rangeRecordAt(2)); | |
145 } | |
146 | |
147 TEST_F(DisplayItemTransformTreeBuilderTest, Nested2DTranslation) | |
148 { | |
149 FloatSize offset1(10, -40); | |
150 TransformationMatrix translation1; | |
151 translation1.translate(offset1.width(), offset1.height()); | |
152 FloatSize offset2(80, 80); | |
153 TransformationMatrix translation2; | |
154 translation2.translate(offset2.width(), offset2.height()); | |
155 | |
156 // These drawings should share a transform node but have different range | |
157 // record offsets. | |
158 processDummyDisplayItem(); | |
159 auto transform1 = processBeginTransform3D(translation1); | |
160 processDummyDisplayItem(); | |
161 auto transform2 = processBeginTransform3D(translation2); | |
162 processDummyDisplayItem(); | |
163 processEndTransform3D(transform2); | |
164 processEndTransform3D(transform1); | |
165 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
166 | |
167 // There should only be a root transform node. | |
168 ASSERT_EQ(1u, tree.nodeCount()); | |
169 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
170 EXPECT_EQ(WebDisplayItemTransformTree::kInvalidIndex, tree.nodeAt(0).parentN
odeIndex); | |
171 | |
172 // Check that the range records have the right offsets. | |
173 ASSERT_EQ(3u, tree.rangeRecordCount()); | |
174 EXPECT_EQ(RangeRecord(0, 1, 0, FloatSize()), tree.rangeRecordAt(0)); | |
175 EXPECT_EQ(RangeRecord(2, 3, 0, offset1), tree.rangeRecordAt(1)); | |
176 EXPECT_EQ(RangeRecord(4, 5, 0, offset1 + offset2), tree.rangeRecordAt(2)); | |
177 } | |
178 | |
179 TEST_F(DisplayItemTransformTreeBuilderTest, ZTranslation) | |
180 { | |
181 TransformationMatrix zTranslation; | |
182 zTranslation.translate3d(0, 0, 1); | |
183 | |
184 // Z translation: we expect another node. | |
185 processDummyDisplayItem(); | |
186 auto transformClient = processBeginTransform3D(zTranslation); | |
187 processDummyDisplayItem(); | |
188 processEndTransform3D(transformClient); | |
189 processDummyDisplayItem(); | |
190 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
191 | |
192 // There should be two nodes here. | |
193 ASSERT_EQ(2u, tree.nodeCount()); | |
194 EXPECT_TRUE(tree.nodeAt(0).isRoot()); | |
195 EXPECT_EQ(0u, tree.nodeAt(1).parentNodeIndex); | |
196 | |
197 // There should be three range records. | |
198 // The middle of these should be transformed, and the others should be | |
199 // attached to the root node. | |
200 ASSERT_EQ(3u, tree.rangeRecordCount()); | |
201 EXPECT_EQ(RangeRecord(0, 1, 0), tree.rangeRecordAt(0)); | |
202 EXPECT_EQ(RangeRecord(2, 3, 1), tree.rangeRecordAt(1)); | |
203 EXPECT_EQ(RangeRecord(4, 5, 0), tree.rangeRecordAt(2)); | |
204 } | |
205 | |
206 size_t nodeDepth( | |
207 const WebDisplayItemTransformTree& tree, | |
208 const WebDisplayItemTransformTree::TransformNode& node) | |
209 { | |
210 const auto* currentNode = &node; | |
211 size_t depth = 0; | |
212 while (!currentNode->isRoot()) { | |
213 currentNode = &tree.parentNode(*currentNode); | |
214 depth++; | |
215 } | |
216 return depth; | |
217 } | |
218 | |
219 TEST_F(DisplayItemTransformTreeBuilderTest, SkipUnnecessaryRangeRecords) | |
220 { | |
221 TransformationMatrix rotation; | |
222 rotation.rotate(1 /* degrees */); | |
223 | |
224 // The only drawing is in the second transform. | |
225 auto transform1 = processBeginTransform3D(rotation); | |
226 auto transform2 = processBeginTransform3D(rotation); | |
227 processDummyDisplayItem(); | |
228 auto transform3 = processBeginTransform3D(rotation); | |
229 processEndTransform3D(transform3); | |
230 processDummyDisplayItem(); | |
231 processEndTransform3D(transform2); | |
232 processEndTransform3D(transform1); | |
233 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
234 | |
235 // There should be only two ranges. | |
236 // They must both belong to the same grandchild of the root node. | |
237 ASSERT_EQ(2u, tree.rangeRecordCount()); | |
238 size_t transformNodeIndex = tree.rangeRecordAt(0).transformNodeIndex; | |
239 EXPECT_EQ(2u, nodeDepth(tree, tree.nodeAt(transformNodeIndex))); | |
240 EXPECT_EQ(RangeRecord(2, 3, transformNodeIndex), tree.rangeRecordAt(0)); | |
241 EXPECT_EQ(RangeRecord(5, 6, transformNodeIndex), tree.rangeRecordAt(1)); | |
242 } | |
243 | |
244 TEST_F(DisplayItemTransformTreeBuilderTest, RootTransformNodeHasIdentityTransfor
m) | |
245 { | |
246 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
247 ASSERT_EQ(1u, tree.nodeCount()); | |
248 EXPECT_TRUE(tree.nodeAt(0).matrix.isIdentity()); | |
249 EXPECT_TRANSFORMS_ALMOST_EQ(TransformationMatrix(), tree.nodeAt(0).matrix); | |
250 } | |
251 | |
252 TEST_F(DisplayItemTransformTreeBuilderTest, Transform3DMatrix) | |
253 { | |
254 TransformationMatrix matrix; | |
255 matrix.rotate3d(45, 45, 45); | |
256 | |
257 auto transform1 = processBeginTransform3D(matrix); | |
258 processDummyDisplayItem(); | |
259 processEndTransform3D(transform1); | |
260 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
261 | |
262 const auto& rangeRecord = tree.rangeRecordAt(0); | |
263 const auto& transformNode = tree.nodeAt(rangeRecord.transformNodeIndex); | |
264 EXPECT_TRANSFORMS_ALMOST_EQ(matrix, transformNode.matrix); | |
265 } | |
266 | |
267 TEST_F(DisplayItemTransformTreeBuilderTest, NestedTransformsAreNotCombined) | |
268 { | |
269 // It's up the consumer of the tree to multiply transformation matrices. | |
270 | |
271 TransformationMatrix matrix1; | |
272 matrix1.rotate3d(45, 45, 45); | |
273 TransformationMatrix matrix2; | |
274 matrix2.translate3d(0, 10, 20); | |
275 EXPECT_NE(matrix2, matrix1 * matrix2); | |
276 | |
277 auto transform1 = processBeginTransform3D(matrix1); | |
278 auto transform2 = processBeginTransform3D(matrix2); | |
279 processDummyDisplayItem(); | |
280 processEndTransform3D(transform2); | |
281 processDummyDisplayItem(); | |
282 processEndTransform3D(transform1); | |
283 WebDisplayItemTransformTree tree(builder().releaseTransformTree()); | |
284 | |
285 const auto& transformNode = tree.nodeAt(tree.rangeRecordAt(0).transformNodeI
ndex); | |
286 ASSERT_FALSE(transformNode.isRoot()); | |
287 EXPECT_TRANSFORMS_ALMOST_EQ(matrix2, transformNode.matrix); | |
288 EXPECT_TRANSFORMS_ALMOST_EQ(matrix1, tree.parentNode(transformNode).matrix); | |
289 } | |
290 | |
291 } // namespace | |
292 } // namespace blink | |
OLD | NEW |