void TableSectionPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutRect localPaintInvalidationRect = LayoutRect(paintInfo.cullRect().m_rect);
    localPaintInvalidationRect.moveBy(-paintOffset);

    LayoutRect tableAlignedRect = m_layoutTableSection.logicalRectForWritingModeAndDirection(localPaintInvalidationRect);

    CellSpan dirtiedRows = m_layoutTableSection.dirtiedRows(tableAlignedRect);
    CellSpan dirtiedColumns = m_layoutTableSection.dirtiedColumns(tableAlignedRect);

    if (dirtiedColumns.start() >= dirtiedColumns.end())
        return;

    const HashSet<LayoutTableCell*>& overflowingCells = m_layoutTableSection.overflowingCells();
    if (!m_layoutTableSection.hasMultipleCellLevels() && !overflowingCells.size()) {
        // Draw the dirty cells in the order that they appear.
        for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
            const LayoutTableRow* row = m_layoutTableSection.rowLayoutObjectAt(r);
            if (row && !row->hasSelfPaintingLayer())
                TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset);
            for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
                const LayoutTableSection::CellStruct& current = m_layoutTableSection.cellAt(r, c);
                const LayoutTableCell* cell = current.primaryCell();
                if (!cell || (r > dirtiedRows.start() && m_layoutTableSection.primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && m_layoutTableSection.primaryCellAt(r, c - 1) == cell))
                    continue;
                paintCell(*cell, paintInfo, paintOffset);
            }
        }
    } else {
        // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
#if ENABLE(ASSERT)
        unsigned totalRows = m_layoutTableSection.numRows();
        unsigned totalCols = m_layoutTableSection.table()->columns().size();
        ASSERT(overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
#endif

        // To make sure we properly paint invalidate the section, we paint invalidated all the overflowing cells that we collected.
        Vector<LayoutTableCell*> cells;
        copyToVector(overflowingCells, cells);

        HashSet<LayoutTableCell*> spanningCells;

        for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
            const LayoutTableRow* row = m_layoutTableSection.rowLayoutObjectAt(r);
            if (row && !row->hasSelfPaintingLayer())
                TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset);
            for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
                const LayoutTableSection::CellStruct& current = m_layoutTableSection.cellAt(r, c);
                if (!current.hasCells())
                    continue;
                for (unsigned i = 0; i < current.cells.size(); ++i) {
                    if (overflowingCells.contains(current.cells[i]))
                        continue;

                    if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
                        if (!spanningCells.add(current.cells[i]).isNewEntry)
                            continue;
                    }

                    cells.append(current.cells[i]);
                }
            }
        }

        // Sort the dirty cells by paint order.
        if (!overflowingCells.size())
            std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
        else
            std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);

        for (unsigned i = 0; i < cells.size(); ++i)
            paintCell(*cells[i], paintInfo, paintOffset);
    }
}
void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    // An empty viewport disables rendering.
    if (borderBoxRect().isEmpty())
        return;

    // Don't paint, if the context explicitly disabled it.
    if (paintInfo.context().paintingDisabled())
        return;

    // SVG outlines are painted during PaintPhaseForeground.
    if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline)
        return;

    // An empty viewBox also disables rendering.
    // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute)
    if (svgSVGElement().hasEmptyViewBox())
        return;

    Page* page = frame().page();

    // Don't paint if we don't have kids, except if we have filters we should paint those.
    if (!firstChild()) {
        auto* resources = SVGResourcesCache::cachedResourcesForRenderer(*this);
        if (!resources || !resources->filter()) {
            if (page && paintInfo.phase == PaintPhaseForeground)
                page->addRelevantUnpaintedObject(this, visualOverflowRect());
            return;
        }
    }

    if (page && paintInfo.phase == PaintPhaseForeground)
        page->addRelevantRepaintedObject(this, visualOverflowRect());

    // Make a copy of the PaintInfo because applyTransform will modify the damage rect.
    PaintInfo childPaintInfo(paintInfo);
    childPaintInfo.context().save();

    // Apply initial viewport clip
    if (shouldApplyViewportClip())
        childPaintInfo.context().clip(snappedIntRect(overflowClipRect(paintOffset, currentRenderNamedFlowFragment())));

    // Convert from container offsets (html renderers) to a relative transform (svg renderers).
    // Transform from our paint container's coordinate system to our local coords.
    IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
    childPaintInfo.applyTransform(AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * localToBorderBoxTransform());

    // SVGRenderingContext must be destroyed before we restore the childPaintInfo.context(), because a filter may have
    // changed the context and it is only reverted when the SVGRenderingContext destructor finishes applying the filter.
    {
        SVGRenderingContext renderingContext;
        bool continueRendering = true;
        if (childPaintInfo.phase == PaintPhaseForeground) {
            renderingContext.prepareToRenderSVGContent(*this, childPaintInfo);
            continueRendering = renderingContext.isRenderingPrepared();
        }

        if (continueRendering) {
            childPaintInfo.updateSubtreePaintRootForChildren(this);
            for (auto& child : childrenOfType<RenderElement>(*this))
                child.paint(childPaintInfo, location());
        }
    }

    childPaintInfo.context().restore();
}
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 {
                // 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());
    }
}
Example #4
0
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::kDocumentBackground))
    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.isInMainFrame();
  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::kDocumentBackground, backgroundRect);

  // 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, SkBlendMode::kSrc);
    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.getGlobalPaintFlags()));

    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 ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
    } else if (shouldClearCanvas) {
      context.fillRect(backgroundRect, Color(), SkBlendMode::kClear);
    }
    return;
  }

  BoxPainter::FillLayerOcclusionOutputList reversedPaintList;
  bool shouldDrawBackgroundInSeparateBuffer =
      BoxPainter(m_layoutView)
          .calculateFillLayerOcclusionCulling(
              reversedPaintList, m_layoutView.style()->backgroundLayers());
  DCHECK(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 ? SkBlendMode::kSrc : SkBlendMode::kSrcOver);
    }
    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)
                         ? SkBlendMode::kSrc
                         : SkBlendMode::kSrcOver);
  } else if (shouldClearCanvas && !shouldDrawBackgroundInSeparateBuffer) {
    context.fillRect(backgroundRect, Color(), SkBlendMode::kClear);
  }

  for (auto it = reversedPaintList.rbegin(); it != reversedPaintList.rend();
       ++it) {
    DCHECK((*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();
}
Example #5
0
void BlockPainter::paintContents(const PaintInfo& paintInfo,
                                 const LayoutPoint& paintOffset) {
  DCHECK(!m_layoutBlock.childrenInline());
  PaintInfo paintInfoForDescendants = paintInfo.forDescendants();
  m_layoutBlock.paintChildren(paintInfoForDescendants, paintOffset);
}
void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);

    if (!shouldPaint(paintInfo, paintOffset))
        return;

    LayoutPoint adjustedPaintOffset = paintOffset + location();

    if (hasBoxDecorationBackground() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection))
        paintBoxDecorationBackground(paintInfo, adjustedPaintOffset);

    if (paintInfo.phase == PaintPhaseMask) {
        paintMask(paintInfo, adjustedPaintOffset);
        return;
    }

    if (paintInfo.phase == PaintPhaseClippingMask && (!hasLayer() || !layer()->hasCompositedClippingMask()))
        return;

    LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size());
    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
        paintOutline(paintInfo, paintRect);

    if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren() && paintInfo.phase != PaintPhaseClippingMask)
        return;

    if (!paintInfo.shouldPaintWithinRoot(this))
        return;

    bool drawSelectionTint = selectionState() != SelectionNone && !document().printing();
    if (paintInfo.phase == PaintPhaseSelection) {
        if (selectionState() == SelectionNone)
            return;
        drawSelectionTint = false;
    }

    bool completelyClippedOut = false;
    if (style()->hasBorderRadius()) {
        LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());

        if (borderRect.isEmpty())
            completelyClippedOut = true;
        else {
            // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
            paintInfo.context->save();
            RoundedRect roundedInnerRect = style()->getRoundedInnerBorderFor(paintRect,
                paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true);
            clipRoundedInnerRect(paintInfo.context, paintRect, roundedInnerRect);
        }
    }

    if (!completelyClippedOut) {
        if (paintInfo.phase == PaintPhaseClippingMask) {
            paintClippingMask(paintInfo, adjustedPaintOffset);
        } else {
            paintReplaced(paintInfo, adjustedPaintOffset);
        }

        if (style()->hasBorderRadius())
            paintInfo.context->restore();
    }

    // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    // surrounding content.
    if (drawSelectionTint) {
        LayoutRect selectionPaintingRect = localSelectionRect();
        selectionPaintingRect.moveBy(adjustedPaintOffset);
        paintInfo.context->fillRect(pixelSnappedIntRect(selectionPaintingRect), selectionBackgroundColor());
    }
}
void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!shouldPaint(paintInfo, paintOffset))
        return;

