Chromium Code Reviews| OLD | NEW |
|---|---|
| 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/RuntimeEnabledFeatures.h" | 7 #include "platform/RuntimeEnabledFeatures.h" |
| 8 #include "platform/geometry/LayoutRect.h" | 8 #include "platform/geometry/LayoutRect.h" |
| 9 | 9 |
| 10 namespace blink { | 10 namespace blink { |
| 11 | 11 |
| 12 FloatRect GeometryMapper::mapToVisualRectInDestinationSpace( | 12 FloatRect GeometryMapper::sourceToDestinationVisualRect( |
| 13 const FloatRect& rect, | |
| 14 const PropertyTreeState& sourceState, | |
| 15 const PropertyTreeState& destinationState, | |
| 16 bool& success) { | |
| 17 FloatRect result = localToVisualRectInAncestorSpace( | |
| 18 rect, sourceState, destinationState, success); | |
| 19 if (success) | |
| 20 return result; | |
| 21 return slowMapToVisualRectInDestinationSpace(rect, sourceState, | |
| 22 destinationState, success); | |
| 23 } | |
| 24 | |
| 25 FloatRect GeometryMapper::mapRectToDestinationSpace( | |
| 26 const FloatRect& rect, | 13 const FloatRect& rect, |
| 27 const PropertyTreeState& sourceState, | 14 const PropertyTreeState& sourceState, |
| 28 const PropertyTreeState& destinationState, | 15 const PropertyTreeState& destinationState, |
| 29 bool& success) { | 16 bool& success) { |
| 30 FloatRect result = | 17 FloatRect result = |
| 31 localToAncestorRect(rect, sourceState, destinationState, success); | 18 localToAncestorVisualRect(rect, sourceState, destinationState, success); |
| 32 if (success) | 19 if (success) |
| 33 return result; | 20 return result; |
| 34 return slowMapRectToDestinationSpace(rect, sourceState, destinationState, | 21 return slowSourceToDestinationVisualRect(rect, sourceState, destinationState, |
| 35 success); | 22 success); |
| 36 } | 23 } |
| 37 | 24 |
| 38 FloatRect GeometryMapper::slowMapToVisualRectInDestinationSpace( | 25 FloatRect GeometryMapper::sourceToDestinationRect( |
| 26 const FloatRect& rect, | |
| 27 const TransformPaintPropertyNode* sourceTransformNode, | |
| 28 const TransformPaintPropertyNode* destinationTransformNode, | |
| 29 bool& success) { | |
| 30 FloatRect result = localToAncestorRect(rect, sourceTransformNode, | |
| 31 destinationTransformNode, success); | |
| 32 if (success) | |
| 33 return result; | |
| 34 return slowSourceToDestinationRect(rect, sourceTransformNode, | |
| 35 destinationTransformNode, success); | |
| 36 } | |
| 37 | |
| 38 FloatRect GeometryMapper::slowSourceToDestinationVisualRect( | |
| 39 const FloatRect& rect, | 39 const FloatRect& rect, |
| 40 const PropertyTreeState& sourceState, | 40 const PropertyTreeState& sourceState, |
| 41 const PropertyTreeState& destinationState, | 41 const PropertyTreeState& destinationState, |
| 42 bool& success) { | 42 bool& success) { |
| 43 const TransformPaintPropertyNode* lcaTransform = leastCommonAncestor( | 43 const TransformPaintPropertyNode* lcaTransform = leastCommonAncestor( |
| 44 sourceState.transform(), destinationState.transform()); | 44 sourceState.transform(), destinationState.transform()); |
| 45 DCHECK(lcaTransform); | 45 DCHECK(lcaTransform); |
| 46 | 46 |
| 47 // Assume that the clip of destinationState is an ancestor of the clip of | 47 // Assume that the clip of destinationState is an ancestor of the clip of |
| 48 // sourceState and is under the space of lcaTransform. Otherwise | 48 // sourceState and is under the space of lcaTransform. Otherwise |
| 49 // localToAncestorClipRect() will fail. | 49 // localToAncestorClipRect() will fail. |
| 50 PropertyTreeState lcaState = destinationState; | 50 PropertyTreeState lcaState = destinationState; |
| 51 lcaState.setTransform(lcaTransform); | 51 lcaState.setTransform(lcaTransform); |
| 52 | 52 |
| 53 const auto clipRect = localToAncestorClipRect(sourceState, lcaState, success); | 53 FloatRect result = |
| 54 localToAncestorVisualRect(rect, sourceState, lcaState, success); | |
| 54 if (!success) | 55 if (!success) |
| 55 return rect; | 56 return result; |
| 56 | 57 |
| 57 FloatRect result = localToAncestorRect(rect, sourceState, lcaState, success); | 58 return ancestorToLocalRect(result, lcaTransform, destinationState.transform(), |
|
chrishtr
2017/01/11 23:23:09
Why remove this?
Xianzhu
2017/01/11 23:54:43
Line 53-59 now becomes a call to localToAncestorVi
| |
| 58 DCHECK(success); | 59 success); |
| 59 result.intersect(clipRect); | |
| 60 | |
| 61 return ancestorToLocalRect(result, destinationState, lcaState, success); | |
| 62 } | 60 } |
| 63 | 61 |
| 64 FloatRect GeometryMapper::slowMapRectToDestinationSpace( | 62 FloatRect GeometryMapper::slowSourceToDestinationRect( |
| 65 const FloatRect& rect, | 63 const FloatRect& rect, |
| 66 const PropertyTreeState& sourceState, | 64 const TransformPaintPropertyNode* sourceTransformNode, |
| 67 const PropertyTreeState& destinationState, | 65 const TransformPaintPropertyNode* destinationTransformNode, |
| 68 bool& success) { | 66 bool& success) { |
| 69 const TransformPaintPropertyNode* lcaTransform = leastCommonAncestor( | 67 const TransformPaintPropertyNode* lcaTransform = |
| 70 sourceState.transform(), destinationState.transform()); | 68 leastCommonAncestor(sourceTransformNode, destinationTransformNode); |
| 71 DCHECK(lcaTransform); | 69 DCHECK(lcaTransform); |
| 72 PropertyTreeState lcaState = sourceState; | |
| 73 lcaState.setTransform(lcaTransform); | |
| 74 | 70 |
| 75 FloatRect result = localToAncestorRect(rect, sourceState, lcaState, success); | 71 FloatRect result = |
| 72 localToAncestorRect(rect, sourceTransformNode, lcaTransform, success); | |
| 76 DCHECK(success); | 73 DCHECK(success); |
| 77 | 74 |
| 78 return ancestorToLocalRect(result, destinationState, lcaState, success); | 75 return ancestorToLocalRect(result, lcaTransform, destinationTransformNode, |
| 76 success); | |
| 79 } | 77 } |
| 80 | 78 |
| 81 FloatRect GeometryMapper::localToVisualRectInAncestorSpace( | 79 FloatRect GeometryMapper::localToAncestorVisualRect( |
| 82 const FloatRect& rect, | 80 const FloatRect& rect, |
| 83 const PropertyTreeState& localState, | 81 const PropertyTreeState& localState, |
| 84 const PropertyTreeState& ancestorState, | 82 const PropertyTreeState& ancestorState, |
| 85 bool& success) { | 83 bool& success) { |
| 86 if (localState == ancestorState) { | 84 if (localState == ancestorState) { |
| 87 success = true; | 85 success = true; |
| 88 return rect; | 86 return rect; |
| 89 } | 87 } |
| 90 | 88 |
| 91 const auto& transformMatrix = | 89 const auto& transformMatrix = localToAncestorMatrix( |
| 92 localToAncestorMatrix(localState.transform(), ancestorState, success); | 90 localState.transform(), ancestorState.transform(), success); |
| 93 if (!success) | 91 if (!success) |
| 94 return rect; | 92 return rect; |
| 95 | 93 |
| 96 FloatRect mappedRect = transformMatrix.mapRect(rect); | 94 FloatRect mappedRect = transformMatrix.mapRect(rect); |
| 97 | 95 |
| 98 const auto clipRect = | 96 const auto clipRect = |
| 99 localToAncestorClipRect(localState, ancestorState, success); | 97 localToAncestorClipRect(localState, ancestorState, success); |
| 100 | 98 |
| 101 if (success) { | 99 if (success) { |
| 102 mappedRect.intersect(clipRect); | 100 mappedRect.intersect(clipRect); |
| 103 } else if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { | 101 } else if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { |
| 104 // On SPv1 we may fail when the paint invalidation container creates an | 102 // On SPv1 we may fail when the paint invalidation container creates an |
| 105 // overflow clip (in ancestorState) which is not in localState of an | 103 // overflow clip (in ancestorState) which is not in localState of an |
| 106 // out-of-flow positioned descendant. See crbug.com/513108 and layout test | 104 // out-of-flow positioned descendant. See crbug.com/513108 and layout test |
| 107 // compositing/overflow/handle-non-ancestor-clip-parent.html (run with | 105 // compositing/overflow/handle-non-ancestor-clip-parent.html (run with |
| 108 // --enable-prefer-compositing-to-lcd-text) for details. | 106 // --enable-prefer-compositing-to-lcd-text) for details. |
| 109 // Ignore it for SPv1 for now. | 107 // Ignore it for SPv1 for now. |
| 110 success = true; | 108 success = true; |
| 111 } else { | |
|
chrishtr
2017/01/11 23:23:09
Why remove this?
Xianzhu
2017/01/11 23:54:43
Now slowSourceToDestinationVisualRect (previously
chrishtr
2017/01/12 18:07:12
The DCHECK is to ensure that it's actually an ance
Xianzhu
2017/01/12 18:39:17
No DCHECK(success) here is mainly a requirement of
| |
| 112 DCHECK(success); | |
| 113 } | 109 } |
| 114 | 110 |
| 115 return mappedRect; | 111 return mappedRect; |
| 116 } | 112 } |
| 117 | 113 |
| 118 FloatRect GeometryMapper::localToAncestorRect( | 114 FloatRect GeometryMapper::localToAncestorRect( |
| 119 const FloatRect& rect, | 115 const FloatRect& rect, |
| 120 const PropertyTreeState& localState, | 116 const TransformPaintPropertyNode* localTransformNode, |
| 121 const PropertyTreeState& ancestorState, | 117 const TransformPaintPropertyNode* ancestorTransformNode, |
| 122 bool& success) { | 118 bool& success) { |
| 123 if (localState.transform() == ancestorState.transform()) { | 119 if (localTransformNode == ancestorTransformNode) { |
| 124 success = true; | 120 success = true; |
| 125 return rect; | 121 return rect; |
| 126 } | 122 } |
| 127 | 123 |
| 128 const auto& transformMatrix = | 124 const auto& transformMatrix = |
| 129 localToAncestorMatrix(localState.transform(), ancestorState, success); | 125 localToAncestorMatrix(localTransformNode, ancestorTransformNode, success); |
| 130 if (!success) | 126 if (!success) |
| 131 return rect; | 127 return rect; |
| 132 return transformMatrix.mapRect(rect); | 128 return transformMatrix.mapRect(rect); |
| 133 } | 129 } |
| 134 | 130 |
| 135 FloatRect GeometryMapper::ancestorToLocalRect( | 131 FloatRect GeometryMapper::ancestorToLocalRect( |
| 136 const FloatRect& rect, | 132 const FloatRect& rect, |
| 137 const PropertyTreeState& localState, | 133 const TransformPaintPropertyNode* ancestorTransformNode, |
| 138 const PropertyTreeState& ancestorState, | 134 const TransformPaintPropertyNode* localTransformNode, |
| 139 bool& success) { | 135 bool& success) { |
| 140 if (localState.transform() == ancestorState.transform()) { | 136 if (localTransformNode == ancestorTransformNode) { |
| 141 success = true; | 137 success = true; |
| 142 return rect; | 138 return rect; |
| 143 } | 139 } |
| 144 | 140 |
| 145 const auto& transformMatrix = | 141 const auto& transformMatrix = |
| 146 localToAncestorMatrix(localState.transform(), ancestorState, success); | 142 localToAncestorMatrix(localTransformNode, ancestorTransformNode, success); |
| 147 if (!success) | 143 if (!success) |
| 148 return rect; | 144 return rect; |
| 149 | 145 |
| 150 if (!transformMatrix.isInvertible()) { | 146 if (!transformMatrix.isInvertible()) { |
| 151 success = false; | 147 success = false; |
| 152 return rect; | 148 return rect; |
| 153 } | 149 } |
| 154 success = true; | 150 success = true; |
| 155 | 151 |
| 156 // TODO(chrishtr): Cache the inverse? | 152 // TODO(chrishtr): Cache the inverse? |
| 157 return transformMatrix.inverse().mapRect(rect); | 153 return transformMatrix.inverse().mapRect(rect); |
| 158 } | 154 } |
| 159 | 155 |
| 160 PrecomputedDataForAncestor& GeometryMapper::getPrecomputedDataForAncestor( | 156 PrecomputedDataForAncestor& GeometryMapper::getPrecomputedDataForAncestor( |
| 161 const PropertyTreeState& ancestorState) { | 157 const TransformPaintPropertyNode* ancestorTransformNode) { |
| 162 auto addResult = m_data.add(ancestorState.transform(), nullptr); | 158 auto addResult = m_data.add(ancestorTransformNode, nullptr); |
| 163 if (addResult.isNewEntry) | 159 if (addResult.isNewEntry) |
| 164 addResult.storedValue->value = PrecomputedDataForAncestor::create(); | 160 addResult.storedValue->value = PrecomputedDataForAncestor::create(); |
| 165 return *addResult.storedValue->value; | 161 return *addResult.storedValue->value; |
| 166 } | 162 } |
| 167 | 163 |
| 168 FloatRect GeometryMapper::localToAncestorClipRect( | 164 FloatRect GeometryMapper::localToAncestorClipRect( |
| 169 const PropertyTreeState& localState, | 165 const PropertyTreeState& localState, |
| 170 const PropertyTreeState& ancestorState, | 166 const PropertyTreeState& ancestorState, |
| 171 bool& success) { | 167 bool& success) { |
| 172 FloatRect clip(LayoutRect::infiniteIntRect()); | 168 FloatRect clip(LayoutRect::infiniteIntRect()); |
| 173 if (localState.clip() == ancestorState.clip()) { | 169 if (localState.clip() == ancestorState.clip()) { |
| 174 success = true; | 170 success = true; |
| 175 return clip; | 171 return clip; |
| 176 } | 172 } |
| 177 | 173 |
| 178 PrecomputedDataForAncestor& precomputedData = | 174 PrecomputedDataForAncestor& precomputedData = |
| 179 getPrecomputedDataForAncestor(ancestorState); | 175 getPrecomputedDataForAncestor(ancestorState.transform()); |
| 180 const ClipPaintPropertyNode* clipNode = localState.clip(); | 176 const ClipPaintPropertyNode* clipNode = localState.clip(); |
| 181 Vector<const ClipPaintPropertyNode*> intermediateNodes; | 177 Vector<const ClipPaintPropertyNode*> intermediateNodes; |
| 182 | 178 |
| 183 bool found = false; | |
| 184 // Iterate over the path from localState.clip to ancestorState.clip. Stop if | 179 // Iterate over the path from localState.clip to ancestorState.clip. Stop if |
| 185 // we've found a memoized (precomputed) clip for any particular node. | 180 // we've found a memoized (precomputed) clip for any particular node. |
| 186 while (clipNode) { | 181 while (clipNode && clipNode != ancestorState.clip()) { |
| 187 auto it = precomputedData.toAncestorClipRects.find(clipNode); | 182 auto it = precomputedData.toAncestorClipRects.find(clipNode); |
| 188 if (it != precomputedData.toAncestorClipRects.end()) { | 183 if (it != precomputedData.toAncestorClipRects.end()) { |
| 189 clip = it->value; | 184 clip = it->value; |
| 190 found = true; | |
| 191 break; | 185 break; |
| 192 } | 186 } |
| 193 intermediateNodes.push_back(clipNode); | 187 intermediateNodes.push_back(clipNode); |
| 194 | |
| 195 if (clipNode == ancestorState.clip()) | |
| 196 break; | |
| 197 | |
| 198 clipNode = clipNode->parent(); | 188 clipNode = clipNode->parent(); |
| 199 } | 189 } |
| 200 if (clipNode != ancestorState.clip() && !found) { | 190 if (!clipNode) { |
|
chrishtr
2017/01/11 23:23:09
What about if clipNode == ancestorState.clip()?
Xianzhu
2017/01/11 23:54:43
In the case we'll execute the loop below, the same
| |
| 201 success = false; | 191 success = false; |
| 202 return clip; | 192 return clip; |
| 203 } | 193 } |
| 204 | 194 |
| 205 // Iterate down from the top intermediate node found in the previous loop, | 195 // Iterate down from the top intermediate node found in the previous loop, |
| 206 // computing and memoizing clip rects as we go. | 196 // computing and memoizing clip rects as we go. |
| 207 for (auto it = intermediateNodes.rbegin(); it != intermediateNodes.rend(); | 197 for (auto it = intermediateNodes.rbegin(); it != intermediateNodes.rend(); |
| 208 ++it) { | 198 ++it) { |
| 209 if ((*it) != ancestorState.clip()) { | 199 success = false; |
| 210 success = false; | 200 const TransformationMatrix& transformMatrix = localToAncestorMatrix( |
| 211 const TransformationMatrix& transformMatrix = localToAncestorMatrix( | 201 (*it)->localTransformSpace(), ancestorState.transform(), success); |
| 212 (*it)->localTransformSpace(), ancestorState, success); | 202 if (!success) |
| 213 if (!success) | 203 return clip; |
| 214 return clip; | 204 FloatRect mappedRect = transformMatrix.mapRect((*it)->clipRect().rect()); |
| 215 FloatRect mappedRect = transformMatrix.mapRect((*it)->clipRect().rect()); | 205 clip.intersect(mappedRect); |
| 216 clip.intersect(mappedRect); | |
| 217 } | |
| 218 | |
| 219 precomputedData.toAncestorClipRects.set(*it, clip); | 206 precomputedData.toAncestorClipRects.set(*it, clip); |
| 220 } | 207 } |
| 221 | 208 |
| 222 success = true; | 209 success = true; |
| 223 return precomputedData.toAncestorClipRects.find(localState.clip())->value; | 210 return precomputedData.toAncestorClipRects.find(localState.clip())->value; |
| 224 } | 211 } |
| 225 | 212 |
| 226 const TransformationMatrix& GeometryMapper::localToAncestorMatrix( | 213 const TransformationMatrix& GeometryMapper::localToAncestorMatrix( |
| 227 const TransformPaintPropertyNode* localTransformNode, | 214 const TransformPaintPropertyNode* localTransformNode, |
| 228 const PropertyTreeState& ancestorState, | 215 const TransformPaintPropertyNode* ancestorTransformNode, |
| 229 bool& success) { | 216 bool& success) { |
| 230 if (localTransformNode == ancestorState.transform()) { | 217 if (localTransformNode == ancestorTransformNode) { |
| 231 success = true; | 218 success = true; |
| 232 return m_identity; | 219 return m_identity; |
| 233 } | 220 } |
| 234 | 221 |
| 235 PrecomputedDataForAncestor& precomputedData = | 222 PrecomputedDataForAncestor& precomputedData = |
| 236 getPrecomputedDataForAncestor(ancestorState); | 223 getPrecomputedDataForAncestor(ancestorTransformNode); |
| 237 | 224 |
| 238 const TransformPaintPropertyNode* transformNode = localTransformNode; | 225 const TransformPaintPropertyNode* transformNode = localTransformNode; |
| 239 Vector<const TransformPaintPropertyNode*> intermediateNodes; | 226 Vector<const TransformPaintPropertyNode*> intermediateNodes; |
| 240 TransformationMatrix transformMatrix; | 227 TransformationMatrix transformMatrix; |
| 241 | 228 |
| 242 // Iterate over the path from localTransformNode to ancestorState.transform. | 229 // Iterate over the path from localTransformNode to ancestorState.transform. |
| 243 // Stop if we've found a memoized (precomputed) transform for any particular | 230 // Stop if we've found a memoized (precomputed) transform for any particular |
| 244 // node. | 231 // node. |
| 245 while (transformNode) { | 232 while (transformNode && transformNode != ancestorTransformNode) { |
| 246 auto it = precomputedData.toAncestorTransforms.find(transformNode); | 233 auto it = precomputedData.toAncestorTransforms.find(transformNode); |
| 247 if (it != precomputedData.toAncestorTransforms.end()) { | 234 if (it != precomputedData.toAncestorTransforms.end()) { |
| 248 transformMatrix = it->value; | 235 transformMatrix = it->value; |
| 249 break; | 236 break; |
| 250 } | 237 } |
| 251 | |
| 252 if (transformNode == ancestorState.transform()) | |
| 253 break; | |
| 254 | |
| 255 intermediateNodes.push_back(transformNode); | 238 intermediateNodes.push_back(transformNode); |
| 256 transformNode = transformNode->parent(); | 239 transformNode = transformNode->parent(); |
| 257 } | 240 } |
| 258 if (!transformNode) { | 241 if (!transformNode) { |
| 259 success = false; | 242 success = false; |
| 260 return m_identity; | 243 return m_identity; |
| 261 } | 244 } |
| 262 | 245 |
| 263 // Iterate down from the top intermediate node found in the previous loop, | 246 // Iterate down from the top intermediate node found in the previous loop, |
| 264 // computing and memoizing transforms as we go. | 247 // computing and memoizing transforms as we go. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 307 | 290 |
| 308 // Walk up until we find the ancestor. | 291 // Walk up until we find the ancestor. |
| 309 while (a != b) { | 292 while (a != b) { |
| 310 a = a->parent(); | 293 a = a->parent(); |
| 311 b = b->parent(); | 294 b = b->parent(); |
| 312 } | 295 } |
| 313 return a; | 296 return a; |
| 314 } | 297 } |
| 315 | 298 |
| 316 } // namespace blink | 299 } // namespace blink |
| OLD | NEW |