void SVGImagePainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground || m_layoutSVGImage.style()->visibility() == HIDDEN || !m_layoutSVGImage.imageResource()->hasImage()) return; FloatRect boundingBox = m_layoutSVGImage.paintInvalidationRectInLocalCoordinates(); if (!paintInfo.cullRect().intersectsCullRect(m_layoutSVGImage.localToParentTransform(), boundingBox)) return; PaintInfo paintInfoBeforeFiltering(paintInfo); // Images cannot have children so do not call updateCullRect. TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGImage, m_layoutSVGImage.localToParentTransform()); { SVGPaintContext paintContext(m_layoutSVGImage, paintInfoBeforeFiltering); if (paintContext.applyClipMaskAndFilterIfNecessary() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintContext.paintInfo().context, m_layoutSVGImage, paintContext.paintInfo().phase, LayoutPoint())) { LayoutObjectDrawingRecorder recorder(paintContext.paintInfo().context, m_layoutSVGImage, paintContext.paintInfo().phase, boundingBox, LayoutPoint()); paintForeground(paintContext.paintInfo()); } } if (m_layoutSVGImage.style()->outlineWidth()) { PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); outlinePaintInfo.phase = PaintPhaseSelfOutline; ObjectPainter(m_layoutSVGImage).paintOutline(outlinePaintInfo, LayoutPoint(boundingBox.location())); } }
void SVGRootInlineBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); bool hasSelection = !paintInfo.isPrinting() && m_svgRootInlineBox.selectionState() != SelectionNone; PaintInfo paintInfoBeforeFiltering(paintInfo); if (hasSelection && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfoBeforeFiltering.context, m_svgRootInlineBox.layoutObject(), paintInfoBeforeFiltering.phase, paintOffset)) { LayoutObjectDrawingRecorder recorder(*paintInfoBeforeFiltering.context, m_svgRootInlineBox.layoutObject(), paintInfoBeforeFiltering.phase, paintInfoBeforeFiltering.cullRect().m_rect, paintOffset); for (InlineBox* child = m_svgRootInlineBox.firstChild(); child; child = child->nextOnLine()) { if (child->isSVGInlineTextBox()) SVGInlineTextBoxPainter(*toSVGInlineTextBox(child)).paintSelectionBackground(paintInfoBeforeFiltering); else if (child->isSVGInlineFlowBox()) SVGInlineFlowBoxPainter(*toSVGInlineFlowBox(child)).paintSelectionBackground(paintInfoBeforeFiltering); } } SVGPaintContext paintContext(m_svgRootInlineBox.layoutObject(), paintInfoBeforeFiltering); if (paintContext.applyClipMaskAndFilterIfNecessary()) { for (InlineBox* child = m_svgRootInlineBox.firstChild(); child; child = child->nextOnLine()) child->paint(paintContext.paintInfo(), paintOffset, 0, 0); } }
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 SVGImagePainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground || m_layoutSVGImage.style()->visibility() == HIDDEN || !m_layoutSVGImage.imageResource()->hasImage()) return; FloatRect boundingBox = m_layoutSVGImage.paintInvalidationRectInLocalCoordinates(); if (!paintInfo.intersectsCullRect(m_layoutSVGImage.localToParentTransform(), boundingBox)) return; PaintInfo paintInfoBeforeFiltering(paintInfo); // Images cannot have children so do not call updateCullRectForSVGTransform. TransformRecorder transformRecorder(*paintInfoBeforeFiltering.context, m_layoutSVGImage, m_layoutSVGImage.localToParentTransform()); { SVGPaintContext paintContext(m_layoutSVGImage, paintInfoBeforeFiltering); if (paintContext.applyClipMaskAndFilterIfNecessary() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintContext.paintInfo().context, m_layoutSVGImage, paintContext.paintInfo().phase)) { LayoutObjectDrawingRecorder recorder(*paintContext.paintInfo().context, m_layoutSVGImage, paintContext.paintInfo().phase, boundingBox); // There's no need to cache a buffered SkPicture with slimming // paint because it's automatically done in the display list. if (m_layoutSVGImage.style()->svgStyle().bufferedRendering() != BR_STATIC || RuntimeEnabledFeatures::slimmingPaintEnabled()) { paintForeground(paintContext.paintInfo()); } else { RefPtr<const SkPicture>& bufferedForeground = m_layoutSVGImage.bufferedForeground(); if (!bufferedForeground) { paintContext.paintInfo().context->beginRecording(m_layoutSVGImage.objectBoundingBox()); paintForeground(paintContext.paintInfo()); bufferedForeground = paintContext.paintInfo().context->endRecording(); } paintContext.paintInfo().context->drawPicture(bufferedForeground.get()); } } } if (m_layoutSVGImage.style()->outlineWidth()) { PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); outlinePaintInfo.phase = PaintPhaseSelfOutline; LayoutRect layoutBoundingBox(boundingBox); LayoutRect visualOverflowRect = ObjectPainter::outlineBounds(layoutBoundingBox, m_layoutSVGImage.styleRef()); ObjectPainter(m_layoutSVGImage).paintOutline(outlinePaintInfo, layoutBoundingBox, visualOverflowRect); } }
void SVGForeignObjectPainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) return; PaintInfo paintInfoBeforeFiltering(paintInfo); paintInfoBeforeFiltering.updateCullRect(m_layoutSVGForeignObject.localTransform()); TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGForeignObject, m_layoutSVGForeignObject.localTransform()); Optional<FloatClipRecorder> clipRecorder; if (SVGLayoutSupport::isOverflowHidden(&m_layoutSVGForeignObject)) clipRecorder.emplace(paintInfoBeforeFiltering.context, m_layoutSVGForeignObject, paintInfoBeforeFiltering.phase, m_layoutSVGForeignObject.viewportRect()); SVGPaintContext paintContext(m_layoutSVGForeignObject, paintInfoBeforeFiltering); bool continueRendering = true; if (paintContext.paintInfo().phase == PaintPhaseForeground) continueRendering = paintContext.applyClipMaskAndFilterIfNecessary(); if (continueRendering) { // Paint all phases of FO elements atomically as though the FO element established its own stacking context. bool preservePhase = paintContext.paintInfo().phase == PaintPhaseSelection || paintContext.paintInfo().phase == PaintPhaseTextClip; const LayoutPoint childPoint = IntPoint(); paintContext.paintInfo().phase = preservePhase ? paintContext.paintInfo().phase : PaintPhaseBlockBackground; BlockPainter(m_layoutSVGForeignObject).paint(paintContext.paintInfo(), childPoint); if (!preservePhase) { paintContext.paintInfo().phase = PaintPhaseChildBlockBackgrounds; BlockPainter(m_layoutSVGForeignObject).paint(paintContext.paintInfo(), childPoint); paintContext.paintInfo().phase = PaintPhaseFloat; BlockPainter(m_layoutSVGForeignObject).paint(paintContext.paintInfo(), childPoint); paintContext.paintInfo().phase = PaintPhaseForeground; BlockPainter(m_layoutSVGForeignObject).paint(paintContext.paintInfo(), childPoint); paintContext.paintInfo().phase = PaintPhaseOutline; BlockPainter(m_layoutSVGForeignObject).paint(paintContext.paintInfo(), childPoint); } } }
void SVGForeignObjectPainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) return; PaintInfo paintInfoBeforeFiltering(paintInfo); paintInfoBeforeFiltering.updateCullRect( m_layoutSVGForeignObject.localSVGTransform()); SVGTransformContext transformContext( paintInfoBeforeFiltering.context, m_layoutSVGForeignObject, m_layoutSVGForeignObject.localSVGTransform()); Optional<FloatClipRecorder> clipRecorder; if (SVGLayoutSupport::isOverflowHidden(&m_layoutSVGForeignObject)) clipRecorder.emplace(paintInfoBeforeFiltering.context, m_layoutSVGForeignObject, paintInfoBeforeFiltering.phase, m_layoutSVGForeignObject.viewportRect()); SVGPaintContext paintContext(m_layoutSVGForeignObject, paintInfoBeforeFiltering); bool continueRendering = true; if (paintContext.paintInfo().phase == PaintPhaseForeground) continueRendering = paintContext.applyClipMaskAndFilterIfNecessary(); if (continueRendering) { // Paint all phases of FO elements atomically as though the FO element // established its own stacking context. The delegate forwards calls to // paint() in LayoutObject::paintAllPhasesAtomically() to // BlockPainter::paint(), instead of m_layoutSVGForeignObject.paint() (which // would call this method again). BlockPainterDelegate delegate(m_layoutSVGForeignObject); ObjectPainter(delegate).paintAllPhasesAtomically(paintContext.paintInfo(), LayoutPoint()); } }
void SVGShapePainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground || m_layoutSVGShape.style()->visibility() == HIDDEN || m_layoutSVGShape.isShapeEmpty()) return; FloatRect boundingBox = m_layoutSVGShape.paintInvalidationRectInLocalCoordinates(); if (!paintInfo.cullRect().intersectsCullRect(m_layoutSVGShape.localTransform(), boundingBox)) return; PaintInfo paintInfoBeforeFiltering(paintInfo); // Shapes cannot have children so do not call updateCullRect. TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGShape, m_layoutSVGShape.localTransform()); { SVGPaintContext paintContext(m_layoutSVGShape, paintInfoBeforeFiltering); if (paintContext.applyClipMaskAndFilterIfNecessary() && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintContext.paintInfo().context, m_layoutSVGShape, paintContext.paintInfo().phase, LayoutPoint())) { LayoutObjectDrawingRecorder recorder(paintContext.paintInfo().context, m_layoutSVGShape, paintContext.paintInfo().phase, boundingBox, LayoutPoint()); const SVGComputedStyle& svgStyle = m_layoutSVGShape.style()->svgStyle(); bool shouldAntiAlias = svgStyle.shapeRendering() != SR_CRISPEDGES; for (int i = 0; i < 3; i++) { switch (svgStyle.paintOrderType(i)) { case PT_FILL: { SkPaint fillPaint; if (!SVGPaintContext::paintForLayoutObject(paintContext.paintInfo(), m_layoutSVGShape.styleRef(), m_layoutSVGShape, ApplyToFillMode, fillPaint)) break; fillPaint.setAntiAlias(shouldAntiAlias); fillShape(paintContext.paintInfo().context, fillPaint, fillRuleFromStyle(paintContext.paintInfo(), svgStyle)); break; } case PT_STROKE: if (svgStyle.hasVisibleStroke()) { GraphicsContextStateSaver stateSaver(paintContext.paintInfo().context, false); AffineTransform nonScalingTransform; const AffineTransform* additionalPaintServerTransform = 0; if (m_layoutSVGShape.hasNonScalingStroke()) { nonScalingTransform = m_layoutSVGShape.nonScalingStrokeTransform(); if (!setupNonScalingStrokeContext(nonScalingTransform, stateSaver)) return; // Non-scaling stroke needs to reset the transform back to the host transform. additionalPaintServerTransform = &nonScalingTransform; } SkPaint strokePaint; if (!SVGPaintContext::paintForLayoutObject(paintContext.paintInfo(), m_layoutSVGShape.styleRef(), m_layoutSVGShape, ApplyToStrokeMode, strokePaint, additionalPaintServerTransform)) break; strokePaint.setAntiAlias(shouldAntiAlias); StrokeData strokeData; SVGLayoutSupport::applyStrokeStyleToStrokeData(strokeData, m_layoutSVGShape.styleRef(), m_layoutSVGShape, m_layoutSVGShape.dashScaleFactor()); strokeData.setupPaint(&strokePaint); strokeShape(paintContext.paintInfo().context, strokePaint); } break; case PT_MARKERS: paintMarkers(paintContext.paintInfo(), boundingBox); break; default: ASSERT_NOT_REACHED(); break; } } } } if (m_layoutSVGShape.style()->outlineWidth()) { PaintInfo outlinePaintInfo(paintInfoBeforeFiltering); outlinePaintInfo.phase = PaintPhaseSelfOutlineOnly; ObjectPainter(m_layoutSVGShape).paintOutline(outlinePaintInfo, LayoutPoint(boundingBox.location())); } }
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(); }