#ifndef NDEBUG
    SetLayoutNeededForbiddenScope scope(this);
#endif
    LayoutPoint adjustedPaintOffset = paintOffset + location();
    
    if (hasBoxDecorations() && paintInfo.phase == PaintPhaseForeground)
        paintBoxDecorations(paintInfo, adjustedPaintOffset);
    
    if (paintInfo.phase == PaintPhaseMask) {
        paintMask(paintInfo, adjustedPaintOffset);
        return;
    }

    LayoutRect paintRect = LayoutRect(adjustedPaintOffset, size());
    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style().outlineWidth())
        paintOutline(paintInfo, paintRect);
    
    if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && !canHaveChildren())
        return;
    
    if (!paintInfo.shouldPaintWithinRoot(*this))
        return;
    
    bool drawSelectionTint = shouldDrawSelectionTint();
    if (paintInfo.phase == PaintPhaseSelection) {
        if (selectionState() == SelectionNone)
            return;
        drawSelectionTint = false;
    }

    bool completelyClippedOut = false;
    if (style().hasBorderRadius()) {
        LayoutRect borderRect = LayoutRect(adjustedPaintOffset, size());

        if (borderRect.isEmpty())
            completelyClippedOut = true;
        else {
            // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
            paintInfo.context().save();
            FloatRoundedRect roundedInnerRect = FloatRoundedRect(style().getRoundedInnerBorderFor(paintRect,
                paddingTop() + borderTop(), paddingBottom() + borderBottom(), paddingLeft() + borderLeft(), paddingRight() + borderRight(), true, true));
            clipRoundedInnerRect(paintInfo.context(), paintRect, roundedInnerRect);
        }
    }

    if (!completelyClippedOut) {
        paintReplaced(paintInfo, adjustedPaintOffset);

        if (style().hasBorderRadius())
            paintInfo.context().restore();
    }
        
    // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    // surrounding content.
    if (drawSelectionTint) {
        LayoutRect selectionPaintingRect = localSelectionRect();
        selectionPaintingRect.moveBy(adjustedPaintOffset);
        paintInfo.context().fillRect(snappedIntRect(selectionPaintingRect), selectionBackgroundColor(), style().colorSpace());
    }
}
Example #8
0
void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty)
{
    if (!shouldPaint(paintInfo, tx, ty))
        return;
    
    tx += x();
    ty += y();
    
    if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 
        paintBoxDecorations(paintInfo, tx, ty);
    
    if (paintInfo.phase == PaintPhaseMask) {
        paintMask(paintInfo, tx, ty);
        return;
    }

    if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth())
        paintOutline(paintInfo.context, tx, ty, width(), height());
    
    if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)
        return;
    
    if (!paintInfo.shouldPaintWithinRoot(this))
        return;
    
    bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing();

// On the device our selection background is opaque white,
// which means images get turned white when they're selected.
// Just draw them as if they weren't selected.
#if OS(LINUX) && !(CPU(X86) || CPU(X86_64))
    drawSelectionTint = false;
