void FieldsetPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_renderFieldset.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; LayoutRect paintRect = LayoutRect(paintOffset, m_renderFieldset.size()); RenderBox* legend = m_renderFieldset.findLegend(); if (!legend) return BoxPainter(m_renderFieldset).paintMask(paintInfo, paintOffset); // FIXME: We need to work with "rl" and "bt" block flow directions. In those // cases the legend is embedded in the right and bottom borders respectively. // https://bugs.webkit.org/show_bug.cgi?id=47236 if (m_renderFieldset.style()->isHorizontalWritingMode()) { LayoutUnit yOff = (legend->location().y() > 0) ? LayoutUnit() : (legend->size().height() - m_renderFieldset.borderTop()) / 2; paintRect.expand(0, -yOff); paintRect.move(0, yOff); } else { LayoutUnit xOff = (legend->location().x() > 0) ? LayoutUnit() : (legend->size().width() - m_renderFieldset.borderLeft()) / 2; paintRect.expand(-xOff, 0); paintRect.move(xOff, 0); } BoxPainter(m_renderFieldset).paintMaskImages(paintInfo, paintRect); }
void FieldsetPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!paintInfo.shouldPaintWithinRoot(&m_renderFieldset)) return; LayoutRect paintRect(paintOffset, m_renderFieldset.size()); RenderBox* legend = m_renderFieldset.findLegend(); if (!legend) return BoxPainter(m_renderFieldset).paintBoxDecorationBackground(paintInfo, paintOffset); // FIXME: We need to work with "rl" and "bt" block flow directions. In those // cases the legend is embedded in the right and bottom borders respectively. // https://bugs.webkit.org/show_bug.cgi?id=47236 if (m_renderFieldset.style()->isHorizontalWritingMode()) { LayoutUnit yOff = (legend->location().y() > 0) ? LayoutUnit() : (legend->size().height() - m_renderFieldset.borderTop()) / 2; paintRect.setHeight(paintRect.height() - yOff); paintRect.setY(paintRect.y() + yOff); } else { LayoutUnit xOff = (legend->location().x() > 0) ? LayoutUnit() : (legend->size().width() - m_renderFieldset.borderLeft()) / 2; paintRect.setWidth(paintRect.width() - xOff); paintRect.setX(paintRect.x() + xOff); } RenderDrawingRecorder recorder(paintInfo.context, m_renderFieldset, paintInfo.phase, pixelSnappedIntRect(paintOffset, paintRect.size())); if (recorder.canUseCachedDrawing()) return; BoxDecorationData boxDecorationData(m_renderFieldset, paintInfo.context); if (boxDecorationData.bleedAvoidance() == BackgroundBleedNone) BoxPainter::paintBoxShadow(paintInfo, paintRect, m_renderFieldset.style(), Normal); BoxPainter(m_renderFieldset).paintFillLayers(paintInfo, boxDecorationData.backgroundColor, m_renderFieldset.style()->backgroundLayers(), paintRect); BoxPainter::paintBoxShadow(paintInfo, paintRect, m_renderFieldset.style(), Inset); if (!boxDecorationData.hasBorder) return; // Create a clipping region around the legend and paint the border as normal GraphicsContext* graphicsContext = paintInfo.context; GraphicsContextStateSaver stateSaver(*graphicsContext); // FIXME: We need to work with "rl" and "bt" block flow directions. In those // cases the legend is embedded in the right and bottom borders respectively. // https://bugs.webkit.org/show_bug.cgi?id=47236 if (m_renderFieldset.style()->isHorizontalWritingMode()) { LayoutUnit clipTop = paintRect.y(); LayoutUnit clipHeight = max(static_cast<LayoutUnit>(m_renderFieldset.style()->borderTopWidth()), legend->size().height() - ((legend->size().height() - m_renderFieldset.borderTop()) / 2)); graphicsContext->clipOut(pixelSnappedIntRect(paintRect.x() + legend->location().x(), clipTop, legend->size().width(), clipHeight)); } else { LayoutUnit clipLeft = paintRect.x(); LayoutUnit clipWidth = max(static_cast<LayoutUnit>(m_renderFieldset.style()->borderLeftWidth()), legend->size().width()); graphicsContext->clipOut(pixelSnappedIntRect(clipLeft, paintRect.y() + legend->location().y(), clipWidth, legend->size().height())); } BoxPainter::paintBorder(m_renderFieldset, paintInfo, paintRect, m_renderFieldset.style()); }
void PartPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!m_layoutPart.shouldPaint(paintInfo, paintOffset)) return; LayoutPoint adjustedPaintOffset = paintOffset + m_layoutPart.location(); LayoutRect borderRect(adjustedPaintOffset, m_layoutPart.size()); if (m_layoutPart.hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) BoxPainter(m_layoutPart).paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { BoxPainter(m_layoutPart).paintMask(paintInfo, adjustedPaintOffset); return; } if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && m_layoutPart.style()->hasOutline()) ObjectPainter(m_layoutPart).paintOutline(paintInfo, adjustedPaintOffset); if (paintInfo.phase != PaintPhaseForeground) return; { Optional<RoundedInnerRectClipper> clipper; if (m_layoutPart.style()->hasBorderRadius()) { if (borderRect.isEmpty()) return; FloatRoundedRect roundedInnerRect = m_layoutPart.style()->getRoundedInnerBorderFor(borderRect, LayoutRectOutsets( -(m_layoutPart.paddingTop() + m_layoutPart.borderTop()), -(m_layoutPart.paddingRight() + m_layoutPart.borderRight()), -(m_layoutPart.paddingBottom() + m_layoutPart.borderBottom()), -(m_layoutPart.paddingLeft() + m_layoutPart.borderLeft())), true, true); clipper.emplace(m_layoutPart, paintInfo, borderRect, roundedInnerRect, ApplyToDisplayList); } if (m_layoutPart.widget()) m_layoutPart.paintContents(paintInfo, paintOffset); } // Paint a partially transparent wash over selected widgets. if (isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutPart, paintInfo.phase, adjustedPaintOffset)) { LayoutRect rect = m_layoutPart.localSelectionRect(); rect.moveBy(adjustedPaintOffset); IntRect selectionRect = pixelSnappedIntRect(rect); LayoutObjectDrawingRecorder drawingRecorder(*paintInfo.context, m_layoutPart, paintInfo.phase, selectionRect, adjustedPaintOffset); paintInfo.context->fillRect(selectionRect, m_layoutPart.selectionBackgroundColor()); } if (m_layoutPart.canResize()) ScrollableAreaPainter(*m_layoutPart.layer()->scrollableArea()).paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.cullRect()); }
void TableCellPainter::paintBackgroundsBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutObject* backgroundObject) { if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell)) return; if (!backgroundObject) return; if (m_layoutTableCell.style()->visibility() != VISIBLE) return; LayoutTable* tableElt = m_layoutTableCell.table(); if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild()) return; Color c = backgroundObject->resolveColor(CSSPropertyBackgroundColor); const FillLayer& bgLayer = backgroundObject->style()->backgroundLayers(); LayoutRect paintRect = paintBounds(paintOffset, backgroundObject != &m_layoutTableCell ? AddOffsetFromParent : DoNotAddOffsetFromParent); if (bgLayer.hasImage() || c.alpha()) { // We have to clip here because the background would paint // on top of the borders otherwise. This only matters for cells and rows. bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == &m_layoutTableCell || backgroundObject == m_layoutTableCell.parent()) && tableElt->collapseBorders(); GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip); if (shouldClip) { LayoutRect clipRect(paintRect.location(), m_layoutTableCell.size()); clipRect.expand(m_layoutTableCell.borderInsets()); paintInfo.context->clip(clipRect); } BoxPainter(m_layoutTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, SkXfermode::kSrcOver_Mode, backgroundObject); } }
void TablePainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!m_layoutTable.hasBoxDecorationBackground() || m_layoutTable.style()->visibility() != VISIBLE) return; LayoutRect rect(paintOffset, m_layoutTable.size()); m_layoutTable.subtractCaptionRect(rect); BoxPainter(m_layoutTable).paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, rect); }
void TablePainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!paintInfo.shouldPaintWithinRoot(&m_layoutTable)) return; LayoutRect rect(paintOffset, m_layoutTable.size()); m_layoutTable.subtractCaptionRect(rect); BoxPainter(m_layoutTable).paintBoxDecorationBackgroundWithRect(paintInfo, paintOffset, rect); }
void TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_layoutTableCell.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; LayoutTable* tableElt = m_layoutTableCell.table(); if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild()) return; BoxPainter(m_layoutTableCell).paintMaskImages(paintInfo, paintBounds(paintOffset, DoNotAddOffsetFromParent)); }
void TablePainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_layoutTable.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutTable, paintInfo.phase)) return; LayoutRect rect(paintOffset, m_layoutTable.size()); m_layoutTable.subtractCaptionRect(rect); LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutTable, paintInfo.phase, pixelSnappedIntRect(rect)); BoxPainter(m_layoutTable).paintMaskImages(paintInfo, rect); }
void TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_layoutTableCell.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; LayoutTable* tableElt = m_layoutTableCell.table(); if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EmptyCellsHide && !m_layoutTableCell.firstChild()) return; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, paintInfo.phase)) return; LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent); LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, paintInfo.phase, paintRect); BoxPainter(m_layoutTableCell).paintMaskImages(paintInfo, paintRect); }
void SVGRootPainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // An empty viewport disables rendering. if (pixelSnappedSize(paintOffset).isEmpty()) return; // SVG outlines are painted during PaintPhaseForeground. if (shouldPaintSelfOutline(paintInfo.phase)) return; // An empty viewBox also disables rendering. // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute) SVGSVGElement* svg = toSVGSVGElement(m_layoutSVGRoot.node()); DCHECK(svg); if (svg->hasEmptyViewBox()) return; // Apply initial viewport clip. Optional<BoxClipper> boxClipper; if (m_layoutSVGRoot.shouldApplyViewportClip()) { // TODO(pdr): Clip the paint info cull rect here. boxClipper.emplace(m_layoutSVGRoot, paintInfo, paintOffset, ForceContentsClip); } PaintInfo paintInfoBeforeFiltering(paintInfo); AffineTransform transformToBorderBox = transformToPixelSnappedBorderBox(paintOffset); paintInfoBeforeFiltering.updateCullRect(transformToBorderBox); SVGTransformContext transformContext(paintInfoBeforeFiltering.context, m_layoutSVGRoot, transformToBorderBox); SVGPaintContext paintContext(m_layoutSVGRoot, paintInfoBeforeFiltering); if (paintContext.paintInfo().phase == PaintPhaseForeground && !paintContext.applyClipMaskAndFilterIfNecessary()) return; BoxPainter(m_layoutSVGRoot).paint(paintContext.paintInfo(), LayoutPoint()); PaintTiming& timing = PaintTiming::from(m_layoutSVGRoot.node()->document().topDocument()); timing.markFirstContentfulPaint(); }
void TableCellPainter::paintBackgroundsBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutObject* backgroundObject, DisplayItem::Type type) { if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell)) return; if (!backgroundObject) return; if (m_layoutTableCell.style()->visibility() != VISIBLE) return; LayoutTable* tableElt = m_layoutTableCell.table(); if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild()) return; LayoutRect paintRect = paintBounds(paintOffset, backgroundObject != &m_layoutTableCell ? AddOffsetFromParent : DoNotAddOffsetFromParent); // Record drawing only if the cell is painting background from containers. Optional<LayoutObjectDrawingRecorder> recorder; if (backgroundObject != &m_layoutTableCell) { LayoutPoint adjustedPaintOffset = paintRect.location(); if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutTableCell, type, adjustedPaintOffset)) return; recorder.emplace(*paintInfo.context, m_layoutTableCell, type, paintRect, adjustedPaintOffset); } else { ASSERT(paintRect.location() == paintOffset); } Color c = backgroundObject->resolveColor(CSSPropertyBackgroundColor); const FillLayer& bgLayer = backgroundObject->style()->backgroundLayers(); if (bgLayer.hasImage() || c.alpha()) { // We have to clip here because the background would paint // on top of the borders otherwise. This only matters for cells and rows. bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == &m_layoutTableCell || backgroundObject == m_layoutTableCell.parent()) && tableElt->collapseBorders(); GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip); if (shouldClip) { LayoutRect clipRect(paintRect.location(), m_layoutTableCell.size()); clipRect.expand(m_layoutTableCell.borderInsets()); paintInfo.context->clip(pixelSnappedIntRect(clipRect)); } BoxPainter(m_layoutTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, SkXfermode::kSrcOver_Mode, backgroundObject); } }
void TableRowPainter::paintBoxShadow(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, ShadowStyle shadowStyle) { DCHECK(shouldPaintSelfBlockBackground(paintInfo.phase)); if (!m_layoutTableRow.styleRef().boxShadow()) return; DisplayItem::Type type = shadowStyle == Normal ? DisplayItem::kTableRowBoxShadowNormal : DisplayItem::kTableRowBoxShadowInset; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible( paintInfo.context, m_layoutTableRow, type)) return; LayoutPoint adjustedPaintOffset = paintOffset + m_layoutTableRow.location(); LayoutRect bounds = BoxPainter(m_layoutTableRow) .boundsForDrawingRecorder(paintInfo, adjustedPaintOffset); LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableRow, type, bounds); BoxPainter::paintBoxShadow( paintInfo, LayoutRect(adjustedPaintOffset, m_layoutTableRow.size()), m_layoutTableRow.styleRef(), shadowStyle); }
void ViewPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo) { if (paintInfo.skipRootBackground()) return; // This function overrides background painting for the LayoutView. // View background painting is special in the following ways: // 1. The view paints background for the root element, the background positioning respects // the positioning and transformation of the root element. // 2. CSS background-clip is ignored, the background layers always expand to cover the whole // canvas. None of the stacking context effects (except transformation) on the root element // affects the background. // 3. The main frame is also responsible for painting the user-agent-defined base background // color. Conceptually it should be painted by the embedder but painting it here allows // culling and pre-blending optimization when possible. GraphicsContext& context = paintInfo.context; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, m_layoutView, DisplayItem::DocumentBackground, LayoutPoint())) return; // The background fill rect is the size of the LayoutView's main GraphicsLayer. IntRect backgroundRect = pixelSnappedIntRect(m_layoutView.layer()->boundingBoxForCompositing()); const Document& document = m_layoutView.document(); const FrameView& frameView = *m_layoutView.frameView(); bool isMainFrame = !document.ownerElement(); bool paintsBaseBackground = isMainFrame && !frameView.isTransparent(); bool shouldClearCanvas = paintsBaseBackground && (document.settings() && document.settings()->shouldClearDocumentBackground()); Color baseBackgroundColor = paintsBaseBackground ? frameView.baseBackgroundColor() : Color(); Color rootBackgroundColor = m_layoutView.style()->visitedDependentColor(CSSPropertyBackgroundColor); const LayoutObject* rootObject = document.documentElement() ? document.documentElement()->layoutObject() : nullptr; LayoutObjectDrawingRecorder recorder(context, m_layoutView, DisplayItem::DocumentBackground, backgroundRect, LayoutPoint()); // Special handling for print economy mode. bool forceBackgroundToWhite = BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(m_layoutView.styleRef(), document); if (forceBackgroundToWhite) { // If for any reason the view background is not transparent, paint white instead, otherwise keep transparent as is. if (paintsBaseBackground || rootBackgroundColor.alpha() || m_layoutView.style()->backgroundLayers().image()) context.fillRect(backgroundRect, Color::white, SkXfermode::kSrc_Mode); return; } // Compute the enclosing rect of the view, in root element space. // // For background colors we can simply paint the document rect in the default space. // However for background image, the root element transform applies. The strategy is to apply // root element transform on the context and issue draw commands in the local space, therefore // we need to apply inverse transform on the document rect to get to the root element space. bool backgroundRenderable = true; TransformationMatrix transform; IntRect paintRect = backgroundRect; if (!rootObject || !rootObject->isBox()) { backgroundRenderable = false; } else if (rootObject->hasLayer()) { const PaintLayer& rootLayer = *toLayoutBoxModelObject(rootObject)->layer(); LayoutPoint offset; rootLayer.convertToLayerCoords(nullptr, offset); transform.translate(offset.x(), offset.y()); transform.multiply(rootLayer.renderableTransform(paintInfo.globalPaintFlags())); if (!transform.isInvertible()) { backgroundRenderable = false; } else { bool isClamped; paintRect = transform.inverse().projectQuad(FloatQuad(backgroundRect), &isClamped).enclosingBoundingBox(); backgroundRenderable = !isClamped; } } if (!backgroundRenderable) { if (baseBackgroundColor.alpha()) context.fillRect(backgroundRect, baseBackgroundColor, shouldClearCanvas ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); else if (shouldClearCanvas) context.fillRect(backgroundRect, Color(), SkXfermode::kClear_Mode); return; } BoxPainter::FillLayerOcclusionOutputList reversedPaintList; bool shouldDrawBackgroundInSeparateBuffer = BoxPainter(m_layoutView).calculateFillLayerOcclusionCulling(reversedPaintList, m_layoutView.style()->backgroundLayers()); ASSERT(reversedPaintList.size()); // If the root background color is opaque, isolation group can be skipped because the canvas // will be cleared by root background color. if (!rootBackgroundColor.hasAlpha()) shouldDrawBackgroundInSeparateBuffer = false; // We are going to clear the canvas with transparent pixels, isolation group can be skipped. if (!baseBackgroundColor.alpha() && shouldClearCanvas) shouldDrawBackgroundInSeparateBuffer = false; if (shouldDrawBackgroundInSeparateBuffer) { if (baseBackgroundColor.alpha()) context.fillRect(backgroundRect, baseBackgroundColor, shouldClearCanvas ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); context.beginLayer(); } Color combinedBackgroundColor = shouldDrawBackgroundInSeparateBuffer ? rootBackgroundColor : baseBackgroundColor.blend(rootBackgroundColor); if (combinedBackgroundColor.alpha()) { if (!combinedBackgroundColor.hasAlpha() && RuntimeEnabledFeatures::slimmingPaintV2Enabled()) recorder.setKnownToBeOpaque(); context.fillRect(backgroundRect, combinedBackgroundColor, (shouldDrawBackgroundInSeparateBuffer || shouldClearCanvas) ? SkXfermode::kSrc_Mode : SkXfermode::kSrcOver_Mode); } else if (shouldClearCanvas && !shouldDrawBackgroundInSeparateBuffer) { context.fillRect(backgroundRect, Color(), SkXfermode::kClear_Mode); } for (auto it = reversedPaintList.rbegin(); it != reversedPaintList.rend(); ++it) { ASSERT((*it)->clip() == BorderFillBox); bool shouldPaintInViewportSpace = (*it)->attachment() == FixedBackgroundAttachment; if (shouldPaintInViewportSpace) { BoxPainter::paintFillLayer(m_layoutView, paintInfo, Color(), **it, LayoutRect(LayoutRect::infiniteIntRect()), BackgroundBleedNone); } else { context.save(); // TODO(trchen): We should be able to handle 3D-transformed root // background with slimming paint by using transform display items. context.concatCTM(transform.toAffineTransform()); BoxPainter::paintFillLayer(m_layoutView, paintInfo, Color(), **it, LayoutRect(paintRect), BackgroundBleedNone); context.restore(); } } if (shouldDrawBackgroundInSeparateBuffer) context.endLayer(); }
void SVGRootPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // An empty viewport disables rendering. if (m_layoutSVGRoot.pixelSnappedBorderBoxRect().isEmpty()) return; // SVG outlines are painted during PaintPhaseForeground. if (shouldPaintSelfOutline(paintInfo.phase)) return; // An empty viewBox also disables rendering. // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute) SVGSVGElement* svg = toSVGSVGElement(m_layoutSVGRoot.node()); ASSERT(svg); if (svg->hasEmptyViewBox()) return; // Don't paint if we don't have kids, except if we have filters we should paint those. if (!m_layoutSVGRoot.firstChild()) { SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(&m_layoutSVGRoot); if (!resources || !resources->filter()) return; } PaintInfo paintInfoBeforeFiltering(paintInfo); // At the HTML->SVG boundary, SVGRoot will have a paint offset transform // paint property but may not have a PaintLayer, so we need to update the // paint properties here since they will not be updated by PaintLayer // (See: PaintPropertyTreeBuilder::createPaintOffsetTranslationIfNeeded). Optional<ScopedPaintChunkProperties> paintOffsetTranslationPropertyScope; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && !m_layoutSVGRoot.hasLayer()) { const auto* objectProperties = m_layoutSVGRoot.objectPaintProperties(); if (objectProperties && objectProperties->paintOffsetTranslation()) { auto& paintController = paintInfoBeforeFiltering.context.paintController(); PaintChunkProperties properties(paintController.currentPaintChunkProperties()); properties.transform = objectProperties->paintOffsetTranslation(); paintOffsetTranslationPropertyScope.emplace(paintController, properties); } } // Apply initial viewport clip. Optional<ClipRecorder> clipRecorder; if (m_layoutSVGRoot.shouldApplyViewportClip()) { // TODO(pdr): Clip the paint info cull rect here. clipRecorder.emplace(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintInfoBeforeFiltering.displayItemTypeForClipping(), LayoutRect(pixelSnappedIntRect(m_layoutSVGRoot.overflowClipRect(paintOffset)))); } // Convert from container offsets (html layoutObjects) to a relative transform (svg layoutObjects). // Transform from our paint container's coordinate system to our local coords. IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset); AffineTransform paintOffsetToBorderBox = AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * m_layoutSVGRoot.localToBorderBoxTransform(); paintInfoBeforeFiltering.updateCullRect(paintOffsetToBorderBox); TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintOffsetToBorderBox); SVGPaintContext paintContext(m_layoutSVGRoot, paintInfoBeforeFiltering); if (paintContext.paintInfo().phase == PaintPhaseForeground && !paintContext.applyClipMaskAndFilterIfNecessary()) return; BoxPainter(m_layoutSVGRoot).paint(paintContext.paintInfo(), LayoutPoint()); PaintTiming& timing = PaintTiming::from(m_layoutSVGRoot.node()->document().topDocument()); timing.markFirstContentfulPaint(); }
void ReplacedPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!m_layoutReplaced.shouldPaint(paintInfo, paintOffset)) return; LayoutPoint adjustedPaintOffset = paintOffset + m_layoutReplaced.location(); LayoutRect borderRect(adjustedPaintOffset, m_layoutReplaced.size()); if (m_layoutReplaced.hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) m_layoutReplaced.paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { m_layoutReplaced.paintMask(paintInfo, adjustedPaintOffset); return; } if (paintInfo.phase == PaintPhaseClippingMask && (!m_layoutReplaced.hasLayer() || !m_layoutReplaced.layer()->hasCompositedClippingMask())) return; if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { if (m_layoutReplaced.styleRef().outlineWidth()) ObjectPainter(m_layoutReplaced).paintOutline(paintInfo, adjustedPaintOffset); return; } if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !m_layoutReplaced.canHaveChildren() && paintInfo.phase != PaintPhaseClippingMask) return; if (!paintInfo.shouldPaintWithinRoot(&m_layoutReplaced)) return; if (paintInfo.phase == PaintPhaseSelection) if (m_layoutReplaced.selectionState() == SelectionNone) return; { Optional<RoundedInnerRectClipper> clipper; bool completelyClippedOut = false; if (m_layoutReplaced.style()->hasBorderRadius()) { if (borderRect.isEmpty()) { completelyClippedOut = true; } else if (shouldApplyViewportClip(m_layoutReplaced)) { // Push a clip if we have a border radius, since we want to round the foreground content that gets painted. FloatRoundedRect roundedInnerRect = m_layoutReplaced.style()->getRoundedInnerBorderFor(borderRect, LayoutRectOutsets( -(m_layoutReplaced.paddingTop() + m_layoutReplaced.borderTop()), -(m_layoutReplaced.paddingRight() + m_layoutReplaced.borderRight()), -(m_layoutReplaced.paddingBottom() + m_layoutReplaced.borderBottom()), -(m_layoutReplaced.paddingLeft() + m_layoutReplaced.borderLeft())), true, true); clipper.emplace(m_layoutReplaced, paintInfo, borderRect, roundedInnerRect, ApplyToDisplayList); } } if (!completelyClippedOut) { if (paintInfo.phase == PaintPhaseClippingMask) { BoxPainter(m_layoutReplaced).paintClippingMask(paintInfo, adjustedPaintOffset); } else { m_layoutReplaced.paintReplaced(paintInfo, adjustedPaintOffset); } } } // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of // surrounding content. bool drawSelectionTint = paintInfo.phase == PaintPhaseForeground && m_layoutReplaced.selectionState() != SelectionNone && !paintInfo.isPrinting(); if (drawSelectionTint && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutReplaced, DisplayItem::SelectionTint, adjustedPaintOffset)) { LayoutRect selectionPaintingRect = m_layoutReplaced.localSelectionRect(); selectionPaintingRect.moveBy(adjustedPaintOffset); IntRect selectionPaintingIntRect = pixelSnappedIntRect(selectionPaintingRect); LayoutObjectDrawingRecorder drawingRecorder(paintInfo.context, m_layoutReplaced, DisplayItem::SelectionTint, selectionPaintingIntRect, adjustedPaintOffset); paintInfo.context.fillRect(selectionPaintingIntRect, m_layoutReplaced.selectionBackgroundColor()); } }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (RuntimeEnabledFeatures::slimmingPaintOffsetCachingEnabled() && m_layoutBlock.childrenInline() && !paintInfo.context.paintController().skippingCache()) { if (m_layoutBlock.paintOffsetChanged(paintOffset)) { LineBoxListPainter(m_layoutBlock.lineBoxes()).invalidateLineBoxPaintOffsets(paintInfo); paintInfo.context.paintController().invalidatePaintOffset(m_layoutBlock); } // Set previousPaintOffset here in case that m_layoutBlock paints nothing and no // LayoutObjectDrawingRecorder updates its previousPaintOffset. // TODO(wangxianzhu): Integrate paint offset checking into new paint invalidation. m_layoutBlock.mutableForPainting().setPreviousPaintOffset(paintOffset); } const PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseSelfBlockBackground || paintPhase == PaintPhaseBlockBackground) && m_layoutBlock.style()->visibility() == VISIBLE && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == VISIBLE) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == VISIBLE) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } // FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872), // this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase. if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); { Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); AffineTransform transform; transform.translate(-scrollOffset.width(), -scrollOffset.height()); scrolledPaintInfo->updateCullRect(transform); } } // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseSelfBlockBackground || paintInfo.paintRootBackgroundOnly()) return; const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (paintPhase != PaintPhaseSelfOutline) paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseForeground && !paintInfo.isPrinting()) m_layoutBlock.paintSelection(contentsPaintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) m_layoutBlock.paintFloats(contentsPaintInfo, paintOffset); } if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutBlock.style()->hasOutline() && m_layoutBlock.style()->visibility() == VISIBLE) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and the paint action is PaintPhaseForeground, // then paint the caret. if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutBlock, DisplayItem::Caret, paintOffset)) { LayoutRect bounds = m_layoutBlock.visualOverflowRect(); bounds.moveBy(paintOffset); LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutBlock, DisplayItem::Caret, bounds, paintOffset); paintCarets(paintInfo, paintOffset); } }
void PartPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutPoint adjustedPaintOffset = paintOffset + m_layoutPart.location(); if (!ReplacedPainter(m_layoutPart) .shouldPaint(paintInfo, adjustedPaintOffset)) return; LayoutRect borderRect(adjustedPaintOffset, m_layoutPart.size()); if (m_layoutPart.hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) BoxPainter(m_layoutPart) .paintBoxDecorationBackground(paintInfo, adjustedPaintOffset); if (paintInfo.phase == PaintPhaseMask) { BoxPainter(m_layoutPart).paintMask(paintInfo, adjustedPaintOffset); return; } if (shouldPaintSelfOutline(paintInfo.phase)) ObjectPainter(m_layoutPart).paintOutline(paintInfo, adjustedPaintOffset); if (paintInfo.phase != PaintPhaseForeground) return; if (m_layoutPart.widget()) { // TODO(schenney) crbug.com/93805 Speculative release assert to verify that // the crashes we see in widget painting are due to a destroyed LayoutPart // object. CHECK(m_layoutPart.node()); Optional<RoundedInnerRectClipper> clipper; if (m_layoutPart.style()->hasBorderRadius()) { if (borderRect.isEmpty()) return; FloatRoundedRect roundedInnerRect = m_layoutPart.style()->getRoundedInnerBorderFor( borderRect, LayoutRectOutsets( -(m_layoutPart.paddingTop() + m_layoutPart.borderTop()), -(m_layoutPart.paddingRight() + m_layoutPart.borderRight()), -(m_layoutPart.paddingBottom() + m_layoutPart.borderBottom()), -(m_layoutPart.paddingLeft() + m_layoutPart.borderLeft())), true, true); clipper.emplace(m_layoutPart, paintInfo, borderRect, roundedInnerRect, ApplyToDisplayList); } m_layoutPart.paintContents(paintInfo, paintOffset); } // Paint a partially transparent wash over selected widgets. if (isSelected() && !paintInfo.isPrinting() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible( paintInfo.context, m_layoutPart, paintInfo.phase)) { LayoutRect rect = m_layoutPart.localSelectionRect(); rect.moveBy(adjustedPaintOffset); IntRect selectionRect = pixelSnappedIntRect(rect); LayoutObjectDrawingRecorder drawingRecorder(paintInfo.context, m_layoutPart, paintInfo.phase, selectionRect); paintInfo.context.fillRect(selectionRect, m_layoutPart.selectionBackgroundColor()); } if (m_layoutPart.canResize()) ScrollableAreaPainter(*m_layoutPart.layer()->getScrollableArea()) .paintResizer(paintInfo.context, roundedIntPoint(adjustedPaintOffset), paintInfo.cullRect()); }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { const PaintPhase paintPhase = paintInfo.phase; if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && m_layoutBlock.style()->visibility() == VISIBLE && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == VISIBLE) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == VISIBLE) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } // FIXME: When Skia supports annotation rect covering (https://code.google.com/p/skia/issues/detail?id=3872), // this rect may be covered by foreground and descendant drawings. Then we may need a dedicated paint phase. if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); { Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(*paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); scrolledPaintInfo->rect.move(scrollOffset); } } // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseBlockBackground || paintInfo.paintRootBackgroundOnly()) return; const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (paintPhase != PaintPhaseSelfOutline) paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseForeground && !paintInfo.isPrinting()) m_layoutBlock.paintSelection(contentsPaintInfo, paintOffset); // Fill in gaps in selection on lines and between blocks. if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) m_layoutBlock.paintFloats(contentsPaintInfo, paintOffset, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); } if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && m_layoutBlock.style()->hasOutline() && m_layoutBlock.style()->visibility() == VISIBLE) { // Don't paint focus ring for anonymous block continuation because the // inline element having outline-style:auto paints the whole focus ring. if (!m_layoutBlock.style()->outlineStyleIsAuto() || !m_layoutBlock.isAnonymousBlockContinuation()) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, LayoutRect(paintOffset, m_layoutBlock.size()), visualOverflowRectWithPaintOffset(m_layoutBlock, paintOffset)); } if (paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines) paintContinuationOutlines(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and the paint action is PaintPhaseForeground, // then paint the caret. if (paintPhase == PaintPhaseForeground && hasCaret() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutBlock, DisplayItem::Caret)) { LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutBlock, DisplayItem::Caret, visualOverflowRectWithPaintOffset(m_layoutBlock, paintOffset)); paintCarets(paintInfo, paintOffset); } }
void BlockPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { const PaintPhase paintPhase = paintInfo.phase; if (shouldPaintSelfBlockBackground(paintPhase)) { if (m_layoutBlock.style()->visibility() == EVisibility::Visible && m_layoutBlock.hasBoxDecorationBackground()) m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset); // We're done. We don't bother painting any children. if (paintPhase == PaintPhaseSelfBlockBackgroundOnly) return; } if (paintInfo.paintRootBackgroundOnly()) return; if (paintPhase == PaintPhaseMask && m_layoutBlock.style()->visibility() == EVisibility::Visible) { m_layoutBlock.paintMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseClippingMask && m_layoutBlock.style()->visibility() == EVisibility::Visible) { BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset); return; } if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting()) ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset); if (paintPhase != PaintPhaseSelfOutlineOnly) { Optional<ScopedPaintChunkProperties> m_scopedScrollProperty; Optional<ScrollRecorder> scrollRecorder; Optional<PaintInfo> scrolledPaintInfo; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { const auto* objectProperties = m_layoutBlock.paintProperties(); if (auto* scroll = objectProperties ? objectProperties->scroll() : nullptr) { PaintChunkProperties properties(paintInfo.context.getPaintController() .currentPaintChunkProperties()); auto* scrollTranslation = objectProperties->scrollTranslation(); DCHECK(scrollTranslation); properties.transform = scrollTranslation; properties.scroll = scroll; m_scopedScrollProperty.emplace( paintInfo.context.getPaintController(), m_layoutBlock, DisplayItem::paintPhaseToDrawingType(paintPhase), properties); scrolledPaintInfo.emplace(paintInfo); scrolledPaintInfo->updateCullRect( scrollTranslation->matrix().toAffineTransform()); } } else if (m_layoutBlock.hasOverflowClip()) { IntSize scrollOffset = m_layoutBlock.scrolledContentOffset(); if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) { scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase, scrollOffset); scrolledPaintInfo.emplace(paintInfo); AffineTransform transform; transform.translate(-scrollOffset.width(), -scrollOffset.height()); scrolledPaintInfo->updateCullRect(transform); } } const PaintInfo& contentsPaintInfo = scrolledPaintInfo ? *scrolledPaintInfo : paintInfo; if (m_layoutBlock.isLayoutBlockFlow()) { BlockFlowPainter blockFlowPainter(toLayoutBlockFlow(m_layoutBlock)); blockFlowPainter.paintContents(contentsPaintInfo, paintOffset); if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip) blockFlowPainter.paintFloats(contentsPaintInfo, paintOffset); } else { paintContents(contentsPaintInfo, paintOffset); } } if (shouldPaintSelfOutline(paintPhase)) ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset); // If the caret's node's layout object's containing block is this block, and // the paint action is PaintPhaseForeground, then paint the caret. if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret()) paintCarets(paintInfo, paintOffset); }