void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const { bool rootLayerScrolls = m_layoutObject.document().settings() && m_layoutObject.document().settings()->rootLayerScrolls(); if (!m_layoutObject.layer()->parent() && !rootLayerScrolls) { // The root layer's clip rect is always infinite. clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); return; } bool isClippingRoot = m_layoutObject.layer() == context.rootLayer; // For transformed layers, the root layer was shifted to be us, so there is no need to // examine the parent. We want to cache clip rects with us as the root. PaintLayer* parentLayer = !isClippingRoot ? m_layoutObject.layer()->parent() : 0; // Ensure that our parent's clip has been calculated so that we can examine the values. if (parentLayer) { // FIXME: Why don't we just call getClipRects here? if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) { clipRects = *parentLayer->clipper().cachedClipRects(context); } else { parentLayer->clipper().calculateClipRects(context, clipRects); } } else { clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect())); } adjustClipRectsForChildren(m_layoutObject, clipRects); if ((m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) || m_layoutObject.hasClip()) { // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across // some transformed layer boundary, for example, in the PaintLayerCompositor overlapMap, where // clipRects are needed in view space. applyClipRects(context, m_layoutObject, roundedLayoutPoint(m_layoutObject.localToContainerPoint(FloatPoint(), context.rootLayer->layoutObject())), clipRects); } }
void PaintLayerClipper::calculateRects(const ClipRectsContext& context, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, ClipRect& backgroundRect, ClipRect& foregroundRect, const LayoutPoint* offsetFromRoot) const { bool isClippingRoot = m_layoutObject.layer() == context.rootLayer; if (!isClippingRoot && m_layoutObject.layer()->parent()) { backgroundRect = backgroundClipRect(context); backgroundRect.move(context.subPixelAccumulation); backgroundRect.intersect(paintDirtyRect); } else { backgroundRect = paintDirtyRect; } foregroundRect = backgroundRect; LayoutPoint offset; if (offsetFromRoot) offset = *offsetFromRoot; else m_layoutObject.layer()->convertToLayerCoords(context.rootLayer, offset); layerBounds = LayoutRect(offset, LayoutSize(m_layoutObject.layer()->size())); // Update the clip rects that will be passed to child layers. if (m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) { foregroundRect.intersect(toLayoutBox(m_layoutObject).overflowClipRect(offset, context.scrollbarRelevancy)); if (m_layoutObject.style()->hasBorderRadius()) foregroundRect.setHasRadius(true); // FIXME: Does not do the right thing with columns yet, since we don't yet factor in the // individual column boxes as overflow. // The LayoutView is special since its overflow clipping rect may be larger than its box rect (crbug.com/492871). LayoutRect layerBoundsWithVisualOverflow = m_layoutObject.isLayoutView() ? toLayoutView(m_layoutObject).viewRect() : toLayoutBox(m_layoutObject).visualOverflowRect(); toLayoutBox(m_layoutObject).flipForWritingMode(layerBoundsWithVisualOverflow); // PaintLayer are in physical coordinates, so the overflow has to be flipped. layerBoundsWithVisualOverflow.moveBy(offset); backgroundRect.intersect(layerBoundsWithVisualOverflow); } // CSS clip (different than clipping due to overflow) can clip to any box, even if it falls outside of the border box. if (m_layoutObject.hasClip()) { // Clip applies to *us* as well, so go ahead and update the damageRect. LayoutRect newPosClip = toLayoutBox(m_layoutObject).clipRect(offset); backgroundRect.intersect(newPosClip); backgroundRect.setIsClippedByClipCss(); foregroundRect.intersect(newPosClip); foregroundRect.setIsClippedByClipCss(); } }
PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerWithTransform(GraphicsContext* context, const PaintLayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { TransformationMatrix layerTransform = m_paintLayer.renderableTransform(paintingInfo.globalPaintFlags()); // If the transform can't be inverted, then don't paint anything. if (!layerTransform.isInvertible()) return FullyPainted; // FIXME: We should make sure that we don't walk past paintingInfo.rootLayer here. // m_paintLayer may be the "root", and then we should avoid looking at its parent. PaintLayer* parentLayer = m_paintLayer.parent(); ClipRect ancestorBackgroundClipRect; if (parentLayer) { // Calculate the clip rectangle that the ancestors establish. ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize); if (shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()) == IgnoreOverflowClip) clipRectsContext.setIgnoreOverflowClip(); ancestorBackgroundClipRect = m_paintLayer.clipper().backgroundClipRect(clipRectsContext); } PaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer(); PaintLayerFragments fragments; if (paginationLayer) { // FIXME: This is a mess. Look closely at this code and the code in Layer and fix any // issues in it & refactor to make it obvious from code structure what it does and that it's // correct. ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; ShouldRespectOverflowClip respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); // Calculate the transformed bounding box in the current coordinate space, to figure out // which fragmentainers (e.g. columns) we need to visit. LayoutRect transformedExtent = PaintLayer::transparencyClipBox(&m_paintLayer, paginationLayer, PaintLayer::PaintingTransparencyClipBox, PaintLayer::RootOfTransparencyClipBox, paintingInfo.subPixelAccumulation, paintingInfo.globalPaintFlags()); // FIXME: we don't check if paginationLayer is within paintingInfo.rootLayer here. paginationLayer->collectFragments(fragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, 0, paintingInfo.subPixelAccumulation, &transformedExtent); } else { // We don't need to collect any fragments in the regular way here. We have already // calculated a clip rectangle for the ancestry if it was needed, and clipping this // layer is something that can be done further down the path, when the transform has // been applied. PaintLayerFragment fragment; fragment.backgroundRect = paintingInfo.paintDirtyRect; fragments.append(fragment); } bool needsScope = fragments.size() > 1; PaintResult result = FullyPainted; for (const auto& fragment : fragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); Optional<LayerClipRecorder> clipRecorder; if (parentLayer) { ClipRect clipRectForFragment(ancestorBackgroundClipRect); clipRectForFragment.moveBy(fragment.paginationOffset); clipRectForFragment.intersect(fragment.backgroundRect); if (clipRectForFragment.isEmpty()) continue; if (needsToClip(paintingInfo, clipRectForFragment)) { if (m_paintLayer.layoutObject()->style()->position() != StaticPosition && clipRectForFragment.isClippedByClipCss()) UseCounter::count(m_paintLayer.layoutObject()->document(), UseCounter::ClipCssOfPositionedElement); clipRecorder.emplace(*context, *parentLayer->layoutObject(), DisplayItem::ClipLayerParent, clipRectForFragment, &paintingInfo, fragment.paginationOffset, paintFlags); } } if (paintFragmentByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset) == MaybeNotFullyPainted) result = MaybeNotFullyPainted; } return result; }
bool PaintLayerClipper::shouldClipOverflow( const ClipRectsContext& context) const { return (m_layer.layoutObject()->hasOverflowClip() || m_layer.layoutObject()->styleRef().containsPaint()) && shouldRespectOverflowClip(context); }
PaintLayerPainter::PaintResult PaintLayerPainter::paintLayerContentsInternal(GraphicsContext* context, const PaintLayerPaintingInfo& paintingInfoArg, PaintLayerFlags paintFlags, FragmentPolicy fragmentPolicy) { ASSERT(m_paintLayer.isSelfPaintingLayer() || m_paintLayer.hasSelfPaintingLayerDescendant()); ASSERT(!(paintFlags & PaintLayerAppliedTransform)); bool isSelfPaintingLayer = m_paintLayer.isSelfPaintingLayer(); bool isPaintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars; bool isPaintingScrollingContent = paintFlags & PaintLayerPaintingCompositingScrollingPhase; bool isPaintingCompositedForeground = paintFlags & PaintLayerPaintingCompositingForegroundPhase; bool isPaintingCompositedBackground = paintFlags & PaintLayerPaintingCompositingBackgroundPhase; bool isPaintingOverflowContents = paintFlags & PaintLayerPaintingOverflowContents; // Outline always needs to be painted even if we have no visible content. Also, // the outline is painted in the background phase during composited scrolling. // If it were painted in the foreground phase, it would move with the scrolled // content. When not composited scrolling, the outline is painted in the // foreground phase. Since scrolled contents are moved by paint invalidation in this // case, the outline won't get 'dragged along'. bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars && ((isPaintingScrollingContent && isPaintingCompositedBackground) || (!isPaintingScrollingContent && isPaintingCompositedForeground)); bool shouldPaintContent = m_paintLayer.hasVisibleContent() && isSelfPaintingLayer && !isPaintingOverlayScrollbars; PaintResult result = FullyPainted; if (paintFlags & PaintLayerPaintingRootBackgroundOnly && !m_paintLayer.layoutObject()->isLayoutView() && !m_paintLayer.layoutObject()->isDocumentElement()) return result; PaintLayerPaintingInfo paintingInfo = paintingInfoArg; // Ensure our lists are up-to-date. m_paintLayer.stackingNode()->updateLayerListsIfNeeded(); LayoutPoint offsetFromRoot; m_paintLayer.convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); if (m_paintLayer.compositingState() == PaintsIntoOwnBacking) offsetFromRoot.move(m_paintLayer.subpixelAccumulation()); else offsetFromRoot.move(paintingInfo.subPixelAccumulation); LayoutRect bounds = m_paintLayer.physicalBoundingBox(offsetFromRoot); if (!paintingInfo.paintDirtyRect.contains(bounds)) result = MaybeNotFullyPainted; LayoutRect rootRelativeBounds; bool rootRelativeBoundsComputed = false; if (paintingInfo.ancestorHasClipPathClipping && m_paintLayer.layoutObject()->style()->position() != StaticPosition) UseCounter::count(m_paintLayer.layoutObject()->document(), UseCounter::ClipPathOfPositionedElement); // These helpers output clip and compositing operations using a RAII pattern. Stack-allocated-varibles are destructed in the reverse order of construction, // so they are nested properly. ClipPathHelper clipPathHelper(context, m_paintLayer, paintingInfo, rootRelativeBounds, rootRelativeBoundsComputed, offsetFromRoot, paintFlags); Optional<CompositingRecorder> compositingRecorder; // Blending operations must be performed only with the nearest ancestor stacking context. // Note that there is no need to composite if we're painting the root. // FIXME: this should be unified further into PaintLayer::paintsWithTransparency(). bool shouldCompositeForBlendMode = (!m_paintLayer.layoutObject()->isDocumentElement() || m_paintLayer.layoutObject()->isSVGRoot()) && m_paintLayer.stackingNode()->isStackingContext() && m_paintLayer.hasNonIsolatedDescendantWithBlendMode(); if (shouldCompositeForBlendMode || m_paintLayer.paintsWithTransparency(paintingInfo.globalPaintFlags())) { FloatRect compositingBounds = FloatRect(m_paintLayer.paintingExtent(paintingInfo.rootLayer, paintingInfo.subPixelAccumulation, paintingInfo.globalPaintFlags())); compositingRecorder.emplace(*context, *m_paintLayer.layoutObject(), WebCoreCompositeToSkiaComposite(CompositeSourceOver, m_paintLayer.layoutObject()->style()->blendMode()), m_paintLayer.layoutObject()->opacity(), &compositingBounds); } PaintLayerPaintingInfo localPaintingInfo(paintingInfo); if (m_paintLayer.compositingState() == PaintsIntoOwnBacking) localPaintingInfo.subPixelAccumulation = m_paintLayer.subpixelAccumulation(); PaintLayerFragments layerFragments; if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment. ClipRectsCacheSlot cacheSlot = (paintFlags & PaintLayerUncachedClipRects) ? UncachedClipRects : PaintingClipRects; ShouldRespectOverflowClip respectOverflowClip = shouldRespectOverflowClip(paintFlags, m_paintLayer.layoutObject()); if (fragmentPolicy == ForceSingleFragment) m_paintLayer.appendSingleFragmentIgnoringPagination(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot, localPaintingInfo.subPixelAccumulation); else m_paintLayer.collectFragments(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.paintDirtyRect, cacheSlot, IgnoreOverlayScrollbarSize, respectOverflowClip, &offsetFromRoot, localPaintingInfo.subPixelAccumulation); if (shouldPaintContent) { // TODO(wangxianzhu): This is for old slow scrolling. Implement similar optimization for slimming paint v2. shouldPaintContent = atLeastOneFragmentIntersectsDamageRect(layerFragments, localPaintingInfo, paintFlags, offsetFromRoot); if (!shouldPaintContent) result = MaybeNotFullyPainted; } } bool selectionOnly = localPaintingInfo.globalPaintFlags() & GlobalPaintSelectionOnly; // If this layer's layoutObject is a child of the paintingRoot, we paint unconditionally, which // is done by passing a nil paintingRoot down to our layoutObject (as if no paintingRoot was ever set). // Else, our layout tree may or may not contain the painting root, so we pass that root along // so it will be tested against as we descend through the layoutObjects. LayoutObject* paintingRootForLayoutObject = 0; if (localPaintingInfo.paintingRoot && !m_paintLayer.layoutObject()->isDescendantOf(localPaintingInfo.paintingRoot)) paintingRootForLayoutObject = localPaintingInfo.paintingRoot; { // Begin block for the lifetime of any filter. FilterPainter filterPainter(m_paintLayer, context, offsetFromRoot, layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect, localPaintingInfo, paintFlags, rootRelativeBounds, rootRelativeBoundsComputed); bool shouldPaintBackground = isPaintingCompositedBackground && shouldPaintContent && !selectionOnly; bool shouldPaintNegZOrderList = (isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground); bool shouldPaintOwnContents = isPaintingCompositedForeground && shouldPaintContent; bool shouldPaintNormalFlowAndPosZOrderLists = isPaintingCompositedForeground; bool shouldPaintOverlayScrollbars = isPaintingOverlayScrollbars; if (shouldPaintBackground) { paintBackgroundForFragments(layerFragments, context, paintingInfo.paintDirtyRect, localPaintingInfo, paintingRootForLayoutObject, paintFlags); } if (shouldPaintNegZOrderList) { if (paintChildren(NegativeZOrderChildren, context, paintingInfo, paintFlags) == MaybeNotFullyPainted) result = MaybeNotFullyPainted; } if (shouldPaintOwnContents) { paintForegroundForFragments(layerFragments, context, paintingInfo.paintDirtyRect, localPaintingInfo, paintingRootForLayoutObject, selectionOnly, paintFlags); } if (shouldPaintOutline) paintOutlineForFragments(layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags); if (shouldPaintNormalFlowAndPosZOrderLists) { if (paintChildren(NormalFlowChildren | PositiveZOrderChildren, context, paintingInfo, paintFlags) == MaybeNotFullyPainted) result = MaybeNotFullyPainted; } if (shouldPaintOverlayScrollbars) paintOverflowControlsForFragments(layerFragments, context, localPaintingInfo, paintFlags); } // FilterPainter block bool shouldPaintMask = (paintFlags & PaintLayerPaintingCompositingMaskPhase) && shouldPaintContent && m_paintLayer.layoutObject()->hasMask() && !selectionOnly; bool shouldPaintClippingMask = (paintFlags & PaintLayerPaintingChildClippingMaskPhase) && shouldPaintContent && !selectionOnly; if (shouldPaintMask) paintMaskForFragments(layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags); if (shouldPaintClippingMask) { // Paint the border radius mask for the fragments. paintChildClippingMaskForFragments(layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags); } m_paintLayer.setPreviousScrollOffsetAccumulationForPainting(paintingInfoArg.scrollOffsetAccumulation); return result; }