#endif

    if (paintInfo.phase == PaintPhaseSelection) {
        if (selectionState() == SelectionNone)
            return;
        drawSelectionTint = false;
    }

    bool completelyClippedOut = false;
    if (style()->hasBorderRadius()) {
        IntRect borderRect = IntRect(tx, ty, width(), height());

        if (borderRect.isEmpty())
            completelyClippedOut = true;
        else {
            // Push a clip if we have a border radius, since we want to round the foreground content that gets painted.
            paintInfo.context->save();
            paintInfo.context->addRoundedRectClip(style()->getRoundedBorderFor(borderRect));
        }
    }

    if (!completelyClippedOut) {
        paintReplaced(paintInfo, tx, ty);

        if (style()->hasBorderRadius())
            paintInfo.context->restore();
    }
        
    // The selection tint never gets clipped by border-radius rounding, since we want it to run right up to the edges of
    // surrounding content.
    if (drawSelectionTint) {
        IntRect selectionPaintingRect = localSelectionRect();
        selectionPaintingRect.move(tx, ty);
        paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor(), style()->colorSpace());
    }
}
Example #9
0
void RenderMathMLMenclose::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
    RenderMathMLRow::paint(info, paintOffset);

    if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground || style().visibility() != VISIBLE)
        return;

    LayoutUnit thickness = ruleThickness();

    // Make a copy of the PaintInfo because applyTransform will modify its rect.
    PaintInfo paintInfo(info);
    GraphicsContextStateSaver stateSaver(paintInfo.context());

    paintInfo.context().setStrokeThickness(thickness);
    paintInfo.context().setStrokeStyle(SolidStroke);
    paintInfo.context().setStrokeColor(style().visitedDependentColor(CSSPropertyColor));
    paintInfo.context().setFillColor(Color::transparent);
    paintInfo.applyTransform(AffineTransform().translate(paintOffset + location()));

    // In the MathML in HTML5 implementation note, the "left" notation is described as follows:
    // - center of the left vertical bar is at 3\xi_8 padding + \xi_8 border/2 = 7\xi_8/2
    // - top space is Overbar Vertical Gap + Overbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8
    // - bottom space is Underbar Vertical Gap + Underbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8
    if (hasNotation(MathMLMencloseElement::Left)) {
        LayoutUnit x = m_contentRect.x() - 7 * thickness / 2;
        LayoutUnit yStart = m_contentRect.y() - 4 * thickness;
        LayoutUnit yEnd = m_contentRect.maxY() + 4 * thickness;
        drawLine(info, x, yStart, x, yEnd);
    }

    // In the MathML in HTML5 implementation note, the "right" notation is described as follows:
    // - center of the right vertical bar is at 3\xi_8 padding + \xi_8 border/2 = 7\xi_8/2
    // - top space is Overbar Vertical Gap + Overbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8
    // - bottom space is Underbar Vertical Gap + Underbar Rule Thickness = 3\xi_8 + \xi_8 = 4\xi_8
    if (hasNotation(MathMLMencloseElement::Right)) {
        LayoutUnit x = m_contentRect.maxX() + 7 * thickness / 2;
        LayoutUnit yStart = m_contentRect.y() - 4 * thickness;
        LayoutUnit yEnd = m_contentRect.maxY() + 4 * thickness;
        drawLine(info, x, yStart, x, yEnd);
    }

    // In the MathML in HTML5 implementation note, the "vertical" notation is horizontally centered.
    if (hasNotation(MathMLMencloseElement::VerticalStrike)) {
        LayoutUnit x = m_contentRect.x() + (m_contentRect.width() - thickness) / 2;
        LayoutUnit yStart = m_contentRect.y();
        LayoutUnit yEnd = m_contentRect.maxY();
        drawLine(info, x, yStart, x, yEnd);
    }

    // In the MathML in HTML5 implementation note, the "top" notation is described as follows:
    // - middle of the top horizontal bar is at Vertical Gap + Rule Thickness / 2 = 7\xi_8/2
    // - left and right spaces have size 4\xi_8
    if (hasNotation(MathMLMencloseElement::Top)) {
        LayoutUnit y = m_contentRect.y() - 7 * thickness / 2;
        LayoutUnit xStart = m_contentRect.x() - 4 * thickness;
        LayoutUnit xEnd = m_contentRect.maxX() + 4 * thickness;
        drawLine(info, xStart, y, xEnd, y);
    }

    // In the MathML in HTML5 implementation note, the "bottom" notation is described as follows:
    // - middle of the bottom horizontal bar is at Vertical Gap + Rule Thickness / 2 = 7\xi_8/2
    // - left and right spaces have size 4\xi_8
    if (hasNotation(MathMLMencloseElement::Bottom)) {
        LayoutUnit y = m_contentRect.maxY() + 7 * thickness / 2;
        LayoutUnit xStart = m_contentRect.x() - 4 * thickness;
        LayoutUnit xEnd = m_contentRect.maxX() + 4 * thickness;
        drawLine(info, xStart, y, xEnd, y);
    }

    // In the MathML in HTML5 implementation note, the "vertical" notation is vertically centered.
    if (hasNotation(MathMLMencloseElement::HorizontalStrike)) {
        LayoutUnit y = m_contentRect.y() + (m_contentRect.height() - thickness) / 2;
        LayoutUnit xStart = m_contentRect.x();
        LayoutUnit xEnd = m_contentRect.maxX();
        drawLine(info, xStart, y, xEnd, y);
    }

    // In the MathML in HTML5 implementation note, the "updiagonalstrike" goes from the bottom left corner
    // to the top right corner.
    if (hasNotation(MathMLMencloseElement::UpDiagonalStrike))
        drawLine(info, m_contentRect.x(), m_contentRect.maxY(), m_contentRect.maxX(), m_contentRect.y());

    // In the MathML in HTML5 implementation note, the "downdiagonalstrike" goes from the top left corner
    // to the bottom right corner.
    if (hasNotation(MathMLMencloseElement::DownDiagonalStrike))
        drawLine(info, m_contentRect.x(), m_contentRect.y(), m_contentRect.maxX(), m_contentRect.maxY());

    // In the MathML in HTML5 implementation note, the "roundedbox" has radii size 3\xi_8 and is obtained
    // by inflating the content box by 3\xi_8 + \xi_8/2 = 7\xi_8/2
    if (hasNotation(MathMLMencloseElement::RoundedBox)) {
        LayoutSize radiiSize(3 * thickness, 3 * thickness);
        RoundedRect::Radii radii(radiiSize, radiiSize, radiiSize, radiiSize);
        RoundedRect roundedRect(m_contentRect, radii);
        roundedRect.inflate(7 * thickness / 2);
        Path path;
        path.addRoundedRect(roundedRect);
        paintInfo.context().strokePath(path);
    }

    // For longdiv, we use our own rules for now:
    // - top space is like "top" notation
    // - bottom space is like "bottom" notation
    // - right space is like "right" notation
    // - left space is longDivLeftSpace * \xi_8
    // - We subtract half of the thickness from these spaces to obtain "top", "bottom", "left"
    //   and "right" coordinates.
    // - The top bar is drawn from "right" to "left" and positioned at vertical offset "top".
    // - The left part is draw as a quadratic Bezier curve with end points going from "top" to
    //   "bottom" and positioned at horizontal offset "left".
    // - In order to force the curvature of the left part, we use a middle point that is vertically
    //   centered and shifted towards the right by longDivLeftSpace * \xi_8
    if (hasNotation(MathMLMencloseElement::LongDiv)) {
        LayoutUnit top = m_contentRect.y() - 7 * thickness / 2;
        LayoutUnit bottom = m_contentRect.maxY() + 7 * thickness / 2;
        LayoutUnit left = m_contentRect.x() - longDivLeftSpace * thickness + thickness / 2;
        LayoutUnit right = m_contentRect.maxX() + 4 * thickness;
        LayoutUnit midX = left + longDivLeftSpace * thickness;
        LayoutUnit midY = (top + bottom) / 2;
        Path path;
        path.moveTo(LayoutPoint(right, top));
        path.addLineTo(LayoutPoint(left, top));
        path.addQuadCurveTo(LayoutPoint(midX, midY), FloatPoint(left, bottom));
        paintInfo.context().strokePath(path);
    }

    // In the MathML in HTML5 implementation note, the "circle" notation is described as follows:
    // - The center and axes are the same as the content bounding box.
    // - The width of the bounding box is \xi_8/2 + contentWidth * \sqrt{2} + \xi_8/2
    // - The height is \xi_8/2 + contentHeight * \sqrt{2} + \xi_8/2
    if (hasNotation(MathMLMencloseElement::Circle)) {
        LayoutRect ellipseRect;
        ellipseRect.setWidth(m_contentRect.width() * sqrtOfTwoFloat + thickness);
        ellipseRect.setHeight(m_contentRect.height() * sqrtOfTwoFloat + thickness);
        ellipseRect.setX(m_contentRect.x() - (ellipseRect.width() - m_contentRect.width()) / 2);
        ellipseRect.setY(m_contentRect.y() - (ellipseRect.height() - m_contentRect.height()) / 2);
        Path path;
        path.addEllipse(ellipseRect);
        paintInfo.context().strokePath(path);
    }
}
void RenderMathMLBlock::paint(PaintInfo& info, const LayoutPoint& paintOffset)
{
    RenderBlock::paint(info, paintOffset);

    if (info.context().paintingDisabled() || info.phase != PaintPhaseForeground)
        return;

    IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset + location());

    GraphicsContextStateSaver stateSaver(info.context());

    info.context().setStrokeThickness(1.0f);
    info.context().setStrokeStyle(SolidStroke);
    info.context().setStrokeColor(Color(0, 0, 255));

    info.context().drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y()));
    info.context().drawLine(IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y()), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));
    info.context().drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));
    info.context().drawLine(adjustedPaintOffset, IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + pixelSnappedOffsetHeight()));

    int topStart = paddingTop();

    info.context().setStrokeColor(Color(0, 255, 0));

    info.context().drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + topStart), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + topStart));

    int baseline = roundToInt(baselinePosition(AlphabeticBaseline, true, HorizontalLine));

    info.context().setStrokeColor(Color(255, 0, 0));

    info.context().drawLine(IntPoint(adjustedPaintOffset.x(), adjustedPaintOffset.y() + baseline), IntPoint(adjustedPaintOffset.x() + pixelSnappedOffsetWidth(), adjustedPaintOffset.y() + baseline));
}
void RootInlineBoxPainter::paintEllipsisBox(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) const
{
    if (m_rootInlineBox.hasEllipsisBox() && paintInfo.shouldPaintWithinRoot(&m_rootInlineBox.layoutObject()) && m_rootInlineBox.lineLayoutItem().style()->visibility() == VISIBLE
        && paintInfo.phase == PaintPhaseForeground)
        m_rootInlineBox.ellipsisBox()->paint(paintInfo, paintOffset, lineTop, lineBottom);
}
void TableCellPainter::paintCollapsedBorders(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    ASSERT(paintInfo.phase == PaintPhaseCollapsedTableBorders);

    if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell) || m_layoutTableCell.style()->visibility() != VISIBLE)
        return;

    LayoutRect paintRect = paintBounds(paintOffset, AddOffsetFromParent);

    LayoutRect drawingCullRect(paintRect);
    drawingCullRect.expandEdges(m_layoutTableCell.table()->outerBorderTop(), m_layoutTableCell.table()->outerBorderRight(),
        m_layoutTableCell.table()->outerBorderBottom(), m_layoutTableCell.table()->outerBorderLeft());
    if (drawingCullRect.y() >= paintInfo.rect.maxY())
        return;

    if (drawingCullRect.maxY() <= paintInfo.rect.y())
        return;

    const CollapsedBorderValue* tableCurrentBorderValue = m_layoutTableCell.table()->currentBorderValue();
    if (!tableCurrentBorderValue)
        return;

    const ComputedStyle& styleForCellFlow = m_layoutTableCell.styleForCellFlow();
    const CollapsedBorderValue& leftBorderValue = cachedCollapsedLeftBorder(styleForCellFlow);
    const CollapsedBorderValue& rightBorderValue = cachedCollapsedRightBorder(styleForCellFlow);
    const CollapsedBorderValue& topBorderValue = cachedCollapsedTopBorder(styleForCellFlow);
    const CollapsedBorderValue& bottomBorderValue = cachedCollapsedBottomBorder(styleForCellFlow);

    bool shouldPaintTop = topBorderValue.shouldPaint(*tableCurrentBorderValue);
    bool shouldPaintBottom = bottomBorderValue.shouldPaint(*tableCurrentBorderValue);
    bool shouldPaintLeft = leftBorderValue.shouldPaint(*tableCurrentBorderValue);
    bool shouldPaintRight = rightBorderValue.shouldPaint(*tableCurrentBorderValue);

    if (!shouldPaintTop && !shouldPaintBottom && !shouldPaintLeft && !shouldPaintRight)
        return;

    GraphicsContext* graphicsContext = paintInfo.context;
    LayoutObjectDrawingRecorder recorder(*graphicsContext, m_layoutTableCell, paintInfo.phase, drawingCullRect);
    if (recorder.canUseCachedDrawing())
        return;

    bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext);

    // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
    int topWidth = topBorderValue.width();
    int bottomWidth = bottomBorderValue.width();
    int leftWidth = leftBorderValue.width();
    int rightWidth = rightBorderValue.width();

    IntRect borderRect = pixelSnappedIntRect(paintRect.x() - leftWidth / 2,
        paintRect.y() - topWidth / 2,
        paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2,
        paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2);

    Color cellColor = m_layoutTableCell.resolveColor(CSSPropertyColor);

    // We never paint diagonals at the joins.  We simply let the border with the highest
    // precedence paint on top of borders with lower precedence.
    if (shouldPaintTop) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, BSTop,
            topBorderValue.color().resolve(cellColor), collapsedBorderStyle(topBorderValue.style()), 0, 0, antialias);
    }
    if (shouldPaintBottom) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), BSBottom,
            bottomBorderValue.color().resolve(cellColor), collapsedBorderStyle(bottomBorderValue.style()), 0, 0, antialias);
    }
    if (shouldPaintLeft) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), BSLeft,
            leftBorderValue.color().resolve(cellColor), collapsedBorderStyle(leftBorderValue.style()), 0, 0, antialias);
    }
    if (shouldPaintRight) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), BSRight,
            rightBorderValue.color().resolve(cellColor), collapsedBorderStyle(rightBorderValue.style()), 0, 0, antialias);
    }
}
void InlineFlowBoxPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!paintInfo.shouldPaintWithinRoot(&m_inlineFlowBox.layoutObject()) || m_inlineFlowBox.layoutObject().style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
        return;

    LayoutRect frameRect = roundedFrameRectClampedToLineTopAndBottomIfNeeded();

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

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

    // Figure out if we need to push a transparency layer to render our mask.
    bool pushTransparencyLayer = false;
    bool compositedMask = m_inlineFlowBox.layoutObject().hasLayer() && m_inlineFlowBox.boxModelObject()->layer()->hasCompositedMask();
    bool flattenCompositingLayers = m_inlineFlowBox.layoutObject().view()->frameView() && m_inlineFlowBox.layoutObject().view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers;
    SkXfermode::Mode compositeOp = SkXfermode::kSrcOver_Mode;
    if (!compositedMask || flattenCompositingLayers) {
        if ((maskBoxImage && m_inlineFlowBox.layoutObject().style()->maskLayers().hasImage()) || m_inlineFlowBox.layoutObject().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.layoutObject().style()->maskLayers(), paintRect, compositeOp);

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

    // 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(*m_inlineFlowBox.boxModelObject(), paintInfo.context, paintRect, m_inlineFlowBox.layoutObject().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);
        LayoutRect clipRect = clipRectForNinePieceImageStrip(&m_inlineFlowBox, maskNinePieceImage, paintRect);
        GraphicsContextStateSaver stateSaver(*paintInfo.context);
        paintInfo.context->clip(clipRect);
        BoxPainter::paintNinePieceImage(*m_inlineFlowBox.boxModelObject(), paintInfo.context, imageStripPaintRect, m_inlineFlowBox.layoutObject().styleRef(), maskNinePieceImage, compositeOp);
    }

    if (pushTransparencyLayer)
        paintInfo.context->endLayer();
}
void InlineFlowBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutUnit lineTop, const LayoutUnit lineBottom)
{
    LayoutRect overflowRect(m_inlineFlowBox.visualOverflowRect(lineTop, lineBottom));
    m_inlineFlowBox.flipForWritingMode(overflowRect);
    overflowRect.moveBy(paintOffset);

    if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
        return;

    if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
        // Add ourselves to the paint info struct's list of inlines that need to paint their
        // outlines.
        if (m_inlineFlowBox.layoutObject().style()->visibility() == VISIBLE && m_inlineFlowBox.layoutObject().style()->hasOutline() && !m_inlineFlowBox.isRootInlineBox()) {
            LayoutInline& inlineFlow = toLayoutInline(m_inlineFlowBox.layoutObject());

            LayoutBlock* cb = 0;
            bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation();
            if (containingBlockPaintsContinuationOutline) {
                // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
                // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
                // anonymous blocks. In this case, it is better to bail out and paint it ourself.
                LayoutBlock* enclosingAnonymousBlock = m_inlineFlowBox.layoutObject().containingBlock();
                if (!enclosingAnonymousBlock->isAnonymousBlock()) {
                    containingBlockPaintsContinuationOutline = false;
                } else {
                    cb = enclosingAnonymousBlock->containingBlock();
                    for (LayoutBoxModelObject* box = m_inlineFlowBox.boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
                        if (box->hasSelfPaintingLayer()) {
                            containingBlockPaintsContinuationOutline = false;
                            break;
                        }
                    }
                }
            }

            if (containingBlockPaintsContinuationOutline) {
                // Add ourselves to the containing block of the entire continuation so that it can
                // paint us atomically.
                cb->addContinuationWithOutline(toLayoutInline(m_inlineFlowBox.layoutObject().node()->layoutObject()));
            } else if (!inlineFlow.isInlineElementContinuation()) {
                paintInfo.outlineObjects()->add(&inlineFlow);
            }
        }
    } else if (paintInfo.phase == PaintPhaseMask) {
        DrawingRecorder recorder(*paintInfo.context, m_inlineFlowBox, DisplayItem::paintPhaseToDrawingType(paintInfo.phase), pixelSnappedIntRect(overflowRect));
        if (!recorder.canUseCachedDrawing())
            paintMask(paintInfo, paintOffset);
        return;
    } else if (paintInfo.phase == PaintPhaseForeground) {
        // Paint our background, border and box-shadow.
        paintBoxDecorationBackground(paintInfo, paintOffset);
    }

    // Paint our children.
    if (paintInfo.phase != PaintPhaseSelfOutline) {
        PaintInfo childInfo(paintInfo);
        childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;

        if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&m_inlineFlowBox.layoutObject()))
            childInfo.paintingRoot = 0;
        else
            childInfo.updatePaintingRootForChildren(&m_inlineFlowBox.layoutObject());

        for (InlineBox* curr = m_inlineFlowBox.firstChild(); curr; curr = curr->nextOnLine()) {
            if (curr->layoutObject().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
                curr->paint(childInfo, paintOffset, lineTop, lineBottom);
        }
    }
}
Example #15
0
void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint&, LayoutUnit, LayoutUnit)
{
    ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
    ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
    ASSERT(truncation() == cNoTruncation);

    if (renderer()->style()->visibility() != VISIBLE)
        return;

    // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox.
    // If we ever need that for SVG, it's very easy to refactor and reuse the code.

    RenderObject* parentRenderer = parent()->renderer();
    ASSERT(parentRenderer);

    bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
    bool hasSelection = !parentRenderer->document()->printing() && selectionState() != RenderObject::SelectionNone;
    if (!hasSelection && paintSelectedTextOnly)
        return;

    RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    ASSERT(textRenderer);
    if (!textShouldBePainted(textRenderer))
        return;

    RenderStyle* style = parentRenderer->style();
    ASSERT(style);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    ASSERT(svgStyle);

    bool hasFill = svgStyle->hasFill();
    bool hasStroke = svgStyle->hasStroke();

    RenderStyle* selectionStyle = style;
    if (hasSelection) {
        selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
        if (selectionStyle) {
            const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
            ASSERT(svgSelectionStyle);

            if (!hasFill)
                hasFill = svgSelectionStyle->hasFill();
            if (!hasStroke)
                hasStroke = svgSelectionStyle->hasStroke();
        } else
            selectionStyle = style;
    }

    AffineTransform fragmentTransform;
    unsigned textFragmentsSize = m_textFragments.size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        SVGTextFragment& fragment = m_textFragments.at(i);
        ASSERT(!m_paintingResource);

        GraphicsContextStateSaver stateSaver(*paintInfo.context);
        fragment.buildFragmentTransform(fragmentTransform);
        if (!fragmentTransform.isIdentity())
            paintInfo.context->concatCTM(fragmentTransform);

        // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations.
        int decorations = style->textDecorationsInEffect();
        if (decorations & UNDERLINE)
            paintDecoration(paintInfo.context, UNDERLINE, fragment);
        if (decorations & OVERLINE)
            paintDecoration(paintInfo.context, OVERLINE, fragment);

        // Fill text
        if (hasFill) {
            m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode;
            paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
        }

        // Stroke text
        if (hasStroke) {
            m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode;
            paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
        }

        // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
        if (decorations & LINE_THROUGH)
            paintDecoration(paintInfo.context, LINE_THROUGH, fragment);

        m_paintingResourceMode = ApplyToDefaultMode;
    }

    ASSERT(!m_paintingResource);
}
Example #16
0
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
{
    GraphicsContext* context = paintInfo.context;
    RenderStyle* style = m_renderer->style(isFirstLineStyle());

    const Font& font = style->font();
    FloatPoint boxOrigin = locationIncludingFlipping();
    LayoutPoint adjustedPaintOffset = paintOffset;
    if (!isHorizontal())
        adjustedPaintOffset.move(0, -virtualLogicalHeight());
    boxOrigin.move(adjustedPaintOffset.x(), adjustedPaintOffset.y());
    FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight()));
    GraphicsContextStateSaver stateSaver(*context);
    if (!isHorizontal())
        context->concatCTM(InlineTextBox::rotation(boxRect, InlineTextBox::Clockwise));
    FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());

    Color styleTextColor = m_renderer->resolveColor(style, CSSPropertyWebkitTextFillColor);
    if (styleTextColor != context->fillColor())
        context->setFillColor(styleTextColor);

    if (selectionState() != RenderObject::SelectionNone) {
        paintSelection(context, boxOrigin, style, font);

        // Select the correct color for painting the text.
        Color foreground = paintInfo.forceBlackText() ? Color::black : renderer()->selectionForegroundColor();
        if (foreground.isValid() && foreground != styleTextColor)
            context->setFillColor(foreground);
    }

    // Text shadows are disabled when printing. http://crbug.com/258321
    const ShadowList* shadowList = context->printing() ? 0 : style->textShadow();
    bool hasShadow = shadowList;
    if (hasShadow) {
        DrawLooper drawLooper;
        for (size_t i = shadowList->shadows().size(); i--; ) {
            const ShadowData& shadow = shadowList->shadows()[i];
            int shadowX = isHorizontal() ? shadow.x() : shadow.y();
            int shadowY = isHorizontal() ? shadow.y() : -shadow.x();
            FloatSize offset(shadowX, shadowY);
            drawLooper.addShadow(offset, shadow.blur(), m_renderer->resolveColor(shadow.color()),
                DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowIgnoresAlpha);
        }
        drawLooper.addUnmodifiedContent();
        context->setDrawLooper(drawLooper);
    }

    // FIXME: Why is this always LTR? Fix by passing correct text run flags below.
    TextRun textRun = RenderBlockFlow::constructTextRun(renderer(), font, m_str, style, TextRun::AllowTrailingExpansion);
    TextRunPaintInfo textRunPaintInfo(textRun);
    textRunPaintInfo.bounds = boxRect;
    context->drawText(font, textRunPaintInfo, textOrigin);

    // Restore the regular fill color.
    if (styleTextColor != context->fillColor())
        context->setFillColor(styleTextColor);

    if (hasShadow)
        context->clearDrawLooper();

    paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style);
}
void TableCellPainter::paintCollapsedBorders(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const CollapsedBorderValue& currentBorderValue)
{
    if (m_layoutTableCell.style()->visibility() != VISIBLE)
        return;

    const ComputedStyle& styleForCellFlow = m_layoutTableCell.styleForCellFlow();
    const CollapsedBorderValue* leftBorderValue = cachedCollapsedLeftBorder(styleForCellFlow);
    const CollapsedBorderValue* rightBorderValue = cachedCollapsedRightBorder(styleForCellFlow);
    const CollapsedBorderValue* topBorderValue = cachedCollapsedTopBorder(styleForCellFlow);
    const CollapsedBorderValue* bottomBorderValue = cachedCollapsedBottomBorder(styleForCellFlow);

    int displayItemType = DisplayItem::TableCollapsedBorderBase;
    int topWidth = 0;
    int bottomWidth = 0;
    int leftWidth = 0;
    int rightWidth = 0;
    if (topBorderValue) {
        if (topBorderValue->shouldPaint(currentBorderValue))
            displayItemType |= DisplayItem::TableCollapsedBorderTop;
        topWidth = topBorderValue->width();
    }
    if (bottomBorderValue) {
        if (bottomBorderValue->shouldPaint(currentBorderValue))
            displayItemType |= DisplayItem::TableCollapsedBorderBottom;
        bottomWidth = bottomBorderValue->width();
    }
    if (leftBorderValue) {
        if (leftBorderValue->shouldPaint(currentBorderValue))
            displayItemType |= DisplayItem::TableCollapsedBorderLeft;
        leftWidth = leftBorderValue->width();
    }
    if (rightBorderValue) {
        if (rightBorderValue->shouldPaint(currentBorderValue))
            displayItemType |= DisplayItem::TableCollapsedBorderRight;
        rightWidth = rightBorderValue->width();
    }
    if (displayItemType == DisplayItem::TableCollapsedBorderBase)
        return;

    // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
    LayoutRect paintRect = paintBounds(paintOffset, AddOffsetFromParent);
    IntRect borderRect = pixelSnappedIntRect(paintRect.x() - leftWidth / 2,
        paintRect.y() - topWidth / 2,
        paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2,
        paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2);

    if (!paintInfo.cullRect().intersectsCullRect(borderRect))
        return;

    GraphicsContext& graphicsContext = paintInfo.context;
    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(graphicsContext, m_layoutTableCell, static_cast<DisplayItem::Type>(displayItemType)))
        return;

    LayoutObjectDrawingRecorder recorder(graphicsContext, m_layoutTableCell, static_cast<DisplayItem::Type>(displayItemType), borderRect);
    Color cellColor = m_layoutTableCell.resolveColor(CSSPropertyColor);

    // We never paint diagonals at the joins.  We simply let the border with the highest
    // precedence paint on top of borders with lower precedence.
    if (displayItemType & DisplayItem::TableCollapsedBorderTop) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, BSTop,
            topBorderValue->color().resolve(cellColor), collapsedBorderStyle(topBorderValue->style()), 0, 0, true);
    }
    if (displayItemType & DisplayItem::TableCollapsedBorderBottom) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), BSBottom,
            bottomBorderValue->color().resolve(cellColor), collapsedBorderStyle(bottomBorderValue->style()), 0, 0, true);
    }
    if (displayItemType & DisplayItem::TableCollapsedBorderLeft) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), BSLeft,
            leftBorderValue->color().resolve(cellColor), collapsedBorderStyle(leftBorderValue->style()), 0, 0, true);
    }
    if (displayItemType & DisplayItem::TableCollapsedBorderRight) {
        ObjectPainter::drawLineForBoxSide(graphicsContext, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), BSRight,
            rightBorderValue->color().resolve(cellColor), collapsedBorderStyle(rightBorderValue->style()), 0, 0, true);
    }
}
Example #18
0
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);
    }
}
Example #19
0
void RenderView::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint&)
{
    if (!paintInfo.shouldPaintWithinRoot(*this))
        return;

    // Check to see if we are enclosed by a layer that requires complex painting rules.  If so, we cannot blit
    // when scrolling, and we need to use slow repaints.  Examples of layers that require this are transparent layers,
    // layers with reflections, or transformed layers.
    // FIXME: This needs to be dynamic.  We should be able to go back to blitting if we ever stop being inside
    // a transform, transparency layer, etc.
    Element* elt;
    for (elt = document().ownerElement(); elt && elt->renderer(); elt = elt->document().ownerElement()) {
        RenderLayer* layer = elt->renderer()->enclosingLayer();
        if (layer->cannotBlitToWindow()) {
            frameView().setCannotBlitToWindow();
            break;
        }

#if USE(ACCELERATED_COMPOSITING)
        if (RenderLayer* compositingLayer = layer->enclosingCompositingLayerForRepaint()) {
            if (!compositingLayer->backing()->paintsIntoWindow()) {
                frameView().setCannotBlitToWindow();
                break;
            }
        }
#endif
    }

    if (document().ownerElement())
        return;

    if (paintInfo.skipRootBackground())
        return;

    bool rootFillsViewport = false;
    bool rootObscuresBackground = false;
    Element* documentElement = document().documentElement();
    if (RenderElement* rootRenderer = documentElement ? documentElement->renderer() : 0) {
        // The document element's renderer is currently forced to be a block, but may not always be.
        RenderBox* rootBox = rootRenderer->isBox() ? toRenderBox(rootRenderer) : 0;
        rootFillsViewport = rootBox && !rootBox->x() && !rootBox->y() && rootBox->width() >= width() && rootBox->height() >= height();
        rootObscuresBackground = rendererObscuresBackground(rootRenderer);
    }
    
    Page* page = document().page();
    float pageScaleFactor = page ? page->pageScaleFactor() : 1;

    // If painting will entirely fill the view, no need to fill the background.
    if (rootFillsViewport && rootObscuresBackground && pageScaleFactor >= 1)
        return;

    // This code typically only executes if the root element's visibility has been set to hidden,
    // if there is a transform on the <html>, or if there is a page scale factor less than 1.
    // Only fill with the base background color (typically white) if we're the root document, 
    // since iframes/frames with no background in the child document should show the parent's background.
    if (frameView().isTransparent()) // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent.
        frameView().setCannotBlitToWindow(); // The parent must show behind the child.
    else {
        Color baseColor = frameView().baseBackgroundColor();
        if (baseColor.alpha()) {
            CompositeOperator previousOperator = paintInfo.context->compositeOperation();
            paintInfo.context->setCompositeOperation(CompositeCopy);
            paintInfo.context->fillRect(paintInfo.rect, baseColor, style()->colorSpace());
            paintInfo.context->setCompositeOperation(previousOperator);
        } else
            paintInfo.context->clearRect(paintInfo.rect);
    }
}
Example #20
0
void BlockPainter::paintOverflowControlsIfNeeded(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    PaintPhase phase = paintInfo.phase;
    if (m_layoutBlock.hasOverflowClip() && m_layoutBlock.style()->visibility() == VISIBLE && (phase == PaintPhaseSelfBlockBackground || phase == PaintPhaseBlockBackground) && paintInfo.shouldPaintWithinRoot(&m_layoutBlock) && !paintInfo.paintRootBackgroundOnly()) {
        Optional<ClipRecorder> clipRecorder;
        if (!m_layoutBlock.layer()->isSelfPaintingLayer()) {
            LayoutRect clipRect = m_layoutBlock.borderBoxRect();
            clipRect.moveBy(paintOffset);
            clipRecorder.emplace(paintInfo.context, m_layoutBlock, DisplayItem::ClipScrollbarsToBoxBounds, clipRect);
        }
        ScrollableAreaPainter(*m_layoutBlock.layer()->scrollableArea()).paintOverflowControls(paintInfo.context, roundedIntPoint(paintOffset), paintInfo.cullRect(), false /* paintingOverlayControls */);
    }
}
Example #21
0
void ListMarkerPainter::paint(const PaintInfo& paintInfo,
                              const LayoutPoint& paintOffset) {
  if (paintInfo.phase != PaintPhaseForeground)
    return;

  if (m_layoutListMarker.style()->visibility() != EVisibility::Visible)
    return;

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

  LayoutPoint boxOrigin(paintOffset + m_layoutListMarker.location());
  LayoutRect overflowRect(m_layoutListMarker.visualOverflowRect());
  overflowRect.moveBy(boxOrigin);

  IntRect pixelSnappedOverflowRect = pixelSnappedIntRect(overflowRect);
  if (!paintInfo.cullRect().intersectsCullRect(overflowRect))
    return;

  LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutListMarker,
                                       paintInfo.phase,
                                       pixelSnappedOverflowRect);

  LayoutRect box(boxOrigin, m_layoutListMarker.size());

  IntRect marker = m_layoutListMarker.getRelativeMarkerRect();
  marker.moveBy(roundedIntPoint(boxOrigin));

  GraphicsContext& context = paintInfo.context;

  if (m_layoutListMarker.isImage()) {
    context.drawImage(m_layoutListMarker.image()
                          ->image(m_layoutListMarker, marker.size(),
                                  m_layoutListMarker.styleRef().effectiveZoom())
                          .get(),
                      marker);
    if (m_layoutListMarker.getSelectionState() != SelectionNone) {
      LayoutRect selRect = m_layoutListMarker.localSelectionRect();
      selRect.moveBy(boxOrigin);
      context.fillRect(
          pixelSnappedIntRect(selRect),
          m_layoutListMarker.listItem()->selectionBackgroundColor());
    }
    return;
  }

  LayoutListMarker::ListStyleCategory styleCategory =
      m_layoutListMarker.getListStyleCategory();
  if (styleCategory == LayoutListMarker::ListStyleCategory::None)
    return;

  Color color(m_layoutListMarker.resolveColor(CSSPropertyColor));

  if (BoxPainter::shouldForceWhiteBackgroundForPrintEconomy(
          m_layoutListMarker.styleRef(),
          m_layoutListMarker.listItem()->document()))
    color = TextPainter::textColorForWhiteBackground(color);

  // Apply the color to the list marker text.
  context.setFillColor(color);

  const EListStyleType listStyle = m_layoutListMarker.style()->listStyleType();
  if (styleCategory == LayoutListMarker::ListStyleCategory::Symbol) {
    paintSymbol(context, color, marker, listStyle);
    return;
  }

  if (m_layoutListMarker.text().isEmpty())
    return;

  const Font& font = m_layoutListMarker.style()->font();
  TextRun textRun = constructTextRun(font, m_layoutListMarker.text(),
                                     m_layoutListMarker.styleRef());

  GraphicsContextStateSaver stateSaver(context, false);
  if (!m_layoutListMarker.style()->isHorizontalWritingMode()) {
    marker.moveBy(roundedIntPoint(-boxOrigin));
    marker = marker.transposedRect();
    marker.moveBy(
        IntPoint(roundToInt(box.x()),
                 roundToInt(box.y() - m_layoutListMarker.logicalHeight())));
    stateSaver.save();
    context.translate(marker.x(), marker.maxY());
    context.rotate(static_cast<float>(deg2rad(90.)));
    context.translate(-marker.x(), -marker.maxY());
  }

  TextRunPaintInfo textRunPaintInfo(textRun);
  textRunPaintInfo.bounds = marker;
  const SimpleFontData* fontData =
      m_layoutListMarker.style()->font().primaryFont();
  IntPoint textOrigin = IntPoint(
      marker.x(),
      marker.y() + (fontData ? fontData->getFontMetrics().ascent() : 0));

  // Text is not arbitrary. We can judge whether it's RTL from the first
  // character, and we only need to handle the direction RightToLeft for now.
  bool textNeedsReversing =
      WTF::Unicode::direction(m_layoutListMarker.text()[0]) ==
      WTF::Unicode::RightToLeft;
  StringBuilder reversedText;
  if (textNeedsReversing) {
    unsigned length = m_layoutListMarker.text().length();
    reversedText.reserveCapacity(length);
    for (int i = length - 1; i >= 0; --i)
      reversedText.append(m_layoutListMarker.text()[i]);
    DCHECK(reversedText.length() == length);
    textRun.setText(reversedText.toString());
  }

  const UChar suffix =
      ListMarkerText::suffix(listStyle, m_layoutListMarker.listItem()->value());
  UChar suffixStr[2] = {suffix, static_cast<UChar>(' ')};
  TextRun suffixRun =
      constructTextRun(font, suffixStr, 2, m_layoutListMarker.styleRef(),
                       m_layoutListMarker.style()->direction());
  TextRunPaintInfo suffixRunInfo(suffixRun);
  suffixRunInfo.bounds = marker;

  if (m_layoutListMarker.style()->isLeftToRightDirection()) {
    context.drawText(font, textRunPaintInfo, textOrigin);
    context.drawText(font, suffixRunInfo,
                     textOrigin + IntSize(font.width(textRun), 0));
  } else {
    context.drawText(font, suffixRunInfo, textOrigin);
    context.drawText(font, textRunPaintInfo,
                     textOrigin + IntSize(font.width(suffixRun), 0));
  }
}
Example #22
0
void RenderImage::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutUnit cWidth = contentWidth();
    LayoutUnit cHeight = contentHeight();
    LayoutUnit leftBorder = borderLeft();
    LayoutUnit topBorder = borderTop();
    LayoutUnit leftPad = paddingLeft();
    LayoutUnit topPad = paddingTop();

    GraphicsContext& context = paintInfo.context();
    float deviceScaleFactor = document().deviceScaleFactor();

    Page* page = frame().page();

    if (!imageResource().hasImage() || imageResource().errorOccurred()) {
        if (paintInfo.phase == PaintPhaseSelection)
            return;

        if (page && paintInfo.phase == PaintPhaseForeground)
            page->addRelevantUnpaintedObject(this, visualOverflowRect());

        if (cWidth > 2 && cHeight > 2) {
            LayoutUnit borderWidth = LayoutUnit(1 / deviceScaleFactor);

            // Draw an outline rect where the image should be.
            context.setStrokeStyle(SolidStroke);
            context.setStrokeColor(Color::lightGray, style().colorSpace());
            context.setFillColor(Color::transparent, style().colorSpace());
            context.drawRect(snapRectToDevicePixels(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight), deviceScaleFactor), borderWidth);

            bool errorPictureDrawn = false;
            LayoutSize imageOffset;
            // When calculating the usable dimensions, exclude the pixels of
            // the ouline rect so the error image/alt text doesn't draw on it.
            LayoutUnit usableWidth = cWidth - 2 * borderWidth;
            LayoutUnit usableHeight = cHeight - 2 * borderWidth;

            RefPtr<Image> image = imageResource().image();

            if (imageResource().errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) {
                // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution.
                std::pair<Image*, float> brokenImageAndImageScaleFactor = imageResource().cachedImage()->brokenImage(document().deviceScaleFactor());
                image = brokenImageAndImageScaleFactor.first;
                FloatSize imageSize = image->size();
                imageSize.scale(1 / brokenImageAndImageScaleFactor.second);
                // Center the error image, accounting for border and padding.
                LayoutUnit centerX = (usableWidth - imageSize.width()) / 2;
                if (centerX < 0)
                    centerX = 0;
                LayoutUnit centerY = (usableHeight - imageSize.height()) / 2;
                if (centerY < 0)
                    centerY = 0;
                imageOffset = LayoutSize(leftBorder + leftPad + centerX + borderWidth, topBorder + topPad + centerY + borderWidth);

                ImageOrientationDescription orientationDescription(shouldRespectImageOrientation());
#if ENABLE(CSS_IMAGE_ORIENTATION)
                orientationDescription.setImageOrientationEnum(style().imageOrientation());
#endif
                context.drawImage(*image, style().colorSpace(), snapRectToDevicePixels(LayoutRect(paintOffset + imageOffset, imageSize), deviceScaleFactor), orientationDescription);
                errorPictureDrawn = true;
            }

            if (!m_altText.isEmpty()) {
                String text = document().displayStringModifiedByEncoding(m_altText);
                context.setFillColor(style().visitedDependentColor(CSSPropertyColor), style().colorSpace());
                const FontCascade& font = style().fontCascade();
                const FontMetrics& fontMetrics = font.fontMetrics();
                LayoutUnit ascent = fontMetrics.ascent();
                LayoutPoint altTextOffset = paintOffset;
                altTextOffset.move(leftBorder + leftPad + (paddingWidth / 2) - borderWidth, topBorder + topPad + ascent + (paddingHeight / 2) - borderWidth);

                // Only draw the alt text if it'll fit within the content box,
                // and only if it fits above the error image.
                TextRun textRun = RenderBlock::constructTextRun(this, font, text, style());
                LayoutUnit textWidth = font.width(textRun);
                if (errorPictureDrawn) {
                    if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height())
                        context.drawText(font, textRun, altTextOffset);
                } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height())
                    context.drawText(font, textRun, altTextOffset);
            }
        }
    } else if (imageResource().hasImage() && cWidth > 0 && cHeight > 0) {
        RefPtr<Image> img = imageResource().image(cWidth, cHeight);
        if (!img || img->isNull()) {
            if (page && paintInfo.phase == PaintPhaseForeground)
                page->addRelevantUnpaintedObject(this, visualOverflowRect());
            return;
        }

        LayoutRect contentBoxRect = this->contentBoxRect();
        contentBoxRect.moveBy(paintOffset);
        LayoutRect replacedContentRect = this->replacedContentRect(intrinsicSize());
        replacedContentRect.moveBy(paintOffset);
        bool clip = !contentBoxRect.contains(replacedContentRect);
        GraphicsContextStateSaver stateSaver(context, clip);
        if (clip)
            context.clip(contentBoxRect);

        paintIntoRect(context, snapRectToDevicePixels(replacedContentRect, deviceScaleFactor));
        
        if (cachedImage() && page && paintInfo.phase == PaintPhaseForeground) {
            // For now, count images as unpainted if they are still progressively loading. We may want 
            // to refine this in the future to account for the portion of the image that has painted.
            LayoutRect visibleRect = intersection(replacedContentRect, contentBoxRect);
            if (cachedImage()->isLoading())
                page->addRelevantUnpaintedObject(this, visibleRect);
            else
                page->addRelevantRepaintedObject(this, visibleRect);
        }
    }
}
Example #23
0
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);
}
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);
    }
}
Example #25
0
void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit, LayoutUnit)
{
    ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
    ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
    ASSERT(truncation() == cNoTruncation);

    if (renderer().style().visibility() != VISIBLE)
        return;

    // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox.
    // If we ever need that for SVG, it's very easy to refactor and reuse the code.

    auto& parentRenderer = parent()->renderer();

    bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
    bool hasSelection = !parentRenderer.document().printing() && selectionState() != RenderObject::SelectionNone;
    if (!hasSelection && paintSelectedTextOnly)
        return;

    if (!textShouldBePainted(renderer()))
        return;

    RenderStyle& style = parentRenderer.style();

    const SVGRenderStyle& svgStyle = style.svgStyle();

    bool hasFill = svgStyle.hasFill();
    bool hasVisibleStroke = svgStyle.hasVisibleStroke();

    RenderStyle* selectionStyle = &style;
    if (hasSelection) {
        selectionStyle = parentRenderer.getCachedPseudoStyle(SELECTION);
        if (selectionStyle) {
            const SVGRenderStyle& svgSelectionStyle = selectionStyle->svgStyle();

            if (!hasFill)
                hasFill = svgSelectionStyle.hasFill();
            if (!hasVisibleStroke)
                hasVisibleStroke = svgSelectionStyle.hasVisibleStroke();
        } else
            selectionStyle = &style;
    }

    if (renderer().view().frameView().paintBehavior() & PaintBehaviorRenderingSVGMask) {
        hasFill = true;
        hasVisibleStroke = false;
    }

    AffineTransform fragmentTransform;
    unsigned textFragmentsSize = m_textFragments.size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        SVGTextFragment& fragment = m_textFragments.at(i);
        ASSERT(!m_paintingResource);

        GraphicsContextStateSaver stateSaver(paintInfo.context());
        fragment.buildFragmentTransform(fragmentTransform);
        if (!fragmentTransform.isIdentity())
            paintInfo.context().concatCTM(fragmentTransform);

        // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations.
        int decorations = style.textDecorationsInEffect();
        if (decorations & TextDecorationUnderline)
            paintDecoration(paintInfo.context(), TextDecorationUnderline, fragment);
        if (decorations & TextDecorationOverline)
            paintDecoration(paintInfo.context(), TextDecorationOverline, fragment);

        auto paintOrder = style.svgStyle().paintTypesForPaintOrder();
        for (unsigned i = 0; i < paintOrder.size(); ++i) {
            switch (paintOrder.at(i)) {
            case PaintTypeFill:
                if (!hasFill)
                    continue;
                m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode;
                paintText(paintInfo.context(), &style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
                break;
            case PaintTypeStroke:
                if (!hasVisibleStroke)
                    continue;
                m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode;
                paintText(paintInfo.context(), &style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly);
                break;
            case PaintTypeMarkers:
                continue;
            }
        }

        // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
        if (decorations & TextDecorationLineThrough)
            paintDecoration(paintInfo.context(), TextDecorationLineThrough, fragment);

        m_paintingResourceMode = ApplyToDefaultMode;
    }

    // Finally, paint the outline if any.
    if (renderer().style().hasOutline() && is<RenderInline>(parentRenderer))
        downcast<RenderInline>(parentRenderer).paintOutline(paintInfo, paintOffset);

    ASSERT(!m_paintingResource);
}
void BlockPainter::paintOverflowControlsIfNeeded(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    PaintPhase phase = paintInfo.phase;
    if (m_layoutBlock.hasOverflowClip() && m_layoutBlock.style()->visibility() == VISIBLE && (phase == PaintPhaseBlockBackground || phase == PaintPhaseChildBlockBackground) && paintInfo.shouldPaintWithinRoot(&m_layoutBlock) && !paintInfo.paintRootBackgroundOnly()) {
        ScrollableAreaPainter(*m_layoutBlock.layer()->scrollableArea()).paintOverflowControls(paintInfo.context, roundedIntPoint(paintOffset), paintInfo.rect, false /* paintingOverlayControls */);
    }
}
void SVGInlineTextBoxPainter::paintTextFragments(const PaintInfo& paintInfo, LayoutObject& parentLayoutObject)
{
    const ComputedStyle& style = parentLayoutObject.styleRef();
    const SVGComputedStyle& svgStyle = style.svgStyle();

    bool hasFill = svgStyle.hasFill();
    bool hasVisibleStroke = svgStyle.hasVisibleStroke();

    const ComputedStyle* selectionStyle = &style;
    bool shouldPaintSelection = this->shouldPaintSelection();
    if (shouldPaintSelection) {
        selectionStyle = parentLayoutObject.getCachedPseudoStyle(SELECTION);
        if (selectionStyle) {
            const SVGComputedStyle& svgSelectionStyle = selectionStyle->svgStyle();

            if (!hasFill)
                hasFill = svgSelectionStyle.hasFill();
            if (!hasVisibleStroke)
                hasVisibleStroke = svgSelectionStyle.hasVisibleStroke();
        } else {
            selectionStyle = &style;
        }
    }

    if (paintInfo.isRenderingClipPathAsMaskImage()) {
        hasFill = true;
        hasVisibleStroke = false;
    }

    AffineTransform fragmentTransform;
    unsigned textFragmentsSize = m_svgInlineTextBox.textFragments().size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        SVGTextFragment& fragment = m_svgInlineTextBox.textFragments().at(i);

        GraphicsContextStateSaver stateSaver(*paintInfo.context, false);
        fragment.buildFragmentTransform(fragmentTransform);
        if (!fragmentTransform.isIdentity()) {
            stateSaver.save();
            paintInfo.context->concatCTM(fragmentTransform);
        }

        // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations.
        unsigned decorations = style.textDecorationsInEffect();
        if (decorations & TextDecorationUnderline)
            paintDecoration(paintInfo, TextDecorationUnderline, fragment);
        if (decorations & TextDecorationOverline)
            paintDecoration(paintInfo, TextDecorationOverline, fragment);

        for (int i = 0; i < 3; i++) {
            switch (svgStyle.paintOrderType(i)) {
            case PT_FILL:
                if (hasFill)
                    paintText(paintInfo, style, *selectionStyle, fragment, ApplyToFillMode, shouldPaintSelection);
                break;
            case PT_STROKE:
                if (hasVisibleStroke)
                    paintText(paintInfo, style, *selectionStyle, fragment, ApplyToStrokeMode, shouldPaintSelection);
                break;
            case PT_MARKERS:
                // Markers don't apply to text
                break;
            default:
                ASSERT_NOT_REACHED();
                break;
            }
        }

        // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text.
        if (decorations & TextDecorationLineThrough)
            paintDecoration(paintInfo, TextDecorationLineThrough, fragment);
    }
}
Example #28
0
void SVGInlineTextBox::paintSelectionBackground(PaintInfo& paintInfo)
{
    ASSERT(paintInfo.shouldPaintWithinRoot(renderer()));
    ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection);
    ASSERT(truncation() == cNoTruncation);

    if (renderer()->style()->visibility() != VISIBLE)
        return;

    RenderObject* parentRenderer = parent()->renderer();
    ASSERT(parentRenderer);
    ASSERT(!parentRenderer->document()->printing());

    // Determine whether or not we're selected.
    bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection;
    bool hasSelection = selectionState() != RenderObject::SelectionNone;
    if (!hasSelection || paintSelectedTextOnly)
        return;

    Color backgroundColor = renderer()->selectionBackgroundColor();
    if (!backgroundColor.isValid() || !backgroundColor.alpha())
        return;

    RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer());
    ASSERT(textRenderer);
    if (!textShouldBePainted(textRenderer))
        return;

    RenderStyle* style = parentRenderer->style();
    ASSERT(style);

    const SVGRenderStyle* svgStyle = style->svgStyle();
    ASSERT(svgStyle);

    bool hasFill = svgStyle->hasFill();
    bool hasStroke = svgStyle->hasStroke();

    RenderStyle* selectionStyle = style;
    if (hasSelection) {
        selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION);
        if (selectionStyle) {
            const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle();
            ASSERT(svgSelectionStyle);

            if (!hasFill)
                hasFill = svgSelectionStyle->hasFill();
            if (!hasStroke)
                hasStroke = svgSelectionStyle->hasStroke();
        } else
            selectionStyle = style;
    }

    int startPosition, endPosition;
    selectionStartEnd(startPosition, endPosition);

    int fragmentStartPosition = 0;
    int fragmentEndPosition = 0;
    AffineTransform fragmentTransform;
    unsigned textFragmentsSize = m_textFragments.size();
    for (unsigned i = 0; i < textFragmentsSize; ++i) {
        SVGTextFragment& fragment = m_textFragments.at(i);
        ASSERT(!m_paintingResource);

        fragmentStartPosition = startPosition;
        fragmentEndPosition = endPosition;
        if (!mapStartEndPositionsIntoFragmentCoordinates(fragment, fragmentStartPosition, fragmentEndPosition))
            continue;

        GraphicsContextStateSaver stateSaver(*paintInfo.context);
        fragment.buildFragmentTransform(fragmentTransform);
        if (!fragmentTransform.isIdentity())
            paintInfo.context->concatCTM(fragmentTransform);

        paintInfo.context->setFillColor(backgroundColor, style->colorSpace());
        paintInfo.context->fillRect(selectionRectForTextFragment(fragment, fragmentStartPosition, fragmentEndPosition, style), backgroundColor, style->colorSpace());

        m_paintingResourceMode = ApplyToDefaultMode;
    }

    ASSERT(!m_paintingResource);
}
static SkPath::FillType fillRuleFromStyle(const PaintInfo& paintInfo, const SVGComputedStyle& svgStyle)
{
    return WebCoreWindRuleToSkFillType(paintInfo.isRenderingClipPathAsMaskImage() ? svgStyle.clipRule() : svgStyle.fillRule());
}
Example #30
0
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());
}