1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
// 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 "config.h"
#include "core/paint/TableSectionPainter.h"
#include "core/paint/BoxClipper.h"
#include "core/paint/GraphicsContextAnnotator.h"
#include "core/paint/ObjectPainter.h"
#include "core/paint/RenderDrawingRecorder.h"
#include "core/paint/TableCellPainter.h"
#include "core/paint/TableRowPainter.h"
#include "core/rendering/PaintInfo.h"
#include "core/rendering/RenderTable.h"
#include "core/rendering/RenderTableCell.h"
#include "core/rendering/RenderTableCol.h"
#include "core/rendering/RenderTableRow.h"
namespace blink {
void TableSectionPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
ANNOTATE_GRAPHICS_CONTEXT(paintInfo, &m_renderTableSection);
ASSERT(!m_renderTableSection.needsLayout());
// avoid crashing on bugs that cause us to paint with dirty layout
if (m_renderTableSection.needsLayout())
return;
unsigned totalRows = m_renderTableSection.numRows();
unsigned totalCols = m_renderTableSection.table()->columns().size();
if (!totalRows || !totalCols)
return;
LayoutPoint adjustedPaintOffset = paintOffset + m_renderTableSection.location();
{
BoxClipper boxClipper(m_renderTableSection, paintInfo, adjustedPaintOffset, ForceContentsClip);
paintObject(paintInfo, adjustedPaintOffset);
}
if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && m_renderTableSection.style()->visibility() == VISIBLE)
ObjectPainter(m_renderTableSection).paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, m_renderTableSection.size()));
}
static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
{
return elem1->rowIndex() < elem2->rowIndex();
}
// This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
// to sort both on rows and columns to properly issue paint invalidations.
static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
{
if (elem1->rowIndex() != elem2->rowIndex())
return elem1->rowIndex() < elem2->rowIndex();
return elem1->col() < elem2->col();
}
void TableSectionPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
LayoutRect localPaintInvalidationRect = paintInfo.rect;
localPaintInvalidationRect.moveBy(-paintOffset);
LayoutRect tableAlignedRect = m_renderTableSection.logicalRectForWritingModeAndDirection(localPaintInvalidationRect);
CellSpan dirtiedRows = m_renderTableSection.dirtiedRows(tableAlignedRect);
CellSpan dirtiedColumns = m_renderTableSection.dirtiedColumns(tableAlignedRect);
WillBeHeapHashSet<RawPtrWillBeMember<RenderTableCell> > overflowingCells = m_renderTableSection.overflowingCells();
if (dirtiedColumns.start() < dirtiedColumns.end()) {
if (!m_renderTableSection.hasMultipleCellLevels() && !overflowingCells.size()) {
if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
// Collapsed borders are painted from the bottom right to the top left so that precedence
// due to cell position is respected.
for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) {
unsigned row = r - 1;
for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) {
unsigned col = c - 1;
RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(row, col);
RenderTableCell* cell = current.primaryCell();
if (!cell || (row > dirtiedRows.start() && m_renderTableSection.primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(row, col - 1) == cell))
continue;
LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cell, paintOffset);
TableCellPainter(*cell).paintCollapsedBorders(paintInfo, cellPoint);
}
}
} else {
// Draw the dirty cells in the order that they appear.
for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
RenderTableRow* row = m_renderTableSection.rowRendererAt(r);
if (row && !row->hasSelfPaintingLayer())
TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset);
for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c);
RenderTableCell* cell = current.primaryCell();
if (!cell || (r > dirtiedRows.start() && m_renderTableSection.primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && m_renderTableSection.primaryCellAt(r, c - 1) == cell))
continue;
paintCell(cell, paintInfo, paintOffset);
}
}
}
} else {
// The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
#if ENABLE(ASSERT)
unsigned totalRows = m_renderTableSection.numRows();
unsigned totalCols = m_renderTableSection.table()->columns().size();
ASSERT(overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
#endif
// To make sure we properly paint invalidate the section, we paint invalidated all the overflowing cells that we collected.
Vector<RenderTableCell*> cells;
copyToVector(overflowingCells, cells);
HashSet<RenderTableCell*> spanningCells;
for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
RenderTableRow* row = m_renderTableSection.rowRendererAt(r);
if (row && !row->hasSelfPaintingLayer())
TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset);
for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
RenderTableSection::CellStruct& current = m_renderTableSection.cellAt(r, c);
if (!current.hasCells())
continue;
for (unsigned i = 0; i < current.cells.size(); ++i) {
if (overflowingCells.contains(current.cells[i]))
continue;
if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
if (!spanningCells.add(current.cells[i]).isNewEntry)
continue;
}
cells.append(current.cells[i]);
}
}
}
// Sort the dirty cells by paint order.
if (!overflowingCells.size())
std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
else
std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
for (unsigned i = cells.size(); i > 0; --i) {
LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cells[i - 1], paintOffset);
TableCellPainter(*cells[i - 1]).paintCollapsedBorders(paintInfo, cellPoint);
}
} else {
for (unsigned i = 0; i < cells.size(); ++i)
paintCell(cells[i], paintInfo, paintOffset);
}
}
}
}
void TableSectionPainter::paintCell(RenderTableCell* cell, const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
LayoutPoint cellPoint = m_renderTableSection.flipForWritingModeForChild(cell, paintOffset);
PaintPhase paintPhase = paintInfo.phase;
RenderTableRow* row = toRenderTableRow(cell->parent());
if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
// We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
// the column group, column, row group, row, and then the cell.
RenderTableCol* column = m_renderTableSection.table()->colElement(cell->col());
RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
TableCellPainter tableCellPainter(*cell);
RenderDrawingRecorder recorder(paintInfo.context, m_renderTableSection, paintPhase, tableCellPainter.paintBounds(paintOffset, TableCellPainter::AddOffsetFromParent));
if (!recorder.canUseCachedDrawing()) {
// Column groups and columns first.
// FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
// the stack, since we have already opened a transparency layer (potentially) for the table row group.
// Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
// cell.
tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, column);
// Paint the row group next.
tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, &m_renderTableSection);
// Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
// painting the row background for the cell.
if (!row->hasSelfPaintingLayer())
tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, row);
}
}
if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
cell->paint(paintInfo, cellPoint);
}
} // namespace blink
|