Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(112)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/paint/GeometryMapperTest.cpp

Issue 2390443002: Unify GeometryPropertyTreeState and PropertyTreeState (Closed)
Patch Set: rebase x2 Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "platform/graphics/paint/GeometryMapper.h" 5 #include "platform/graphics/paint/GeometryMapper.h"
6 6
7 #include "platform/geometry/GeometryTestHelpers.h" 7 #include "platform/geometry/GeometryTestHelpers.h"
8 #include "platform/geometry/LayoutRect.h" 8 #include "platform/geometry/LayoutRect.h"
9 #include "platform/graphics/paint/ClipPaintPropertyNode.h" 9 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
10 #include "platform/graphics/paint/EffectPaintPropertyNode.h" 10 #include "platform/graphics/paint/EffectPaintPropertyNode.h"
11 #include "platform/graphics/paint/ScrollPaintPropertyNode.h"
11 #include "platform/graphics/paint/TransformPaintPropertyNode.h" 12 #include "platform/graphics/paint/TransformPaintPropertyNode.h"
12 #include "testing/gtest/include/gtest/gtest.h" 13 #include "testing/gtest/include/gtest/gtest.h"
13 14
14 namespace blink { 15 namespace blink {
15 16
16 class GeometryMapperTest : public ::testing::Test { 17 class GeometryMapperTest : public ::testing::Test {
17 public: 18 public:
18 RefPtr<TransformPaintPropertyNode> rootTransformNode; 19 RefPtr<TransformPaintPropertyNode> rootTransformNode;
19 RefPtr<ClipPaintPropertyNode> rootClipNode; 20 RefPtr<ClipPaintPropertyNode> rootClipNode;
20 RefPtr<EffectPaintPropertyNode> rootEffectNode; 21 RefPtr<EffectPaintPropertyNode> rootEffectNode;
22 RefPtr<ScrollPaintPropertyNode> rootScrollNode;
21 23
22 std::unique_ptr<GeometryMapper> geometryMapper; 24 std::unique_ptr<GeometryMapper> geometryMapper;
23 25
24 GeometryPropertyTreeState rootGeometryPropertyTreeState() { 26 PropertyTreeState rootPropertyTreeState() {
25 GeometryPropertyTreeState state(rootTransformNode.get(), rootClipNode.get(), 27 PropertyTreeState state(rootTransformNode.get(), rootClipNode.get(),
26 rootEffectNode.get()); 28 rootEffectNode.get(), rootScrollNode.get());
27 return state; 29 return state;
28 } 30 }
29 31
30 PrecomputedDataForAncestor& getPrecomputedDataForAncestor( 32 PrecomputedDataForAncestor& getPrecomputedDataForAncestor(
31 const GeometryPropertyTreeState& geometryPropertyTreeState) { 33 const PropertyTreeState& propertyTreeState) {
32 return geometryMapper->getPrecomputedDataForAncestor( 34 return geometryMapper->getPrecomputedDataForAncestor(propertyTreeState);
33 geometryPropertyTreeState);
34 } 35 }
35 36
36 const TransformPaintPropertyNode* leastCommonAncestor( 37 const TransformPaintPropertyNode* leastCommonAncestor(
37 const TransformPaintPropertyNode* a, 38 const TransformPaintPropertyNode* a,
38 const TransformPaintPropertyNode* b) { 39 const TransformPaintPropertyNode* b) {
39 return GeometryMapper::leastCommonAncestor(a, b); 40 return GeometryMapper::leastCommonAncestor(a, b);
40 } 41 }
41 42
42 private: 43 private:
43 void SetUp() override { 44 void SetUp() override {
44 rootTransformNode = TransformPaintPropertyNode::create( 45 rootTransformNode = TransformPaintPropertyNode::create(
45 nullptr, TransformationMatrix(), FloatPoint3D()); 46 nullptr, TransformationMatrix(), FloatPoint3D());
46 rootClipNode = ClipPaintPropertyNode::create( 47 rootClipNode = ClipPaintPropertyNode::create(
47 nullptr, rootTransformNode, 48 nullptr, rootTransformNode,
48 FloatRoundedRect(LayoutRect::infiniteIntRect())); 49 FloatRoundedRect(LayoutRect::infiniteIntRect()));
49 rootEffectNode = EffectPaintPropertyNode::create(nullptr, 1.0); 50 rootEffectNode = EffectPaintPropertyNode::create(nullptr, 1.0);
51 rootScrollNode = ScrollPaintPropertyNode::create(
52 nullptr, rootTransformNode, IntSize(), IntSize(), false, false);
50 geometryMapper = wrapUnique(new GeometryMapper()); 53 geometryMapper = wrapUnique(new GeometryMapper());
51 } 54 }
52 55
53 void TearDown() override { geometryMapper.reset(); } 56 void TearDown() override { geometryMapper.reset(); }
54 }; 57 };
55 58
56 const static float kTestEpsilon = 1e-6; 59 const static float kTestEpsilon = 1e-6;
57 60
58 #define EXPECT_RECT_EQ(expected, actual) \ 61 #define EXPECT_RECT_EQ(expected, actual) \
59 do { \ 62 do { \
60 const FloatRect& actualRect = actual; \ 63 const FloatRect& actualRect = actual; \
61 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.x(), actualRect.x(), \ 64 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.x(), actualRect.x(), \
62 kTestEpsilon)) \ 65 kTestEpsilon)) \
63 << "actual: " << actualRect.x() << ", expected: " << expected.x(); \ 66 << "actual: " << actualRect.x() << ", expected: " << expected.x(); \
64 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.y(), actualRect.y(), \ 67 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.y(), actualRect.y(), \
65 kTestEpsilon)) \ 68 kTestEpsilon)) \
66 << "actual: " << actualRect.y() << ", expected: " << expected.y(); \ 69 << "actual: " << actualRect.y() << ", expected: " << expected.y(); \
67 EXPECT_TRUE(GeometryTest::ApproximatelyEqual( \ 70 EXPECT_TRUE(GeometryTest::ApproximatelyEqual( \
68 expected.width(), actualRect.width(), kTestEpsilon)) \ 71 expected.width(), actualRect.width(), kTestEpsilon)) \
69 << "actual: " << actualRect.width() \ 72 << "actual: " << actualRect.width() \
70 << ", expected: " << expected.width(); \ 73 << ", expected: " << expected.width(); \
71 EXPECT_TRUE(GeometryTest::ApproximatelyEqual( \ 74 EXPECT_TRUE(GeometryTest::ApproximatelyEqual( \
72 expected.height(), actualRect.height(), kTestEpsilon)) \ 75 expected.height(), actualRect.height(), kTestEpsilon)) \
73 << "actual: " << actualRect.height() \ 76 << "actual: " << actualRect.height() \
74 << ", expected: " << expected.height(); \ 77 << ", expected: " << expected.height(); \
75 } while (false) 78 } while (false)
76 79
77 #define CHECK_MAPPINGS(inputRect, expectedVisualRect, expectedTransformedRect, \ 80 #define CHECK_MAPPINGS(inputRect, expectedVisualRect, expectedTransformedRect, \
78 expectedTransformToAncestor, \ 81 expectedTransformToAncestor, \
79 expectedClipInAncestorSpace, \ 82 expectedClipInAncestorSpace, localPropertyTreeState, \
80 localGeometryPropertyTreeState, \ 83 ancestorPropertyTreeState) \
81 ancestorGeometryPropertyTreeState) \
82 do { \ 84 do { \
83 bool success = false; \ 85 bool success = false; \
84 EXPECT_RECT_EQ(expectedVisualRect, \ 86 EXPECT_RECT_EQ(expectedVisualRect, \
85 geometryMapper->localToVisualRectInAncestorSpace( \ 87 geometryMapper->localToVisualRectInAncestorSpace( \
86 inputRect, localGeometryPropertyTreeState, \ 88 inputRect, localPropertyTreeState, \
87 ancestorGeometryPropertyTreeState, success)); \ 89 ancestorPropertyTreeState, success)); \
88 EXPECT_TRUE(success); \ 90 EXPECT_TRUE(success); \
89 EXPECT_RECT_EQ(expectedVisualRect, \ 91 EXPECT_RECT_EQ(expectedVisualRect, \
90 geometryMapper->mapToVisualRectInDestinationSpace( \ 92 geometryMapper->mapToVisualRectInDestinationSpace( \
91 inputRect, localGeometryPropertyTreeState, \ 93 inputRect, localPropertyTreeState, \
92 ancestorGeometryPropertyTreeState, success)); \ 94 ancestorPropertyTreeState, success)); \
93 EXPECT_TRUE(success); \ 95 EXPECT_TRUE(success); \
94 EXPECT_RECT_EQ(expectedTransformedRect, \ 96 EXPECT_RECT_EQ(expectedTransformedRect, \
95 geometryMapper->localToAncestorRect( \ 97 geometryMapper->localToAncestorRect( \
96 inputRect, localGeometryPropertyTreeState, \ 98 inputRect, localPropertyTreeState, \
97 ancestorGeometryPropertyTreeState, success)); \ 99 ancestorPropertyTreeState, success)); \
98 EXPECT_RECT_EQ(expectedTransformedRect, \ 100 EXPECT_RECT_EQ(expectedTransformedRect, \
99 geometryMapper->mapRectToDestinationSpace( \ 101 geometryMapper->mapRectToDestinationSpace( \
100 inputRect, localGeometryPropertyTreeState, \ 102 inputRect, localPropertyTreeState, \
101 ancestorGeometryPropertyTreeState, success)); \ 103 ancestorPropertyTreeState, success)); \
102 EXPECT_TRUE(success); \ 104 EXPECT_TRUE(success); \
103 EXPECT_EQ(expectedTransformToAncestor, \ 105 EXPECT_EQ( \
104 getPrecomputedDataForAncestor(ancestorGeometryPropertyTreeState) \ 106 expectedTransformToAncestor, \
105 .toAncestorTransforms.get( \ 107 getPrecomputedDataForAncestor(ancestorPropertyTreeState) \
106 localGeometryPropertyTreeState.transform.get())); \ 108 .toAncestorTransforms.get(localPropertyTreeState.transform())); \
107 EXPECT_EQ(expectedClipInAncestorSpace, \ 109 EXPECT_EQ(expectedClipInAncestorSpace, \
108 getPrecomputedDataForAncestor(ancestorGeometryPropertyTreeState) \ 110 getPrecomputedDataForAncestor(ancestorPropertyTreeState) \
109 .toAncestorClipRects.get( \ 111 .toAncestorClipRects.get(localPropertyTreeState.clip())); \
110 localGeometryPropertyTreeState.clip.get())); \
111 } while (false) 112 } while (false)
112 113
113 TEST_F(GeometryMapperTest, Root) { 114 TEST_F(GeometryMapperTest, Root) {
114 FloatRect input(0, 0, 100, 100); 115 FloatRect input(0, 0, 100, 100);
115 116
116 CHECK_MAPPINGS(input, input, input, rootTransformNode->matrix(), 117 CHECK_MAPPINGS(input, input, input, rootTransformNode->matrix(),
117 rootClipNode->clipRect().rect(), 118 rootClipNode->clipRect().rect(), rootPropertyTreeState(),
118 rootGeometryPropertyTreeState(), 119 rootPropertyTreeState());
119 rootGeometryPropertyTreeState());
120 } 120 }
121 121
122 TEST_F(GeometryMapperTest, IdentityTransform) { 122 TEST_F(GeometryMapperTest, IdentityTransform) {
123 RefPtr<TransformPaintPropertyNode> transform = 123 RefPtr<TransformPaintPropertyNode> transform =
124 TransformPaintPropertyNode::create( 124 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
125 rootGeometryPropertyTreeState().transform, TransformationMatrix(), 125 TransformationMatrix(),
126 FloatPoint3D()); 126 FloatPoint3D());
127 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 127 PropertyTreeState localState = rootPropertyTreeState();
128 localState.transform = transform.get(); 128 localState.setTransform(transform.get());
129 129
130 FloatRect input(0, 0, 100, 100); 130 FloatRect input(0, 0, 100, 100);
131 131
132 CHECK_MAPPINGS(input, input, input, transform->matrix(), 132 CHECK_MAPPINGS(input, input, input, transform->matrix(),
133 rootClipNode->clipRect().rect(), localState, 133 rootClipNode->clipRect().rect(), localState,
134 rootGeometryPropertyTreeState()); 134 rootPropertyTreeState());
135 } 135 }
136 136
137 TEST_F(GeometryMapperTest, TranslationTransform) { 137 TEST_F(GeometryMapperTest, TranslationTransform) {
138 TransformationMatrix transformMatrix; 138 TransformationMatrix transformMatrix;
139 transformMatrix.translate(20, 10); 139 transformMatrix.translate(20, 10);
140 RefPtr<TransformPaintPropertyNode> transform = 140 RefPtr<TransformPaintPropertyNode> transform =
141 TransformPaintPropertyNode::create( 141 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
142 rootGeometryPropertyTreeState().transform, transformMatrix, 142 transformMatrix, FloatPoint3D());
143 FloatPoint3D()); 143 PropertyTreeState localState = rootPropertyTreeState();
144 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 144 localState.setTransform(transform.get());
145 localState.transform = transform.get();
146 145
147 FloatRect input(0, 0, 100, 100); 146 FloatRect input(0, 0, 100, 100);
148 FloatRect output = transformMatrix.mapRect(input); 147 FloatRect output = transformMatrix.mapRect(input);
149 148
150 CHECK_MAPPINGS(input, output, output, transform->matrix(), 149 CHECK_MAPPINGS(input, output, output, transform->matrix(),
151 rootClipNode->clipRect().rect(), localState, 150 rootClipNode->clipRect().rect(), localState,
152 rootGeometryPropertyTreeState()); 151 rootPropertyTreeState());
153 152
154 bool success = false; 153 bool success = false;
155 EXPECT_RECT_EQ( 154 EXPECT_RECT_EQ(input,
156 input, geometryMapper->ancestorToLocalRect( 155 geometryMapper->ancestorToLocalRect(
157 output, localState, rootGeometryPropertyTreeState(), success)); 156 output, localState, rootPropertyTreeState(), success));
158 EXPECT_TRUE(success); 157 EXPECT_TRUE(success);
159 } 158 }
160 159
161 TEST_F(GeometryMapperTest, RotationAndScaleTransform) { 160 TEST_F(GeometryMapperTest, RotationAndScaleTransform) {
162 TransformationMatrix transformMatrix; 161 TransformationMatrix transformMatrix;
163 transformMatrix.rotate(45); 162 transformMatrix.rotate(45);
164 transformMatrix.scale(2); 163 transformMatrix.scale(2);
165 RefPtr<TransformPaintPropertyNode> transform = 164 RefPtr<TransformPaintPropertyNode> transform =
166 TransformPaintPropertyNode::create( 165 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
167 rootGeometryPropertyTreeState().transform, transformMatrix, 166 transformMatrix,
168 FloatPoint3D(0, 0, 0)); 167 FloatPoint3D(0, 0, 0));
169 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 168 PropertyTreeState localState = rootPropertyTreeState();
170 localState.transform = transform.get(); 169 localState.setTransform(transform.get());
171 170
172 FloatRect input(0, 0, 100, 100); 171 FloatRect input(0, 0, 100, 100);
173 FloatRect output = transformMatrix.mapRect(input); 172 FloatRect output = transformMatrix.mapRect(input);
174 173
175 CHECK_MAPPINGS(input, output, output, transformMatrix, 174 CHECK_MAPPINGS(input, output, output, transformMatrix,
176 rootClipNode->clipRect().rect(), localState, 175 rootClipNode->clipRect().rect(), localState,
177 rootGeometryPropertyTreeState()); 176 rootPropertyTreeState());
178 } 177 }
179 178
180 TEST_F(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) { 179 TEST_F(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) {
181 TransformationMatrix transformMatrix; 180 TransformationMatrix transformMatrix;
182 transformMatrix.rotate(45); 181 transformMatrix.rotate(45);
183 transformMatrix.scale(2); 182 transformMatrix.scale(2);
184 RefPtr<TransformPaintPropertyNode> transform = 183 RefPtr<TransformPaintPropertyNode> transform =
185 TransformPaintPropertyNode::create( 184 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
186 rootGeometryPropertyTreeState().transform, transformMatrix, 185 transformMatrix,
187 FloatPoint3D(50, 50, 0)); 186 FloatPoint3D(50, 50, 0));
188 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 187 PropertyTreeState localState = rootPropertyTreeState();
189 localState.transform = transform.get(); 188 localState.setTransform(transform.get());
190 189
191 FloatRect input(0, 0, 100, 100); 190 FloatRect input(0, 0, 100, 100);
192 transformMatrix.applyTransformOrigin(50, 50, 0); 191 transformMatrix.applyTransformOrigin(50, 50, 0);
193 FloatRect output = transformMatrix.mapRect(input); 192 FloatRect output = transformMatrix.mapRect(input);
194 193
195 CHECK_MAPPINGS(input, output, output, transformMatrix, 194 CHECK_MAPPINGS(input, output, output, transformMatrix,
196 rootClipNode->clipRect().rect(), localState, 195 rootClipNode->clipRect().rect(), localState,
197 rootGeometryPropertyTreeState()); 196 rootPropertyTreeState());
198 } 197 }
199 198
200 TEST_F(GeometryMapperTest, NestedTransforms) { 199 TEST_F(GeometryMapperTest, NestedTransforms) {
201 TransformationMatrix rotateTransform; 200 TransformationMatrix rotateTransform;
202 rotateTransform.rotate(45); 201 rotateTransform.rotate(45);
203 RefPtr<TransformPaintPropertyNode> transform1 = 202 RefPtr<TransformPaintPropertyNode> transform1 =
204 TransformPaintPropertyNode::create( 203 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
205 rootGeometryPropertyTreeState().transform, rotateTransform, 204 rotateTransform, FloatPoint3D());
206 FloatPoint3D());
207 205
208 TransformationMatrix scaleTransform; 206 TransformationMatrix scaleTransform;
209 scaleTransform.scale(2); 207 scaleTransform.scale(2);
210 RefPtr<TransformPaintPropertyNode> transform2 = 208 RefPtr<TransformPaintPropertyNode> transform2 =
211 TransformPaintPropertyNode::create(transform1, scaleTransform, 209 TransformPaintPropertyNode::create(transform1, scaleTransform,
212 FloatPoint3D()); 210 FloatPoint3D());
213 211
214 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 212 PropertyTreeState localState = rootPropertyTreeState();
215 localState.transform = transform2.get(); 213 localState.setTransform(transform2.get());
216 214
217 FloatRect input(0, 0, 100, 100); 215 FloatRect input(0, 0, 100, 100);
218 TransformationMatrix final = rotateTransform * scaleTransform; 216 TransformationMatrix final = rotateTransform * scaleTransform;
219 FloatRect output = final.mapRect(input); 217 FloatRect output = final.mapRect(input);
220 218
221 CHECK_MAPPINGS(input, output, output, final, rootClipNode->clipRect().rect(), 219 CHECK_MAPPINGS(input, output, output, final, rootClipNode->clipRect().rect(),
222 localState, rootGeometryPropertyTreeState()); 220 localState, rootPropertyTreeState());
223 221
224 // Check the cached matrix for the intermediate transform. 222 // Check the cached matrix for the intermediate transform.
225 EXPECT_EQ(rotateTransform, 223 EXPECT_EQ(rotateTransform,
226 getPrecomputedDataForAncestor(rootGeometryPropertyTreeState()) 224 getPrecomputedDataForAncestor(rootPropertyTreeState())
227 .toAncestorTransforms.get(transform1.get())); 225 .toAncestorTransforms.get(transform1.get()));
228 } 226 }
229 227
230 TEST_F(GeometryMapperTest, NestedTransformsScaleAndTranslation) { 228 TEST_F(GeometryMapperTest, NestedTransformsScaleAndTranslation) {
231 TransformationMatrix scaleTransform; 229 TransformationMatrix scaleTransform;
232 scaleTransform.scale(2); 230 scaleTransform.scale(2);
233 RefPtr<TransformPaintPropertyNode> transform1 = 231 RefPtr<TransformPaintPropertyNode> transform1 =
234 TransformPaintPropertyNode::create( 232 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
235 rootGeometryPropertyTreeState().transform, scaleTransform, 233 scaleTransform, FloatPoint3D());
236 FloatPoint3D());
237 234
238 TransformationMatrix translateTransform; 235 TransformationMatrix translateTransform;
239 translateTransform.translate(100, 0); 236 translateTransform.translate(100, 0);
240 RefPtr<TransformPaintPropertyNode> transform2 = 237 RefPtr<TransformPaintPropertyNode> transform2 =
241 TransformPaintPropertyNode::create(transform1, translateTransform, 238 TransformPaintPropertyNode::create(transform1, translateTransform,
242 FloatPoint3D()); 239 FloatPoint3D());
243 240
244 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 241 PropertyTreeState localState = rootPropertyTreeState();
245 localState.transform = transform2.get(); 242 localState.setTransform(transform2.get());
246 243
247 FloatRect input(0, 0, 100, 100); 244 FloatRect input(0, 0, 100, 100);
248 // Note: unlike NestedTransforms, the order of these transforms matters. This tests correct order of matrix multiplication. 245 // Note: unlike NestedTransforms, the order of these transforms matters. This tests correct order of matrix multiplication.
249 TransformationMatrix final = scaleTransform * translateTransform; 246 TransformationMatrix final = scaleTransform * translateTransform;
250 FloatRect output = final.mapRect(input); 247 FloatRect output = final.mapRect(input);
251 248
252 CHECK_MAPPINGS(input, output, output, final, rootClipNode->clipRect().rect(), 249 CHECK_MAPPINGS(input, output, output, final, rootClipNode->clipRect().rect(),
253 localState, rootGeometryPropertyTreeState()); 250 localState, rootPropertyTreeState());
254 251
255 // Check the cached matrix for the intermediate transform. 252 // Check the cached matrix for the intermediate transform.
256 EXPECT_EQ(scaleTransform, 253 EXPECT_EQ(scaleTransform,
257 getPrecomputedDataForAncestor(rootGeometryPropertyTreeState()) 254 getPrecomputedDataForAncestor(rootPropertyTreeState())
258 .toAncestorTransforms.get(transform1.get())); 255 .toAncestorTransforms.get(transform1.get()));
259 } 256 }
260 257
261 TEST_F(GeometryMapperTest, NestedTransformsIntermediateDestination) { 258 TEST_F(GeometryMapperTest, NestedTransformsIntermediateDestination) {
262 TransformationMatrix rotateTransform; 259 TransformationMatrix rotateTransform;
263 rotateTransform.rotate(45); 260 rotateTransform.rotate(45);
264 RefPtr<TransformPaintPropertyNode> transform1 = 261 RefPtr<TransformPaintPropertyNode> transform1 =
265 TransformPaintPropertyNode::create( 262 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
266 rootGeometryPropertyTreeState().transform, rotateTransform, 263 rotateTransform, FloatPoint3D());
267 FloatPoint3D());
268 264
269 TransformationMatrix scaleTransform; 265 TransformationMatrix scaleTransform;
270 scaleTransform.scale(2); 266 scaleTransform.scale(2);
271 RefPtr<TransformPaintPropertyNode> transform2 = 267 RefPtr<TransformPaintPropertyNode> transform2 =
272 TransformPaintPropertyNode::create(transform1, scaleTransform, 268 TransformPaintPropertyNode::create(transform1, scaleTransform,
273 FloatPoint3D()); 269 FloatPoint3D());
274 270
275 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 271 PropertyTreeState localState = rootPropertyTreeState();
276 localState.transform = transform2.get(); 272 localState.setTransform(transform2.get());
277 273
278 GeometryPropertyTreeState intermediateState = rootGeometryPropertyTreeState(); 274 PropertyTreeState intermediateState = rootPropertyTreeState();
279 intermediateState.transform = transform1.get(); 275 intermediateState.setTransform(transform1.get());
280 276
281 FloatRect input(0, 0, 100, 100); 277 FloatRect input(0, 0, 100, 100);
282 FloatRect output = scaleTransform.mapRect(input); 278 FloatRect output = scaleTransform.mapRect(input);
283 279
284 CHECK_MAPPINGS(input, output, output, scaleTransform, 280 CHECK_MAPPINGS(input, output, output, scaleTransform,
285 rootClipNode->clipRect().rect(), localState, 281 rootClipNode->clipRect().rect(), localState,
286 intermediateState); 282 intermediateState);
287 } 283 }
288 284
289 TEST_F(GeometryMapperTest, SimpleClip) { 285 TEST_F(GeometryMapperTest, SimpleClip) {
290 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create( 286 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(
291 rootClipNode, rootTransformNode, FloatRoundedRect(10, 10, 50, 50)); 287 rootClipNode, rootTransformNode, FloatRoundedRect(10, 10, 50, 50));
292 288
293 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 289 PropertyTreeState localState = rootPropertyTreeState();
294 localState.clip = clip.get(); 290 localState.setClip(clip.get());
295 291
296 FloatRect input(0, 0, 100, 100); 292 FloatRect input(0, 0, 100, 100);
297 FloatRect output(10, 10, 50, 50); 293 FloatRect output(10, 10, 50, 50);
298 294
299 CHECK_MAPPINGS( 295 CHECK_MAPPINGS(
300 input, // Input 296 input, // Input
301 output, // Visual rect 297 output, // Visual rect
302 input, // Transformed rect (not clipped). 298 input, // Transformed rect (not clipped).
303 rootTransformNode->matrix(), // Transform matrix to ancestor space 299 rootTransformNode->matrix(), // Transform matrix to ancestor space
304 clip->clipRect().rect(), // Clip rect in ancestor space 300 clip->clipRect().rect(), // Clip rect in ancestor space
305 localState, rootGeometryPropertyTreeState()); 301 localState, rootPropertyTreeState());
306 } 302 }
307 303
308 TEST_F(GeometryMapperTest, ClipBeforeTransform) { 304 TEST_F(GeometryMapperTest, ClipBeforeTransform) {
309 TransformationMatrix rotateTransform; 305 TransformationMatrix rotateTransform;
310 rotateTransform.rotate(45); 306 rotateTransform.rotate(45);
311 RefPtr<TransformPaintPropertyNode> transform = 307 RefPtr<TransformPaintPropertyNode> transform =
312 TransformPaintPropertyNode::create( 308 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
313 rootGeometryPropertyTreeState().transform, rotateTransform, 309 rotateTransform, FloatPoint3D());
314 FloatPoint3D());
315 310
316 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create( 311 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(
317 rootClipNode, transform.get(), FloatRoundedRect(10, 10, 50, 50)); 312 rootClipNode, transform.get(), FloatRoundedRect(10, 10, 50, 50));
318 313
319 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 314 PropertyTreeState localState = rootPropertyTreeState();
320 localState.clip = clip.get(); 315 localState.setClip(clip.get());
321 localState.transform = transform.get(); 316 localState.setTransform(transform.get());
322 317
323 FloatRect input(0, 0, 100, 100); 318 FloatRect input(0, 0, 100, 100);
324 FloatRect output(input); 319 FloatRect output(input);
325 output.intersect(clip->clipRect().rect()); 320 output.intersect(clip->clipRect().rect());
326 output = rotateTransform.mapRect(output); 321 output = rotateTransform.mapRect(output);
327 322
328 CHECK_MAPPINGS( 323 CHECK_MAPPINGS(
329 input, // Input 324 input, // Input
330 output, // Visual rect 325 output, // Visual rect
331 rotateTransform.mapRect(input), // Transformed rect (not clipped). 326 rotateTransform.mapRect(input), // Transformed rect (not clipped).
332 rotateTransform, // Transform matrix to ancestor space 327 rotateTransform, // Transform matrix to ancestor space
333 rotateTransform.mapRect( 328 rotateTransform.mapRect(
334 clip->clipRect().rect()), // Clip rect in ancestor space 329 clip->clipRect().rect()), // Clip rect in ancestor space
335 localState, 330 localState,
336 rootGeometryPropertyTreeState()); 331 rootPropertyTreeState());
337 } 332 }
338 333
339 TEST_F(GeometryMapperTest, ClipAfterTransform) { 334 TEST_F(GeometryMapperTest, ClipAfterTransform) {
340 TransformationMatrix rotateTransform; 335 TransformationMatrix rotateTransform;
341 rotateTransform.rotate(45); 336 rotateTransform.rotate(45);
342 RefPtr<TransformPaintPropertyNode> transform = 337 RefPtr<TransformPaintPropertyNode> transform =
343 TransformPaintPropertyNode::create( 338 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
344 rootGeometryPropertyTreeState().transform, rotateTransform, 339 rotateTransform, FloatPoint3D());
345 FloatPoint3D());
346 340
347 RefPtr<ClipPaintPropertyNode> clip = 341 RefPtr<ClipPaintPropertyNode> clip =
348 ClipPaintPropertyNode::create(rootClipNode, rootTransformNode.get(), 342 ClipPaintPropertyNode::create(rootClipNode, rootTransformNode.get(),
349 FloatRoundedRect(10, 10, 200, 200)); 343 FloatRoundedRect(10, 10, 200, 200));
350 344
351 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 345 PropertyTreeState localState = rootPropertyTreeState();
352 localState.clip = clip.get(); 346 localState.setClip(clip.get());
353 localState.transform = transform.get(); 347 localState.setTransform(transform.get());
354 348
355 FloatRect input(0, 0, 100, 100); 349 FloatRect input(0, 0, 100, 100);
356 FloatRect output(input); 350 FloatRect output(input);
357 output = rotateTransform.mapRect(output); 351 output = rotateTransform.mapRect(output);
358 output.intersect(clip->clipRect().rect()); 352 output.intersect(clip->clipRect().rect());
359 353
360 CHECK_MAPPINGS( 354 CHECK_MAPPINGS(
361 input, // Input 355 input, // Input
362 output, // Visual rect 356 output, // Visual rect
363 rotateTransform.mapRect(input), // Transformed rect (not clipped) 357 rotateTransform.mapRect(input), // Transformed rect (not clipped)
364 rotateTransform, // Transform matrix to ancestor space 358 rotateTransform, // Transform matrix to ancestor space
365 clip->clipRect().rect(), // Clip rect in ancestor space 359 clip->clipRect().rect(), // Clip rect in ancestor space
366 localState, rootGeometryPropertyTreeState()); 360 localState, rootPropertyTreeState());
367 } 361 }
368 362
369 TEST_F(GeometryMapperTest, TwoClipsWithTransformBetween) { 363 TEST_F(GeometryMapperTest, TwoClipsWithTransformBetween) {
370 RefPtr<ClipPaintPropertyNode> clip1 = 364 RefPtr<ClipPaintPropertyNode> clip1 =
371 ClipPaintPropertyNode::create(rootClipNode, rootTransformNode.get(), 365 ClipPaintPropertyNode::create(rootClipNode, rootTransformNode.get(),
372 FloatRoundedRect(10, 10, 200, 200)); 366 FloatRoundedRect(10, 10, 200, 200));
373 367
374 TransformationMatrix rotateTransform; 368 TransformationMatrix rotateTransform;
375 rotateTransform.rotate(45); 369 rotateTransform.rotate(45);
376 RefPtr<TransformPaintPropertyNode> transform = 370 RefPtr<TransformPaintPropertyNode> transform =
377 TransformPaintPropertyNode::create( 371 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
378 rootGeometryPropertyTreeState().transform, rotateTransform, 372 rotateTransform, FloatPoint3D());
379 FloatPoint3D());
380 373
381 RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create( 374 RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create(
382 clip1, transform.get(), FloatRoundedRect(10, 10, 200, 200)); 375 clip1, transform.get(), FloatRoundedRect(10, 10, 200, 200));
383 376
384 FloatRect input(0, 0, 100, 100); 377 FloatRect input(0, 0, 100, 100);
385 378
386 { 379 {
387 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 380 PropertyTreeState localState = rootPropertyTreeState();
388 localState.clip = clip1.get(); 381 localState.setClip(clip1.get());
389 localState.transform = transform.get(); 382 localState.setTransform(transform.get());
390 383
391 FloatRect output(input); 384 FloatRect output(input);
392 output = rotateTransform.mapRect(output); 385 output = rotateTransform.mapRect(output);
393 output.intersect(clip1->clipRect().rect()); 386 output.intersect(clip1->clipRect().rect());
394 387
395 CHECK_MAPPINGS( 388 CHECK_MAPPINGS(
396 input, // Input 389 input, // Input
397 output, // Visual rect 390 output, // Visual rect
398 rotateTransform.mapRect(input), // Transformed rect (not clipped) 391 rotateTransform.mapRect(input), // Transformed rect (not clipped)
399 rotateTransform, // Transform matrix to ancestor space 392 rotateTransform, // Transform matrix to ancestor space
400 clip1->clipRect().rect(), // Clip rect in ancestor space 393 clip1->clipRect().rect(), // Clip rect in ancestor space
401 localState, rootGeometryPropertyTreeState()); 394 localState, rootPropertyTreeState());
402 } 395 }
403 396
404 { 397 {
405 GeometryPropertyTreeState localState = rootGeometryPropertyTreeState(); 398 PropertyTreeState localState = rootPropertyTreeState();
406 localState.clip = clip2.get(); 399 localState.setClip(clip2.get());
407 localState.transform = transform.get(); 400 localState.setTransform(transform.get());
408 401
409 FloatRect mappedClip = rotateTransform.mapRect(clip2->clipRect().rect()); 402 FloatRect mappedClip = rotateTransform.mapRect(clip2->clipRect().rect());
410 mappedClip.intersect(clip1->clipRect().rect()); 403 mappedClip.intersect(clip1->clipRect().rect());
411 404
412 // All clips are performed in the space of the ancestor. In cases such as th is, this means the 405 // All clips are performed in the space of the ancestor. In cases such as th is, this means the
413 // clip is a bit lossy. 406 // clip is a bit lossy.
414 FloatRect output(input); 407 FloatRect output(input);
415 // Map to transformed rect in ancestor space. 408 // Map to transformed rect in ancestor space.
416 output = rotateTransform.mapRect(output); 409 output = rotateTransform.mapRect(output);
417 // Intersect with all clips between local and ancestor, independently mapped to ancestor space. 410 // Intersect with all clips between local and ancestor, independently mapped to ancestor space.
418 output.intersect(mappedClip); 411 output.intersect(mappedClip);
419 412
420 CHECK_MAPPINGS( 413 CHECK_MAPPINGS(
421 input, // Input 414 input, // Input
422 output, // Visual rect 415 output, // Visual rect
423 rotateTransform.mapRect(input), // Transformed rect (not clipped) 416 rotateTransform.mapRect(input), // Transformed rect (not clipped)
424 rotateTransform, // Transform matrix to ancestor space 417 rotateTransform, // Transform matrix to ancestor space
425 mappedClip, // Clip rect in ancestor space 418 mappedClip, // Clip rect in ancestor space
426 localState, rootGeometryPropertyTreeState()); 419 localState, rootPropertyTreeState());
427 } 420 }
428 } 421 }
429 422
430 TEST_F(GeometryMapperTest, SiblingTransforms) { 423 TEST_F(GeometryMapperTest, SiblingTransforms) {
431 // These transforms are siblings. Thus mapping from one to the other requires going through the root. 424 // These transforms are siblings. Thus mapping from one to the other requires going through the root.
432 TransformationMatrix rotateTransform1; 425 TransformationMatrix rotateTransform1;
433 rotateTransform1.rotate(45); 426 rotateTransform1.rotate(45);
434 RefPtr<TransformPaintPropertyNode> transform1 = 427 RefPtr<TransformPaintPropertyNode> transform1 =
435 TransformPaintPropertyNode::create( 428 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
436 rootGeometryPropertyTreeState().transform, rotateTransform1, 429 rotateTransform1, FloatPoint3D());
437 FloatPoint3D());
438 430
439 TransformationMatrix rotateTransform2; 431 TransformationMatrix rotateTransform2;
440 rotateTransform2.rotate(-45); 432 rotateTransform2.rotate(-45);
441 RefPtr<TransformPaintPropertyNode> transform2 = 433 RefPtr<TransformPaintPropertyNode> transform2 =
442 TransformPaintPropertyNode::create( 434 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
443 rootGeometryPropertyTreeState().transform, rotateTransform2, 435 rotateTransform2, FloatPoint3D());
444 FloatPoint3D());
445 436
446 GeometryPropertyTreeState transform1State = rootGeometryPropertyTreeState(); 437 PropertyTreeState transform1State = rootPropertyTreeState();
447 transform1State.transform = transform1; 438 transform1State.setTransform(transform1.get());
448 GeometryPropertyTreeState transform2State = rootGeometryPropertyTreeState(); 439 PropertyTreeState transform2State = rootPropertyTreeState();
449 transform2State.transform = transform2; 440 transform2State.setTransform(transform2.get());
450 441
451 bool success; 442 bool success;
452 FloatRect input(0, 0, 100, 100); 443 FloatRect input(0, 0, 100, 100);
453 FloatRect result = geometryMapper->localToVisualRectInAncestorSpace( 444 FloatRect result = geometryMapper->localToVisualRectInAncestorSpace(
454 input, transform1State, transform2State, success); 445 input, transform1State, transform2State, success);
455 // Fails, because the transform2state is not an ancestor of transform1State. 446 // Fails, because the transform2state is not an ancestor of transform1State.
456 EXPECT_FALSE(success); 447 EXPECT_FALSE(success);
457 EXPECT_RECT_EQ(input, result); 448 EXPECT_RECT_EQ(input, result);
458 449
459 result = geometryMapper->localToAncestorRect(input, transform1State, 450 result = geometryMapper->localToAncestorRect(input, transform1State,
(...skipping 25 matching lines...) Expand all
485 transform2State, success); 476 transform2State, success);
486 EXPECT_TRUE(success); 477 EXPECT_TRUE(success);
487 EXPECT_RECT_EQ(expected, result); 478 EXPECT_RECT_EQ(expected, result);
488 } 479 }
489 480
490 TEST_F(GeometryMapperTest, SiblingTransformsWithClip) { 481 TEST_F(GeometryMapperTest, SiblingTransformsWithClip) {
491 // These transforms are siblings. Thus mapping from one to the other requires going through the root. 482 // These transforms are siblings. Thus mapping from one to the other requires going through the root.
492 TransformationMatrix rotateTransform1; 483 TransformationMatrix rotateTransform1;
493 rotateTransform1.rotate(45); 484 rotateTransform1.rotate(45);
494 RefPtr<TransformPaintPropertyNode> transform1 = 485 RefPtr<TransformPaintPropertyNode> transform1 =
495 TransformPaintPropertyNode::create( 486 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
496 rootGeometryPropertyTreeState().transform, rotateTransform1, 487 rotateTransform1, FloatPoint3D());
497 FloatPoint3D());
498 488
499 TransformationMatrix rotateTransform2; 489 TransformationMatrix rotateTransform2;
500 rotateTransform2.rotate(-45); 490 rotateTransform2.rotate(-45);
501 RefPtr<TransformPaintPropertyNode> transform2 = 491 RefPtr<TransformPaintPropertyNode> transform2 =
502 TransformPaintPropertyNode::create( 492 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
503 rootGeometryPropertyTreeState().transform, rotateTransform2, 493 rotateTransform2, FloatPoint3D());
504 FloatPoint3D());
505 494
506 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create( 495 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(
507 rootGeometryPropertyTreeState().clip, transform2.get(), 496 rootPropertyTreeState().clip(), transform2.get(),
508 FloatRoundedRect(10, 10, 70, 70)); 497 FloatRoundedRect(10, 10, 70, 70));
509 498
510 GeometryPropertyTreeState transform1State = rootGeometryPropertyTreeState(); 499 PropertyTreeState transform1State = rootPropertyTreeState();
511 transform1State.transform = transform1; 500 transform1State.setTransform(transform1.get());
512 GeometryPropertyTreeState transform2AndClipState = 501 PropertyTreeState transform2AndClipState = rootPropertyTreeState();
513 rootGeometryPropertyTreeState(); 502 transform2AndClipState.setTransform(transform2.get());
514 transform2AndClipState.transform = transform2; 503 transform2AndClipState.setClip(clip.get());
515 transform2AndClipState.clip = clip;
516 504
517 bool success; 505 bool success;
518 FloatRect input(0, 0, 100, 100); 506 FloatRect input(0, 0, 100, 100);
519 507
520 // Test map from transform1State to transform2AndClipState. 508 // Test map from transform1State to transform2AndClipState.
521 FloatRect expected = 509 FloatRect expected =
522 rotateTransform2.inverse().mapRect(rotateTransform1.mapRect(input)); 510 rotateTransform2.inverse().mapRect(rotateTransform1.mapRect(input));
523 511
524 // mapToVisualRectInDestinationSpace ignores clip from the common ancestor to destination. 512 // mapToVisualRectInDestinationSpace ignores clip from the common ancestor to destination.
525 FloatRect result = geometryMapper->mapToVisualRectInDestinationSpace( 513 FloatRect result = geometryMapper->mapToVisualRectInDestinationSpace(
(...skipping 22 matching lines...) Expand all
548 // mapRectToDestinationSpace ignores clip. 536 // mapRectToDestinationSpace ignores clip.
549 result = geometryMapper->mapRectToDestinationSpace( 537 result = geometryMapper->mapRectToDestinationSpace(
550 input, transform2AndClipState, transform1State, success); 538 input, transform2AndClipState, transform1State, success);
551 EXPECT_TRUE(success); 539 EXPECT_TRUE(success);
552 EXPECT_RECT_EQ(expectedUnclipped, result); 540 EXPECT_RECT_EQ(expectedUnclipped, result);
553 } 541 }
554 542
555 TEST_F(GeometryMapperTest, LeastCommonAncestor) { 543 TEST_F(GeometryMapperTest, LeastCommonAncestor) {
556 TransformationMatrix matrix; 544 TransformationMatrix matrix;
557 RefPtr<TransformPaintPropertyNode> child1 = 545 RefPtr<TransformPaintPropertyNode> child1 =
558 TransformPaintPropertyNode::create( 546 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
559 rootGeometryPropertyTreeState().transform, matrix, FloatPoint3D()); 547 matrix, FloatPoint3D());
560 RefPtr<TransformPaintPropertyNode> child2 = 548 RefPtr<TransformPaintPropertyNode> child2 =
561 TransformPaintPropertyNode::create( 549 TransformPaintPropertyNode::create(rootPropertyTreeState().transform(),
562 rootGeometryPropertyTreeState().transform, matrix, FloatPoint3D()); 550 matrix, FloatPoint3D());
563 551
564 RefPtr<TransformPaintPropertyNode> childOfChild1 = 552 RefPtr<TransformPaintPropertyNode> childOfChild1 =
565 TransformPaintPropertyNode::create(child1, matrix, FloatPoint3D()); 553 TransformPaintPropertyNode::create(child1, matrix, FloatPoint3D());
566 RefPtr<TransformPaintPropertyNode> childOfChild2 = 554 RefPtr<TransformPaintPropertyNode> childOfChild2 =
567 TransformPaintPropertyNode::create(child2, matrix, FloatPoint3D()); 555 TransformPaintPropertyNode::create(child2, matrix, FloatPoint3D());
568 556
569 EXPECT_EQ(rootGeometryPropertyTreeState().transform, 557 EXPECT_EQ(rootPropertyTreeState().transform(),
570 leastCommonAncestor(childOfChild1.get(), childOfChild2.get())); 558 leastCommonAncestor(childOfChild1.get(), childOfChild2.get()));
571 EXPECT_EQ(rootGeometryPropertyTreeState().transform, 559 EXPECT_EQ(rootPropertyTreeState().transform(),
572 leastCommonAncestor(childOfChild1.get(), child2.get())); 560 leastCommonAncestor(childOfChild1.get(), child2.get()));
573 EXPECT_EQ( 561 EXPECT_EQ(rootPropertyTreeState().transform(),
574 rootGeometryPropertyTreeState().transform, 562 leastCommonAncestor(childOfChild1.get(),
575 leastCommonAncestor(childOfChild1.get(), 563 rootPropertyTreeState().transform()));
576 rootGeometryPropertyTreeState().transform.get()));
577 EXPECT_EQ(child1, leastCommonAncestor(childOfChild1.get(), child1.get())); 564 EXPECT_EQ(child1, leastCommonAncestor(childOfChild1.get(), child1.get()));
578 565
579 EXPECT_EQ(rootGeometryPropertyTreeState().transform, 566 EXPECT_EQ(rootPropertyTreeState().transform(),
580 leastCommonAncestor(childOfChild2.get(), childOfChild1.get())); 567 leastCommonAncestor(childOfChild2.get(), childOfChild1.get()));
581 EXPECT_EQ(rootGeometryPropertyTreeState().transform, 568 EXPECT_EQ(rootPropertyTreeState().transform(),
582 leastCommonAncestor(childOfChild2.get(), child1.get())); 569 leastCommonAncestor(childOfChild2.get(), child1.get()));
583 EXPECT_EQ( 570 EXPECT_EQ(rootPropertyTreeState().transform(),
584 rootGeometryPropertyTreeState().transform, 571 leastCommonAncestor(childOfChild2.get(),
585 leastCommonAncestor(childOfChild2.get(), 572 rootPropertyTreeState().transform()));
586 rootGeometryPropertyTreeState().transform.get()));
587 EXPECT_EQ(child2, leastCommonAncestor(childOfChild2.get(), child2.get())); 573 EXPECT_EQ(child2, leastCommonAncestor(childOfChild2.get(), child2.get()));
588 574
589 EXPECT_EQ(rootGeometryPropertyTreeState().transform, 575 EXPECT_EQ(rootPropertyTreeState().transform(),
590 leastCommonAncestor(child1.get(), child2.get())); 576 leastCommonAncestor(child1.get(), child2.get()));
591 } 577 }
592 578
593 } // namespace blink 579 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698