| 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 |