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 ClipScope::clip(const LayoutRect& clipRect, SkRegion::Op operation) { m_context->save(); m_context->clipRect(pixelSnappedIntRect(clipRect), NotAntiAliased, operation); m_clipCount++; }
IntRect RenderView::unscaledDocumentRect() const { return pixelSnappedIntRect(layoutOverflowRect()); }
void FilterEffectRendererHelper::applyFilterEffect(GraphicsContext* destinationContext) { ASSERT(m_haveFilterEffect && m_renderLayer->filterRenderer()); FilterEffectRenderer* filter = m_renderLayer->filterRenderer(); filter->inputContext()->restore(); filter->apply(); // Get the filtered output and draw it in place. LayoutRect destRect = filter->outputRect(); destRect.move(m_paintOffset.x(), m_paintOffset.y()); destinationContext->drawImageBuffer(filter->output(), m_renderLayer->renderer().style()->colorSpace(), pixelSnappedIntRect(destRect), CompositeSourceOver); filter->clearIntermediateResults(); }
void SVGRootPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // An empty viewport disables rendering. if (m_layoutSVGRoot.pixelSnappedBorderBoxRect().isEmpty()) return; // SVG outlines are painted during PaintPhaseForeground. if (shouldPaintSelfOutline(paintInfo.phase)) return; // An empty viewBox also disables rendering. // (http://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute) SVGSVGElement* svg = toSVGSVGElement(m_layoutSVGRoot.node()); ASSERT(svg); if (svg->hasEmptyViewBox()) return; // Don't paint if we don't have kids, except if we have filters we should paint those. if (!m_layoutSVGRoot.firstChild()) { SVGResources* resources = SVGResourcesCache::cachedResourcesForLayoutObject(&m_layoutSVGRoot); if (!resources || !resources->filter()) return; } PaintInfo paintInfoBeforeFiltering(paintInfo); // At the HTML->SVG boundary, SVGRoot will have a paint offset transform // paint property but may not have a PaintLayer, so we need to update the // paint properties here since they will not be updated by PaintLayer // (See: PaintPropertyTreeBuilder::createPaintOffsetTranslationIfNeeded). Optional<ScopedPaintChunkProperties> paintOffsetTranslationPropertyScope; if (RuntimeEnabledFeatures::slimmingPaintV2Enabled() && !m_layoutSVGRoot.hasLayer()) { const auto* objectProperties = m_layoutSVGRoot.objectPaintProperties(); if (objectProperties && objectProperties->paintOffsetTranslation()) { auto& paintController = paintInfoBeforeFiltering.context.paintController(); PaintChunkProperties properties(paintController.currentPaintChunkProperties()); properties.transform = objectProperties->paintOffsetTranslation(); paintOffsetTranslationPropertyScope.emplace(paintController, properties); } } // Apply initial viewport clip. Optional<ClipRecorder> clipRecorder; if (m_layoutSVGRoot.shouldApplyViewportClip()) { // TODO(pdr): Clip the paint info cull rect here. clipRecorder.emplace(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintInfoBeforeFiltering.displayItemTypeForClipping(), LayoutRect(pixelSnappedIntRect(m_layoutSVGRoot.overflowClipRect(paintOffset)))); } // Convert from container offsets (html layoutObjects) to a relative transform (svg layoutObjects). // Transform from our paint container's coordinate system to our local coords. IntPoint adjustedPaintOffset = roundedIntPoint(paintOffset); AffineTransform paintOffsetToBorderBox = AffineTransform::translation(adjustedPaintOffset.x(), adjustedPaintOffset.y()) * m_layoutSVGRoot.localToBorderBoxTransform(); paintInfoBeforeFiltering.updateCullRect(paintOffsetToBorderBox); TransformRecorder transformRecorder(paintInfoBeforeFiltering.context, m_layoutSVGRoot, paintOffsetToBorderBox); SVGPaintContext paintContext(m_layoutSVGRoot, paintInfoBeforeFiltering); if (paintContext.paintInfo().phase == PaintPhaseForeground && !paintContext.applyClipMaskAndFilterIfNecessary()) return; BoxPainter(m_layoutSVGRoot).paint(paintContext.paintInfo(), LayoutPoint()); PaintTiming& timing = PaintTiming::from(m_layoutSVGRoot.node()->document().topDocument()); timing.markFirstContentfulPaint(); }
void RenderView::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const { rects.append(pixelSnappedIntRect(accumulatedOffset, layer()->size())); }
void RenderMultiColumnSet::paintColumnRules(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (paintInfo.context->paintingDisabled()) return; RenderMultiColumnFlowThread* flowThread = toRenderBlockFlow(parent())->multiColumnFlowThread(); const RenderStyle& blockStyle = parent()->style(); const Color& ruleColor = blockStyle.visitedDependentColor(CSSPropertyWebkitColumnRuleColor); bool ruleTransparent = blockStyle.columnRuleIsTransparent(); EBorderStyle ruleStyle = blockStyle.columnRuleStyle(); LayoutUnit ruleThickness = blockStyle.columnRuleWidth(); LayoutUnit colGap = columnGap(); bool renderRule = ruleStyle > BHIDDEN && !ruleTransparent; if (!renderRule) return; unsigned colCount = columnCount(); if (colCount <= 1) return; bool antialias = shouldAntialiasLines(paintInfo.context); if (flowThread->progressionIsInline()) { bool leftToRight = style().isLeftToRightDirection() ^ flowThread->progressionIsReversed(); LayoutUnit currLogicalLeftOffset = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit ruleAdd = logicalLeftOffsetForContent(); LayoutUnit ruleLogicalLeft = leftToRight ? LayoutUnit() : contentLogicalWidth(); LayoutUnit inlineDirectionSize = computedColumnWidth(); BoxSide boxSide = isHorizontalWritingMode() ? leftToRight ? BSLeft : BSRight : leftToRight ? BSTop : BSBottom; for (unsigned i = 0; i < colCount; i++) { // Move to the next position. if (leftToRight) { ruleLogicalLeft += inlineDirectionSize + colGap / 2; currLogicalLeftOffset += inlineDirectionSize + colGap; } else { ruleLogicalLeft -= (inlineDirectionSize + colGap / 2); currLogicalLeftOffset -= (inlineDirectionSize + colGap); } // Now paint the column rule. if (i < colCount - 1) { LayoutUnit ruleLeft = isHorizontalWritingMode() ? paintOffset.x() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd : paintOffset.x() + borderLeft() + paddingLeft(); LayoutUnit ruleRight = isHorizontalWritingMode() ? ruleLeft + ruleThickness : ruleLeft + contentWidth(); LayoutUnit ruleTop = isHorizontalWritingMode() ? paintOffset.y() + borderTop() + paddingTop() : paintOffset.y() + ruleLogicalLeft - ruleThickness / 2 + ruleAdd; LayoutUnit ruleBottom = isHorizontalWritingMode() ? ruleTop + contentHeight() : ruleTop + ruleThickness; IntRect pixelSnappedRuleRect = pixelSnappedIntRectFromEdges(ruleLeft, ruleTop, ruleRight, ruleBottom); drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); } ruleLogicalLeft = currLogicalLeftOffset; } } else { bool topToBottom = !style().isFlippedBlocksWritingMode() ^ flowThread->progressionIsReversed(); LayoutUnit ruleLeft = isHorizontalWritingMode() ? LayoutUnit() : colGap / 2 - colGap - ruleThickness / 2; LayoutUnit ruleWidth = isHorizontalWritingMode() ? contentWidth() : ruleThickness; LayoutUnit ruleTop = isHorizontalWritingMode() ? colGap / 2 - colGap - ruleThickness / 2 : LayoutUnit(); LayoutUnit ruleHeight = isHorizontalWritingMode() ? ruleThickness : contentHeight(); LayoutRect ruleRect(ruleLeft, ruleTop, ruleWidth, ruleHeight); if (!topToBottom) { if (isHorizontalWritingMode()) ruleRect.setY(height() - ruleRect.maxY()); else ruleRect.setX(width() - ruleRect.maxX()); } ruleRect.moveBy(paintOffset); BoxSide boxSide = isHorizontalWritingMode() ? topToBottom ? BSTop : BSBottom : topToBottom ? BSLeft : BSRight; LayoutSize step(0, topToBottom ? computedColumnHeight() + colGap : -(computedColumnHeight() + colGap)); if (!isHorizontalWritingMode()) step = step.transposedSize(); for (unsigned i = 1; i < colCount; i++) { ruleRect.move(step); IntRect pixelSnappedRuleRect = pixelSnappedIntRect(ruleRect); drawLineForBoxSide(paintInfo.context, pixelSnappedRuleRect.x(), pixelSnappedRuleRect.y(), pixelSnappedRuleRect.maxX(), pixelSnappedRuleRect.maxY(), boxSide, ruleColor, ruleStyle, 0, 0, antialias); } } }
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); } }
void RenderSVGRoot::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { // An empty viewport disables rendering. if (pixelSnappedBorderBoxRect().isEmpty()) return; // Don't paint, if the context explicitly disabled it. if (paintInfo.context->paintingDisabled()) 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()) { SVGResources* resources = SVGResourcesCache::cachedResourcesForRenderObject(*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 - not affected by overflow handling childPaintInfo.context->clip(pixelSnappedIntRect(overflowClipRect(paintOffset, paintInfo.renderNamedFlowFragment))); // 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 InlinePainter::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset, const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline, const Color outlineColor) { const ComputedStyle& styleToUse = m_layoutInline.styleRef(); int outlineWidth = styleToUse.outlineWidth(); EBorderStyle outlineStyle = styleToUse.outlineStyle(); bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext); int offset = m_layoutInline.style()->outlineOffset(); LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset), LayoutSize(thisline.width() + offset, thisline.height() + offset)); IntRect pixelSnappedBox = pixelSnappedIntRect(box); if (pixelSnappedBox.width() < 0 || pixelSnappedBox.height() < 0) return; // Note that we use IntRect below for working with solely x/width values, simplifying logic at cost of a bit of memory. IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x() - offset, 0, lastline.width() + offset, 0); IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x() - offset, 0, nextline.width() + offset, 0); const int fallbackMaxOutlineX = std::numeric_limits<int>::max(); const int fallbackMinOutlineX = std::numeric_limits<int>::min(); // left edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), pixelSnappedBox.x(), pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), BSLeft, outlineColor, outlineStyle, (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), antialias); // right edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.maxX(), pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0), BSRight, outlineColor, outlineStyle, (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth), antialias); // upper edge if (thisline.x() < lastline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, std::min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? fallbackMaxOutlineX : pixelSnappedLastLine.x())), pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (lastline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(lastline.isEmpty() ? fallbackMinOutlineX : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } // lower edge if (thisline.x() < nextline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), std::min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : fallbackMaxOutlineX), pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (nextline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : fallbackMinOutlineX, pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } }
void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutTable* table = m_layoutTableCell.table(); if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EmptyCellsHide && !m_layoutTableCell.firstChild()) return; bool needsToPaintBorder = m_layoutTableCell.styleRef().hasBorderDecoration() && !table->collapseBorders(); if (!m_layoutTableCell.hasBackground() && !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder) return; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground)) return; LayoutRect visualOverflowRect = m_layoutTableCell.visualOverflowRect(); visualOverflowRect.moveBy(paintOffset); // TODO(chrishtr): the pixel-snapping here is likely incorrect. LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(visualOverflowRect)); LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent); BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Normal); // Paint our cell background. paintBackgroundsBehindCell(paintInfo, paintOffset, &m_layoutTableCell, DisplayItem::BoxDecorationBackground); BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Inset); if (!needsToPaintBorder) return; BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, m_layoutTableCell.styleRef()); }
void RenderTextControl::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject*) { if (!size().isEmpty()) rects.append(pixelSnappedIntRect(additionalOffset, size())); }
BoxClipper::BoxClipper(const LayoutBox& box, const PaintInfo& paintInfo, const LayoutPoint& accumulatedOffset, ContentsClipBehavior contentsClipBehavior) : m_box(box) , m_paintInfo(paintInfo) , m_clipType(DisplayItem::UninitializedType) { if (m_paintInfo.phase == PaintPhaseSelfBlockBackground || m_paintInfo.phase == PaintPhaseSelfOutline || m_paintInfo.phase == PaintPhaseMask) return; bool isControlClip = m_box.hasControlClip(); bool isOverflowOrContainmentClip = (m_box.hasOverflowClip() && !m_box.layer()->isSelfPaintingLayer()) || m_box.style()->containsPaint(); if (!isControlClip && !isOverflowOrContainmentClip) return; LayoutRect clipRect = isControlClip ? m_box.controlClipRect(accumulatedOffset) : m_box.overflowClipRect(accumulatedOffset); FloatRoundedRect clipRoundedRect(0, 0, 0, 0); bool hasBorderRadius = m_box.style()->hasBorderRadius(); if (hasBorderRadius) clipRoundedRect = m_box.style()->getRoundedInnerBorderFor(LayoutRect(accumulatedOffset, m_box.size())); if (contentsClipBehavior == SkipContentsClipIfPossible) { LayoutRect contentsVisualOverflow = m_box.contentsVisualOverflowRect(); if (contentsVisualOverflow.isEmpty()) return; LayoutRect conservativeClipRect = clipRect; if (hasBorderRadius) conservativeClipRect.intersect(LayoutRect(clipRoundedRect.radiusCenterRect())); conservativeClipRect.moveBy(-accumulatedOffset); if (m_box.hasLayer()) conservativeClipRect.move(m_box.scrolledContentOffset()); if (conservativeClipRect.contains(contentsVisualOverflow)) return; } if (!m_paintInfo.context.paintController().displayItemConstructionIsDisabled()) { m_clipType = m_paintInfo.displayItemTypeForClipping(); Vector<FloatRoundedRect> roundedRects; if (hasBorderRadius) roundedRects.append(clipRoundedRect); m_paintInfo.context.paintController().createAndAppend<ClipDisplayItem>(m_box, m_clipType, pixelSnappedIntRect(clipRect), roundedRects); } }
void RenderFrameSet::positionFramesWithFlattening() { RenderBox* child = firstChildBox(); if (!child) return; int rows = frameSet()->totalRows(); int cols = frameSet()->totalCols(); int borderThickness = frameSet()->border(); bool repaintNeeded = false; // calculate frameset height based on actual content height to eliminate scrolling bool out = false; for (int r = 0; r < rows && !out; r++) { int extra = 0; int height = m_rows.m_sizes[r]; for (int c = 0; c < cols; c++) { IntRect oldFrameRect = pixelSnappedIntRect(child->frameRect()); int width = m_cols.m_sizes[c]; bool fixedWidth = frameSet()->colLengths() && frameSet()->colLengths()[c].isFixed(); bool fixedHeight = frameSet()->rowLengths() && frameSet()->rowLengths()[r].isFixed(); // has to be resized and itself resize its contents if (!fixedWidth) child->setWidth(width ? width + extra / (cols - c) : 0); else child->setWidth(width); child->setHeight(height); child->setNeedsLayout(true); if (child->isFrameSet()) toRenderFrameSet(child)->layout(); else toRenderFrame(child)->layoutWithFlattening(fixedWidth, fixedHeight); if (child->height() > m_rows.m_sizes[r]) m_rows.m_sizes[r] = child->height(); if (child->width() > m_cols.m_sizes[c]) m_cols.m_sizes[c] = child->width(); if (child->frameRect() != oldFrameRect) repaintNeeded = true; // difference between calculated frame width and the width it actually decides to have extra += width - m_cols.m_sizes[c]; child = child->nextSiblingBox(); if (!child) { out = true; break; } } } int xPos = 0; int yPos = 0; out = false; child = firstChildBox(); for (int r = 0; r < rows && !out; r++) { xPos = 0; for (int c = 0; c < cols; c++) { // ensure the rows and columns are filled IntRect oldRect = pixelSnappedIntRect(child->frameRect()); child->setLocation(IntPoint(xPos, yPos)); child->setHeight(m_rows.m_sizes[r]); child->setWidth(m_cols.m_sizes[c]); if (child->frameRect() != oldRect) { repaintNeeded = true; // update to final size child->setNeedsLayout(true); if (child->isFrameSet()) toRenderFrameSet(child)->layout(); else toRenderFrame(child)->layoutWithFlattening(true, true); } xPos += m_cols.m_sizes[c] + borderThickness; child = child->nextSiblingBox(); if (!child) { out = true; break; } } yPos += m_rows.m_sizes[r] + borderThickness; } setWidth(xPos - borderThickness); setHeight(yPos - borderThickness); if (repaintNeeded) repaint(); // all the remaining frames are hidden to avoid ugly spurious unflowed frames for (; child; child = child->nextSiblingBox()) { child->setWidth(0); child->setHeight(0); child->setNeedsLayout(false); } }
void TablePainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_layoutTable.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutTable, paintInfo.phase)) return; LayoutRect rect(paintOffset, m_layoutTable.size()); m_layoutTable.subtractCaptionRect(rect); LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutTable, paintInfo.phase, pixelSnappedIntRect(rect)); BoxPainter(m_layoutTable).paintMaskImages(paintInfo, rect); }
IntRect FrameView::convertFromRenderer(const RenderObject& renderer, const IntRect& rendererRect) const { return pixelSnappedIntRect(enclosingLayoutRect(renderer.localToAbsoluteQuad(FloatRect(rendererRect)).boundingBox())); }
void InlinePainter::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset, const LayoutRect& lastline, const LayoutRect& thisline, const LayoutRect& nextline, const Color outlineColor) { RenderStyle* styleToUse = m_renderInline.style(); int outlineWidth = styleToUse->outlineWidth(); EBorderStyle outlineStyle = styleToUse->outlineStyle(); bool antialias = BoxPainter::shouldAntialiasLines(graphicsContext); int offset = m_renderInline.style()->outlineOffset(); LayoutRect box(LayoutPoint(paintOffset.x() + thisline.x() - offset, paintOffset.y() + thisline.y() - offset), LayoutSize(thisline.width() + offset, thisline.height() + offset)); IntRect pixelSnappedBox = pixelSnappedIntRect(box); if (pixelSnappedBox.width() < 0 || pixelSnappedBox.height() < 0) return; IntRect pixelSnappedLastLine = pixelSnappedIntRect(paintOffset.x() + lastline.x(), 0, lastline.width(), 0); IntRect pixelSnappedNextLine = pixelSnappedIntRect(paintOffset.x() + nextline.x(), 0, nextline.width(), 0); // left edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), pixelSnappedBox.x(), pixelSnappedBox.maxY() + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : 0), BSLeft, outlineColor, outlineStyle, (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.maxX() - 1) <= thisline.x() ? outlineWidth : -outlineWidth), antialias); // right edge ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.maxX(), pixelSnappedBox.y() - (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : 0), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : 0), BSRight, outlineColor, outlineStyle, (lastline.isEmpty() || lastline.maxX() < thisline.maxX() || (thisline.maxX() - 1) <= lastline.x() ? outlineWidth : -outlineWidth), (nextline.isEmpty() || nextline.maxX() <= thisline.maxX() || (thisline.maxX() - 1) <= nextline.x() ? outlineWidth : -outlineWidth), antialias); // upper edge if (thisline.x() < lastline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, std::min(pixelSnappedBox.maxX() + outlineWidth, (lastline.isEmpty() ? 1000000 : pixelSnappedLastLine.x())), pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, (!lastline.isEmpty() && paintOffset.x() + lastline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (lastline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(lastline.isEmpty() ? -1000000 : pixelSnappedLastLine.maxX(), pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, (!lastline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + lastline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.y() - outlineWidth, pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.y(), BSTop, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } // lower edge if (thisline.x() < nextline.x()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), std::min(pixelSnappedBox.maxX() + outlineWidth, !nextline.isEmpty() ? pixelSnappedNextLine.x() + 1 : 1000000), pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, (!nextline.isEmpty() && paintOffset.x() + nextline.x() + 1 < pixelSnappedBox.maxX() + outlineWidth) ? -outlineWidth : outlineWidth, antialias); } if (nextline.maxX() < thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, std::max(!nextline.isEmpty() ? pixelSnappedNextLine.maxX() : -1000000, pixelSnappedBox.x() - outlineWidth), pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, (!nextline.isEmpty() && pixelSnappedBox.x() - outlineWidth < paintOffset.x() + nextline.maxX()) ? -outlineWidth : outlineWidth, outlineWidth, antialias); } if (thisline.x() == thisline.maxX()) { ObjectPainter::drawLineForBoxSide(graphicsContext, pixelSnappedBox.x() - outlineWidth, pixelSnappedBox.maxY(), pixelSnappedBox.maxX() + outlineWidth, pixelSnappedBox.maxY() + outlineWidth, BSBottom, outlineColor, outlineStyle, outlineWidth, outlineWidth, antialias); } }
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)); } }
IntRect RenderView::unscaledDocumentRect() const { LayoutRect overflowRect(layoutOverflowRect()); flipForWritingMode(overflowRect); return pixelSnappedIntRect(overflowRect); }
void CompositingInputsUpdater::updateRecursive(DeprecatedPaintLayer* layer, UpdateType updateType, AncestorInfo info) { if (!layer->childNeedsCompositingInputsUpdate() && updateType != ForceUpdate) return; m_geometryMap.pushMappingsToAncestor(layer, layer->parent()); if (layer->hasCompositedDeprecatedPaintLayerMapping()) info.enclosingCompositedLayer = layer; if (layer->needsCompositingInputsUpdate()) { if (info.enclosingCompositedLayer) info.enclosingCompositedLayer->compositedDeprecatedPaintLayerMapping()->setNeedsGraphicsLayerUpdate(GraphicsLayerUpdateSubtree); updateType = ForceUpdate; } if (updateType == ForceUpdate) { DeprecatedPaintLayer::AncestorDependentCompositingInputs properties; if (!layer->isRootLayer()) { properties.clippedAbsoluteBoundingBox = enclosingIntRect(m_geometryMap.absoluteRect(layer->boundingBoxForCompositingOverlapTest())); // FIXME: Setting the absBounds to 1x1 instead of 0x0 makes very little sense, // but removing this code will make JSGameBench sad. // See https://codereview.chromium.org/13912020/ if (properties.clippedAbsoluteBoundingBox.isEmpty()) properties.clippedAbsoluteBoundingBox.setSize(IntSize(1, 1)); IntRect clipRect = pixelSnappedIntRect(layer->clipper().backgroundClipRect(ClipRectsContext(m_rootLayer, AbsoluteClipRects)).rect()); properties.clippedAbsoluteBoundingBox.intersect(clipRect); const DeprecatedPaintLayer* parent = layer->parent(); properties.opacityAncestor = parent->isTransparent() ? parent : parent->opacityAncestor(); properties.transformAncestor = parent->hasTransformRelatedProperty() ? parent : parent->transformAncestor(); properties.filterAncestor = parent->hasFilter() ? parent : parent->filterAncestor(); bool layerIsFixedPosition = layer->layoutObject()->style()->position() == FixedPosition; properties.nearestFixedPositionLayer = layerIsFixedPosition ? layer : parent->nearestFixedPositionLayer(); if (info.hasAncestorWithClipOrOverflowClip) { const DeprecatedPaintLayer* parentLayerOnClippingContainerChain = findParentLayerOnClippingContainerChain(layer); const bool parentHasClipOrOverflowClip = parentLayerOnClippingContainerChain->layoutObject()->hasClipOrOverflowClip(); properties.clippingContainer = parentHasClipOrOverflowClip ? parentLayerOnClippingContainerChain->layoutObject() : parentLayerOnClippingContainerChain->clippingContainer(); } if (info.lastScrollingAncestor) { const LayoutObject* containingBlock = layer->layoutObject()->containingBlock(); const DeprecatedPaintLayer* parentLayerOnContainingBlockChain = findParentLayerOnContainingBlockChain(containingBlock); properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain->ancestorScrollingLayer(); if (parentLayerOnContainingBlockChain->scrollsOverflow()) properties.ancestorScrollingLayer = parentLayerOnContainingBlockChain; if (layer->layoutObject()->isOutOfFlowPositioned() && !layer->subtreeIsInvisible()) { const DeprecatedPaintLayer* clippingLayer = properties.clippingContainer ? properties.clippingContainer->enclosingLayer() : layer->compositor()->rootLayer(); if (hasClippedStackingAncestor(layer, clippingLayer)) properties.clipParent = clippingLayer; } if (layer->stackingNode()->isTreatedAsStackingContextForPainting() && properties.ancestorScrollingLayer && !info.ancestorStackingContext->layoutObject()->isDescendantOf(properties.ancestorScrollingLayer->layoutObject())) properties.scrollParent = properties.ancestorScrollingLayer; } } properties.hasAncestorWithClipPath = info.hasAncestorWithClipPath; layer->updateAncestorDependentCompositingInputs(properties); } if (layer->stackingNode()->isStackingContext()) info.ancestorStackingContext = layer; if (layer->scrollsOverflow()) info.lastScrollingAncestor = layer; if (layer->layoutObject()->hasClipOrOverflowClip()) info.hasAncestorWithClipOrOverflowClip = true; if (layer->layoutObject()->hasClipPath()) info.hasAncestorWithClipPath = true; DeprecatedPaintLayer::DescendantDependentCompositingInputs descendantProperties; for (DeprecatedPaintLayer* child = layer->firstChild(); child; child = child->nextSibling()) { updateRecursive(child, updateType, info); descendantProperties.hasDescendantWithClipPath |= child->hasDescendantWithClipPath() || child->layoutObject()->hasClipPath(); descendantProperties.hasNonIsolatedDescendantWithBlendMode |= (!child->stackingNode()->isStackingContext() && child->hasNonIsolatedDescendantWithBlendMode()) || child->layoutObject()->style()->hasBlendMode(); } layer->updateDescendantDependentCompositingInputs(descendantProperties); layer->didUpdateCompositingInputs(); m_geometryMap.popMappingsToAncestor(layer->parent()); }
void ImagePainter::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutUnit cWidth = m_renderImage.contentWidth(); LayoutUnit cHeight = m_renderImage.contentHeight(); GraphicsContext* context = paintInfo.context; if (!m_renderImage.imageResource()->hasImage() || m_renderImage.imageResource()->errorOccurred()) { if (paintInfo.phase == PaintPhaseSelection) return; if (cWidth > 2 && cHeight > 2) { const int borderWidth = 1; LayoutUnit leftBorder = m_renderImage.borderLeft(); LayoutUnit topBorder = m_renderImage.borderTop(); LayoutUnit leftPad = m_renderImage.paddingLeft(); LayoutUnit topPad = m_renderImage.paddingTop(); // Draw an outline rect where the image should be. context->setStrokeStyle(SolidStroke); context->setStrokeColor(Color::lightGray); context->setFillColor(Color::transparent); context->drawRect(pixelSnappedIntRect(LayoutRect(paintOffset.x() + leftBorder + leftPad, paintOffset.y() + topBorder + topPad, cWidth, cHeight))); 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 = m_renderImage.imageResource()->image(); if (m_renderImage.imageResource()->errorOccurred() && !image->isNull() && usableWidth >= image->width() && usableHeight >= image->height()) { float deviceScaleFactor = blink::deviceScaleFactor(m_renderImage.frame()); // Call brokenImage() explicitly to ensure we get the broken image icon at the appropriate resolution. pair<Image*, float> brokenImageAndImageScaleFactor = ImageResource::brokenImage(deviceScaleFactor); image = brokenImageAndImageScaleFactor.first; IntSize 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); context->drawImage(image.get(), pixelSnappedIntRect(LayoutRect(paintOffset + imageOffset, imageSize)), CompositeSourceOver, m_renderImage.shouldRespectImageOrientation()); errorPictureDrawn = true; } if (!m_renderImage.altText().isEmpty()) { const Font& font = m_renderImage.style()->font(); const FontMetrics& fontMetrics = font.fontMetrics(); LayoutUnit ascent = fontMetrics.ascent(); LayoutPoint textRectOrigin = paintOffset; textRectOrigin.move(leftBorder + leftPad + (RenderImage::paddingWidth / 2) - borderWidth, topBorder + topPad + (RenderImage::paddingHeight / 2) - borderWidth); LayoutPoint textOrigin(textRectOrigin.x(), textRectOrigin.y() + ascent); // Only draw the alt text if it'll fit within the content box, // and only if it fits above the error image. TextRun textRun = constructTextRun(&m_renderImage, font, m_renderImage.altText(), m_renderImage.style(), TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags | RespectDirection); float textWidth = font.width(textRun); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = FloatRect(textRectOrigin, FloatSize(textWidth, fontMetrics.height())); context->setFillColor(m_renderImage.resolveColor(CSSPropertyColor)); if (textRun.direction() == RTL) { int availableWidth = cWidth - static_cast<int>(RenderImage::paddingWidth); textOrigin.move(availableWidth - ceilf(textWidth), 0); } if (errorPictureDrawn) { if (usableWidth >= textWidth && fontMetrics.height() <= imageOffset.height()) context->drawBidiText(font, textRunPaintInfo, textOrigin); } else if (usableWidth >= textWidth && usableHeight >= fontMetrics.height()) { context->drawBidiText(font, textRunPaintInfo, textOrigin); } } } } else if (m_renderImage.imageResource()->hasImage() && cWidth > 0 && cHeight > 0) { LayoutRect contentRect = m_renderImage.contentBoxRect(); contentRect.moveBy(paintOffset); LayoutRect paintRect = m_renderImage.replacedContentRect(); paintRect.moveBy(paintOffset); bool clip = !contentRect.contains(paintRect); if (clip) { context->save(); context->clip(contentRect); } paintIntoRect(context, paintRect); if (clip) context->restore(); } }
bool RenderTheme::paint(const RenderObject& o, ControlStates* controlStates, const PaintInfo& paintInfo, const LayoutRect& r) { // If painting is disabled, but we aren't updating control tints, then just bail. // If we are updating control tints, just schedule a repaint if the theme supports tinting // for that control. if (paintInfo.context->updatingControlTints()) { if (controlSupportsTints(o)) o.repaint(); return false; } if (paintInfo.context->paintingDisabled()) return false; ControlPart part = o.style().appearance(); IntRect integralSnappedRect = pixelSnappedIntRect(r); #if USE(NEW_THEME) switch (part) { case CheckboxPart: case RadioPart: case PushButtonPart: case SquareButtonPart: case DefaultButtonPart: case ButtonPart: case InnerSpinButtonPart: updateControlStatesForRenderer(o, controlStates); m_theme->paint(part, controlStates, const_cast<GraphicsContext*>(paintInfo.context), integralSnappedRect, o.style().effectiveZoom(), &o.view().frameView()); return false; default: break; } #else UNUSED_PARAM(controlStates); #endif // Call the appropriate paint method based off the appearance value. switch (part) { #if !USE(NEW_THEME) case CheckboxPart: return paintCheckbox(o, paintInfo, integralSnappedRect); case RadioPart: return paintRadio(o, paintInfo, integralSnappedRect); case PushButtonPart: case SquareButtonPart: case DefaultButtonPart: case ButtonPart: return paintButton(o, paintInfo, integralSnappedRect); case InnerSpinButtonPart: return paintInnerSpinButton(o, paintInfo, integralSnappedRect); #endif case MenulistPart: return paintMenuList(o, paintInfo, integralSnappedRect); #if ENABLE(METER_ELEMENT) case MeterPart: case RelevancyLevelIndicatorPart: case ContinuousCapacityLevelIndicatorPart: case DiscreteCapacityLevelIndicatorPart: case RatingLevelIndicatorPart: return paintMeter(o, paintInfo, integralSnappedRect); #endif #if ENABLE(PROGRESS_ELEMENT) case ProgressBarPart: return paintProgressBar(o, paintInfo, integralSnappedRect); #endif case SliderHorizontalPart: case SliderVerticalPart: return paintSliderTrack(o, paintInfo, integralSnappedRect); case SliderThumbHorizontalPart: case SliderThumbVerticalPart: return paintSliderThumb(o, paintInfo, integralSnappedRect); case MediaEnterFullscreenButtonPart: case MediaExitFullscreenButtonPart: return paintMediaFullscreenButton(o, paintInfo, integralSnappedRect); case MediaPlayButtonPart: return paintMediaPlayButton(o, paintInfo, integralSnappedRect); case MediaOverlayPlayButtonPart: return paintMediaOverlayPlayButton(o, paintInfo, integralSnappedRect); case MediaMuteButtonPart: return paintMediaMuteButton(o, paintInfo, integralSnappedRect); case MediaSeekBackButtonPart: return paintMediaSeekBackButton(o, paintInfo, integralSnappedRect); case MediaSeekForwardButtonPart: return paintMediaSeekForwardButton(o, paintInfo, integralSnappedRect); case MediaRewindButtonPart: return paintMediaRewindButton(o, paintInfo, integralSnappedRect); case MediaReturnToRealtimeButtonPart: return paintMediaReturnToRealtimeButton(o, paintInfo, integralSnappedRect); case MediaToggleClosedCaptionsButtonPart: return paintMediaToggleClosedCaptionsButton(o, paintInfo, integralSnappedRect); case MediaSliderPart: return paintMediaSliderTrack(o, paintInfo, integralSnappedRect); case MediaSliderThumbPart: return paintMediaSliderThumb(o, paintInfo, integralSnappedRect); case MediaVolumeSliderMuteButtonPart: return paintMediaMuteButton(o, paintInfo, integralSnappedRect); case MediaVolumeSliderContainerPart: return paintMediaVolumeSliderContainer(o, paintInfo, integralSnappedRect); case MediaVolumeSliderPart: return paintMediaVolumeSliderTrack(o, paintInfo, integralSnappedRect); case MediaVolumeSliderThumbPart: return paintMediaVolumeSliderThumb(o, paintInfo, integralSnappedRect); case MediaFullScreenVolumeSliderPart: return paintMediaFullScreenVolumeSliderTrack(o, paintInfo, integralSnappedRect); case MediaFullScreenVolumeSliderThumbPart: return paintMediaFullScreenVolumeSliderThumb(o, paintInfo, integralSnappedRect); case MediaTimeRemainingPart: return paintMediaTimeRemaining(o, paintInfo, integralSnappedRect); case MediaCurrentTimePart: return paintMediaCurrentTime(o, paintInfo, integralSnappedRect); case MediaControlsBackgroundPart: return paintMediaControlsBackground(o, paintInfo, integralSnappedRect); case MenulistButtonPart: case TextFieldPart: case TextAreaPart: case ListboxPart: return true; case SearchFieldPart: return paintSearchField(o, paintInfo, integralSnappedRect); case SearchFieldCancelButtonPart: return paintSearchFieldCancelButton(o, paintInfo, integralSnappedRect); case SearchFieldDecorationPart: return paintSearchFieldDecorationPart(o, paintInfo, integralSnappedRect); case SearchFieldResultsDecorationPart: return paintSearchFieldResultsDecorationPart(o, paintInfo, integralSnappedRect); case SearchFieldResultsButtonPart: return paintSearchFieldResultsButton(o, paintInfo, integralSnappedRect); case SnapshottedPluginOverlayPart: return paintSnapshottedPluginOverlay(o, paintInfo, integralSnappedRect); #if ENABLE(INPUT_SPEECH) case InputSpeechButtonPart: return paintInputFieldSpeechButton(o, paintInfo, integralSnappedRect); #endif #if ENABLE(SERVICE_CONTROLS) case ImageControlsButtonPart: return paintImageControlsButton(o, paintInfo, integralSnappedRect); #endif default: break; } return true; // We don't support the appearance, so let the normal background/border paint. }
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 if (shouldApplyViewportClip(m_layoutReplaced)) { // 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()); } }
bool RenderTheme::paintDecorations(const RenderObject& renderer, const PaintInfo& paintInfo, const LayoutRect& rect) { if (paintInfo.context->paintingDisabled()) return false; IntRect integralSnappedRect = pixelSnappedIntRect(rect); FloatRect devicePixelSnappedRect = pixelSnappedForPainting(rect, renderer.document().deviceScaleFactor()); // Call the appropriate paint method based off the appearance value. switch (renderer.style().appearance()) { case MenulistButtonPart: return paintMenuListButtonDecorations(renderer, paintInfo, integralSnappedRect); case TextFieldPart: return paintTextFieldDecorations(renderer, paintInfo, devicePixelSnappedRect); case TextAreaPart: return paintTextAreaDecorations(renderer, paintInfo, devicePixelSnappedRect); case CheckboxPart: return paintCheckboxDecorations(renderer, paintInfo, integralSnappedRect); case RadioPart: return paintRadioDecorations(renderer, paintInfo, integralSnappedRect); case PushButtonPart: return paintPushButtonDecorations(renderer, paintInfo, integralSnappedRect); case SquareButtonPart: return paintSquareButtonDecorations(renderer, paintInfo, integralSnappedRect); case ButtonPart: return paintButtonDecorations(renderer, paintInfo, integralSnappedRect); case MenulistPart: return paintMenuListDecorations(renderer, paintInfo, integralSnappedRect); case SliderThumbHorizontalPart: case SliderThumbVerticalPart: return paintSliderThumbDecorations(renderer, paintInfo, integralSnappedRect); case SearchFieldPart: return paintSearchFieldDecorations(renderer, paintInfo, integralSnappedRect); #if ENABLE(METER_ELEMENT) case MeterPart: case RelevancyLevelIndicatorPart: case ContinuousCapacityLevelIndicatorPart: case DiscreteCapacityLevelIndicatorPart: case RatingLevelIndicatorPart: #endif #if ENABLE(PROGRESS_ELEMENT) case ProgressBarPart: #endif case SliderHorizontalPart: case SliderVerticalPart: case ListboxPart: case DefaultButtonPart: case SearchFieldCancelButtonPart: case SearchFieldDecorationPart: case SearchFieldResultsDecorationPart: case SearchFieldResultsButtonPart: #if ENABLE(INPUT_SPEECH) case InputSpeechButtonPart: #endif #if ENABLE(SERVICE_CONTROLS) case ImageControlsButtonPart: #endif default: break; } return false; }
void RenderReplaced::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!shouldPaint(paintInfo, paintOffset)) return; LayoutPoint adjustedPaintOffset = paintOffset + location(); if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) 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 = 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) { 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(), style().colorSpace()); } }
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(); }
void Page::addRelevantRepaintedObject(RenderObject* object, const LayoutRect& objectPaintRect) { if (!isCountingRelevantRepaintedObjects()) return; // Objects inside sub-frames are not considered to be relevant. if (object->document().frame() != mainFrame()) return; RenderView* view = object->view(); if (!view) return; LayoutRect relevantRect = relevantViewRect(view); // The objects are only relevant if they are being painted within the viewRect(). if (!objectPaintRect.intersects(pixelSnappedIntRect(relevantRect))) return; IntRect snappedPaintRect = pixelSnappedIntRect(objectPaintRect); // If this object was previously counted as an unpainted object, remove it from that HashSet // and corresponding Region. FIXME: This doesn't do the right thing if the objects overlap. HashSet<RenderObject*>::iterator it = m_relevantUnpaintedRenderObjects.find(object); if (it != m_relevantUnpaintedRenderObjects.end()) { m_relevantUnpaintedRenderObjects.remove(it); m_relevantUnpaintedRegion.subtract(snappedPaintRect); } // Split the relevantRect into a top half and a bottom half. Making sure we have coverage in // both halves helps to prevent cases where we have a fully loaded menu bar or masthead with // no content beneath that. LayoutRect topRelevantRect = relevantRect; topRelevantRect.contract(LayoutSize(0, relevantRect.height() / 2)); LayoutRect bottomRelevantRect = topRelevantRect; bottomRelevantRect.setY(relevantRect.height() / 2); // If the rect straddles both Regions, split it appropriately. if (topRelevantRect.intersects(snappedPaintRect) && bottomRelevantRect.intersects(snappedPaintRect)) { IntRect topIntersection = snappedPaintRect; topIntersection.intersect(pixelSnappedIntRect(topRelevantRect)); m_topRelevantPaintedRegion.unite(topIntersection); IntRect bottomIntersection = snappedPaintRect; bottomIntersection.intersect(pixelSnappedIntRect(bottomRelevantRect)); m_bottomRelevantPaintedRegion.unite(bottomIntersection); } else if (topRelevantRect.intersects(snappedPaintRect)) m_topRelevantPaintedRegion.unite(snappedPaintRect); else m_bottomRelevantPaintedRegion.unite(snappedPaintRect); float topPaintedArea = m_topRelevantPaintedRegion.totalArea(); float bottomPaintedArea = m_bottomRelevantPaintedRegion.totalArea(); float viewArea = relevantRect.width() * relevantRect.height(); float ratioThatIsPaintedOnTop = topPaintedArea / viewArea; float ratioThatIsPaintedOnBottom = bottomPaintedArea / viewArea; float ratioOfViewThatIsUnpainted = m_relevantUnpaintedRegion.totalArea() / viewArea; if (ratioThatIsPaintedOnTop > (gMinimumPaintedAreaRatio / 2) && ratioThatIsPaintedOnBottom > (gMinimumPaintedAreaRatio / 2) && ratioOfViewThatIsUnpainted < gMaximumUnpaintedAreaRatio) { m_isCountingRelevantRepaintedObjects = false; resetRelevantPaintedObjectCounter(); if (Frame* frame = mainFrame()) frame->loader()->didLayout(DidHitRelevantRepaintedObjectsAreaThreshold); } }
IntRect SVGRootPainter::pixelSnappedSize(const LayoutPoint& paintOffset) const { return pixelSnappedIntRect(paintOffset, m_layoutSVGRoot.size()); }
std::unique_ptr<Shape> Shape::createRasterShape(Image* image, float threshold, const LayoutRect& imageR, const LayoutRect& marginR, WritingMode writingMode, float margin) { IntRect imageRect = pixelSnappedIntRect(imageR); IntRect marginRect = pixelSnappedIntRect(marginR); std::unique_ptr<RasterShapeIntervals> intervals = wrapUnique( new RasterShapeIntervals(marginRect.height(), -marginRect.y())); std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::create(imageRect.size()); if (image && imageBuffer) { // FIXME: This is not totally correct but it is needed to prevent shapes // that loads SVG Images during paint invalidations to mark layoutObjects // for layout, which is not allowed. See https://crbug.com/429346 ImageObserverDisabler disabler(image); SkPaint paint; IntRect imageSourceRect(IntPoint(), image->size()); IntRect imageDestRect(IntPoint(), imageRect.size()); image->draw(imageBuffer->canvas(), paint, imageDestRect, imageSourceRect, DoNotRespectImageOrientation, Image::DoNotClampImageToSourceRect); WTF::ArrayBufferContents contents; imageBuffer->getImageData(Unmultiplied, IntRect(IntPoint(), imageRect.size()), contents); DOMArrayBuffer* arrayBuffer = DOMArrayBuffer::create(contents); DOMUint8ClampedArray* pixelArray = DOMUint8ClampedArray::create(arrayBuffer, 0, arrayBuffer->byteLength()); unsigned pixelArrayOffset = 3; // Each pixel is four bytes: RGBA. uint8_t alphaPixelThreshold = threshold * 255; ASSERT(static_cast<unsigned>(imageRect.width() * imageRect.height() * 4) == pixelArray->length()); int minBufferY = std::max(0, marginRect.y() - imageRect.y()); int maxBufferY = std::min(imageRect.height(), marginRect.maxY() - imageRect.y()); for (int y = minBufferY; y < maxBufferY; ++y) { int startX = -1; for (int x = 0; x < imageRect.width(); ++x, pixelArrayOffset += 4) { uint8_t alpha = pixelArray->item(pixelArrayOffset); bool alphaAboveThreshold = alpha > alphaPixelThreshold; if (startX == -1 && alphaAboveThreshold) { startX = x; } else if (startX != -1 && (!alphaAboveThreshold || x == imageRect.width() - 1)) { int endX = alphaAboveThreshold ? x + 1 : x; intervals->intervalAt(y + imageRect.y()) .unite(IntShapeInterval(startX + imageRect.x(), endX + imageRect.x())); startX = -1; } } } } std::unique_ptr<RasterShape> rasterShape = wrapUnique(new RasterShape(std::move(intervals), marginRect.size())); rasterShape->m_writingMode = writingMode; rasterShape->m_margin = margin; return std::move(rasterShape); }
void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell)) return; LayoutTable* table = m_layoutTableCell.table(); if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild()) return; bool needsToPaintBorder = m_layoutTableCell.styleRef().hasBorder() && !table->collapseBorders(); if (!m_layoutTableCell.hasBackground() && !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder) return; LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent); LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(paintRect)); if (recorder.canUseCachedDrawing()) return; BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Normal); // Paint our cell background. paintBackgroundsBehindCell(paintInfo, paintOffset, &m_layoutTableCell); BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Inset); if (!needsToPaintBorder) return; BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, m_layoutTableCell.styleRef()); }