| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/paint/TableCellPainter.h" |
| |
| #include "core/layout/LayoutTableCell.h" |
| #include "core/paint/BlockPainter.h" |
| #include "core/paint/BoxPainter.h" |
| #include "core/paint/LayoutObjectDrawingRecorder.h" |
| #include "core/paint/ObjectPainter.h" |
| #include "core/paint/PaintInfo.h" |
| #include "platform/graphics/GraphicsContextStateSaver.h" |
| |
| namespace blink { |
| |
| static const CollapsedBorderValue& collapsedLeftBorder(const ComputedStyle& styleForCellFlow, const LayoutTableCell::CollapsedBorderValues& values) |
| { |
| if (styleForCellFlow.isHorizontalWritingMode()) |
| return styleForCellFlow.isLeftToRightDirection() ? values.startBorder : values.endBorder; |
| return styleForCellFlow.isFlippedBlocksWritingMode() ? values.afterBorder : values.beforeBorder; |
| } |
| |
| static const CollapsedBorderValue& collapsedRightBorder(const ComputedStyle& styleForCellFlow, const LayoutTableCell::CollapsedBorderValues& values) |
| { |
| if (styleForCellFlow.isHorizontalWritingMode()) |
| return styleForCellFlow.isLeftToRightDirection() ? values.endBorder : values.startBorder; |
| return styleForCellFlow.isFlippedBlocksWritingMode() ? values.beforeBorder : values.afterBorder; |
| } |
| |
| static const CollapsedBorderValue& collapsedTopBorder(const ComputedStyle& styleForCellFlow, const LayoutTableCell::CollapsedBorderValues& values) |
| { |
| if (styleForCellFlow.isHorizontalWritingMode()) |
| return values.beforeBorder; |
| return styleForCellFlow.isLeftToRightDirection() ? values.startBorder : values.endBorder; |
| } |
| |
| static const CollapsedBorderValue& collapsedBottomBorder(const ComputedStyle& styleForCellFlow, const LayoutTableCell::CollapsedBorderValues& values) |
| { |
| if (styleForCellFlow.isHorizontalWritingMode()) |
| return values.afterBorder; |
| return styleForCellFlow.isLeftToRightDirection() ? values.endBorder : values.startBorder; |
| } |
| |
| void TableCellPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| { |
| BlockPainter(m_layoutTableCell).paint(paintInfo, paintOffset); |
| } |
| |
| static EBorderStyle collapsedBorderStyle(EBorderStyle style) |
| { |
| if (style == BorderStyleOutset) |
| return BorderStyleGroove; |
| if (style == BorderStyleInset) |
| return BorderStyleRidge; |
| return style; |
| } |
| |
| void TableCellPainter::paintCollapsedBorders(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const CollapsedBorderValue& currentBorderValue) |
| { |
| if (m_layoutTableCell.style()->visibility() != EVisibility::Visible) |
| return; |
| |
| LayoutPoint adjustedPaintOffset = paintOffset + m_layoutTableCell.location(); |
| if (!BlockPainter(m_layoutTableCell).intersectsPaintRect(paintInfo, adjustedPaintOffset)) |
| return; |
| |
| const LayoutTableCell::CollapsedBorderValues* values = m_layoutTableCell.collapsedBorderValues(); |
| if (!values) |
| return; |
| |
| const ComputedStyle& styleForCellFlow = m_layoutTableCell.styleForCellFlow(); |
| const CollapsedBorderValue& leftBorderValue = collapsedLeftBorder(styleForCellFlow, *values); |
| const CollapsedBorderValue& rightBorderValue = collapsedRightBorder(styleForCellFlow, *values); |
| const CollapsedBorderValue& topBorderValue = collapsedTopBorder(styleForCellFlow, *values); |
| const CollapsedBorderValue& bottomBorderValue = collapsedBottomBorder(styleForCellFlow, *values); |
| |
| int displayItemType = DisplayItem::kTableCollapsedBorderBase; |
| if (topBorderValue.shouldPaint(currentBorderValue)) |
| displayItemType |= DisplayItem::TableCollapsedBorderTop; |
| if (bottomBorderValue.shouldPaint(currentBorderValue)) |
| displayItemType |= DisplayItem::TableCollapsedBorderBottom; |
| if (leftBorderValue.shouldPaint(currentBorderValue)) |
| displayItemType |= DisplayItem::TableCollapsedBorderLeft; |
| if (rightBorderValue.shouldPaint(currentBorderValue)) |
| displayItemType |= DisplayItem::TableCollapsedBorderRight; |
| if (displayItemType == DisplayItem::kTableCollapsedBorderBase) |
| return; |
| |
| int topWidth = topBorderValue.width(); |
| int bottomWidth = bottomBorderValue.width(); |
| int leftWidth = leftBorderValue.width(); |
| int rightWidth = rightBorderValue.width(); |
| |
| // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. |
| LayoutRect paintRect = paintRectNotIncludingVisualOverflow(adjustedPaintOffset); |
| 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); |
| |
| 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 TableCellPainter::paintContainerBackgroundBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutObject& backgroundObject, DisplayItem::Type type) |
| { |
| DCHECK(backgroundObject != m_layoutTableCell); |
| |
| if (m_layoutTableCell.style()->visibility() != EVisibility::Visible) |
| return; |
| |
| LayoutPoint adjustedPaintOffset = paintOffset + m_layoutTableCell.location(); |
| if (!BlockPainter(m_layoutTableCell).intersectsPaintRect(paintInfo, adjustedPaintOffset)) |
| return; |
| |
| LayoutTable* table = m_layoutTableCell.table(); |
| if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EEmptyCells::Hide && !m_layoutTableCell.firstChild()) |
| return; |
| |
| if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, type)) |
| return; |
| |
| LayoutRect paintRect = paintRectNotIncludingVisualOverflow(adjustedPaintOffset); |
| LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, type, paintRect); |
| paintBackground(paintInfo, paintRect, backgroundObject); |
| } |
| |
| void TableCellPainter::paintBackground(const PaintInfo& paintInfo, const LayoutRect& paintRect, const LayoutObject& backgroundObject) |
| { |
| if (m_layoutTableCell.backgroundStolenForBeingBody()) |
| return; |
| |
| Color c = backgroundObject.resolveColor(CSSPropertyBackgroundColor); |
| const FillLayer& bgLayer = backgroundObject.styleRef().backgroundLayers(); |
| if (bgLayer.hasImage() || c.alpha()) { |
| // We have to clip here because the background would paint |
| // on top of the borders otherwise. This only matters for cells and rows. |
| bool shouldClip = backgroundObject.hasLayer() && (backgroundObject == m_layoutTableCell || backgroundObject == m_layoutTableCell.parent()) && m_layoutTableCell.table()->collapseBorders(); |
| GraphicsContextStateSaver stateSaver(paintInfo.context, shouldClip); |
| if (shouldClip) { |
| LayoutRect clipRect(paintRect.location(), m_layoutTableCell.size()); |
| clipRect.expand(m_layoutTableCell.borderInsets()); |
| paintInfo.context.clip(pixelSnappedIntRect(clipRect)); |
| } |
| BoxPainter(m_layoutTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, SkXfermode::kSrcOver_Mode, &backgroundObject); |
| } |
| } |
| |
| void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| { |
| LayoutTable* table = m_layoutTableCell.table(); |
| if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EEmptyCells::Hide && !m_layoutTableCell.firstChild()) |
| return; |
| |
| bool needsToPaintBorder = m_layoutTableCell.styleRef().hasBorderDecoration() && !table->collapseBorders(); |
| if (!m_layoutTableCell.styleRef().hasBackground() && !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder) |
| return; |
| |
| if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, DisplayItem::kBoxDecorationBackground)) |
| 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::kBoxDecorationBackground, pixelSnappedIntRect(visualOverflowRect)); |
| |
| LayoutRect paintRect = paintRectNotIncludingVisualOverflow(paintOffset); |
| |
| BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Normal); |
| paintBackground(paintInfo, paintRect, m_layoutTableCell); |
| BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Inset); |
| |
| if (!needsToPaintBorder) |
| return; |
| |
| BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, m_layoutTableCell.styleRef()); |
| } |
| |
| void TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) |
| { |
| if (m_layoutTableCell.style()->visibility() != EVisibility::Visible || paintInfo.phase != PaintPhaseMask) |
| return; |
| |
| LayoutTable* tableElt = m_layoutTableCell.table(); |
| if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EEmptyCells::Hide && !m_layoutTableCell.firstChild()) |
| return; |
| |
| if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, paintInfo.phase)) |
| return; |
| |
| LayoutRect paintRect = paintRectNotIncludingVisualOverflow(paintOffset); |
| LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, paintInfo.phase, paintRect); |
| BoxPainter(m_layoutTableCell).paintMaskImages(paintInfo, paintRect); |
| } |
| |
| LayoutRect TableCellPainter::paintRectNotIncludingVisualOverflow(const LayoutPoint& paintOffset) |
| { |
| return LayoutRect(paintOffset, LayoutSize(m_layoutTableCell.pixelSnappedSize())); |
| } |
| |
| } // namespace blink |
| |