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();
}
Exemple #3
0
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);
    }
}
Exemple #10
0
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());
    }
}
Exemple #11
0
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();
}