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 "core/paint/BoxPaintInvalidator.h" | |
6 | |
7 #include "core/frame/Settings.h" | |
8 #include "core/layout/LayoutView.h" | |
9 #include "core/paint/ObjectPaintInvalidator.h" | |
10 #include "core/paint/PaintInvalidator.h" | |
11 #include "core/paint/PaintLayer.h" | |
12 #include "core/paint/PaintLayerScrollableArea.h" | |
13 #include "platform/geometry/LayoutRect.h" | |
14 | |
15 namespace blink { | |
16 | |
17 struct PreviousBoxSizes { | |
18 LayoutSize borderBoxSize; | |
19 LayoutRect contentBoxRect; | |
20 LayoutRect layoutOverflowRect; | |
21 }; | |
22 | |
23 typedef HashMap<const LayoutBox*, PreviousBoxSizes> PreviousBoxSizesMap; | |
24 static PreviousBoxSizesMap& previousBoxSizesMap() | |
25 { | |
26 DEFINE_STATIC_LOCAL(PreviousBoxSizesMap, map, ()); | |
27 return map; | |
28 } | |
29 | |
30 void BoxPaintInvalidator::boxWillBeDestroyed(const LayoutBox& box) | |
31 { | |
32 previousBoxSizesMap().remove(&box); | |
33 } | |
34 | |
35 // This is called when ObjectPaintInvalidator already did incremental invalidati on, | |
36 // so this function just does what is additionally needed for the LayoutBox. | |
37 void BoxPaintInvalidator::incrementallyInvalidatePaint() | |
38 { | |
39 bool hasBoxDecorations = m_box.styleRef().hasBoxDecorations(); | |
40 if (!m_box.styleRef().hasBackground() && !hasBoxDecorations) | |
41 return; | |
42 | |
43 const LayoutRect& oldBounds = m_context.oldBounds; | |
44 const LayoutRect& newBounds = m_context.newBounds; | |
45 | |
46 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(oldBounds.size()) ; | |
47 LayoutSize newBorderBoxSize = m_box.size(); | |
48 | |
49 // If border m_box size didn't change, LayoutObject's incrementallyInvalidat ePaint() is good. | |
50 if (oldBorderBoxSize == newBorderBoxSize) | |
51 return; | |
52 | |
53 // If size of the paint invalidation rect equals to size of border box, Obje ctPaintInvalidator::incrementallyInvalidatePaint() | |
54 // is good for boxes having background without box decorations. | |
55 DCHECK(oldBounds.location() == newBounds.location()); // Otherwise we won't do incremental invalidation. | |
56 if (!hasBoxDecorations | |
57 && m_context.newLocation == newBounds.location() | |
58 && oldBorderBoxSize == oldBounds.size() | |
59 && newBorderBoxSize == newBounds.size()) | |
60 return; | |
61 | |
62 // Invalidate the right delta part and the right border of the old or new m_ box which has smaller width. | |
63 if (LayoutUnit deltaWidth = (oldBorderBoxSize.width() - newBorderBoxSize.wid th()).abs()) { | |
64 LayoutUnit smallerWidth = std::min(oldBorderBoxSize.width(), newBorderBo xSize.width()); | |
65 LayoutUnit borderTopRightRadiusWidth = valueForLength(m_box.styleRef().b orderTopRightRadius().width(), smallerWidth); | |
66 LayoutUnit borderBottomRightRadiusWidth = valueForLength(m_box.styleRef( ).borderBottomRightRadius().width(), smallerWidth); | |
67 LayoutUnit borderWidth = std::max(LayoutUnit(m_box.borderRight()), std:: max(borderTopRightRadiusWidth, borderBottomRightRadiusWidth)); | |
68 LayoutRect rightDeltaRect(m_context.newLocation.x() + smallerWidth - bor derWidth, m_context.newLocation.y(), | |
69 deltaWidth + borderWidth, std::max(oldBorderBoxSize.height(), newBor derBoxSize.height())); | |
70 invalidatePaintRectClippedByOldAndNewBounds(rightDeltaRect); | |
71 } | |
72 | |
73 // Invalidate the bottom delta part and the bottom border of the old or new m_box which has smaller height. | |
74 if (LayoutUnit deltaHeight = (oldBorderBoxSize.height() - newBorderBoxSize.h eight()).abs()) { | |
75 LayoutUnit smallerHeight = std::min(oldBorderBoxSize.height(), newBorder BoxSize.height()); | |
76 LayoutUnit borderBottomLeftRadiusHeight = valueForLength(m_box.styleRef( ).borderBottomLeftRadius().height(), smallerHeight); | |
77 LayoutUnit borderBottomRightRadiusHeight = valueForLength(m_box.styleRef ().borderBottomRightRadius().height(), smallerHeight); | |
78 LayoutUnit borderHeight = std::max(LayoutUnit(m_box.borderBottom()), std ::max(borderBottomLeftRadiusHeight, borderBottomRightRadiusHeight)); | |
79 LayoutRect bottomDeltaRect(m_context.newLocation.x(), m_context.newLocat ion.y() + smallerHeight - borderHeight, | |
80 std::max(oldBorderBoxSize.width(), newBorderBoxSize.width()), deltaH eight + borderHeight); | |
81 invalidatePaintRectClippedByOldAndNewBounds(bottomDeltaRect); | |
82 } | |
83 } | |
84 | |
85 void BoxPaintInvalidator::invalidatePaintRectClippedByOldAndNewBounds(const Layo utRect& rect) | |
86 { | |
87 if (rect.isEmpty()) | |
88 return; | |
89 | |
90 LayoutRect rectClippedByOldBounds = intersection(rect, m_context.oldBounds); | |
91 LayoutRect rectClippedByNewBounds = intersection(rect, m_context.newBounds); | |
92 // Invalidate only once if the clipped rects equal. | |
93 if (rectClippedByOldBounds == rectClippedByNewBounds) { | |
94 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByOldBounds, PaintInvalidationIncremental); | |
95 return; | |
96 } | |
97 // Invalidate the bigger one if one contains another. Otherwise invalidate b oth. | |
98 if (!rectClippedByNewBounds.contains(rectClippedByOldBounds)) | |
99 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByOldBounds, PaintInvalidationIncremental); | |
100 if (!rectClippedByOldBounds.contains(rectClippedByNewBounds)) | |
101 m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContaine r, rectClippedByNewBounds, PaintInvalidationIncremental); | |
102 } | |
103 | |
104 PaintInvalidationReason BoxPaintInvalidator::computePaintInvalidationReason() | |
105 { | |
106 PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).co mputePaintInvalidationReason(); | |
107 | |
108 if (reason != PaintInvalidationDelayedFull && isFullPaintInvalidationReason( reason)) | |
109 return reason; | |
110 | |
111 if (m_box.mayNeedPaintInvalidationAnimatedBackgroundImage() && !m_box.backgr oundIsKnownToBeObscured()) | |
112 reason = PaintInvalidationDelayedFull; | |
113 | |
114 // If the current paint invalidation reason is PaintInvalidationDelayedFull, then this paint invalidation can delayed if the | |
115 // LayoutBox in question is not on-screen. The logic to decide whether this is appropriate exists at the site of the original | |
116 // paint invalidation that chose PaintInvalidationDelayedFull. | |
117 if (reason == PaintInvalidationDelayedFull) { | |
118 // Do regular full paint invalidation if the object is onscreen. | |
119 return m_box.intersectsVisibleViewport() ? PaintInvalidationFull : Paint InvalidationDelayedFull; | |
120 } | |
121 | |
122 if (m_box.isLayoutView()) { | |
123 const LayoutView& layoutView = toLayoutView(m_box); | |
124 // In normal compositing mode, root background doesn't need to be invali dated for | |
125 // box changes, because the background always covers the whole document rect | |
126 // and clipping is done by compositor()->m_containerLayer. Also the scro llbars | |
127 // are always composited. There are no other box decoration on the Layou tView thus | |
128 // we can safely exit here. | |
129 if (layoutView.usesCompositing() && (!layoutView.document().settings() | | !layoutView.document().settings()->rootLayerScrolls())) | |
130 return reason; | |
131 } | |
132 | |
133 // If the transform is not identity or translation, incremental invalidation is not applicable | |
134 // because the difference between oldBounds and newBounds doesn't cover all area needing invalidation. | |
135 // FIXME: Should also consider ancestor transforms since paintInvalidationCo ntainer. crbug.com/426111. | |
136 if (reason == PaintInvalidationIncremental | |
137 && m_context.paintInvalidationContainer != m_box | |
138 && m_box.hasLayer() && m_box.layer()->transform() | |
139 && !m_box.layer()->transform()->isIdentityOrTranslation()) | |
140 return PaintInvalidationBoundsChange; | |
141 | |
142 const ComputedStyle& style = m_box.styleRef(); | |
143 if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.maskLa yers().thisOrNextLayersUseContentBox() || style.boxSizing() == BoxSizingBorderBo x) { | |
144 if (previousBoxSizesMap().get(&m_box).contentBoxRect != m_box.contentBox Rect()) | |
145 return PaintInvalidationContentBoxChange; | |
146 } | |
147 | |
148 if (!style.hasBackground() && !style.hasBoxDecorations()) { | |
149 // We could let incremental invalidation cover non-composited scrollbars , but just | |
150 // do a full invalidation because incremental invalidation will go away with slimming paint. | |
151 if (reason == PaintInvalidationIncremental && m_box.hasNonCompositedScro llbars()) | |
152 return PaintInvalidationBorderBoxChange; | |
153 return reason; | |
154 } | |
155 | |
156 if (style.backgroundLayers().thisOrNextLayersHaveLocalAttachment()) { | |
157 if (previousBoxSizesMap().get(&m_box).layoutOverflowRect != m_box.layout OverflowRect()) | |
158 return PaintInvalidationLayoutOverflowBoxChange; | |
159 } | |
160 | |
161 LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(m_context.oldBoun ds.size()); | |
162 LayoutSize newBorderBoxSize = m_box.size(); | |
163 | |
164 if (oldBorderBoxSize == newBorderBoxSize) | |
165 return reason; | |
166 | |
167 // LayoutBox::incrementallyInvalidatePaint() depends on positionFromPaintInv alidationBacking | |
168 // which is not available when slimmingPaintOffsetCachingEnabled. | |
169 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && (style.has BoxDecorations() || style.hasBackground())) | |
170 return PaintInvalidationBorderBoxChange; | |
171 | |
172 // See another hasNonCompositedScrollbars() callsite above. | |
173 if (m_box.hasNonCompositedScrollbars()) | |
174 return PaintInvalidationBorderBoxChange; | |
175 | |
176 if (style.hasVisualOverflowingEffect() || style.hasAppearance() || style.has FilterInducingProperty() || style.resize() != RESIZE_NONE) | |
177 return PaintInvalidationBorderBoxChange; | |
178 | |
179 if (style.hasBorderRadius()) { | |
180 // If a border-radius exists and width/height is smaller than radius wid th/height, | |
181 // we need to fully invalidate to cover the changed radius. | |
182 FloatRoundedRect oldRoundedRect = style.getRoundedBorderFor(LayoutRect(L ayoutPoint(0, 0), oldBorderBoxSize)); | |
183 FloatRoundedRect newRoundedRect = style.getRoundedBorderFor(LayoutRect(L ayoutPoint(0, 0), newBorderBoxSize)); | |
184 if (oldRoundedRect.getRadii() != newRoundedRect.getRadii()) | |
185 return PaintInvalidationBorderBoxChange; | |
186 } | |
187 | |
188 if (oldBorderBoxSize.width() != newBorderBoxSize.width() && m_box.mustInvali dateBackgroundOrBorderPaintOnWidthChange()) | |
189 return PaintInvalidationBorderBoxChange; | |
190 if (oldBorderBoxSize.height() != newBorderBoxSize.height() && m_box.mustInva lidateBackgroundOrBorderPaintOnHeightChange()) | |
191 return PaintInvalidationBorderBoxChange; | |
192 | |
193 if (reason == PaintInvalidationNone && (style.hasBackground() || style.hasBo xDecorations())) | |
194 reason = PaintInvalidationIncremental; | |
195 | |
196 return reason; | |
197 } | |
198 | |
199 PaintInvalidationReason BoxPaintInvalidator::invalidatePaintIfNeeded() | |
200 { | |
201 PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).in validatePaintIfNeededWithComputedReason(computePaintInvalidationReason()); | |
202 | |
203 // For incremental invalidation, ObjectPaintInvalidator::incrementallyInvali datePaint() is | |
204 // not enough for LayoutBox in some cases, e.g. having border, border-radius , etc. | |
205 // The following will do additional incremental invalidation for LayoutBox i f needed. | |
206 if (reason == PaintInvalidationIncremental) | |
chrishtr
2016/08/10 20:47:32
The old code would not condition on PaintInvalidat
Xianzhu
2016/08/10 22:16:43
Yes.
The old code works like the following for a
| |
207 incrementallyInvalidatePaint(); | |
208 | |
209 if (PaintLayerScrollableArea* area = m_box.getScrollableArea()) | |
210 area->invalidatePaintOfScrollControlsIfNeeded(m_context); | |
211 | |
212 // This is for the next invalidatePaintIfNeeded so must be at the end. | |
213 savePreviousBoxSizesIfNeeded(); | |
214 | |
215 return reason; | |
216 } | |
217 | |
218 bool BoxPaintInvalidator::needsToSavePreviousBoxSizes() | |
219 { | |
220 LayoutSize paintInvalidationSize = m_context.newBounds.size(); | |
221 // Don't save old box sizes if the paint rect is empty because we'll | |
222 // full invalidate once the paint rect becomes non-empty. | |
223 if (paintInvalidationSize.isEmpty()) | |
224 return false; | |
225 | |
226 const ComputedStyle& style = m_box.styleRef(); | |
227 | |
228 // If we use border-box sizing we need to track changes in the size of the c ontent box. | |
229 if (style.boxSizing() == BoxSizingBorderBox) | |
230 return true; | |
231 | |
232 // We need the old box sizes only when the box has background, decorations, or masks. | |
233 // Main LayoutView paints base background, thus interested in box size. | |
234 if (!m_box.isLayoutView() && !style.hasBackground() && !style.hasBoxDecorati ons() && !style.hasMask()) | |
235 return false; | |
236 | |
237 // No need to save old border box size if we can use size of the old paint | |
238 // rect as the old border box size in the next invalidation. | |
239 if (paintInvalidationSize != m_box.size()) | |
240 return true; | |
241 | |
242 // Background and mask layers can depend on other boxes than border box. See crbug.com/490533 | |
243 if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.backgr oundLayers().thisOrNextLayersHaveLocalAttachment() | |
244 || style.maskLayers().thisOrNextLayersUseContentBox()) | |
245 return true; | |
246 | |
247 return false; | |
248 } | |
249 | |
250 void BoxPaintInvalidator::savePreviousBoxSizesIfNeeded() | |
251 { | |
252 if (!needsToSavePreviousBoxSizes()) { | |
253 previousBoxSizesMap().remove(&m_box); | |
254 return; | |
255 } | |
256 | |
257 PreviousBoxSizes sizes = { | |
258 m_box.size(), | |
259 m_box.contentBoxRect(), | |
260 m_box.layoutOverflowRect() | |
261 }; | |
262 previousBoxSizesMap().set(&m_box, sizes); | |
263 } | |
264 | |
265 LayoutSize BoxPaintInvalidator::computePreviousBorderBoxSize(const LayoutSize& p reviousBoundsSize) | |
266 { | |
267 // PreviousBorderBoxSize is only valid when there is background or box decor ations. | |
268 DCHECK(m_box.styleRef().hasBackground() || m_box.styleRef().hasBoxDecoration s()); | |
269 | |
270 auto it = previousBoxSizesMap().find(&m_box); | |
271 if (it != previousBoxSizesMap().end()) | |
272 return it->value.borderBoxSize; | |
273 | |
274 // We didn't save the old border box size because it was the same as the siz e of oldBounds. | |
275 return previousBoundsSize; | |
276 } | |
277 | |
278 } // namespace blink | |
OLD | NEW |