void DeprecatedPaintLayerPainter::paintChildClippingMaskForFragments(const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, LayoutObject* paintingRootForLayoutObject, PaintLayerFlags paintFlags) { bool needsScope = layerFragments.size() > 1; for (auto& fragment: layerFragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); paintFragmentWithPhase(PaintPhaseClippingMask, fragment, context, fragment.foregroundRect, localPaintingInfo, paintingRootForLayoutObject, paintFlags, HasNotClipped); } }
void DeprecatedPaintLayerPainter::paintBackgroundForFragments(const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const LayoutRect& transparencyPaintDirtyRect, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, LayoutObject* paintingRootForLayoutObject, PaintLayerFlags paintFlags) { bool needsScope = layerFragments.size() > 1; for (auto& fragment : layerFragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); paintFragmentWithPhase(PaintPhaseBlockBackground, fragment, context, fragment.backgroundRect, localPaintingInfo, paintingRootForLayoutObject, paintFlags, HasNotClipped); } }
void DeprecatedPaintLayerPainter::paintOutlineForFragments(const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, LayoutObject* paintingRootForLayoutObject, PaintLayerFlags paintFlags) { bool needsScope = layerFragments.size() > 1; for (auto& fragment : layerFragments) { if (!fragment.outlineRect.isEmpty()) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); paintFragmentWithPhase(PaintPhaseSelfOutline, fragment, context, fragment.outlineRect, localPaintingInfo, paintingRootForLayoutObject, paintFlags, HasNotClipped); } } }
void DeprecatedPaintLayerPainter::paintForegroundForFragmentsWithPhase(PaintPhase phase, const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, LayoutObject* paintingRootForLayoutObject, PaintLayerFlags paintFlags, ClipState clipState) { bool needsScope = layerFragments.size() > 1; for (auto& fragment : layerFragments) { if (!fragment.foregroundRect.isEmpty()) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); paintFragmentWithPhase(phase, fragment, context, fragment.foregroundRect, localPaintingInfo, paintingRootForLayoutObject, paintFlags, clipState); } } }
void DeprecatedPaintLayerPainter::paintOverflowControlsForFragments(const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, PaintLayerFlags paintFlags) { bool needsScope = layerFragments.size() > 1; for (auto& fragment : layerFragments) { Optional<ScopeRecorder> scopeRecorder; if (needsScope) scopeRecorder.emplace(*context); Optional<LayerClipRecorder> clipRecorder; if (needsToClip(localPaintingInfo, fragment.backgroundRect)) clipRecorder.emplace(*context, *m_paintLayer.layoutObject(), DisplayItem::ClipLayerOverflowControls, fragment.backgroundRect, &localPaintingInfo, fragment.paginationOffset, paintFlags); if (DeprecatedPaintLayerScrollableArea* scrollableArea = m_paintLayer.scrollableArea()) ScrollableAreaPainter(*scrollableArea).paintOverflowControls(context, roundedIntPoint(toPoint(fragment.layerBounds.location() - m_paintLayer.layoutBoxLocation())), pixelSnappedIntRect(fragment.backgroundRect.rect()), true); } }
void DeprecatedPaintLayerPainter::paintForegroundForFragments(const DeprecatedPaintLayerFragments& layerFragments, GraphicsContext* context, const LayoutRect& transparencyPaintDirtyRect, const DeprecatedPaintLayerPaintingInfo& localPaintingInfo, LayoutObject* paintingRootForLayoutObject, bool selectionOnly, PaintLayerFlags paintFlags) { // Optimize clipping for the single fragment case. bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && !layerFragments[0].foregroundRect.isEmpty(); ClipState clipState = HasNotClipped; Optional<LayerClipRecorder> clipRecorder; if (shouldClip && needsToClip(localPaintingInfo, layerFragments[0].foregroundRect)) { clipRecorder.emplace(*context, *m_paintLayer.layoutObject(), DisplayItem::ClipLayerForeground, layerFragments[0].foregroundRect, &localPaintingInfo, layerFragments[0].paginationOffset, paintFlags); clipState = HasClipped; } // We have to loop through every fragment multiple times, since we have to issue paint invalidations in each specific phase in order for // interleaving of the fragments to work properly. paintForegroundForFragmentsWithPhase(selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags, clipState); if (!selectionOnly) { paintForegroundForFragmentsWithPhase(PaintPhaseFloat, layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags, clipState); paintForegroundForFragmentsWithPhase(PaintPhaseForeground, layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags, clipState); paintForegroundForFragmentsWithPhase(PaintPhaseChildOutlines, layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags, clipState); } }
void MultiColumnFragmentainerGroup::collectLayerFragments(DeprecatedPaintLayerFragments& fragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect) const { // |layerBoundingBox| is in the flow thread coordinate space, relative to the top/left edge of // the flow thread, but note that it has been converted with respect to writing mode (so that // it's visual/physical in that sense). // // |dirtyRect| is visual, relative to the multicol container. // // Then there's the output from this method - the stuff we put into the list of fragments. The // fragment.paginationOffset point is the actual visual translation required to get from a // location in the flow thread to a location in a given column. The fragment.paginationClip // rectangle, on the other hand, is in flow thread coordinates, but otherwise completely // physical in terms of writing mode. // // All other rectangles in this method are sized physically, and the inline direction coordinate // is physical too, but the block direction coordinate is "logical top". This is the same as // e.g. LayoutBox::frameRect(). These rectangles also pretend that there's only one long column, // i.e. they are for the flow thread. LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); bool isHorizontalWritingMode = m_columnSet.isHorizontalWritingMode(); // Put the layer bounds into flow thread-local coordinates by flipping it first. Since we're in // a layoutObject, most rectangles are represented this way. LayoutRect layerBoundsInFlowThread(layerBoundingBox); flowThread->flipForWritingMode(layerBoundsInFlowThread); // Now we can compare with the flow thread portions owned by each column. First let's // see if the rect intersects our flow thread portion at all. LayoutRect clippedRect(layerBoundsInFlowThread); clippedRect.intersect(m_columnSet.flowThreadPortionOverflowRect()); if (clippedRect.isEmpty()) return; // Now we know we intersect at least one column. Let's figure out the logical top and logical // bottom of the area we're checking. LayoutUnit layerLogicalTop = isHorizontalWritingMode ? layerBoundsInFlowThread.y() : layerBoundsInFlowThread.x(); LayoutUnit layerLogicalBottom = (isHorizontalWritingMode ? layerBoundsInFlowThread.maxY() : layerBoundsInFlowThread.maxX()) - 1; // Figure out the start and end columns and only check within that range so that we don't walk the // entire column row. unsigned startColumn = columnIndexAtOffset(layerLogicalTop); unsigned endColumn = columnIndexAtOffset(layerLogicalBottom); LayoutUnit colLogicalWidth = m_columnSet.pageLogicalWidth(); LayoutUnit colGap = m_columnSet.columnGap(); unsigned colCount = actualColumnCount(); bool progressionIsInline = flowThread->progressionIsInline(); bool leftToRight = m_columnSet.style()->isLeftToRightDirection(); LayoutUnit initialBlockOffset = m_columnSet.logicalTop() + logicalTop() - flowThread->logicalTop(); for (unsigned i = startColumn; i <= endColumn; i++) { // Get the portion of the flow thread that corresponds to this column. LayoutRect flowThreadPortion = flowThreadPortionRectAt(i); // Now get the overflow rect that corresponds to the column. LayoutRect flowThreadOverflowPortion = flowThreadPortionOverflowRect(flowThreadPortion, i, colCount, colGap); // In order to create a fragment we must intersect the portion painted by this column. LayoutRect clippedRect(layerBoundsInFlowThread); clippedRect.intersect(flowThreadOverflowPortion); if (clippedRect.isEmpty()) continue; // We also need to intersect the dirty rect. We have to apply a translation and shift based off // our column index. LayoutPoint translationOffset; LayoutUnit inlineOffset = progressionIsInline ? i * (colLogicalWidth + colGap) : LayoutUnit(); if (!leftToRight) inlineOffset = -inlineOffset; translationOffset.setX(inlineOffset); LayoutUnit blockOffset; if (progressionIsInline) { blockOffset = initialBlockOffset + (isHorizontalWritingMode ? -flowThreadPortion.y() : -flowThreadPortion.x()); } else { // Column gap can apply in the block direction for page fragmentainers. // There is currently no spec which calls for column-gap to apply // for page fragmentainers at all, but it's applied here for compatibility // with the old multicolumn implementation. blockOffset = i * colGap; } if (isFlippedBlocksWritingMode(m_columnSet.style()->writingMode())) blockOffset = -blockOffset; translationOffset.setY(blockOffset); if (!isHorizontalWritingMode) translationOffset = translationOffset.transposedPoint(); // Shift the dirty rect to be in flow thread coordinates with this translation applied. LayoutRect translatedDirtyRect(dirtyRect); translatedDirtyRect.moveBy(-translationOffset); // See if we intersect the dirty rect. clippedRect = layerBoundingBox; clippedRect.intersect(translatedDirtyRect); if (clippedRect.isEmpty()) continue; // Something does need to paint in this column. Make a fragment now and supply the physical translation // offset and the clip rect for the column with that offset applied. DeprecatedPaintLayerFragment fragment; fragment.paginationOffset = translationOffset; LayoutRect flippedFlowThreadOverflowPortion(flowThreadOverflowPortion); // Flip it into more a physical (DeprecatedPaintLayer-style) rectangle. flowThread->flipForWritingMode(flippedFlowThreadOverflowPortion); fragment.paginationClip = flippedFlowThreadOverflowPortion; fragments.append(fragment); } }
void DeprecatedPaintLayerPainter::paintLayerWithTransform(GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& 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; // 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. DeprecatedPaintLayer* 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); } DeprecatedPaintLayer* paginationLayer = m_paintLayer.enclosingPaginationLayer(); DeprecatedPaintLayerFragments 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 = DeprecatedPaintLayer::transparencyClipBox(&m_paintLayer, paginationLayer, DeprecatedPaintLayer::PaintingTransparencyClipBox, DeprecatedPaintLayer::RootOfTransparencyClipBox, 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. DeprecatedPaintLayerFragment fragment; fragment.backgroundRect = paintingInfo.paintDirtyRect; fragments.append(fragment); } bool needsScope = fragments.size() > 1; 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)) clipRecorder.emplace(*context, *parentLayer->layoutObject(), DisplayItem::ClipLayerParent, clipRectForFragment, &paintingInfo, fragment.paginationOffset, paintFlags); } paintFragmentByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset); } }
void DeprecatedPaintLayerPainter::paintLayerContents(GraphicsContext* context, const DeprecatedPaintLayerPaintingInfo& paintingInfo, 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; if (paintFlags & PaintLayerPaintingRootBackgroundOnly && !m_paintLayer.layoutObject()->isLayoutView() && !m_paintLayer.layoutObject()->isDocumentElement()) return; // 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 rootRelativeBounds; bool rootRelativeBoundsComputed = false; // 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<LayerClipRecorder> clipRecorder; 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 DeprecatedPaintLayer::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())) { clipRecorder.emplace(*context, *m_paintLayer.layoutObject(), DisplayItem::TransparencyClip, m_paintLayer.paintingExtent(paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.globalPaintFlags()), &paintingInfo, LayoutPoint(), paintFlags); compositingRecorder.emplace(*context, *m_paintLayer.layoutObject(), WebCoreCompositeToSkiaComposite(CompositeSourceOver, m_paintLayer.layoutObject()->style()->blendMode()), m_paintLayer.layoutObject()->opacity()); } DeprecatedPaintLayerPaintingInfo localPaintingInfo(paintingInfo); DeprecatedPaintLayerFragments 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) shouldPaintContent = atLeastOneFragmentIntersectsDamageRect(layerFragments, localPaintingInfo, paintFlags, offsetFromRoot); } 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) paintChildren(NegativeZOrderChildren, context, paintingInfo, paintFlags); if (shouldPaintOwnContents) { paintForegroundForFragments(layerFragments, context, paintingInfo.paintDirtyRect, localPaintingInfo, paintingRootForLayoutObject, selectionOnly, paintFlags); } if (shouldPaintOutline) paintOutlineForFragments(layerFragments, context, localPaintingInfo, paintingRootForLayoutObject, paintFlags); if (shouldPaintNormalFlowAndPosZOrderLists) paintChildren(NormalFlowChildren | PositiveZOrderChildren, context, paintingInfo, paintFlags); 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); } }