#include "config.h"
#include "core/rendering/RenderTableSection.h"
+#include "core/paint/TableSectionPainter.h"
#include <limits>
#include "core/rendering/GraphicsContextAnnotator.h"
#include "core/rendering/HitTestResult.h"
using namespace HTMLNames;
-// Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
+// This variable is used to balance the memory consumption vs the paint invalidation time on big tables.
static unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
-static float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
static inline void setRowLogicalHeightToRowStyleLogicalHeight(RenderTableSection::RowStruct& row)
{
remainder += (extraHeight * rowHeight) % totalHeight;
}
+// This is mainly used to distribute whole extra rowspanning height in percent rows when all spanning rows are
+// percent rows.
+// Distributing whole extra rowspanning height in percent rows based on the ratios of percent because this method works
+// same as percent distribution when only percent rows are present and percent is 100. Also works perfectly fine when
+// percent is not equal to 100.
+void RenderTableSection::distributeWholeExtraRowSpanHeightToPercentRows(RenderTableCell* cell, int totalPercent, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
+{
+ if (!extraRowSpanningHeight || !totalPercent)
+ return;
+
+ const unsigned rowSpan = cell->rowSpan();
+ const unsigned rowIndex = cell->rowIndex();
+ int remainder = 0;
+
+ int accumulatedPositionIncrease = 0;
+ for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
+ if (m_grid[row].logicalHeight.isPercent()) {
+ updatePositionIncreasedWithRowHeight(extraRowSpanningHeight, m_grid[row].logicalHeight.percent(), totalPercent, accumulatedPositionIncrease, remainder);
+
+ // While whole extra spanning height is distributing in percent spanning rows, rational parts remains
+ // in every integer division. So accumulating all remainder part in integer division and when total remainder
+ // is equvalent to divisor then 1 unit increased in row position.
+ // Note that this algorithm is biased towards adding more space towards the lower rows.
+ if (remainder >= totalPercent) {
+ remainder -= totalPercent;
+ accumulatedPositionIncrease++;
+ }
+ }
+ m_rowPos[row + 1] += accumulatedPositionIncrease;
+ }
+
+ ASSERT(!remainder);
+
+ extraRowSpanningHeight -= accumulatedPositionIncrease;
+}
+
void RenderTableSection::distributeExtraRowSpanHeightToAutoRows(RenderTableCell* cell, int totalAutoRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
{
if (!extraRowSpanningHeight || !totalAutoRowsHeight)
int extraRowSpanningHeight = spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing - spanningRowsHeight.totalRowsHeight;
- distributeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
- distributeExtraRowSpanHeightToAutoRows(cell, totalAutoRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
- distributeExtraRowSpanHeightToRemainingRows(cell, totalRemainingRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
+ if (totalPercent < 100 && !totalAutoRowsHeight && !totalRemainingRowsHeight) {
+ // Distributing whole extra rowspanning height in percent row when only non-percent rows height is 0.
+ distributeWholeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
+ } else {
+ distributeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
+ distributeExtraRowSpanHeightToAutoRows(cell, totalAutoRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
+ distributeExtraRowSpanHeightToRemainingRows(cell, totalRemainingRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
+ }
ASSERT(!extraRowSpanningHeight);
LayoutSize childOffset(cell->location() - oldCellRect.location());
if (childOffset.width() || childOffset.height()) {
- // If the child moved, we have to repaint it as well as any floating/positioned
+ // If the child moved, we have to issue paint invalidations to it as well as any floating/positioned
// descendants. An exception is if we need a layout. In this case, we know we're going to
- // repaint ourselves (and the child) anyway.
+ // issue paint invalidations ourselves (and the child) anyway.
if (!table()->selfNeedsLayout() && cell->checkForPaintInvalidation())
cell->setMayNeedPaintInvalidation(true);
}
void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
- ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
-
- ASSERT(!needsLayout());
- // avoid crashing on bugs that cause us to paint with dirty layout
- if (needsLayout())
- return;
-
- unsigned totalRows = m_grid.size();
- unsigned totalCols = table()->columns().size();
-
- if (!totalRows || !totalCols)
- return;
-
- LayoutPoint adjustedPaintOffset = paintOffset + location();
-
- PaintPhase phase = paintInfo.phase;
- bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
- paintObject(paintInfo, adjustedPaintOffset);
- if (pushedClip)
- popContentsClip(paintInfo, phase, adjustedPaintOffset);
-
- if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE)
- paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, 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 repaint.
-static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
-{
- if (elem1->rowIndex() != elem2->rowIndex())
- return elem1->rowIndex() < elem2->rowIndex();
-
- return elem1->col() < elem2->col();
-}
-
-void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
-{
- LayoutPoint cellPoint = 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 = table()->colElement(cell->col());
- RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
-
- // 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.
- cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
- cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
-
- // Paint the row group next.
- cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
-
- // 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())
- cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row);
- }
- if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
- cell->paint(paintInfo, cellPoint);
+ TableSectionPainter(*this).paint(paintInfo, paintOffset);
}
LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
CellSpan coveredRows = spannedRows(damageRect);
- // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
+ // To issue paint invalidations for the border we might need to paint invalidate the first or last row even if they are not spanned themselves.
if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
--coveredRows.start();
CellSpan coveredColumns = spannedColumns(damageRect);
const Vector<int>& columnPos = table()->columnPositions();
- // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
+ // To issue paint invalidations for the border we might need to paint invalidate the first or last column even if they are not spanned themselves.
if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
--coveredColumns.start();
void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
- LayoutRect localRepaintRect = paintInfo.rect;
- localRepaintRect.moveBy(-paintOffset);
-
- LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
-
- CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
- CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
-
- if (dirtiedColumns.start() < dirtiedColumns.end()) {
- if (!m_hasMultipleCellLevels && !m_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;
- CellStruct& current = cellAt(row, col);
- RenderTableCell* cell = current.primaryCell();
- if (!cell || (row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell))
- continue;
- LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
- 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_grid[r].rowRenderer;
- if (row && !row->hasSelfPaintingLayer())
- row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
- for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
- CellStruct& current = cellAt(r, c);
- RenderTableCell* cell = current.primaryCell();
- if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && 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_grid.size();
- unsigned totalCols = table()->columns().size();
- ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
-#endif
-
- // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
- Vector<RenderTableCell*> cells;
- copyToVector(m_overflowingCells, cells);
-
- HashSet<RenderTableCell*> spanningCells;
-
- for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
- RenderTableRow* row = m_grid[r].rowRenderer;
- if (row && !row->hasSelfPaintingLayer())
- row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
- for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
- CellStruct& current = cellAt(r, c);
- if (!current.hasCells())
- continue;
- for (unsigned i = 0; i < current.cells.size(); ++i) {
- if (m_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 (!m_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 = flipForWritingModeForChild(cells[i - 1], paintOffset);
- cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
- }
- } else {
- for (unsigned i = 0; i < cells.size(); ++i)
- paintCell(cells[i], paintInfo, paintOffset);
- }
- }
- }
+ TableSectionPainter(*this).paintObject(paintInfo, paintOffset);
}
void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
{
- // FIXME: Examine cells and repaint only the rect the image paints in.
- paintInvalidationForWholeRenderer();
+ // FIXME: Examine cells and issue paint invalidations of only the rect the image paints in.
+ setShouldDoFullPaintInvalidation(true);
}
void RenderTableSection::recalcCells()
}
// FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
-void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
+void RenderTableSection::rowLogicalHeightChanged(RenderTableRow* row)
{
if (needsCellRecalc())
return;
+ unsigned rowIndex = row->rowIndex();
setRowLogicalHeightToRowStyleLogicalHeight(m_grid[rowIndex]);
for (RenderTableCell* cell = m_grid[rowIndex].rowRenderer->firstCell(); cell; cell = cell->nextCell())