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

Side by Side Diff: third_party/WebKit/Source/core/paint/PaintPropertyTreeBuilder.cpp

Issue 1774193002: New paint invalidation using paint property tree walk (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 9 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
(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 "core/paint/PaintPropertyTreeBuilder.h"
6
7 #include "core/frame/FrameView.h"
8 #include "core/layout/LayoutPart.h"
9 #include "core/layout/LayoutView.h"
10 #include "core/paint/ObjectPaintProperties.h"
11 #include "core/paint/PaintLayer.h"
12 #include "platform/graphics/paint/ClipPaintPropertyNode.h"
13 #include "platform/graphics/paint/TransformPaintPropertyNode.h"
14 #include "platform/transforms/TransformationMatrix.h"
15
16 namespace blink {
17
18 // The context for layout tree walk.
19 // The walk will be done in the primary tree order (= DOM order), thus the conte xt will also be
20 // responsible for bookkeeping tree state in other order, for example, the most recent position
21 // container seen.
22 struct PaintPropertyTreeBuilderContext {
23 PaintPropertyTreeBuilderContext()
24 : currentTransform(nullptr)
25 , currentClip(nullptr)
26 , transformForAbsolutePosition(nullptr)
27 , clipForAbsolutePosition(nullptr)
28 , transformForFixedPosition(nullptr)
29 , clipForFixedPosition(nullptr)
30 , currentEffect(nullptr) { }
31
32 // The combination of a transform and paint offset describes a linear space.
33 // When a layout object recur to its children, the main context is expected to refer
34 // the object's border box, then the callee will derive its own border box b y translating
35 // the space with its own layout location.
36 TransformPaintPropertyNode* currentTransform;
37 LayoutPoint paintOffset;
38 // The clip node describes the accumulated raster clip for the current subtr ee.
39 // Note that the computed raster region in canvas space for a clip node is i ndependent from
40 // the transform and paint offset above. Also the actual raster region may b e affected
41 // by layerization and occlusion tracking.
42 ClipPaintPropertyNode* currentClip;
43
44 // Separate context for out-of-flow positioned and fixed positioned elements are needed
45 // because they don't use DOM parent as their containing block.
46 // These additional contexts normally pass through untouched, and are only c opied from
47 // the main context when the current element serves as the containing block of corresponding
48 // positioned descendants.
49 // Overflow clips are also inherited by containing block tree instead of DOM tree, thus they
50 // are included in the additional context too.
51 TransformPaintPropertyNode* transformForAbsolutePosition;
52 LayoutPoint paintOffsetForAbsolutePosition;
53 ClipPaintPropertyNode* clipForAbsolutePosition;
54
55 TransformPaintPropertyNode* transformForFixedPosition;
56 LayoutPoint paintOffsetForFixedPosition;
57 ClipPaintPropertyNode* clipForFixedPosition;
58
59 // The effect hierarchy is applied by the stacking context tree. It is guara nteed that every
60 // DOM descendant is also a stacking context descendant. Therefore, we don't need extra
61 // bookkeeping for effect nodes and can generate the effect tree from a DOM- order traversal.
62 EffectPaintPropertyNode* currentEffect;
63 };
64
65 void PaintPropertyTreeBuilder::buildPropertyTrees(FrameView& rootFrame)
66 {
67 walk(rootFrame, PaintPropertyTreeBuilderContext());
68 }
69
70 void PaintPropertyTreeBuilder::walk(FrameView& frameView, const PaintPropertyTre eBuilderContext& context)
71 {
72 PaintPropertyTreeBuilderContext localContext(context);
73
74 // TODO(pdr): Creating paint properties for FrameView here will not be
75 // needed once settings()->rootLayerScrolls() is enabled.
76 // TODO(pdr): Make this conditional on the rootLayerScrolls setting.
77
78 TransformationMatrix frameTranslate;
79 frameTranslate.translate(frameView.x() + context.paintOffset.x(), frameView. y() + context.paintOffset.y());
80 RefPtr<TransformPaintPropertyNode> newTransformNodeForPreTranslation = Trans formPaintPropertyNode::create(frameTranslate, FloatPoint3D(), context.currentTra nsform);
81 localContext.transformForFixedPosition = newTransformNodeForPreTranslation.g et();
82 localContext.paintOffsetForFixedPosition = LayoutPoint();
83
84 FloatRoundedRect contentClip(IntRect(IntPoint(), frameView.visibleContentSiz e()));
85 RefPtr<ClipPaintPropertyNode> newClipNodeForContentClip = ClipPaintPropertyN ode::create(newTransformNodeForPreTranslation.get(), contentClip, localContext.c urrentClip);
86 localContext.currentClip = localContext.clipForAbsolutePosition = localConte xt.clipForFixedPosition = newClipNodeForContentClip.get();
87
88 DoubleSize scrollOffset = frameView.scrollOffsetDouble();
89 TransformationMatrix frameScroll;
90 frameScroll.translate(-scrollOffset.width(), -scrollOffset.height());
91 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = Tr ansformPaintPropertyNode::create(frameScroll, FloatPoint3D(), newTransformNodeFo rPreTranslation);
92 localContext.currentTransform = localContext.transformForAbsolutePosition = newTransformNodeForScrollTranslation.get();
93 localContext.paintOffset = localContext.paintOffsetForAbsolutePosition = Lay outPoint();
94
95 frameView.setPreTranslation(newTransformNodeForPreTranslation.release());
96 frameView.setScrollTranslation(newTransformNodeForScrollTranslation.release( ));
97 frameView.setContentClip(newClipNodeForContentClip.release());
98
99 if (LayoutView* layoutView = frameView.layoutView())
100 walk(*layoutView, localContext);
101 }
102
103 static void deriveBorderBoxFromContainerContext(const LayoutObject& object, Pain tPropertyTreeBuilderContext& context)
104 {
105 if (!object.isBoxModelObject())
106 return;
107
108 const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);
109
110 // TODO(trchen): There is some insanity going on with tables. Double check r esults.
111 switch (object.styleRef().position()) {
112 case StaticPosition:
113 break;
114 case RelativePosition:
115 context.paintOffset += boxModelObject.offsetForInFlowPosition();
116 break;
117 case AbsolutePosition:
118 context.currentTransform = context.transformForAbsolutePosition;
119 context.paintOffset = context.paintOffsetForAbsolutePosition;
120 context.currentClip = context.clipForAbsolutePosition;
121 break;
122 case StickyPosition:
123 context.paintOffset += boxModelObject.offsetForInFlowPosition();
124 break;
125 case FixedPosition:
126 context.currentTransform = context.transformForFixedPosition;
127 context.paintOffset = context.paintOffsetForFixedPosition;
128 context.currentClip = context.clipForFixedPosition;
129 break;
130 default:
131 ASSERT_NOT_REACHED();
132 }
133 if (boxModelObject.isBox())
134 context.paintOffset += toLayoutBox(boxModelObject).locationOffset();
135 }
136
137 static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeed ed(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
138 {
139 bool shouldCreatePaintOffsetTranslationNode = false;
140 if (object.isSVGRoot()) {
141 // SVG doesn't use paint offset internally so emit a paint offset at the html->svg boundary.
142 shouldCreatePaintOffsetTranslationNode = true;
143 } else if (object.isBoxModelObject()) {
144 // TODO(trchen): Eliminate PaintLayer dependency.
145 PaintLayer* layer = toLayoutBoxModelObject(object).layer();
146 shouldCreatePaintOffsetTranslationNode = layer && layer->paintsWithTrans form(GlobalPaintNormalPhase);
147 }
148
149 if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslat ionNode)
150 return nullptr;
151
152 RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = TransformPaintPropertyNode::create(
153 TransformationMatrix().translate(context.paintOffset.x(), context.paintO ffset.y()),
154 FloatPoint3D(), context.currentTransform);
155 context.currentTransform = newTransformNodeForPaintOffsetTranslation.get();
156 context.paintOffset = LayoutPoint();
157 return newTransformNodeForPaintOffsetTranslation.release();
158 }
159
160 static FloatPoint3D transformOrigin(const LayoutBox& box)
161 {
162 const ComputedStyle& style = box.styleRef();
163 FloatSize borderBoxSize(box.size());
164 return FloatPoint3D(
165 floatValueForLength(style.transformOriginX(), borderBoxSize.width()),
166 floatValueForLength(style.transformOriginY(), borderBoxSize.height()),
167 style.transformOriginZ());
168 }
169
170 static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const Layo utObject& object, PaintPropertyTreeBuilderContext& context)
171 {
172 if (object.isSVG() && !object.isSVGRoot()) {
173 const AffineTransform& transform = object.localToParentTransform();
174 if (transform.isIdentity())
175 return nullptr;
176
177 // SVG's transform origin is baked into the localToParentTransform.
178 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = Transf ormPaintPropertyNode::create(
179 transform, FloatPoint3D(0, 0, 0), context.currentTransform);
180 context.currentTransform = newTransformNodeForTransform.get();
181 return newTransformNodeForTransform.release();
182 }
183
184 const ComputedStyle& style = object.styleRef();
185 if (!object.isBox() || !style.hasTransform())
186 return nullptr;
187
188 ASSERT(context.paintOffset == LayoutPoint());
189
190 TransformationMatrix matrix;
191 style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::Excl udeTransformOrigin,
192 ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTrans formProperties);
193 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformP aintPropertyNode::create(
194 matrix, transformOrigin(toLayoutBox(object)), context.currentTransform);
195 context.currentTransform = newTransformNodeForTransform.get();
196 return newTransformNodeForTransform.release();
197 }
198
199 static PassRefPtr<EffectPaintPropertyNode> createEffectIfNeeded(const LayoutObje ct& object, PaintPropertyTreeBuilderContext& context)
200 {
201 const ComputedStyle& style = object.styleRef();
202 if (!style.hasOpacity())
203 return nullptr;
204 RefPtr<EffectPaintPropertyNode> newEffectNode = EffectPaintPropertyNode::cre ate(style.opacity(), context.currentEffect);
205 context.currentEffect = newEffectNode.get();
206 return newEffectNode.release();
207 }
208
209 static PassRefPtr<ClipPaintPropertyNode> createOverflowClipIfNeeded(const Layout Object& object, PaintPropertyTreeBuilderContext& context)
210 {
211 if (!object.isBox())
212 return nullptr;
213 const LayoutBox& box = toLayoutBox(object);
214
215 // The <input> elements can't have contents thus CSS overflow property doesn 't apply.
216 // However for layout purposes we do generate child layout objects for them, e.g. button label.
217 // We should clip the overflow from those children. This is called control c lip and we
218 // technically treat them like overflow clip.
219 LayoutRect clipRect;
220 if (box.hasControlClip())
221 clipRect = box.controlClipRect(context.paintOffset);
222 else if (box.hasOverflowClip())
223 clipRect = box.overflowClipRect(context.paintOffset);
224 else
225 return nullptr;
226
227 RefPtr<ClipPaintPropertyNode> newClipNodeForBorderRadiusClip;
228 const ComputedStyle& style = box.styleRef();
229 if (style.hasBorderRadius()) {
230 newClipNodeForBorderRadiusClip = ClipPaintPropertyNode::create(
231 context.currentTransform,
232 style.getRoundedInnerBorderFor(LayoutRect(context.paintOffset, box.s ize())),
233 context.currentClip);
234 }
235
236 RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = ClipPaintProperty Node::create(
237 context.currentTransform,
238 FloatRoundedRect(FloatRect(clipRect)),
239 newClipNodeForBorderRadiusClip ? newClipNodeForBorderRadiusClip.release( ) : context.currentClip);
240 context.currentClip = newClipNodeForOverflowClip.get();
241 return newClipNodeForOverflowClip.release();
242 }
243
244 static FloatPoint perspectiveOrigin(const LayoutBox& box)
245 {
246 const ComputedStyle& style = box.styleRef();
247 FloatSize borderBoxSize(box.size());
248 return FloatPoint(
249 floatValueForLength(style.perspectiveOriginX(), borderBoxSize.width()),
250 floatValueForLength(style.perspectiveOriginY(), borderBoxSize.height())) ;
251 }
252
253 static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const La youtObject& object, PaintPropertyTreeBuilderContext& context)
254 {
255 const ComputedStyle& style = object.styleRef();
256 if (!object.isBox() || !style.hasPerspective())
257 return nullptr;
258
259 RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = Transfor mPaintPropertyNode::create(
260 TransformationMatrix().applyPerspective(style.perspective()),
261 perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffse t), context.currentTransform);
262 context.currentTransform = newTransformNodeForPerspective.get();
263 return newTransformNodeForPerspective.release();
264 }
265
266 static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(co nst LayoutObject& object, PaintPropertyTreeBuilderContext& context)
267 {
268 if (!object.isBoxModelObject() || !object.hasOverflowClip())
269 return nullptr;
270
271 PaintLayer* layer = toLayoutBoxModelObject(object).layer();
272 ASSERT(layer);
273 DoubleSize scrollOffset = layer->getScrollableArea()->scrollOffset();
274 if (scrollOffset.isZero() && !layer->scrollsOverflow())
275 return nullptr;
276
277 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = Tr ansformPaintPropertyNode::create(
278 TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.he ight()),
279 FloatPoint3D(), context.currentTransform);
280 context.currentTransform = newTransformNodeForScrollTranslation.get();
281 return newTransformNodeForScrollTranslation.release();
282 }
283
284 static void updateOutOfFlowContext(const LayoutObject& object, PaintPropertyTree BuilderContext& context)
285 {
286 // At the html->svg boundary (see: createPaintOffsetTranslationIfNeeded) the currentTransform is
287 // up-to-date for all children of the svg root element. Additionally, inside SVG, all positioning
288 // uses transforms. Therefore, we only need to check createdNewTransform and isSVGRoot() to
289 // ensure out-of-flow and fixed positioning is correct at the svg->html boun dary.
290
291 if (object.canContainAbsolutePositionObjects()) {
292 context.transformForAbsolutePosition = context.currentTransform;
293 context.paintOffsetForAbsolutePosition = context.paintOffset;
294 context.clipForAbsolutePosition = context.currentClip;
295 }
296
297 // TODO(pdr): Remove the !object.isLayoutView() condition when removing Fram eView
298 // paint properties for rootLayerScrolls.
299 if (!object.isLayoutView() && object.canContainFixedPositionObjects()) {
300 context.transformForFixedPosition = context.currentTransform;
301 context.paintOffsetForFixedPosition = context.paintOffset;
302 context.clipForFixedPosition = context.currentClip;
303 }
304 }
305
306 static PassOwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> recordTreeCon textIfNeeded(LayoutObject& object, const PaintPropertyTreeBuilderContext& contex t)
307 {
308 // Note: Currently only layer painter makes use of the pre-computed context.
309 // This condition may be loosened with no adverse effects beside memory use.
310 if (!object.hasLayer())
311 return nullptr;
312
313 OwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> recordedContext = ad optPtr(new ObjectPaintProperties::LocalBorderBoxProperties);
314 recordedContext->paintOffset = context.paintOffset;
315 recordedContext->transform = context.currentTransform;
316 recordedContext->clip = context.currentClip;
317 recordedContext->effect = context.currentEffect;
318 return recordedContext.release();
319 }
320
321 void PaintPropertyTreeBuilder::walk(LayoutObject& object, const PaintPropertyTre eBuilderContext& context)
322 {
323 PaintPropertyTreeBuilderContext localContext(context);
324
325 deriveBorderBoxFromContainerContext(object, localContext);
326 RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = createPaintOffsetTranslationIfNeeded(object, localContext);
327 RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = createTran sformIfNeeded(object, localContext);
328 RefPtr<EffectPaintPropertyNode> newEffectNode = createEffectIfNeeded(object, localContext);
329 OwnPtr<ObjectPaintProperties::LocalBorderBoxProperties> newRecordedContext = recordTreeContextIfNeeded(object, localContext);
330 RefPtr<ClipPaintPropertyNode> newClipNodeForOverflowClip = createOverflowCli pIfNeeded(object, localContext);
331 // TODO(trchen): Insert flattening transform here, as specified by
332 // http://www.w3.org/TR/css3-transforms/#transform-style-property
333 RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPe rspectiveIfNeeded(object, localContext);
334 RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = cr eateScrollTranslationIfNeeded(object, localContext);
335 updateOutOfFlowContext(object, localContext);
336
337 if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransfor m || newEffectNode || newClipNodeForOverflowClip || newTransformNodeForPerspecti ve || newTransformNodeForScrollTranslation || newRecordedContext) {
338 OwnPtr<ObjectPaintProperties> updatedPaintProperties = ObjectPaintProper ties::create(
339 newTransformNodeForPaintOffsetTranslation.release(),
340 newTransformNodeForTransform.release(),
341 newEffectNode.release(),
342 newClipNodeForOverflowClip.release(),
343 newTransformNodeForPerspective.release(),
344 newTransformNodeForScrollTranslation.release(),
345 newRecordedContext.release());
346 object.setObjectPaintProperties(updatedPaintProperties.release());
347 } else {
348 object.clearObjectPaintProperties();
349 }
350
351 for (LayoutObject* child = object.slowFirstChild(); child; child = child->ne xtSibling()) {
352 if (child->isBoxModelObject() || child->isSVG())
353 walk(*child, localContext);
354 }
355
356 if (object.isLayoutPart()) {
357 Widget* widget = toLayoutPart(object).widget();
358 if (widget && widget->isFrameView())
359 walk(*toFrameView(widget), localContext);
360 // TODO(pdr): Investigate RemoteFrameView (crbug.com/579281).
361 }
362 }
363
364 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698