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

Unified Diff: third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp

Issue 2208463003: First step of PaintInvalidator implementation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: - Created 4 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
diff --git a/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1625c3af3e9e00c5a9f149d6868482d0e95d497
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/BoxPaintInvalidator.cpp
@@ -0,0 +1,278 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "core/paint/BoxPaintInvalidator.h"
+
+#include "core/frame/Settings.h"
+#include "core/layout/LayoutView.h"
+#include "core/paint/ObjectPaintInvalidator.h"
+#include "core/paint/PaintInvalidator.h"
+#include "core/paint/PaintLayer.h"
+#include "core/paint/PaintLayerScrollableArea.h"
+#include "platform/geometry/LayoutRect.h"
+
+namespace blink {
+
+struct PreviousBoxSizes {
+ LayoutSize borderBoxSize;
+ LayoutRect contentBoxRect;
+ LayoutRect layoutOverflowRect;
+};
+
+typedef HashMap<const LayoutBox*, PreviousBoxSizes> PreviousBoxSizesMap;
+static PreviousBoxSizesMap& previousBoxSizesMap()
+{
+ DEFINE_STATIC_LOCAL(PreviousBoxSizesMap, map, ());
+ return map;
+}
+
+void BoxPaintInvalidator::boxWillBeDestroyed(const LayoutBox& box)
+{
+ previousBoxSizesMap().remove(&box);
+}
+
+// This is called when ObjectPaintInvalidator already did incremental invalidation,
+// so this function just does what is additionally needed for the LayoutBox.
+void BoxPaintInvalidator::incrementallyInvalidatePaint()
+{
+ bool hasBoxDecorations = m_box.styleRef().hasBoxDecorations();
+ if (!m_box.styleRef().hasBackground() && !hasBoxDecorations)
+ return;
+
+ const LayoutRect& oldBounds = m_context.oldBounds;
+ const LayoutRect& newBounds = m_context.newBounds;
+
+ LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(oldBounds.size());
+ LayoutSize newBorderBoxSize = m_box.size();
+
+ // If border m_box size didn't change, LayoutObject's incrementallyInvalidatePaint() is good.
+ if (oldBorderBoxSize == newBorderBoxSize)
+ return;
+
+ // If size of the paint invalidation rect equals to size of border box, ObjectPaintInvalidator::incrementallyInvalidatePaint()
+ // is good for boxes having background without box decorations.
+ DCHECK(oldBounds.location() == newBounds.location()); // Otherwise we won't do incremental invalidation.
+ if (!hasBoxDecorations
+ && m_context.newLocation == newBounds.location()
+ && oldBorderBoxSize == oldBounds.size()
+ && newBorderBoxSize == newBounds.size())
+ return;
+
+ // Invalidate the right delta part and the right border of the old or new m_box which has smaller width.
+ if (LayoutUnit deltaWidth = (oldBorderBoxSize.width() - newBorderBoxSize.width()).abs()) {
+ LayoutUnit smallerWidth = std::min(oldBorderBoxSize.width(), newBorderBoxSize.width());
+ LayoutUnit borderTopRightRadiusWidth = valueForLength(m_box.styleRef().borderTopRightRadius().width(), smallerWidth);
+ LayoutUnit borderBottomRightRadiusWidth = valueForLength(m_box.styleRef().borderBottomRightRadius().width(), smallerWidth);
+ LayoutUnit borderWidth = std::max(LayoutUnit(m_box.borderRight()), std::max(borderTopRightRadiusWidth, borderBottomRightRadiusWidth));
+ LayoutRect rightDeltaRect(m_context.newLocation.x() + smallerWidth - borderWidth, m_context.newLocation.y(),
+ deltaWidth + borderWidth, std::max(oldBorderBoxSize.height(), newBorderBoxSize.height()));
+ invalidatePaintRectClippedByOldAndNewBounds(rightDeltaRect);
+ }
+
+ // Invalidate the bottom delta part and the bottom border of the old or new m_box which has smaller height.
+ if (LayoutUnit deltaHeight = (oldBorderBoxSize.height() - newBorderBoxSize.height()).abs()) {
+ LayoutUnit smallerHeight = std::min(oldBorderBoxSize.height(), newBorderBoxSize.height());
+ LayoutUnit borderBottomLeftRadiusHeight = valueForLength(m_box.styleRef().borderBottomLeftRadius().height(), smallerHeight);
+ LayoutUnit borderBottomRightRadiusHeight = valueForLength(m_box.styleRef().borderBottomRightRadius().height(), smallerHeight);
+ LayoutUnit borderHeight = std::max(LayoutUnit(m_box.borderBottom()), std::max(borderBottomLeftRadiusHeight, borderBottomRightRadiusHeight));
+ LayoutRect bottomDeltaRect(m_context.newLocation.x(), m_context.newLocation.y() + smallerHeight - borderHeight,
+ std::max(oldBorderBoxSize.width(), newBorderBoxSize.width()), deltaHeight + borderHeight);
+ invalidatePaintRectClippedByOldAndNewBounds(bottomDeltaRect);
+ }
+}
+
+void BoxPaintInvalidator::invalidatePaintRectClippedByOldAndNewBounds(const LayoutRect& rect)
+{
+ if (rect.isEmpty())
+ return;
+
+ LayoutRect rectClippedByOldBounds = intersection(rect, m_context.oldBounds);
+ LayoutRect rectClippedByNewBounds = intersection(rect, m_context.newBounds);
+ // Invalidate only once if the clipped rects equal.
+ if (rectClippedByOldBounds == rectClippedByNewBounds) {
+ m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, rectClippedByOldBounds, PaintInvalidationIncremental);
+ return;
+ }
+ // Invalidate the bigger one if one contains another. Otherwise invalidate both.
+ if (!rectClippedByNewBounds.contains(rectClippedByOldBounds))
+ m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, rectClippedByOldBounds, PaintInvalidationIncremental);
+ if (!rectClippedByOldBounds.contains(rectClippedByNewBounds))
+ m_box.invalidatePaintUsingContainer(*m_context.paintInvalidationContainer, rectClippedByNewBounds, PaintInvalidationIncremental);
+}
+
+PaintInvalidationReason BoxPaintInvalidator::computePaintInvalidationReason()
+{
+ PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).computePaintInvalidationReason();
+
+ if (reason != PaintInvalidationDelayedFull && isFullPaintInvalidationReason(reason))
+ return reason;
+
+ if (m_box.mayNeedPaintInvalidationAnimatedBackgroundImage() && !m_box.backgroundIsKnownToBeObscured())
+ reason = PaintInvalidationDelayedFull;
+
+ // If the current paint invalidation reason is PaintInvalidationDelayedFull, then this paint invalidation can delayed if the
+ // LayoutBox in question is not on-screen. The logic to decide whether this is appropriate exists at the site of the original
+ // paint invalidation that chose PaintInvalidationDelayedFull.
+ if (reason == PaintInvalidationDelayedFull) {
+ // Do regular full paint invalidation if the object is onscreen.
+ return m_box.intersectsVisibleViewport() ? PaintInvalidationFull : PaintInvalidationDelayedFull;
+ }
+
+ if (m_box.isLayoutView()) {
+ const LayoutView& layoutView = toLayoutView(m_box);
+ // In normal compositing mode, root background doesn't need to be invalidated for
+ // box changes, because the background always covers the whole document rect
+ // and clipping is done by compositor()->m_containerLayer. Also the scrollbars
+ // are always composited. There are no other box decoration on the LayoutView thus
+ // we can safely exit here.
+ if (layoutView.usesCompositing() && (!layoutView.document().settings() || !layoutView.document().settings()->rootLayerScrolls()))
+ return reason;
+ }
+
+ // If the transform is not identity or translation, incremental invalidation is not applicable
+ // because the difference between oldBounds and newBounds doesn't cover all area needing invalidation.
+ // FIXME: Should also consider ancestor transforms since paintInvalidationContainer. crbug.com/426111.
+ if (reason == PaintInvalidationIncremental
+ && m_context.paintInvalidationContainer != m_box
+ && m_box.hasLayer() && m_box.layer()->transform()
+ && !m_box.layer()->transform()->isIdentityOrTranslation())
+ return PaintInvalidationBoundsChange;
+
+ const ComputedStyle& style = m_box.styleRef();
+ if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.maskLayers().thisOrNextLayersUseContentBox() || style.boxSizing() == BoxSizingBorderBox) {
+ if (previousBoxSizesMap().get(&m_box).contentBoxRect != m_box.contentBoxRect())
+ return PaintInvalidationContentBoxChange;
+ }
+
+ if (!style.hasBackground() && !style.hasBoxDecorations()) {
+ // We could let incremental invalidation cover non-composited scrollbars, but just
+ // do a full invalidation because incremental invalidation will go away with slimming paint.
+ if (reason == PaintInvalidationIncremental && m_box.hasNonCompositedScrollbars())
+ return PaintInvalidationBorderBoxChange;
+ return reason;
+ }
+
+ if (style.backgroundLayers().thisOrNextLayersHaveLocalAttachment()) {
+ if (previousBoxSizesMap().get(&m_box).layoutOverflowRect != m_box.layoutOverflowRect())
+ return PaintInvalidationLayoutOverflowBoxChange;
+ }
+
+ LayoutSize oldBorderBoxSize = computePreviousBorderBoxSize(m_context.oldBounds.size());
+ LayoutSize newBorderBoxSize = m_box.size();
+
+ if (oldBorderBoxSize == newBorderBoxSize)
+ return reason;
+
+ // LayoutBox::incrementallyInvalidatePaint() depends on positionFromPaintInvalidationBacking
+ // which is not available when slimmingPaintOffsetCachingEnabled.
+ if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() && (style.hasBoxDecorations() || style.hasBackground()))
+ return PaintInvalidationBorderBoxChange;
+
+ // See another hasNonCompositedScrollbars() callsite above.
+ if (m_box.hasNonCompositedScrollbars())
+ return PaintInvalidationBorderBoxChange;
+
+ if (style.hasVisualOverflowingEffect() || style.hasAppearance() || style.hasFilterInducingProperty() || style.resize() != RESIZE_NONE)
+ return PaintInvalidationBorderBoxChange;
+
+ if (style.hasBorderRadius()) {
+ // If a border-radius exists and width/height is smaller than radius width/height,
+ // we need to fully invalidate to cover the changed radius.
+ FloatRoundedRect oldRoundedRect = style.getRoundedBorderFor(LayoutRect(LayoutPoint(0, 0), oldBorderBoxSize));
+ FloatRoundedRect newRoundedRect = style.getRoundedBorderFor(LayoutRect(LayoutPoint(0, 0), newBorderBoxSize));
+ if (oldRoundedRect.getRadii() != newRoundedRect.getRadii())
+ return PaintInvalidationBorderBoxChange;
+ }
+
+ if (oldBorderBoxSize.width() != newBorderBoxSize.width() && m_box.mustInvalidateBackgroundOrBorderPaintOnWidthChange())
+ return PaintInvalidationBorderBoxChange;
+ if (oldBorderBoxSize.height() != newBorderBoxSize.height() && m_box.mustInvalidateBackgroundOrBorderPaintOnHeightChange())
+ return PaintInvalidationBorderBoxChange;
+
+ if (reason == PaintInvalidationNone && (style.hasBackground() || style.hasBoxDecorations()))
+ reason = PaintInvalidationIncremental;
+
+ return reason;
+}
+
+PaintInvalidationReason BoxPaintInvalidator::invalidatePaintIfNeeded()
+{
+ PaintInvalidationReason reason = ObjectPaintInvalidator(m_box, m_context).invalidatePaintIfNeededWithComputedReason(computePaintInvalidationReason());
+
+ // For incremental invalidation, ObjectPaintInvalidator::incrementallyInvalidatePaint() is
+ // not enough for LayoutBox in some cases, e.g. having border, border-radius, etc.
+ // The following will do additional incremental invalidation for LayoutBox if needed.
+ if (reason == PaintInvalidationIncremental)
+ incrementallyInvalidatePaint();
+
+ if (PaintLayerScrollableArea* area = m_box.getScrollableArea())
+ area->invalidatePaintOfScrollControlsIfNeeded(m_context);
+
+ // This is for the next invalidatePaintIfNeeded so must be at the end.
+ savePreviousBoxSizesIfNeeded();
+
+ return reason;
+}
+
+bool BoxPaintInvalidator::needsToSavePreviousBoxSizes()
+{
+ LayoutSize paintInvalidationSize = m_context.newBounds.size();
+ // Don't save old box sizes if the paint rect is empty because we'll
+ // full invalidate once the paint rect becomes non-empty.
+ if (paintInvalidationSize.isEmpty())
+ return false;
+
+ const ComputedStyle& style = m_box.styleRef();
+
+ // If we use border-box sizing we need to track changes in the size of the content box.
+ if (style.boxSizing() == BoxSizingBorderBox)
+ return true;
+
+ // We need the old box sizes only when the box has background, decorations, or masks.
+ // Main LayoutView paints base background, thus interested in box size.
+ if (!m_box.isLayoutView() && !style.hasBackground() && !style.hasBoxDecorations() && !style.hasMask())
+ return false;
+
+ // No need to save old border box size if we can use size of the old paint
+ // rect as the old border box size in the next invalidation.
+ if (paintInvalidationSize != m_box.size())
+ return true;
+
+ // Background and mask layers can depend on other boxes than border box. See crbug.com/490533
+ if (style.backgroundLayers().thisOrNextLayersUseContentBox() || style.backgroundLayers().thisOrNextLayersHaveLocalAttachment()
+ || style.maskLayers().thisOrNextLayersUseContentBox())
+ return true;
+
+ return false;
+}
+
+void BoxPaintInvalidator::savePreviousBoxSizesIfNeeded()
+{
+ if (!needsToSavePreviousBoxSizes()) {
+ previousBoxSizesMap().remove(&m_box);
+ return;
+ }
+
+ PreviousBoxSizes sizes = {
+ m_box.size(),
+ m_box.contentBoxRect(),
+ m_box.layoutOverflowRect()
+ };
+ previousBoxSizesMap().set(&m_box, sizes);
+}
+
+LayoutSize BoxPaintInvalidator::computePreviousBorderBoxSize(const LayoutSize& previousBoundsSize)
+{
+ // PreviousBorderBoxSize is only valid when there is background or box decorations.
+ DCHECK(m_box.styleRef().hasBackground() || m_box.styleRef().hasBoxDecorations());
+
+ auto it = previousBoxSizesMap().find(&m_box);
+ if (it != previousBoxSizesMap().end())
+ return it->value.borderBoxSize;
+
+ // We didn't save the old border box size because it was the same as the size of oldBounds.
+ return previousBoundsSize;
+}
+
+} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698