void VideoPainter::paintReplaced(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    WebMediaPlayer* mediaPlayer = m_layoutVideo.mediaElement()->webMediaPlayer();
    bool displayingPoster = m_layoutVideo.videoElement()->shouldDisplayPosterImage();
    if (!displayingPoster && !mediaPlayer)
        return;

    LayoutRect rect(m_layoutVideo.videoBox());
    if (rect.isEmpty())
        return;
    rect.moveBy(paintOffset);

    GraphicsContext& context = paintInfo.context;
    LayoutRect contentRect = m_layoutVideo.contentBoxRect();
    contentRect.moveBy(paintOffset);

    Optional<ClipRecorder> clipRecorder;
    if (!contentRect.contains(rect)) {
        clipRecorder.emplace(context, m_layoutVideo, paintInfo.displayItemTypeForClipping(), contentRect);
    }

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(context, m_layoutVideo, paintInfo.phase, paintOffset))
        return;

    LayoutObjectDrawingRecorder drawingRecorder(context, m_layoutVideo, paintInfo.phase, contentRect, paintOffset);

    // Video frames are only painted in software for printing or capturing node images via web APIs.
    bool forceSoftwareVideoPaint = paintInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers;

    if (displayingPoster || !forceSoftwareVideoPaint) {
        // This will display the poster image, if one is present, and otherwise paint nothing.
        ImagePainter(m_layoutVideo).paintIntoRect(context, rect);
    } else {
        SkPaint videoPaint = context.fillPaint();
        videoPaint.setColor(SK_ColorBLACK);
        m_layoutVideo.videoElement()->paintCurrentFrame(context.canvas(), pixelSnappedIntRect(rect), &videoPaint);
    }
}
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 InlineFlowBoxPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (m_inlineFlowBox.lineLayoutItem().style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
        return;

    LayoutRect frameRect = frameRectClampedToLineTopAndBottomIfNeeded();

    // Move x/y to our coordinates.
    LayoutRect localRect(frameRect);
    m_inlineFlowBox.flipForWritingMode(localRect);
    LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();

    const NinePieceImage& maskNinePieceImage = m_inlineFlowBox.lineLayoutItem().style()->maskBoxImage();
    StyleImage* maskBoxImage = m_inlineFlowBox.lineLayoutItem().style()->maskBoxImage().image();

    // Figure out if we need to push a transparency layer to render our mask.
    bool pushTransparencyLayer = false;
    bool compositedMask = m_inlineFlowBox.lineLayoutItem().hasLayer() && m_inlineFlowBox.boxModelObject().layer()->hasCompositedMask();
    bool flattenCompositingLayers = paintInfo.globalPaintFlags() & GlobalPaintFlattenCompositingLayers;
    SkXfermode::Mode compositeOp = SkXfermode::kSrcOver_Mode;
    if (!compositedMask || flattenCompositingLayers) {
        if ((maskBoxImage && m_inlineFlowBox.lineLayoutItem().style()->maskLayers().hasImage()) || m_inlineFlowBox.lineLayoutItem().style()->maskLayers().next()) {
            pushTransparencyLayer = true;
            paintInfo.context.beginLayer(1.0f, SkXfermode::kDstIn_Mode);
        } else {
            // TODO(fmalita): passing a dst-in xfer mode down to paintFillLayers/paintNinePieceImage
            //   seems dangerous: it is only correct if applied atomically (single draw call). While
            //   the heuristic above presumably ensures that is the case, this approach seems super
            //   fragile. We should investigate dropping this optimization in favour of the more
            //   robust layer branch above.
            compositeOp = SkXfermode::kDstIn_Mode;
        }
    }

    LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size());
    paintFillLayers(paintInfo, Color::transparent, m_inlineFlowBox.lineLayoutItem().style()->maskLayers(), paintRect, compositeOp);

    bool hasBoxImage = maskBoxImage && maskBoxImage->canRender();
    if (!hasBoxImage || !maskBoxImage->isLoaded()) {
        if (pushTransparencyLayer)
            paintInfo.context.endLayer();
        return; // Don't paint anything while we wait for the image to load.
    }

    LayoutBoxModelObject* boxModel = toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject()));
    // The simple case is where we are the only box for this object. In those
    // cases only a single call to draw is required.
    if (!m_inlineFlowBox.prevLineBox() && !m_inlineFlowBox.nextLineBox()) {
        BoxPainter::paintNinePieceImage(*boxModel, paintInfo.context, paintRect, m_inlineFlowBox.lineLayoutItem().styleRef(), maskNinePieceImage, compositeOp);
    } else {
        // We have a mask image that spans multiple lines.
        // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
        // but it isn't even clear how this should work at all.
        LayoutRect imageStripPaintRect = paintRectForImageStrip(adjustedPaintOffset, frameRect.size(), LTR);
        FloatRect clipRect(clipRectForNinePieceImageStrip(m_inlineFlowBox, maskNinePieceImage, paintRect));
        GraphicsContextStateSaver stateSaver(paintInfo.context);
        // TODO(chrishtr): this should be pixel-snapped.
        paintInfo.context.clip(clipRect);
        BoxPainter::paintNinePieceImage(*boxModel, paintInfo.context, imageStripPaintRect, m_inlineFlowBox.lineLayoutItem().styleRef(), maskNinePieceImage, compositeOp);
    }

    if (pushTransparencyLayer)
        paintInfo.context.endLayer();
}