blob: 3ac8ce1dd396bd8695b68d3217bceb3dd2b2ddd3 [file] [log] [blame]
// 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"
#include "platform/graphics/paint/DrawingRecorder.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;
}
const DisplayItemClient& TableCellPainter::displayItemClientForBorders() const {
// TODO(wkorman): We may need to handle PaintInvalidationDelayedFull.
// http://crbug.com/657186
return m_layoutTableCell.usesCompositedCellDisplayItemClients()
? static_cast<const DisplayItemClient&>(
*m_layoutTableCell.collapsedBorderValues())
: m_layoutTableCell;
}
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;
const DisplayItemClient& client = displayItemClientForBorders();
if (DrawingRecorder::useCachedDrawingIfPossible(
graphicsContext, client,
static_cast<DisplayItem::Type>(displayItemType)))
return;
DrawingRecorder recorder(graphicsContext, client,
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;
const DisplayItemClient& client =
m_layoutTableCell.backgroundDisplayItemClient();
if (DrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, client,
type))
return;
LayoutRect paintRect =
paintRectNotIncludingVisualOverflow(adjustedPaintOffset);
DrawingRecorder recorder(paintInfo.context, client, type,
FloatRect(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,
SkBlendMode::kSrcOver, &backgroundObject);
}
}
void TableCellPainter::paintBoxDecorationBackground(
const PaintInfo& paintInfo,
const LayoutPoint& paintOffset) {
LayoutTable* table = m_layoutTableCell.table();
const ComputedStyle& style = m_layoutTableCell.styleRef();
if (!table->collapseBorders() && style.emptyCells() == EEmptyCells::Hide &&
!m_layoutTableCell.firstChild())
return;
bool needsToPaintBorder =
style.hasBorderDecoration() && !table->collapseBorders();
if (!style.hasBackground() && !style.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::paintNormalBoxShadow(paintInfo, paintRect, style);
paintBackground(paintInfo, paintRect, m_layoutTableCell);
// TODO(wangxianzhu): Calculate the inset shadow bounds by insetting paintRect
// by half widths of collapsed borders.
BoxPainter::paintInsetBoxShadow(paintInfo, paintRect, style);
if (!needsToPaintBorder)
return;
BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, style);
}
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