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())); } }
PassRefPtr<SkPicture> LayoutSVGResourcePattern::asPicture(const FloatRect& tileBounds, const AffineTransform& tileTransform) const { ASSERT(!m_shouldCollectPatternAttributes); AffineTransform contentTransform; if (attributes().patternContentUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) contentTransform = tileTransform; FloatRect bounds(FloatPoint(), tileBounds.size()); SkPictureBuilder pictureBuilder(bounds); const LayoutSVGResourceContainer* patternLayoutObject = resolveContentElement(); ASSERT(patternLayoutObject && !patternLayoutObject->needsLayout()); SubtreeContentTransformScope contentTransformScope(contentTransform); { TransformRecorder transformRecorder(pictureBuilder.context(), *patternLayoutObject, tileTransform); for (LayoutObject* child = patternLayoutObject->firstChild(); child; child = child->nextSibling()) SVGPaintContext::paintSubtree(pictureBuilder.context(), child); } return pictureBuilder.endRecording(); }
void SVGShapePainter::paintMarker(const PaintInfo& paintInfo, RenderSVGResourceMarker& marker, const MarkerPosition& position, float strokeWidth) { // An empty viewBox disables rendering. SVGMarkerElement* markerElement = toSVGMarkerElement(marker.element()); ASSERT(markerElement); if (markerElement->hasAttribute(SVGNames::viewBoxAttr) && markerElement->viewBox()->currentValue()->isValid() && markerElement->viewBox()->currentValue()->value().isEmpty()) return; OwnPtr<DisplayItemList> displayItemList; if (RuntimeEnabledFeatures::slimmingPaintEnabled()) displayItemList = DisplayItemList::create(); GraphicsContext recordingContext(nullptr, displayItemList.get()); recordingContext.beginRecording(m_renderSVGShape.paintInvalidationRectInLocalCoordinates()); PaintInfo markerPaintInfo(paintInfo); markerPaintInfo.context = &recordingContext; { TransformRecorder transformRecorder(*markerPaintInfo.context, marker.displayItemClient(), marker.markerTransformation(position.origin, position.angle, strokeWidth)); OwnPtr<FloatClipRecorder> clipRecorder; if (SVGRenderSupport::isOverflowHidden(&marker)) clipRecorder = adoptPtr(new FloatClipRecorder(recordingContext, marker.displayItemClient(), markerPaintInfo.phase, marker.viewport())); SVGContainerPainter(marker).paint(markerPaintInfo); } if (displayItemList) displayItemList->replay(&recordingContext); RefPtr<const SkPicture> recording = recordingContext.endRecording(); paintInfo.context->drawPicture(recording.get()); }
sk_sp<SkImageFilter> FEImage::createImageFilterForLayoutObject(const LayoutObject& layoutObject) { FloatRect dstRect = filterPrimitiveSubregion(); AffineTransform transform; SVGElement* contextNode = toSVGElement(layoutObject.node()); if (contextNode->hasRelativeLengths()) { SVGLengthContext lengthContext(contextNode); FloatSize viewportSize; // If we're referencing an element with percentage units, eg. <rect with="30%"> those values were resolved against the viewport. // Build up a transformation that maps from the viewport space to the filter primitive subregion. if (lengthContext.determineViewport(viewportSize)) transform = makeMapBetweenRects(FloatRect(FloatPoint(), viewportSize), dstRect); } else { transform.translate(dstRect.x(), dstRect.y()); } SkPictureBuilder filterPicture(dstRect); { TransformRecorder transformRecorder(filterPicture.context(), layoutObject, transform); SVGPaintContext::paintSubtree(filterPicture.context(), &layoutObject); } return SkPictureImageFilter::Make(toSkSp(filterPicture.endRecording()), dstRect); }
void FramePainter::paint(GraphicsContext* context, const IntRect& rect) { m_frameView.notifyPageThatContentAreaWillPaint(); IntRect documentDirtyRect = rect; IntRect visibleAreaWithoutScrollbars(m_frameView.location(), m_frameView.visibleContentRect().size()); documentDirtyRect.intersect(visibleAreaWithoutScrollbars); if (!documentDirtyRect.isEmpty()) { TransformRecorder transformRecorder(*context, *m_frameView.layoutView(), AffineTransform::translation(m_frameView.x() - m_frameView.scrollX(), m_frameView.y() - m_frameView.scrollY())); ClipRecorder recorder(*context, *m_frameView.layoutView(), DisplayItem::ClipFrameToVisibleContentRect, LayoutRect(m_frameView.visibleContentRect())); documentDirtyRect.moveBy(-m_frameView.location() + m_frameView.scrollPosition()); paintContents(context, documentDirtyRect); } calculateAndPaintOverhangAreas(context, rect); // Now paint the scrollbars. if (!m_frameView.scrollbarsSuppressed() && (m_frameView.horizontalScrollbar() || m_frameView.verticalScrollbar())) { IntRect scrollViewDirtyRect = rect; IntRect visibleAreaWithScrollbars(m_frameView.location(), m_frameView.visibleContentRect(IncludeScrollbars).size()); scrollViewDirtyRect.intersect(visibleAreaWithScrollbars); scrollViewDirtyRect.moveBy(-m_frameView.location()); TransformRecorder transformRecorder(*context, *m_frameView.layoutView(), AffineTransform::translation(m_frameView.x(), m_frameView.y())); ClipRecorder recorder(*context, *m_frameView.layoutView(), DisplayItem::ClipFrameScrollbars, LayoutRect(IntPoint(), visibleAreaWithScrollbars.size())); paintScrollbars(context, scrollViewDirtyRect); } // Paint the panScroll Icon if (m_frameView.drawPanScrollIcon()) m_frameView.paintPanScrollIcon(context); }
void SVGShapePainter::paintMarker(const PaintInfo& paintInfo, LayoutSVGResourceMarker& marker, const MarkerPosition& position, float strokeWidth) { // An empty viewBox disables rendering. SVGMarkerElement* markerElement = toSVGMarkerElement(marker.element()); ASSERT(markerElement); if (markerElement->hasAttribute(SVGNames::viewBoxAttr) && markerElement->viewBox()->currentValue()->isValid() && markerElement->viewBox()->currentValue()->value().isEmpty()) return; TransformRecorder transformRecorder(paintInfo.context, marker, marker.markerTransformation(position.origin, position.angle, strokeWidth)); Optional<FloatClipRecorder> clipRecorder; if (SVGLayoutSupport::isOverflowHidden(&marker)) clipRecorder.emplace(paintInfo.context, marker, paintInfo.phase, marker.viewport()); SVGContainerPainter(marker).paint(paintInfo); }
void SVGImage::draw(SkCanvas* canvas, const SkPaint& paint, const FloatRect& dstRect, const FloatRect& srcRect, RespectImageOrientationEnum, ImageClampingMode) { if (!m_page) return; FrameView* view = frameView(); view->resize(containerSize()); // Always call processUrlFragment, even if the url is empty, because // there may have been a previous url/fragment that needs to be reset. view->processUrlFragment(m_url); SkPictureBuilder imagePicture(dstRect); { ClipRecorder clipRecorder(imagePicture.context(), *this, DisplayItem::ClipNodeImage, LayoutRect(enclosingIntRect(dstRect))); // We can only draw the entire frame, clipped to the rect we want. So compute where the top left // of the image would be if we were drawing without clipping, and translate accordingly. FloatSize scale(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height()); FloatSize topLeftOffset(srcRect.location().x() * scale.width(), srcRect.location().y() * scale.height()); FloatPoint destOffset = dstRect.location() - topLeftOffset; AffineTransform transform = AffineTransform::translation(destOffset.x(), destOffset.y()); transform.scale(scale.width(), scale.height()); TransformRecorder transformRecorder(imagePicture.context(), *this, transform); view->updateAllLifecyclePhases(); view->paint(&imagePicture.context(), enclosingIntRect(srcRect)); ASSERT(!view->needsLayout()); } { SkAutoCanvasRestore ar(canvas, false); if (drawNeedsLayer(paint)) { SkRect layerRect = dstRect; canvas->saveLayer(&layerRect, &paint); } RefPtr<const SkPicture> recording = imagePicture.endRecording(); canvas->drawPicture(recording.get()); } if (imageObserver()) imageObserver()->didDraw(this); // Start any (SMIL) animations if needed. This will restart or continue // animations if preceded by calls to resetAnimation or stopAnimation // respectively. startAnimation(); }
static void paintScrollbar(const Scrollbar* scrollbar, GraphicsContext& context, const IntRect& clip) { if (!scrollbar) return; // Frame scrollbars are painted in the space of the containing frame, not the local space of the scrollbar. const IntPoint& paintOffset = scrollbar->frameRect().location(); IntRect transformedClip = clip; transformedClip.moveBy(paintOffset); AffineTransform translation; translation.translate(-paintOffset.x(), -paintOffset.y()); TransformRecorder transformRecorder(context, *scrollbar, translation); scrollbar->paint(context, CullRect(transformedClip)); }
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 SVGTextPainter::paint(const PaintInfo& paintInfo) { if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) return; PaintInfo blockInfo(paintInfo); TransformRecorder transformRecorder(*blockInfo.context, m_renderSVGText.displayItemClient(), m_renderSVGText.localToParentTransform()); // When transitioning from SVG to block painters we need to keep the PaintInfo rect up-to-date // because it can be used for clipping. m_renderSVGText.updatePaintInfoRect(blockInfo.rect); BlockPainter(m_renderSVGText).paint(blockInfo, LayoutPoint()); // Paint the outlines, if any if (paintInfo.phase == PaintPhaseForeground) { blockInfo.phase = PaintPhaseSelfOutline; BlockPainter(m_renderSVGText).paint(blockInfo, LayoutPoint()); } }
void SVGImagePainter::paint(const PaintInfo& paintInfo) { ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderSVGImage); if (paintInfo.phase != PaintPhaseForeground || m_renderSVGImage.style()->visibility() == HIDDEN || !m_renderSVGImage.imageResource()->hasImage()) return; FloatRect boundingBox = m_renderSVGImage.paintInvalidationRectInLocalCoordinates(); PaintInfo childPaintInfo(paintInfo); GraphicsContextStateSaver stateSaver(*childPaintInfo.context); TransformRecorder transformRecorder(*childPaintInfo.context, m_renderSVGImage.displayItemClient(), m_renderSVGImage.localToParentTransform()); SVGRenderingContext renderingContext(&m_renderSVGImage, childPaintInfo); if (renderingContext.isRenderingPrepared()) { RenderDrawingRecorder recorder(childPaintInfo.context, m_renderSVGImage, childPaintInfo.phase, boundingBox); if (!recorder.canUseCachedDrawing()) { if (m_renderSVGImage.style()->svgStyle().bufferedRendering() != BR_STATIC) { paintForeground(childPaintInfo); } else { RefPtr<const SkPicture>& bufferedForeground = m_renderSVGImage.bufferedForeground(); if (!bufferedForeground) { childPaintInfo.context->beginRecording(m_renderSVGImage.objectBoundingBox()); paintForeground(childPaintInfo); bufferedForeground = childPaintInfo.context->endRecording(); } childPaintInfo.context->drawPicture(bufferedForeground.get()); } } } if (m_renderSVGImage.style()->outlineWidth()) ObjectPainter(m_renderSVGImage).paintOutline(childPaintInfo, IntRect(boundingBox)); }
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 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 FramePainter::paint(GraphicsContext& context, const GlobalPaintFlags globalPaintFlags, const CullRect& rect) { frameView().notifyPageThatContentAreaWillPaint(); IntRect documentDirtyRect = rect.m_rect; IntRect visibleAreaWithoutScrollbars(frameView().location(), frameView().visibleContentRect().size()); documentDirtyRect.intersect(visibleAreaWithoutScrollbars); bool shouldPaintContents = !documentDirtyRect.isEmpty(); bool shouldPaintScrollbars = !frameView().scrollbarsSuppressed() && (frameView().horizontalScrollbar() || frameView().verticalScrollbar()); if (!shouldPaintContents && !shouldPaintScrollbars) return; if (shouldPaintContents) { // TODO(pdr): Creating frame paint properties here will not be needed once // settings()->rootLayerScrolls() is enabled. // TODO(pdr): Make this conditional on the rootLayerScrolls setting. Optional<ScopedPaintChunkProperties> scopedPaintChunkProperties; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { TransformPaintPropertyNode* transform = m_frameView->scrollTranslation() ? m_frameView->scrollTranslation() : m_frameView->preTranslation(); ClipPaintPropertyNode* clip = m_frameView->contentClip(); if (transform || clip) { PaintChunkProperties properties(context.paintController().currentPaintChunkProperties()); if (transform) properties.transform = transform; if (clip) properties.clip = clip; scopedPaintChunkProperties.emplace(context.paintController(), properties); } } TransformRecorder transformRecorder(context, *frameView().layoutView(), AffineTransform::translation(frameView().x() - frameView().scrollX(), frameView().y() - frameView().scrollY())); ClipRecorder recorder(context, *frameView().layoutView(), DisplayItem::ClipFrameToVisibleContentRect, LayoutRect(frameView().visibleContentRect())); documentDirtyRect.moveBy(-frameView().location() + frameView().scrollPosition()); paintContents(context, globalPaintFlags, documentDirtyRect); } if (shouldPaintScrollbars) { IntRect scrollViewDirtyRect = rect.m_rect; IntRect visibleAreaWithScrollbars(frameView().location(), frameView().visibleContentRect(IncludeScrollbars).size()); scrollViewDirtyRect.intersect(visibleAreaWithScrollbars); scrollViewDirtyRect.moveBy(-frameView().location()); Optional<ScopedPaintChunkProperties> scopedPaintChunkProperties; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) { if (TransformPaintPropertyNode* transform = m_frameView->preTranslation()) { PaintChunkProperties properties(context.paintController().currentPaintChunkProperties()); properties.transform = transform; scopedPaintChunkProperties.emplace(context.paintController(), properties); } } TransformRecorder transformRecorder(context, *frameView().layoutView(), AffineTransform::translation(frameView().x(), frameView().y())); ClipRecorder recorder(context, *frameView().layoutView(), DisplayItem::ClipFrameScrollbars, LayoutRect(IntPoint(), visibleAreaWithScrollbars.size())); paintScrollbars(context, scrollViewDirtyRect); } }
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(); }