| Index: third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp
|
| diff --git a/third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp b/third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..58344aab4e0ae6907e719f12da8c5000bdbbcc44
|
| --- /dev/null
|
| +++ b/third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp
|
| @@ -0,0 +1,1187 @@
|
| +// 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/TableCollapsedBorderPainter.h"
|
| +
|
| +#include "core/layout/LayoutTableCell.h"
|
| +#include "core/layout/LayoutTableCol.h"
|
| +#include "core/paint/BlockPainter.h"
|
| +#include "core/paint/BoxPainter.h"
|
| +#include "core/paint/LayoutObjectDrawingRecorder.h"
|
| +#include "core/paint/PaintInfo.h"
|
| +#include "platform/graphics/GraphicsContextStateSaver.h"
|
| +#include <algorithm>
|
| +
|
| +namespace blink {
|
| +
|
| +// Comparator: returns
|
| +// -1 => e1 < e2
|
| +// 0 => e1 == e2
|
| +// 1 => e1 > e2
|
| +static int compareEdges(const TableCollapsedBorderPainter::EdgeRecord& e1, const TableCollapsedBorderPainter::EdgeRecord& e2)
|
| +{
|
| +
|
| + // if (!e1.m_border) {
|
| + // if (!e2.m_border)
|
| + // return 0;
|
| + // else
|
| + // return -1;
|
| + // }
|
| + // if (!e2.m_border)
|
| + // return 1;
|
| +
|
| + EBorderStyle e1Style = e1.m_border.style();
|
| + EBorderStyle e2Style = e2.m_border.style();
|
| +
|
| +// RULES: https://www.w3.org/TR/CSS2/tables.html#border-conflict-resolution
|
| +// 1) Hidden edges win
|
| +// 2) None edges lose
|
| +// 3) Wider border-width win
|
| +// 4) Louder border-style win
|
| +// 5) Higher precedence wins
|
| +
|
| + // Rule #1
|
| + if (e1Style == BorderStyleHidden) {
|
| + if (e2Style == BorderStyleHidden)
|
| + return 0;
|
| + // else
|
| + return 1;
|
| + }
|
| + if (e2Style == BorderStyleHidden)
|
| + return -1;
|
| +
|
| + // Rule #2
|
| + if (e1Style == BorderStyleNone) {
|
| + if (e2Style == BorderStyleNone)
|
| + return 0;
|
| + // else
|
| + return -1;
|
| + }
|
| + if (e2Style == BorderStyleNone)
|
| + return 1;
|
| +
|
| + // Rule #3
|
| + if (e1.m_border.width() < e2.m_border.width())
|
| + return -1;
|
| + // else
|
| + if (e2.m_border.width() < e1.m_border.width())
|
| + return 1;
|
| +
|
| + // Rule #4
|
| + if (e1Style > e2Style)
|
| + return 1;
|
| + // else
|
| + if (e2Style > e1Style)
|
| + return -1;
|
| +
|
| + // Rule #5
|
| + if (e1.m_precedence != e2.m_precedence)
|
| + return e1.m_precedence < e2.m_precedence ? -1 : 1;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +// Like compareEdges, except that HIDDEN edges lose
|
| +static int compareIntersectionEdges(const TableCollapsedBorderPainter::EdgeRecord& e1, const TableCollapsedBorderPainter::EdgeRecord& e2)
|
| +{
|
| + EBorderStyle e1Style = e1.m_border.style();
|
| + EBorderStyle e2Style = e2.m_border.style();
|
| + // Rule #1
|
| + if (e1Style == BorderStyleHidden) {
|
| + if (e2Style == BorderStyleHidden)
|
| + return 0;
|
| + // else
|
| + return -1;
|
| + }
|
| + if (e2Style == BorderStyleHidden)
|
| + return 1;
|
| + return compareEdges(e1, e2);
|
| +}
|
| +
|
| +#ifndef NDEBUG
|
| +
|
| +static std::string directionToStr(TableCollapsedBorderPainter::EdgeDirection dir)
|
| +{
|
| + switch (dir) {
|
| + case TableCollapsedBorderPainter::North: return "North";
|
| + case TableCollapsedBorderPainter::West: return "West";
|
| + case TableCollapsedBorderPainter::East: return "East";
|
| + case TableCollapsedBorderPainter::South: return "South";
|
| + case TableCollapsedBorderPainter::None: return "None";
|
| + }
|
| +}
|
| +
|
| +static std::string precedenceToStr(EBorderPrecedence p)
|
| +{
|
| + switch (p) {
|
| + case BorderPrecedenceOff: return "OFF";
|
| + case BorderPrecedenceTable: return "TABLE";
|
| + case BorderPrecedenceColumnGroup: return "COLGROUP";
|
| + case BorderPrecedenceColumn: return "COL";
|
| + case BorderPrecedenceRowGroup: return "ROWGROUP";
|
| + case BorderPrecedenceRow: return "ROW";
|
| + case BorderPrecedenceCell: return "CELL";
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::showEdge(unsigned row, unsigned col, EdgeDirection direction, EBorderPrecedence precedence, const BorderValue& border, Color resolvedColor) const
|
| +{
|
| + Color c = border.color().resolve(resolvedColor);
|
| + LOG(INFO) << "[" << row << " " << col << " " << directionToStr(direction)
|
| + << "] " << precedenceToStr(precedence) << " "
|
| + << border.width() << " " << c.serializedAsCSSComponentValue().ascii().data();
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::showEdges(bool showHidden) const
|
| +{
|
| + LOG(INFO) << "edges";
|
| + for (auto r = m_startVisibleRow; r <= m_endVisibleRow; r++) {
|
| + LOG(INFO) << "row:" << r;
|
| + for (auto c = m_startVisibleColumn; c < m_endVisibleColumn; c++) {
|
| + auto edgeIdx = edgeToIndex(r, c, East);
|
| + if (edgeIdx != npos) {
|
| + EdgeRecord edge = m_edges[edgeIdx];
|
| + if (showHidden || edge.m_precedence != BorderPrecedenceOff)
|
| + showEdge(r, c, East, edge.m_precedence, edge.m_border, edge.m_resolvedColor);
|
| + }
|
| + edgeIdx = edgeToIndex(r, c, South);
|
| + if (edgeIdx != npos) {
|
| + EdgeRecord edge = m_edges[edgeIdx];
|
| + if (showHidden || edge.m_precedence != BorderPrecedenceOff)
|
| + showEdge(r, c, South, edge.m_precedence, edge.m_border, edge.m_resolvedColor);
|
| + }
|
| + }
|
| + }
|
| +}
|
| +void TableCollapsedBorderPainter::showVisibleEdges(const VisibleEdgeContainer& edges) const
|
| +{
|
| + LOG(INFO) << "visedges";
|
| + for (auto edge = edges.begin() ; edge < edges.end(); edge++) {
|
| + showEdge((*edge).m_row, (*edge).m_column, (*edge).m_direction, (*edge).m_precedence, (*edge).m_border, (*edge).m_resolvedColor);
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::showIntersection(unsigned row, unsigned col) const
|
| +{
|
| + auto i = intersectionToIndex(row, col);
|
| + if (i != npos) {
|
| + const Intersection in = m_intersections[i];
|
| + if (in.m_direction != None) {
|
| + LOG(INFO) << "[" << row << ", " << col << "] "
|
| + << in.m_width.toInt() << " " << in.m_height.toInt() << " Win:" << directionToStr(in.m_direction);
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::showIntersections() const
|
| +{
|
| + LOG(INFO) << "intersections";
|
| + for (auto r = m_startVisibleRow; r <= m_endVisibleRow; r++) {
|
| + for (auto c = m_startVisibleColumn; c < m_endVisibleColumn; c++) {
|
| + showIntersection(r, c);
|
| + }
|
| + }
|
| +}
|
| +
|
| +#endif
|
| +
|
| +
|
| +void TableCollapsedBorderPainter::paintBorders(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const CellSpan& dirtyRows, const CellSpan& dirtyColumns, const TableCollapsedBorderPainter& previousPainter)
|
| +{
|
| + initEdges(dirtyRows, dirtyColumns, previousPainter);
|
| + // draw all the edges
|
| + // initial algorithm: just draw everything
|
| + // later algorithms: draw continuous lines if possible
|
| + initIntersections();
|
| +
|
| + paintEdges(paintInfo, paintOffset, dirtyRows, dirtyColumns);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::initEdges(const CellSpan& dirtyRows, const CellSpan& dirtyColumns, const TableCollapsedBorderPainter& previousPainter)
|
| +{
|
| + // Initialize sizes
|
| + // number of rows/cols in the section
|
| + m_numRows = m_layoutTableSection->numRows();
|
| + m_numEffectiveColumns = m_layoutTableSection->table()->numEffectiveColumns();
|
| +
|
| + // Coordinates of intersections whose edges we'd like to measure.
|
| + // Measurement area is dirtyRows x dirtyCols + single-cell wide border zone above/left
|
| + // of dirty area.
|
| + //
|
| + // Border zone is needed to measure intersections inside dirty area correctly.
|
| + //
|
| + // Number of intersections inside n-rows is n+1, inside m-cols is m+1
|
| + // start and end. End points to one beyond, equivalent to iterator's .end()
|
| + m_startVisibleRow = dirtyRows.start() == 0 ? 0 : dirtyRows.start() - 1;
|
| + m_startVisibleColumn = dirtyColumns.start() == 0 ? 0 : dirtyColumns.start() - 1;
|
| + m_endVisibleRow = dirtyRows.end() + 1;
|
| + m_endVisibleColumn = dirtyColumns.end() + 1;
|
| +
|
| + unsigned edgeRowCount = m_endVisibleRow - m_startVisibleRow;
|
| + unsigned edgeColumnCount = m_endVisibleColumn - m_startVisibleColumn;
|
| + m_edges.resize(2 * edgeRowCount * edgeColumnCount);
|
| + populateEdges(dirtyRows, dirtyColumns, previousPainter);
|
| +#ifndef NDEBUG
|
| + // showEdges();
|
| +#endif
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::initIntersections()
|
| +{
|
| + m_maxIntersectionWidth = 0;
|
| + m_maxIntersectionHeight = 0;
|
| + m_intersections.resize((m_endVisibleRow - m_startVisibleRow) * (m_endVisibleColumn - m_startVisibleColumn));
|
| + for (unsigned r = m_startVisibleRow; r < m_endVisibleRow; r++) {
|
| + for (unsigned c = m_startVisibleColumn; c < m_endVisibleColumn; c++) {
|
| + initIntersection(r, c);
|
| + }
|
| + }
|
| +#ifndef NDEBUG
|
| + // showIntersections();
|
| +#endif
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::initIntersection(unsigned row, unsigned col)
|
| +{
|
| + EdgeRecord edges[None];
|
| +
|
| + for (int d = North; d < None; d++) {
|
| + unsigned index = edgeToIndex(row, col, (EdgeDirection)d);
|
| + if (index != npos)
|
| + edges[d] = m_edges[index];
|
| + }
|
| +
|
| + EdgeDirection winner = North;
|
| + for (int d = West; d < None; d++) {
|
| + if (compareIntersectionEdges(edges[winner], edges[d]) == -1)
|
| + winner = (EdgeDirection)d;
|
| + }
|
| +
|
| + unsigned index = intersectionToIndex(row, col);
|
| + // only set if our winner is really an edge
|
| + if (edges[winner].m_precedence != BorderPrecedenceOff) {
|
| + unsigned width = 0;
|
| + unsigned height = 0;
|
| + if (winner == North || winner == South) {
|
| + width = edges[winner].m_border.width();
|
| + if (compareIntersectionEdges(edges[East], edges[West]) < 1) // West won
|
| + height = edges[West].m_border.width();
|
| + else
|
| + height = edges[East].m_border.width();
|
| + } else { // winner == East || West
|
| + height = edges[winner].m_border.width();
|
| + if (compareIntersectionEdges(edges[North], edges[South]) < 1) // South won
|
| + width = edges[South].m_border.width();
|
| + else
|
| + width = edges[North].m_border.width();
|
| + }
|
| + m_maxIntersectionHeight = std::max(m_maxIntersectionHeight, height);
|
| + m_maxIntersectionWidth = std::max(m_maxIntersectionWidth, width);
|
| + m_intersections[index] = Intersection(LayoutUnit(width), LayoutUnit(height), winner);
|
| + // LOG(INFO) << "In: " << row << " " << col << ": " << width << "px " << height << "px " << directionToStr(winner);
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::populateEdges(const CellSpan& dirtyRows, const CellSpan& dirtyColumns, const TableCollapsedBorderPainter& previousPainter)
|
| +{
|
| + VisibleEdgeContainer edges;
|
| +
|
| + // Iterate everything in reverse priority order
|
| +
|
| + // populate adjacent edges from adjacent section
|
| + getVisibleEdgesSiblingSection(edges, previousPainter);
|
| + mergeVisibleEdges(edges);
|
| +
|
| + LayoutTable * table = m_layoutTableSection->table();
|
| + // populate <table>
|
| + getVisibleEdgesTable(edges, m_layoutTableSection->table());
|
| + mergeVisibleEdges(edges);
|
| +
|
| + // populate <colgroup>
|
| + Vector<const LayoutTableCol*>colgroups;
|
| + for (LayoutTableCol* colgroup = table->firstColumn(); colgroup; colgroup = colgroup->nextColumn())
|
| + colgroups.append(colgroup);
|
| +
|
| + for (auto colGroup = colgroups.rbegin(); colGroup != colgroups.rend(); colGroup++) {
|
| + if ((*colGroup)->isTableColumnGroup()) {
|
| + getVisibleEdgesColgroup(edges, *colGroup);
|
| + mergeVisibleEdges(edges);
|
| + }
|
| + }
|
| + // populate <col>
|
| + for (auto col = colgroups.rbegin(); col != colgroups.rend(); col++) {
|
| + if ((*col)->isTableColumn()) {
|
| + getVisibleEdgesCol(edges, *col);
|
| + mergeVisibleEdges(edges);
|
| + }
|
| + }
|
| +
|
| + // populate <tbody>
|
| + getVisibleEdgesSection(edges);
|
| + mergeVisibleEdges(edges);
|
| + // populate <tr>
|
| + for (int r = dirtyRows.end() - 1; r >= (int) dirtyRows.start(); r--) {
|
| + const LayoutTableRow * row = m_layoutTableSection->rowLayoutObjectAt(r);
|
| + if (row) {
|
| + getVisibleEdgesRow(edges, row);
|
| + mergeVisibleEdges(edges);
|
| + }
|
| + }
|
| + // populate <td>, iterate in reverse
|
| + TextDirection direction = m_layoutTableSection->style()->direction();
|
| + for (int r = dirtyRows.end() - 1; r >= (int) dirtyRows.start(); r--) {
|
| + for (int c = dirtyColumns.end() - 1; c >= (int)dirtyColumns.start(); c--) {
|
| + // HashSet for primary cell to eliminate duplicates
|
| + const LayoutTableCell * cell = m_layoutTableSection->primaryCellAt(r, c);
|
| + if (cell) {
|
| + getVisibleEdgesCell(edges, cell, direction);
|
| + mergeVisibleEdges(edges);
|
| + }
|
| + }
|
| + }
|
| +#ifndef NDEBUG
|
| + // showEdges();
|
| +#endif
|
| +}
|
| +
|
| +// Gets edges of the neighboring section
|
| +void TableCollapsedBorderPainter::getVisibleEdgesSiblingSection(VisibleEdgeContainer& edges, const TableCollapsedBorderPainter& previousPainter)
|
| +{
|
| + edges.clear();
|
| +
|
| + bool isAbove = previousPainter.m_layoutTableSection == m_layoutTableSection->table()->sectionAbove(m_layoutTableSection);
|
| +
|
| + bool isBelow = previousPainter.m_layoutTableSection == m_layoutTableSection->table()->sectionBelow(m_layoutTableSection);
|
| +
|
| + if (!isAbove && !isBelow) {
|
| + // Happens if we are the first section
|
| + // LOG(INFO) << "not sure where sibling section is";
|
| + return;
|
| + }
|
| +
|
| + unsigned siblingRow = isAbove ? previousPainter.m_numRows : 0;
|
| + unsigned myRow = isAbove ? 0 : m_numRows;
|
| + for (unsigned c = previousPainter.m_startVisibleColumn; c <= previousPainter.m_endVisibleColumn; c++) {
|
| + auto siblingIndex = previousPainter.edgeToIndex(siblingRow, c, East);
|
| + auto myIndex = edgeToIndex(myRow, c, East);
|
| + if (siblingIndex != npos && myIndex != npos) {
|
| + EdgeRecord siblingEdge = previousPainter.m_edges[siblingIndex];
|
| + edges.append(VisibleEdgeRecord(myRow, c, East, siblingEdge.m_border, siblingEdge.m_precedence, siblingEdge.m_resolvedColor));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesTable(VisibleEdgeContainer& edges, const LayoutTable * table)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = table->resolveColor(CSSPropertyColor);
|
| + const ComputedStyle* borderStyle = table->style();
|
| + const BorderValue* topBorder = &borderStyle->borderTop();
|
| + const BorderValue* rightBorder = &borderStyle->borderRight();
|
| + const BorderValue* bottomBorder = &borderStyle->borderBottom();
|
| + const BorderValue* leftBorder = &borderStyle->borderLeft();
|
| +
|
| + rotateBorders(m_layoutTableSection->style()->direction(), table->style()->getWritingMode(), &topBorder, &rightBorder, &bottomBorder, &leftBorder);
|
| +
|
| + // top row, only if we abut table top
|
| + if (this->m_layoutTableSection == table->topNonEmptySection()) {
|
| + fillHorizontalEdges(edges, 0, 0, m_numEffectiveColumns, *topBorder, BorderPrecedenceTable, resolvedColor);
|
| + }
|
| + // bottom row, only if we abut table bottom
|
| + if (this->m_layoutTableSection == table->bottomNonEmptySection()) {
|
| + fillHorizontalEdges(edges, m_numRows, 0, m_numEffectiveColumns, *bottomBorder, BorderPrecedenceTable, resolvedColor);
|
| + }
|
| + fillVerticalEdges(edges, 0, 0, m_numRows, *leftBorder, BorderPrecedenceTable, resolvedColor);
|
| + fillVerticalEdges(edges, m_numEffectiveColumns, 0, m_numRows, *rightBorder, BorderPrecedenceTable, resolvedColor);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesSection(VisibleEdgeContainer& edges)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = m_layoutTableSection->resolveColor(CSSPropertyColor);
|
| + // Traverse all the section edges, and assign them
|
| + fillVisibleRect(edges, 0, 0, m_numRows, m_numEffectiveColumns, m_layoutTableSection->style(),
|
| + LTR, m_layoutTableSection->style()->getWritingMode(), resolvedColor, BorderPrecedenceRowGroup);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesColgroup(VisibleEdgeContainer& edges, const LayoutTableCol * colgroup)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = colgroup->resolveColor(CSSPropertyColor);
|
| + Vector<unsigned> indexes = colgroup->getEffectiveColumnIndexes();
|
| + if (indexes.size() > 0) {
|
| + unsigned startColIndex = indexes[0];
|
| + unsigned endColIndex = indexes.last() + 1;
|
| + fillVisibleRect(edges, 0, startColIndex, m_numRows, endColIndex, colgroup->style(),
|
| + colgroup->style()->direction(), colgroup->style()->getWritingMode(), resolvedColor, BorderPrecedenceColumnGroup);
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesCol(VisibleEdgeContainer& edges, const LayoutTableCol * col)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = col->resolveColor(CSSPropertyColor);
|
| + LayoutTable* table = m_layoutTableSection->table();
|
| + if (!table)
|
| + return;
|
| +
|
| + Vector<unsigned> indexes = col->getEffectiveColumnIndexes();
|
| + if (indexes.size() > 0) {
|
| + // fill multiple virtual columns
|
| + for (auto idx = indexes.rbegin(); idx!= indexes.rend(); ++idx) {
|
| + fillVisibleRect(edges, 0, *idx, m_numRows, *idx + 1, col->style(),
|
| + col->style()->direction(), col->style()->getWritingMode(), resolvedColor, BorderPrecedenceColumn);
|
| + }
|
| + }
|
| + // unsigned startAbsoluteColIndex = table->colElementToAbsoluteColumn(col);
|
| + // unsigned endAbsoluteColIndex = startAbsoluteColIndex + col->span();
|
| + // unsigned startColIndex = table->absoluteColumnToEffectiveColumn(startAbsoluteColIndex);
|
| + // unsigned endColIndex = table->absoluteColumnToEffectiveColumn(endAbsoluteColIndex);
|
| +
|
| + // // We replicat column span times. Weird, but that is the standard:
|
| + // // "For the purposes of the CSS table model, the col element is expected to be treated as if it was present as many times as its span attribute specifies."
|
| + // for (auto tmpEnd = endColIndex; tmpEnd > startColIndex; tmpEnd--) {
|
| + // fillVisibleRect(edges, 0, tmpEnd - 1, m_numRows, tmpEnd, col->style(),
|
| + // col->style()->direction(), col->style()->getWritingMode(), resolvedColor, BorderPrecedenceColumn);
|
| + // }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesRow(VisibleEdgeContainer& edges, const LayoutTableRow * row)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = row->resolveColor(CSSPropertyColor);
|
| + unsigned rowIndex = row->rowIndex();
|
| + // We get direction from section because row direction only applies to cells,
|
| + // not the entire row.
|
| + fillVisibleRect(edges, rowIndex, 0, rowIndex + 1, m_numEffectiveColumns, row->style(),
|
| + m_layoutTableSection->style()->direction(), row->style()->getWritingMode(), resolvedColor, BorderPrecedenceRow);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::getVisibleEdgesCell(VisibleEdgeContainer& edges, const LayoutTableCell * cell, TextDirection direction)
|
| +{
|
| + edges.clear();
|
| + Color resolvedColor = cell->resolveColor(CSSPropertyColor);
|
| +
|
| + if (!cell->parent()) {
|
| + LOG(INFO) << "DETACHED CELL";
|
| + return;
|
| + }
|
| +
|
| + const LayoutTable * table = m_layoutTableSection->table();
|
| + unsigned startRow = cell->parent() ? cell->rowIndex() : 65000;
|
| + unsigned absoluteCol = cell->absoluteColumnIndex();
|
| +
|
| +
|
| + unsigned effStartCol = table->absoluteColumnToEffectiveColumn(absoluteCol);
|
| + unsigned effEndCol = table->absoluteColumnToEffectiveColumn(absoluteCol + cell->colSpan());
|
| + unsigned endRow = std::min(startRow + cell->rowSpan(), m_numRows);
|
| +
|
| + // LOG(INFO) << "m_columns: " << table->effectiveColumns().size()
|
| + // << " startRow: " << startRow << " endRow: " << endRow
|
| + // << " startCol: " << effStartCol << " endCol:" << effEndCol
|
| + // << " abscol:" << absoluteCol;
|
| +
|
| + // Fill border
|
| + // cell's direction is LTR because we do not rotate cell's borders
|
| + fillVisibleRect(edges, startRow, effStartCol, endRow, effEndCol, cell->style(),
|
| + direction, cell->style()->getWritingMode(), resolvedColor, BorderPrecedenceCell);
|
| +
|
| + // Hide row inner edges
|
| + for (unsigned row = startRow + 1; row < endRow; row++) {
|
| + if (row >= m_startVisibleRow && row < m_endVisibleRow) {
|
| + for (unsigned effCol = effStartCol; effCol < effEndCol; effCol++) {
|
| + if (effCol >= m_startVisibleColumn && effCol < m_endVisibleColumn) {
|
| + edges.append(VisibleEdgeRecord(row, effCol, East, m_hiddenBorder, BorderPrecedenceCell, resolvedColor, true));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + // Hide col inner edges
|
| + for (unsigned effCol = effStartCol + 1; effCol < effEndCol; effCol++) {
|
| + if (effCol >= m_startVisibleColumn && effCol < m_endVisibleColumn) {
|
| + for (unsigned row = startRow; row < endRow; row++) {
|
| + if (row >= m_startVisibleRow && row < m_endVisibleRow) {
|
| + edges.append(VisibleEdgeRecord(row, effCol, South, m_hiddenBorder, BorderPrecedenceCell, resolvedColor, true));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +#ifdef NDEBUG
|
| + // showVisibleEdges(edges);
|
| +#endif
|
| +}
|
| +
|
| +
|
| +void TableCollapsedBorderPainter::rotateBorders(TextDirection textDirection, WritingMode writingMode,
|
| + const BorderValue** top, const BorderValue** right, const BorderValue** bottom, const BorderValue** left) const
|
| +{
|
| + bool isLTR = blink::isLeftToRightDirection(textDirection);
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + if (isLTR)
|
| + {} // do nothing
|
| + else {
|
| + std::swap(*right, *left);
|
| + }
|
| + } else if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + if (isLTR) {
|
| + auto tmp = *top;
|
| + *top = *right;
|
| + *right = *bottom;
|
| + *bottom = *left;
|
| + *left = tmp;
|
| + } else {
|
| + std::swap(*top, *right);
|
| + std::swap(*bottom, *left);
|
| + }
|
| + } else { // isFlippedLinesWritingMode
|
| + if (isLTR) {
|
| + std::swap(*bottom, *right);
|
| + std::swap(*left, *top);
|
| + } else {
|
| + auto tmp = *top;
|
| + *top = *left;
|
| + *left = *bottom;
|
| + *bottom = *right;
|
| + *right = tmp;
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::fillVisibleRect(VisibleEdgeContainer& edges,
|
| + unsigned startRow, unsigned startColumn, unsigned endRow, unsigned endColumn,
|
| + const ComputedStyle* borderStyle, TextDirection textDirection, WritingMode writingMode,
|
| + Color resolvedColor, EBorderPrecedence precedence)
|
| +{
|
| + const BorderValue* topBorder = &borderStyle->borderTop();
|
| + const BorderValue* bottomBorder = &borderStyle->borderBottom();
|
| + const BorderValue* rightBorder = &borderStyle->borderRight();
|
| + const BorderValue* leftBorder = &borderStyle->borderLeft();
|
| +
|
| + rotateBorders(textDirection, writingMode, &topBorder, &rightBorder, &bottomBorder, &leftBorder);
|
| +
|
| + fillHorizontalEdges(edges, startRow, startColumn, endColumn, *topBorder, precedence, resolvedColor);
|
| + fillHorizontalEdges(edges, endRow, startColumn, endColumn, *bottomBorder, precedence, resolvedColor);
|
| + fillVerticalEdges(edges, startColumn, startRow, endRow, *leftBorder, precedence, resolvedColor);
|
| + fillVerticalEdges(edges, endColumn, startRow, endRow, *rightBorder, precedence, resolvedColor);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::fillHorizontalEdges(VisibleEdgeContainer& edges, unsigned row, unsigned startColumn, unsigned endColumn,
|
| + const BorderValue &border, EBorderPrecedence precedence, Color resolvedColor)
|
| +{
|
| + if (border.style() == BorderStyleNone)
|
| + return;
|
| +
|
| + if (row >= m_startVisibleRow && row < m_endVisibleRow) {
|
| + for (unsigned c = startColumn; c < endColumn; c++) {
|
| + // FIXME: modify for loop instead of comparing guard
|
| + if (c >= m_startVisibleColumn && c < m_endVisibleColumn)
|
| + edges.append(VisibleEdgeRecord(row, c, East, border, precedence, resolvedColor));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::fillVerticalEdges(VisibleEdgeContainer& edges, unsigned column, unsigned startRow, unsigned endRow, const BorderValue &border, EBorderPrecedence precedence, Color resolvedColor)
|
| +{
|
| + if (border.style() == BorderStyleNone)
|
| + return;
|
| +
|
| + if (column >= m_startVisibleColumn && column < m_endVisibleColumn) {
|
| + for (unsigned r = startRow; r < endRow; r++) {
|
| + // FIXME: modify for loop instead of comparing guard
|
| + if (r >= m_startVisibleRow && r < m_endVisibleRow)
|
| + edges.append(VisibleEdgeRecord(r, column, South, border, precedence, resolvedColor));
|
| + }
|
| + }
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::mergeVisibleEdges(VisibleEdgeContainer& edges)
|
| +{
|
| + for (auto edge = edges.begin(); edge < edges.end(); edge++) {
|
| + unsigned idx = edgeToIndex((*edge).m_row, (*edge).m_column, (*edge).m_direction);
|
| + ASSERT(idx != npos);
|
| + EdgeRecord edgeRecord((*edge).m_border, (*edge).m_precedence, (*edge).m_resolvedColor);
|
| + if ((*edge).m_forceRemoval)
|
| + m_edges[idx] = edgeRecord;
|
| + // if both edges are same priority, new edge wins
|
| + if (compareEdges(m_edges[idx], edgeRecord ) != 1)
|
| + m_edges[idx] = edgeRecord;
|
| + }
|
| +}
|
| +
|
| +static EBorderStyle collapsedBorderStyle(EBorderStyle style)
|
| +{
|
| + if (style == BorderStyleOutset)
|
| + return BorderStyleGroove;
|
| + if (style == BorderStyleInset)
|
| + return BorderStyleRidge;
|
| + return style;
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::paintEdges(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const CellSpan& rows, const CellSpan& cols)
|
| +{
|
| + // see TableCellPainter::paintCollapsedBorders
|
| + LayoutRect paintRect = m_layoutTableSection->getCellPhysicalPosition(rows.start(), cols.start());
|
| + paintRect.unite(m_layoutTableSection->getCellPhysicalPosition(rows.end() > 0 ? rows.end() - 1 : 0, cols.end() > 0 ? cols.end() - 1 : 0));
|
| +
|
| + // paintRect size calculation is not exact, we expand by maxborder, instead of exact border
|
| + paintRect.expand(LayoutSize(LayoutUnit(m_maxIntersectionWidth * 2), LayoutUnit(m_maxIntersectionHeight * 2)));
|
| + paintRect.moveBy(paintOffset);
|
| +
|
| + LayoutPoint location = m_layoutTableSection->location();
|
| + paintRect.moveBy(location);
|
| +
|
| + if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(
|
| + paintInfo.context,
|
| + *m_layoutTableSection,
|
| + DisplayItem::TableCollapsedBorderBase))
|
| + return;
|
| + LayoutObjectDrawingRecorder recorder(
|
| + paintInfo.context,
|
| + *m_layoutTableSection,
|
| + DisplayItem::TableCollapsedBorderBase,
|
| + paintRect);
|
| +
|
| + // Paint edges carefully to get them all
|
| + // Every row paint : ┌┌┌┌┌, then |
|
| + // Bottom border paint: _____
|
| + WritingMode writingMode = m_layoutTableSection->style()->getWritingMode();
|
| + TextDirection textDirection = m_layoutTableSection->style()->direction();
|
| + for (unsigned r = rows.start(); r < rows.end(); r++) {
|
| + // const LayoutTableRow * row = m_layoutTableSection->rowLayoutObjectAt(r);
|
| + // if (row)
|
| + // textDirection = row->style()->direction();
|
| + for (unsigned c = cols.start(); c < cols.end(); c++) {
|
| + paintOneEdge(paintInfo, paintOffset, r, c, East, writingMode, textDirection);
|
| + paintOneEdge(paintInfo, paintOffset, r, c, South, writingMode, textDirection);
|
| + }
|
| + // paint the last column on edge
|
| + paintOneEdge(paintInfo, paintOffset, r, cols.end(), South, writingMode, textDirection);
|
| + }
|
| + // Paint the last row
|
| + for (unsigned c = cols.start(); c < cols.end(); c++)
|
| + paintOneEdge(paintInfo, paintOffset, rows.end(), c, East, writingMode, textDirection);
|
| +}
|
| +
|
| +// Returns a rect that encloses 'direction' border of the cellRect
|
| +LayoutRect TableCollapsedBorderPainter::cellRectAsBorder(const LayoutRect& cellRect, BoxSide side, unsigned borderWidth, WritingMode writingMode, TextDirection textDirection)
|
| +{
|
| +
|
| + unsigned smallHalf = borderWidth / 2; // C++ always rounds down
|
| + unsigned bigHalf = borderWidth - smallHalf;
|
| +
|
| + unsigned topOffset;
|
| + unsigned leftOffset;
|
| +
|
| + /* Tricky: if the border width is odd, we need a consistent way of
|
| + distributing the extra pixel. The rule is:
|
| + - south and east edges always get the extra pixel on the inside
|
| + of the cell
|
| + */
|
| +
|
| + bool isLTR = blink::isLeftToRightDirection(textDirection);
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + if (isLTR) {
|
| + topOffset = smallHalf;
|
| + leftOffset = smallHalf;
|
| + } else {
|
| + topOffset = smallHalf;
|
| + leftOffset = bigHalf;
|
| + }
|
| + } else { // vertical
|
| + if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + if (isLTR) {
|
| + topOffset = smallHalf;
|
| + leftOffset = bigHalf;
|
| + } else {
|
| + topOffset = bigHalf;
|
| + leftOffset = bigHalf;
|
| + }
|
| + } else { // flippedLines
|
| + if (isLTR) {
|
| + topOffset = smallHalf;
|
| + leftOffset = smallHalf;
|
| + } else {
|
| + topOffset = bigHalf;
|
| + leftOffset = smallHalf;
|
| + }
|
| + }
|
| + }
|
| + switch (side) {
|
| + case BSTop:
|
| + return LayoutRect(
|
| + LayoutPoint(cellRect.x(), cellRect.y() - topOffset),
|
| + LayoutSize(cellRect.width(), LayoutUnit(borderWidth)));
|
| + case BSBottom:
|
| + return LayoutRect(
|
| + LayoutPoint(cellRect.x(), cellRect.maxY() - topOffset),
|
| + LayoutSize(cellRect.width(), LayoutUnit(borderWidth)));
|
| + case BSLeft:
|
| + return LayoutRect(
|
| + LayoutPoint(cellRect.x() - leftOffset, cellRect.y()),
|
| + LayoutSize(LayoutUnit(borderWidth), cellRect.height()));
|
| + case BSRight:
|
| + return LayoutRect(
|
| + LayoutPoint(cellRect.maxX() - leftOffset, cellRect.y()),
|
| + LayoutSize(LayoutUnit(borderWidth), cellRect.height()));
|
| + }
|
| +
|
| +}
|
| +//
|
| +LayoutRect TableCollapsedBorderPainter::edgePaintPosition(unsigned row, unsigned col, unsigned borderWidth, EdgeDirection direction, WritingMode writingMode, TextDirection textDirection)
|
| +{
|
| + // Compute edge's size from size of the cell
|
| + LayoutRect position;
|
| +
|
| + bool isLTR = blink::isLeftToRightDirection(textDirection);
|
| + BoxSide side = BSLeft;
|
| + LayoutRect cellRect;
|
| + // If this is the last row intersection, get size from cell in next to last
|
| + if (row == m_numRows) {
|
| + ASSERT(direction == East);
|
| + cellRect = getCellPhysicalPosition(row - 1, col);
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + side = BSBottom;
|
| + } else if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + side = BSLeft;
|
| + } else {
|
| + side = BSRight;
|
| + }
|
| + } else if (col == m_numEffectiveColumns) {
|
| + // If this is the last column, get size from the cell before
|
| + ASSERT(direction == South);
|
| + cellRect = getCellPhysicalPosition(row, col - 1);
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + side = isLTR ? BSRight : BSLeft;
|
| + } else if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + side = isLTR ? BSBottom : BSTop;
|
| + } else {
|
| + side = isLTR ? BSBottom : BSTop;
|
| + }
|
| + } else {
|
| + cellRect = getCellPhysicalPosition(row, col);
|
| + if (direction == East) {
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + side = BSTop;
|
| + } else if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + side = BSRight;
|
| + } else {
|
| + side = BSLeft;
|
| + }
|
| + } else { // direction == South
|
| + if (blink::isHorizontalWritingMode(writingMode)) {
|
| + side = isLTR ? BSLeft : BSRight;
|
| + } else if (blink::isFlippedBlocksWritingMode(writingMode)) {
|
| + side = isLTR ? BSTop : BSBottom;
|
| + } else { // flippedLines
|
| + side = isLTR ? BSTop : BSBottom;
|
| + }
|
| + }
|
| + }
|
| + return cellRectAsBorder(cellRect, side, borderWidth, writingMode, textDirection);
|
| +}
|
| +
|
| +// Paints an edge starting from intersection[row, col] in direction
|
| +void TableCollapsedBorderPainter::paintOneEdge(
|
| + const PaintInfo& paintInfo, const LayoutPoint& paintOffset,
|
| + unsigned row, unsigned col, EdgeDirection direction,
|
| + WritingMode writingMode, TextDirection textDirection)
|
| +{
|
| + ASSERT(direction == South || direction == East);
|
| +
|
| + unsigned idx = edgeToIndex(row, col, direction);
|
| + ASSERT(idx != npos);
|
| + const BorderValue& border = m_edges[idx].m_border;
|
| +
|
| + if (border.style() == BorderStyleHidden || !border.nonZero())
|
| + return;
|
| +
|
| +#ifndef NDEBUG
|
| + // LOG(INFO) << "painting";
|
| + // EdgeRecord edge = m_edges[idx];
|
| + // showEdge(row, col, direction, edge.m_precedence, edge.m_border, border->color().resolve(m_edges[idx].m_resolvedColor));
|
| +#endif
|
| + LayoutRect position = edgePaintPosition(row, col, border.width(), direction, writingMode, textDirection);
|
| +
|
| + // LOG(INFO) << "paint " << position.x().toInt() << ", " << position.y().toInt() << "; "
|
| + // << position.width().toInt() << ", " << position.height().toInt();
|
| +
|
| + adjustForIntersections(position, row, col, direction, writingMode, textDirection);
|
| +
|
| + // LOG(INFO) << "intersection " << position.x().toInt() << ", " << position.y().toInt() << "; "
|
| + // << position.width().toInt() << ", " << position.height().toInt();
|
| +
|
| + // Offset by sectionLocation
|
| + LayoutPoint sectionLocation = m_layoutTableSection->location();
|
| + position.moveBy(sectionLocation);
|
| + position.moveBy(paintOffset);
|
| +
|
| + BoxSide side;
|
| + if (blink::isHorizontalWritingMode(writingMode))
|
| + side = direction == East ? BSTop : BSLeft;
|
| + else
|
| + side = direction == East ? BSLeft : BSTop;
|
| +
|
| + ObjectPainter::drawLineForBoxSide(
|
| + paintInfo.context,
|
| + position.x(), position.y(), position.maxX(), position.maxY(), // left, top, right, bottom,
|
| + side,
|
| + border.color().resolve(m_edges[idx].m_resolvedColor),
|
| + collapsedBorderStyle(border.style()),
|
| + 0, 0, true);
|
| +}
|
| +
|
| +void TableCollapsedBorderPainter::adjustForIntersections(LayoutRect& position, unsigned row, unsigned col, EdgeDirection direction, WritingMode writingMode, TextDirection textDirection)
|
| +{
|
| +
|
| + ASSERT(direction == South || direction == East);
|
| +
|
| + const Intersection * startIntersection = nullptr;
|
| + const Intersection * endIntersection = nullptr;
|
| + bool winnerStart = false;
|
| + bool winnerEnd = false;
|
| + // START intersection
|
| + unsigned index = intersectionToIndex(row, col);
|
| + if (index != npos) {
|
| + startIntersection = &m_intersections[index];
|
| + winnerStart = startIntersection->m_direction == direction;
|
| + }
|
| + // END intersection
|
| + if (direction == South)
|
| + row += 1;
|
| + else
|
| + col += 1;
|
| + index = intersectionToIndex(row, col);
|
| + if (index != npos) {
|
| + endIntersection = &m_intersections[index];
|
| + if (direction == South)
|
| + winnerEnd = endIntersection->m_direction == North;
|
| + else
|
| + winnerEnd = endIntersection->m_direction == West;
|
| + }
|
| +
|
| + bool isLTR = blink::isLeftToRightDirection(textDirection);
|
| +
|
| + if (blink::isHorizontalWritingMode(writingMode))
|
| + adjustForIntersectionsHorizontal(position, direction, isLTR, startIntersection, winnerStart, endIntersection, winnerEnd);
|
| + else if (blink::isFlippedBlocksWritingMode(writingMode))
|
| + adjustForIntersectionsFlippedBlocks(position, direction, isLTR, startIntersection, winnerStart, endIntersection, winnerEnd);
|
| + else if (blink::isFlippedLinesWritingMode(writingMode))
|
| + adjustForIntersectionsFlippedLines(position, direction, isLTR, startIntersection, winnerStart, endIntersection, winnerEnd);
|
| + else
|
| + ASSERT(false);
|
| +}
|
| +
|
| +/*
|
| +writing-mode: HORIZONTAL-TB
|
| + N
|
| +compass: W--+--E intersection: <--width -->
|
| + S
|
| + start intersection
|
| + South edge, win : expand top by height/2
|
| + South edge, lose: shrink top by height/2
|
| + East edge, win : expand left by width/2
|
| + East edge, lose : shrink left by width/2
|
| + end intersection
|
| + South edge, win : expand bottom by height/2
|
| + South edge lose : shrink bottom by height/2
|
| + East edge, win : expand right by width/2
|
| + East edge, lose : shrink right by width/2
|
| +
|
| +writing-mode: HORIZONTAL(RTL)
|
| + N
|
| +compass: E--+--W intersection: <-- width -->
|
| + S
|
| +same as HORIZONTAL-TB in South direction
|
| + start intersection
|
| + South: same as LTR
|
| + East edge, win : expand right by width/2
|
| + East edge, lose : shrink right by width/2
|
| + end intersection
|
| + South: same as LTR
|
| + East edge, win : expand left by width/2
|
| + East edge, lose : shrink left by width/2
|
| +*/
|
| +void TableCollapsedBorderPainter::adjustForIntersectionsHorizontal(LayoutRect& position, EdgeDirection direction, bool isLTR,
|
| + const Intersection* startIntersection, bool winnerStart,
|
| + const Intersection* endIntersection, bool winnerEnd)
|
| +{
|
| + if (startIntersection) {
|
| +
|
| + if (direction == South) {
|
| + unsigned smallDelta = startIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerStart)
|
| + position.expandEdges(LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + else
|
| + position.contractEdges(LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + } else { // East
|
| + unsigned smallDelta = startIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta));
|
| + }
|
| + } else { // RTL
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (endIntersection) {
|
| + if (direction == South) {
|
| + unsigned smallDelta = endIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0));
|
| + } else { // East
|
| + unsigned smallDelta = endIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerEnd) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0));
|
| + } else { // loser
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + } else { // RTL
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +writing-mode: VERTICAL-RL (flipped blocks)
|
| + W
|
| +compass: vertical: S--+--N intersection: <--- height ---->
|
| + E
|
| +
|
| + start intersection
|
| + South edge, win : expand right by height/2
|
| + South edge, lose: shrink right by height/2
|
| + East edge, win : expand top by width/2
|
| + East edge, lose : shrink top by width/2
|
| + end intersection
|
| + South edge, win : expand left by height/2
|
| + South edge, lose: shrink left by height/2
|
| + East edge, win : expand bottom by width/2
|
| + East edge, lose : shrink bottom by width/2
|
| +
|
| +writing-mode: VERTICAL-RL RTL
|
| + E
|
| +compass: vertical: S--+--N intersection: <--- height ---->
|
| + W
|
| + start intersection
|
| + South edge, win : expand right by height/2
|
| + South edge, lose: shrink right by height/2
|
| + East edge, win : expand bottom by width/2
|
| + East edge, lose : shrink bottom by width/2
|
| + end intersection
|
| + South edge, win : expand left by height/2
|
| + South edge, lose: shrink left by height/2
|
| + East edge, win : expand top by width/2
|
| + East edge, lose : shrink top by width/2
|
| +
|
| +*/
|
| +void TableCollapsedBorderPainter::adjustForIntersectionsFlippedBlocks(LayoutRect& position, EdgeDirection direction, bool isLTR,
|
| + const Intersection* startIntersection, bool winnerStart,
|
| + const Intersection* endIntersection, bool winnerEnd)
|
| +{
|
| + if (startIntersection) {
|
| +
|
| + if (direction == South) {
|
| + unsigned smallDelta = startIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerStart)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0));
|
| + } else { // East
|
| + unsigned smallDelta = startIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + } else { // RTL
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (endIntersection) {
|
| + if (direction == South) {
|
| + unsigned smallDelta = endIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta));
|
| + } else { // East
|
| + unsigned smallDelta = endIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0));
|
| + else // loser
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0));
|
| + } else { // RTL
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + else
|
| + position.contractEdges(LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/*
|
| +writing-mode: VERTICAL-LR (flipped lines)
|
| + W
|
| +compass: rotated N--+--S
|
| + E
|
| + start intersection
|
| + East edge, win : expand top by width/2
|
| + East edge, lose : shrink top by width/2
|
| + South edge, win : expand left by height/2
|
| + South edge, lose: shrink left by height/2
|
| + end intersection
|
| + East edge, win : expand bottom by width/2
|
| + East edge, lose : shrink bottom by width/2
|
| + South edge, win : expand right by height/2
|
| + South edge lose : shrink right by height/2
|
| +
|
| +writing-mode: VERTICAL-LR, DIRECTION RTL
|
| + E
|
| +compass: N-+-S intersection <--height -->
|
| + W
|
| + start intersection
|
| + East edge. win : expand bottom by width / 2
|
| + East edge, lose: shrink bottom by width / 2
|
| + end intersection
|
| + East edge, win : expand top by width / 2
|
| + East edge, lose: expand top by width / 2
|
| +*/
|
| +
|
| +void TableCollapsedBorderPainter::adjustForIntersectionsFlippedLines(LayoutRect& position, EdgeDirection direction, bool isLTR,
|
| + const Intersection* startIntersection, bool winnerStart,
|
| + const Intersection* endIntersection, bool winnerEnd)
|
| +{
|
| + if (startIntersection) {
|
| +
|
| + if (direction == South) {
|
| + unsigned smallDelta = startIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerStart)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta));
|
| + } else { // East
|
| + unsigned smallDelta = startIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = startIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + } else { // RTL
|
| + if (winnerStart) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0));
|
| + }
|
| + }
|
| + }
|
| + }
|
| + if (endIntersection) {
|
| + if (direction == South) {
|
| + unsigned smallDelta = endIntersection->m_height.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_height.toUnsigned() - smallDelta;
|
| + if (winnerEnd)
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0));
|
| + else
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0));
|
| + } else { // East
|
| + unsigned smallDelta = endIntersection->m_width.toUnsigned() / 2;
|
| + unsigned bigDelta = endIntersection->m_width.toUnsigned() - smallDelta;
|
| + if (isLTR) {
|
| + if (winnerEnd) {
|
| + position.expandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(bigDelta), LayoutUnit(0));
|
| + } else { // loser
|
| + position.contractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(smallDelta), LayoutUnit(0));
|
| + }
|
| + } else { // RTL
|
| + if (winnerEnd) {
|
| + position.expandEdges(LayoutUnit(bigDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + } else {
|
| + position.contractEdges(LayoutUnit(smallDelta), LayoutUnit(0), LayoutUnit(0), LayoutUnit(0));
|
| + }
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +// returns edge index for position, TableCollapsedBorderPainter::npos if out of range
|
| +// topLeft intersection is 0, 0, bottomRight is numRows+1, numColumns + 1
|
| +unsigned TableCollapsedBorderPainter::edgeToIndex(unsigned intersectionRow, unsigned intersectionColumn, EdgeDirection direction) const
|
| +{
|
| +/* Edges map to m_edges like this:
|
| + - each intersection 'stores' only two edges: east and south edges
|
| + - to map edge to index:
|
| + - find intersection so that requested edge is east or south
|
| + - edge is row * cols * 2 + (east ? 0 : 1)
|
| + - for example:
|
| + - North edge at intersection 5, 3, is also South edge at intersection 4, 3
|
| + - its index is 4 (rows) * 3 (cols) * 2 + 1 (for South)
|
| +*/
|
| + switch (direction) {
|
| + case North:
|
| + intersectionRow -= 1;
|
| + direction = South;
|
| + break;
|
| + case West:
|
| + intersectionColumn -= 1;
|
| + direction = East;
|
| + break;
|
| + case South:
|
| + case East:
|
| + case None:
|
| + break;
|
| + }
|
| + // Check if we are inside the covered area
|
| + if (intersectionRow < m_startVisibleRow || intersectionColumn < m_startVisibleColumn
|
| + || intersectionRow >= m_endVisibleRow || intersectionColumn >= m_endVisibleColumn) {
|
| + // LOG(ERROR) << "GOT AN NPOS";
|
| + return TableCollapsedBorderPainter::npos;
|
| + }
|
| +
|
| + unsigned index = (intersectionRow - m_startVisibleRow) * (m_endVisibleColumn - m_startVisibleColumn) * 2;
|
| + index += (intersectionColumn - m_startVisibleColumn) * 2;
|
| + return direction == East ? index : index + 1; // each vertex holds [East, South]
|
| +}
|
| +
|
| +unsigned TableCollapsedBorderPainter::intersectionToIndex(unsigned row, unsigned column) const
|
| +{
|
| + if (row < m_startVisibleRow || column < m_startVisibleColumn
|
| + || row >= m_endVisibleRow || column >= m_endVisibleColumn )
|
| + return TableCollapsedBorderPainter::npos;
|
| + return (row - m_startVisibleRow) * (m_endVisibleColumn - m_startVisibleColumn) + column;
|
| +}
|
| +
|
| +} // blink
|
|
|