| OLD | NEW |
| (Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "platform/graphics/paint/GeometryMapper.h" |
| 6 |
| 7 #include "platform/geometry/GeometryTestHelpers.h" |
| 8 #include "platform/geometry/LayoutRect.h" |
| 9 #include "platform/graphics/paint/ClipPaintPropertyNode.h" |
| 10 #include "platform/graphics/paint/EffectPaintPropertyNode.h" |
| 11 #include "platform/graphics/paint/TransformPaintPropertyNode.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 |
| 14 namespace blink { |
| 15 |
| 16 class GeometryMapperTest : public ::testing::Test { |
| 17 public: |
| 18 RefPtr<TransformPaintPropertyNode> rootTransformNode; |
| 19 RefPtr<ClipPaintPropertyNode> rootClipNode; |
| 20 RefPtr<EffectPaintPropertyNode> rootEffectNode; |
| 21 |
| 22 std::unique_ptr<GeometryMapper> geometryMapper; |
| 23 |
| 24 PropertyTreeState rootPropertyTreeState() |
| 25 { |
| 26 PropertyTreeState state(rootTransformNode.get(), rootClipNode.get(), roo
tEffectNode.get()); |
| 27 return state; |
| 28 } |
| 29 |
| 30 PrecomputedDataForAncestor& GetPrecomputedDataForAncestor(const PropertyTree
State& propertyTreeState) |
| 31 { |
| 32 return geometryMapper->GetPrecomputedDataForAncestor(propertyTreeState); |
| 33 } |
| 34 |
| 35 private: |
| 36 void SetUp() override |
| 37 { |
| 38 rootTransformNode = TransformPaintPropertyNode::create(TransformationMat
rix(), FloatPoint3D(), nullptr); |
| 39 rootClipNode = ClipPaintPropertyNode::create(rootTransformNode, FloatRou
ndedRect(LayoutRect::infiniteIntRect()), nullptr); |
| 40 rootEffectNode = EffectPaintPropertyNode::create(1.0, nullptr); |
| 41 geometryMapper = wrapUnique(new GeometryMapper()); |
| 42 } |
| 43 |
| 44 void TearDown() override |
| 45 { |
| 46 geometryMapper.reset(); |
| 47 } |
| 48 }; |
| 49 |
| 50 const static float kTestEpsilon = 1e-6; |
| 51 |
| 52 #define EXPECT_RECT_EQ(expected, actual) \ |
| 53 do { \ |
| 54 const FloatRect& actualRect = actual; \ |
| 55 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.x(), actualRect.x(), k
TestEpsilon)) << "actual: " << actualRect.x() << ", expected: " << expected.x();
\ |
| 56 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.y(), actualRect.y(), k
TestEpsilon)) << "actual: " << actualRect.y() << ", expected: " << expected.y();
\ |
| 57 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.width(), actualRect.wi
dth(), kTestEpsilon)) << "actual: " << actualRect.width() << ", expected: " << e
xpected.width(); \ |
| 58 EXPECT_TRUE(GeometryTest::ApproximatelyEqual(expected.height(), actualRect.h
eight(), kTestEpsilon)) << "actual: " << actualRect.height() << ", expected: " <
< expected.height(); \ |
| 59 } while (false) |
| 60 |
| 61 #define CHECK_MAPPINGS(inputRect, expectedVisualRect, expectedTransformedRect, e
xpectedTransformToAncestor, expectedClipInAncestorSpace, localPropertyTreeState,
ancestorPropertyTreeState) \ |
| 62 do { \ |
| 63 EXPECT_RECT_EQ(expectedVisualRect, \ |
| 64 geometryMapper->LocalToVisualRectInAncestorSpace(inputRect, localPropert
yTreeState, ancestorPropertyTreeState)); \ |
| 65 EXPECT_RECT_EQ(expectedTransformedRect, \ |
| 66 geometryMapper->LocalToAncestorRect(inputRect, localPropertyTreeState, a
ncestorPropertyTreeState)); \ |
| 67 EXPECT_EQ(expectedTransformToAncestor, GetPrecomputedDataForAncestor(ancesto
rPropertyTreeState).toAncestorTransforms.get(localPropertyTreeState.transform));
\ |
| 68 EXPECT_EQ(expectedClipInAncestorSpace, GetPrecomputedDataForAncestor(ancesto
rPropertyTreeState).toAncestorClipRects.get(localPropertyTreeState.clip)); \ |
| 69 } while (false) |
| 70 |
| 71 TEST_F(GeometryMapperTest, Root) |
| 72 { |
| 73 FloatRect input(0, 0, 100, 100); |
| 74 |
| 75 CHECK_MAPPINGS(input, input, input, rootTransformNode->matrix(), rootClipNod
e->clipRect().rect(), rootPropertyTreeState(), rootPropertyTreeState()); |
| 76 } |
| 77 |
| 78 TEST_F(GeometryMapperTest, IdentityTransform) |
| 79 { |
| 80 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(TransformationMatrix(), FloatPoint3D(), rootPropertyTreeState().transform)
; |
| 81 PropertyTreeState localState = rootPropertyTreeState(); |
| 82 localState.transform = transform.get(); |
| 83 |
| 84 FloatRect input(0, 0, 100, 100); |
| 85 |
| 86 CHECK_MAPPINGS(input, input, input, transform->matrix(), rootClipNode->clipR
ect().rect(), localState, rootPropertyTreeState()); |
| 87 } |
| 88 |
| 89 TEST_F(GeometryMapperTest, TranslationTransform) |
| 90 { |
| 91 TransformationMatrix transformMatrix; |
| 92 transformMatrix.translate(20, 10); |
| 93 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(transformMatrix, FloatPoint3D(), rootPropertyTreeState().transform); |
| 94 PropertyTreeState localState = rootPropertyTreeState(); |
| 95 localState.transform = transform.get(); |
| 96 |
| 97 FloatRect input(0, 0, 100, 100); |
| 98 FloatRect output = transformMatrix.mapRect(input); |
| 99 |
| 100 CHECK_MAPPINGS(input, output, output, transform->matrix(), rootClipNode->cli
pRect().rect(), localState, rootPropertyTreeState()); |
| 101 |
| 102 bool success = false; |
| 103 EXPECT_RECT_EQ(input, |
| 104 geometryMapper->AncestorToLocalRect(output, localState, rootPropertyTree
State(), &success)); |
| 105 EXPECT_TRUE(success); |
| 106 } |
| 107 |
| 108 TEST_F(GeometryMapperTest, RotationAndScaleTransform) |
| 109 { |
| 110 TransformationMatrix transformMatrix; |
| 111 transformMatrix.rotate(45); |
| 112 transformMatrix.scale(2); |
| 113 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(transformMatrix, FloatPoint3D(0, 0, 0), rootPropertyTreeState().transform)
; |
| 114 PropertyTreeState localState = rootPropertyTreeState(); |
| 115 localState.transform = transform.get(); |
| 116 |
| 117 FloatRect input(0, 0, 100, 100); |
| 118 FloatRect output = transformMatrix.mapRect(input); |
| 119 |
| 120 CHECK_MAPPINGS(input, output, output, transformMatrix, rootClipNode->clipRec
t().rect(), localState, rootPropertyTreeState()); |
| 121 } |
| 122 |
| 123 TEST_F(GeometryMapperTest, RotationAndScaleTransformWithTransformOrigin) |
| 124 { |
| 125 TransformationMatrix transformMatrix; |
| 126 transformMatrix.rotate(45); |
| 127 transformMatrix.scale(2); |
| 128 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(transformMatrix, FloatPoint3D(50, 50, 0), rootPropertyTreeState().transfor
m); |
| 129 PropertyTreeState localState = rootPropertyTreeState(); |
| 130 localState.transform = transform.get(); |
| 131 |
| 132 FloatRect input(0, 0, 100, 100); |
| 133 transformMatrix.applyTransformOrigin(50, 50, 0); |
| 134 FloatRect output = transformMatrix.mapRect(input); |
| 135 |
| 136 CHECK_MAPPINGS(input, output, output, transformMatrix, rootClipNode->clipRec
t().rect(), localState, rootPropertyTreeState()); |
| 137 } |
| 138 |
| 139 TEST_F(GeometryMapperTest, NestedTransforms) |
| 140 { |
| 141 TransformationMatrix rotateTransform; |
| 142 rotateTransform.rotate(45); |
| 143 RefPtr<TransformPaintPropertyNode> transform1 = TransformPaintPropertyNode::
create(rotateTransform, FloatPoint3D(), rootPropertyTreeState().transform); |
| 144 |
| 145 TransformationMatrix scaleTransform; |
| 146 scaleTransform.scale(2); |
| 147 RefPtr<TransformPaintPropertyNode> transform2 = TransformPaintPropertyNode::
create(scaleTransform, FloatPoint3D(), transform1); |
| 148 |
| 149 PropertyTreeState localState = rootPropertyTreeState(); |
| 150 localState.transform = transform2.get(); |
| 151 |
| 152 FloatRect input(0, 0, 100, 100); |
| 153 TransformationMatrix final = rotateTransform * scaleTransform; |
| 154 FloatRect output = final.mapRect(input); |
| 155 |
| 156 CHECK_MAPPINGS(input, output, output, final, rootClipNode->clipRect().rect()
, localState, rootPropertyTreeState()); |
| 157 |
| 158 // Check the cached matrix for the intermediate transform. |
| 159 EXPECT_EQ(rotateTransform, GetPrecomputedDataForAncestor(rootPropertyTreeSta
te()).toAncestorTransforms.get(transform1.get())); |
| 160 } |
| 161 |
| 162 TEST_F(GeometryMapperTest, NestedTransformsIntermediateDestination) |
| 163 { |
| 164 TransformationMatrix rotateTransform; |
| 165 rotateTransform.rotate(45); |
| 166 RefPtr<TransformPaintPropertyNode> transform1 = TransformPaintPropertyNode::
create(rotateTransform, FloatPoint3D(), rootPropertyTreeState().transform); |
| 167 |
| 168 TransformationMatrix scaleTransform; |
| 169 scaleTransform.scale(2); |
| 170 RefPtr<TransformPaintPropertyNode> transform2 = TransformPaintPropertyNode::
create(scaleTransform, FloatPoint3D(), transform1); |
| 171 |
| 172 PropertyTreeState localState = rootPropertyTreeState(); |
| 173 localState.transform = transform2.get(); |
| 174 |
| 175 PropertyTreeState intermediateState = rootPropertyTreeState(); |
| 176 intermediateState.transform = transform1.get(); |
| 177 |
| 178 FloatRect input(0, 0, 100, 100); |
| 179 FloatRect output = scaleTransform.mapRect(input); |
| 180 |
| 181 CHECK_MAPPINGS(input, output, output, scaleTransform, rootClipNode->clipRect
().rect(), localState, intermediateState); |
| 182 } |
| 183 |
| 184 TEST_F(GeometryMapperTest, SimpleClip) |
| 185 { |
| 186 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(rootTrans
formNode, FloatRoundedRect(10, 10, 50, 50), rootClipNode); |
| 187 |
| 188 PropertyTreeState localState = rootPropertyTreeState(); |
| 189 localState.clip = clip.get(); |
| 190 |
| 191 FloatRect input(0, 0, 100, 100); |
| 192 FloatRect output(10, 10, 50, 50); |
| 193 |
| 194 CHECK_MAPPINGS( |
| 195 input, // Input |
| 196 output, // Visual rect |
| 197 input, // Transformed rect (not clipped). |
| 198 rootTransformNode->matrix(), // Transform matrix to ancestor space |
| 199 clip->clipRect().rect(), // Clip rect in ancestor space |
| 200 localState, rootPropertyTreeState()); |
| 201 } |
| 202 |
| 203 TEST_F(GeometryMapperTest, ClipBeforeTransform) |
| 204 { |
| 205 TransformationMatrix rotateTransform; |
| 206 rotateTransform.rotate(45); |
| 207 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(rotateTransform, FloatPoint3D(), rootPropertyTreeState().transform); |
| 208 |
| 209 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(transform
.get(), FloatRoundedRect(10, 10, 50, 50), rootClipNode); |
| 210 |
| 211 PropertyTreeState localState = rootPropertyTreeState(); |
| 212 localState.clip = clip.get(); |
| 213 localState.transform = transform.get(); |
| 214 |
| 215 FloatRect input(0, 0, 100, 100); |
| 216 FloatRect output(input); |
| 217 output.intersect(clip->clipRect().rect()); |
| 218 output = rotateTransform.mapRect(output); |
| 219 |
| 220 CHECK_MAPPINGS( |
| 221 input, // Input |
| 222 output, // Visual rect |
| 223 rotateTransform.mapRect(input), // Transformed rect (not clipped). |
| 224 rotateTransform, // Transform matrix to ancestor space |
| 225 rotateTransform.mapRect(clip->clipRect().rect()), // Clip rect in ancest
or space |
| 226 localState, rootPropertyTreeState()); |
| 227 } |
| 228 |
| 229 TEST_F(GeometryMapperTest, ClipAfterTransform) |
| 230 { |
| 231 TransformationMatrix rotateTransform; |
| 232 rotateTransform.rotate(45); |
| 233 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(rotateTransform, FloatPoint3D(), rootPropertyTreeState().transform); |
| 234 |
| 235 RefPtr<ClipPaintPropertyNode> clip = ClipPaintPropertyNode::create(rootTrans
formNode.get(), FloatRoundedRect(10, 10, 200, 200), rootClipNode); |
| 236 |
| 237 PropertyTreeState localState = rootPropertyTreeState(); |
| 238 localState.clip = clip.get(); |
| 239 localState.transform = transform.get(); |
| 240 |
| 241 FloatRect input(0, 0, 100, 100); |
| 242 FloatRect output(input); |
| 243 output = rotateTransform.mapRect(output); |
| 244 output.intersect(clip->clipRect().rect()); |
| 245 |
| 246 CHECK_MAPPINGS( |
| 247 input, // Input |
| 248 output, // Visual rect |
| 249 rotateTransform.mapRect(input), // Transformed rect (not clipped) |
| 250 rotateTransform, // Transform matrix to ancestor space |
| 251 clip->clipRect().rect(), // Clip rect in ancestor space |
| 252 localState, rootPropertyTreeState()); |
| 253 } |
| 254 |
| 255 TEST_F(GeometryMapperTest, TwoClipsWithTransformBetween) |
| 256 { |
| 257 RefPtr<ClipPaintPropertyNode> clip1 = ClipPaintPropertyNode::create(rootTran
sformNode.get(), FloatRoundedRect(10, 10, 200, 200), rootClipNode); |
| 258 |
| 259 TransformationMatrix rotateTransform; |
| 260 rotateTransform.rotate(45); |
| 261 RefPtr<TransformPaintPropertyNode> transform = TransformPaintPropertyNode::c
reate(rotateTransform, FloatPoint3D(), rootPropertyTreeState().transform); |
| 262 |
| 263 RefPtr<ClipPaintPropertyNode> clip2 = ClipPaintPropertyNode::create(transfor
m.get(), FloatRoundedRect(10, 10, 200, 200), clip1.get()); |
| 264 |
| 265 FloatRect input(0, 0, 100, 100); |
| 266 |
| 267 { |
| 268 PropertyTreeState localState = rootPropertyTreeState(); |
| 269 localState.clip = clip1.get(); |
| 270 localState.transform = transform.get(); |
| 271 |
| 272 FloatRect output(input); |
| 273 output = rotateTransform.mapRect(output); |
| 274 output.intersect(clip1->clipRect().rect()); |
| 275 |
| 276 CHECK_MAPPINGS( |
| 277 input, // Input |
| 278 output, // Visual rect |
| 279 rotateTransform.mapRect(input), // Transformed rect (not clipped) |
| 280 rotateTransform, // Transform matrix to ancestor space |
| 281 clip1->clipRect().rect(), // Clip rect in ancestor space |
| 282 localState, rootPropertyTreeState()); |
| 283 } |
| 284 |
| 285 { |
| 286 PropertyTreeState localState = rootPropertyTreeState(); |
| 287 localState.clip = clip2.get(); |
| 288 localState.transform = transform.get(); |
| 289 |
| 290 |
| 291 FloatRect mappedClip = rotateTransform.mapRect(clip2->clipRect().rect())
; |
| 292 mappedClip.intersect(clip1->clipRect().rect()); |
| 293 |
| 294 // All clips are performed in the space of the ancestor. In cases such a
s this, this means the |
| 295 // clip is a bit lossy. |
| 296 FloatRect output(input); |
| 297 // Map to transformed rect in ancestor space. |
| 298 output = rotateTransform.mapRect(output); |
| 299 // Intersect with all clips between local and ancestor, independently ma
pped to ancestor space. |
| 300 output.intersect(mappedClip); |
| 301 |
| 302 CHECK_MAPPINGS( |
| 303 input, // Input |
| 304 output, // Visual rect |
| 305 rotateTransform.mapRect(input), // Transformed rect (not clipped) |
| 306 rotateTransform, // Transform matrix to ancestor space |
| 307 mappedClip, // Clip rect in ancestor space |
| 308 localState, rootPropertyTreeState()); |
| 309 } |
| 310 } |
| 311 |
| 312 } // namespace blink |
| OLD | NEW |