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