Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(575)

Unified Diff: third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp

Issue 2791433003: Fix Border collapsing with colpsan / rowspan cells
Patch Set: bug 2902 Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..e5da41fcd30b46e7d80e17a3b2cce4adc9722177
--- /dev/null
+++ b/third_party/WebKit/Source/core/paint/TableCollapsedBorderPainter.cpp
@@ -0,0 +1,1314 @@
+// 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 <algorithm>
+#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/ObjectPainter.h"
+#include "core/paint/PaintInfo.h"
+#include "platform/graphics/GraphicsContextStateSaver.h"
+
+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.border_) {
+ // if (!e2.border_)
+ // return 0;
+ // else
+ // return -1;
+ // }
+ // if (!e2.border_)
+ // return 1;
+ EBorderStyle e1_style = e1.border_.Style();
+ EBorderStyle e2_style = e2.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 (e1_style == kBorderStyleHidden) {
+ if (e2_style == kBorderStyleHidden)
+ return 0;
+ // else
+ return 1;
+ }
+ if (e2_style == kBorderStyleHidden)
+ return -1;
+ // Rule #2
+ if (e1_style == kBorderStyleNone) {
+ if (e2_style == kBorderStyleNone)
+ return 0;
+ // else
+ return -1;
+ }
+ if (e2_style == kBorderStyleNone)
+ return 1;
+ // Rule #3
+ if (e1.border_.Width() < e2.border_.Width())
+ return -1;
+ // else
+ if (e2.border_.Width() < e1.border_.Width())
+ return 1;
+ // Rule #4
+ if (e1_style > e2_style)
+ return 1;
+ // else
+ if (e2_style > e1_style)
+ return -1;
+ // Rule #5
+ if (e1.precedence_ != e2.precedence_)
+ return e1.precedence_ < e2.precedence_ ? -1 : 1;
+ return 0;
+}
+
+// Like CompareEdges, except that HIDDEN edges lose
+static int CompareIntersectionEdges(
+ const TableCollapsedBorderPainter::EdgeRecord& e1,
+ const TableCollapsedBorderPainter::EdgeRecord& e2) {
+ EBorderStyle e1_style = e1.border_.Style();
+ EBorderStyle e2_style = e2.border_.Style();
+ // Rule #1
+ if (e1_style == kBorderStyleHidden) {
+ if (e2_style == kBorderStyleHidden)
+ return 0;
+ // else
+ return -1;
+ }
+ if (e2_style == kBorderStyleHidden)
+ return 1;
+ return CompareEdges(e1, e2);
+}
+
+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 kBorderPrecedenceOff:
+ return "OFF";
+ case kBorderPrecedenceTable:
+ return "TABLE";
+ case kBorderPrecedenceColumnGroup:
+ return "COLGROUP";
+ case kBorderPrecedenceColumn:
+ return "COL";
+ case kBorderPrecedenceRowGroup:
+ return "ROWGROUP";
+ case kBorderPrecedenceRow:
+ return "ROW";
+ case kBorderPrecedenceCell:
+ return "CELL";
+ }
+}
+
+void TableCollapsedBorderPainter::ShowEdge(unsigned row,
+ unsigned col,
+ EdgeDirection direction,
+ EBorderPrecedence precedence,
+ const BorderValue& border,
+ Color resolved_color) const {
+ Color c = border.GetColor().Resolve(resolved_color);
+ LOG(INFO) << "[" << row << " " << col << " " << DirectionToStr(direction)
+ << "] " << PrecedenceToStr(precedence) << " " << border.Width()
+ << " " << c.SerializedAsCSSComponentValue().Ascii().data();
+}
+
+void TableCollapsedBorderPainter::ShowEdges(bool show_hidden) const {
+ LOG(INFO) << "edges";
+ for (auto r = start_visible_row_; r <= end_visible_row_; r++) {
+ LOG(INFO) << "row:" << r;
+ for (auto c = start_visible_column_; c < end_visible_column_; c++) {
+ auto edgeIdx = EdgeToIndex(r, c, East);
+ if (edgeIdx != npos) {
+ EdgeRecord edge = edges_[edgeIdx];
+ if (show_hidden || edge.precedence_ != kBorderPrecedenceOff) {
+ ShowEdge(r, c, East, edge.precedence_, edge.border_,
+ edge.resolved_color_);
+ }
+ }
+ edgeIdx = EdgeToIndex(r, c, South);
+ if (edgeIdx != npos) {
+ EdgeRecord edge = edges_[edgeIdx];
+ if (show_hidden || edge.precedence_ != kBorderPrecedenceOff) {
+ ShowEdge(r, c, South, edge.precedence_, edge.border_,
+ edge.resolved_color_);
+ }
+ }
+ }
+ }
+}
+
+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).direction_,
+ (*edge).precedence_, (*edge).border_, (*edge).resolved_color_);
+ }
+}
+
+void TableCollapsedBorderPainter::ShowIntersection(unsigned row,
+ unsigned col) const {
+ auto i = IntersectionToIndex(row, col);
+ if (i != npos) {
+ const Intersection in = intersections_[i];
+ if (in.direction_ != None) {
+ LOG(INFO) << "[" << row << ", " << col << "] " << in.width_.ToInt() << " "
+ << in.height_.ToInt()
+ << " Win:" << DirectionToStr(in.direction_);
+ }
+ }
+}
+
+void TableCollapsedBorderPainter::ShowIntersections() const {
+ LOG(INFO) << "intersections";
+ for (auto r = start_visible_row_; r <= end_visible_row_; r++) {
+ for (auto c = start_visible_column_; c < end_visible_column_; c++) {
+ ShowIntersection(r, c);
+ }
+ }
+}
+
+void TableCollapsedBorderPainter::PaintBorders(
+ const PaintInfo& paint_info,
+ const LayoutPoint& paint_offset,
+ const CellSpan& dirty_rows,
+ const CellSpan& dirty_columns,
+ const TableCollapsedBorderPainter& previous_painter) {
+ InitEdges(dirty_rows, dirty_columns, previous_painter);
+ // draw all the edges
+ // initial algorithm: just draw everything
+ // later algorithms: draw continuous lines if possible
+ InitIntersections();
+ PaintEdges(paint_info, paint_offset, dirty_rows, dirty_columns);
+}
+
+void TableCollapsedBorderPainter::InitEdges(
+ const CellSpan& dirty_rows,
+ const CellSpan& dirty_columns,
+ const TableCollapsedBorderPainter& previous_painter) {
+ // Initialize sizes
+ // number of rows/cols in the section
+ num_rows_ = layout_table_section_->NumRows();
+ num_effective_columns_ =
+ layout_table_section_->Table()->NumEffectiveColumns();
+ // Coordinates of intersections whose edges we'd like to measure.
+ // Measurement area is dirty_rows 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()
+ start_visible_row_ = dirty_rows.Start() == 0 ? 0 : dirty_rows.Start() - 1;
+ start_visible_column_ =
+ dirty_columns.Start() == 0 ? 0 : dirty_columns.Start() - 1;
+ end_visible_row_ = dirty_rows.end() + 1;
+ end_visible_column_ = dirty_columns.end() + 1;
+ unsigned edgeRowCount = end_visible_row_ - start_visible_row_;
+ unsigned edgeColumnCount = end_visible_column_ - start_visible_column_;
+ edges_.Resize(2 * edgeRowCount * edgeColumnCount);
+ PopulateEdges(dirty_rows, dirty_columns, previous_painter);
+ // ShowEdges();
+}
+
+void TableCollapsedBorderPainter::InitIntersections() {
+ max_intersection_width_ = 0;
+ max_intersection_height_ = 0;
+ intersections_.Resize((end_visible_row_ - start_visible_row_) *
+ (end_visible_column_ - start_visible_column_));
+ for (unsigned r = start_visible_row_; r < end_visible_row_; r++) {
+ for (unsigned c = start_visible_column_; c < end_visible_column_; c++) {
+ InitIntersection(r, c);
+ }
+ }
+ // ShowIntersections();
+}
+
+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] = 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].precedence_ != kBorderPrecedenceOff) {
+ unsigned width = 0;
+ unsigned height = 0;
+ if (winner == North || winner == South) {
+ width = edges[winner].border_.Width();
+ if (CompareIntersectionEdges(edges[East], edges[West]) < 1) // West won
+ height = edges[West].border_.Width();
+ else
+ height = edges[East].border_.Width();
+ } else { // winner == East || West
+ height = edges[winner].border_.Width();
+ if (CompareIntersectionEdges(edges[North], edges[South]) <
+ 1) // South won
+ width = edges[South].border_.Width();
+ else
+ width = edges[North].border_.Width();
+ }
+ max_intersection_height_ = std::max(max_intersection_height_, height);
+ max_intersection_width_ = std::max(max_intersection_width_, width);
+ intersections_[index] =
+ Intersection(LayoutUnit(width), LayoutUnit(height), winner);
+ // LOG(INFO) << "In: " << row << " " << col << ": " << width << "px " <<
+ // height << "px " << DirectionToStr(winner);
+ }
+}
+
+void TableCollapsedBorderPainter::PopulateEdges(
+ const CellSpan& dirty_rows,
+ const CellSpan& dirty_columns,
+ const TableCollapsedBorderPainter& previous_painter) {
+ VisibleEdgeContainer edges;
+ // Iterate everything in reverse priority order
+ // populate adjacent edges from adjacent section
+ GetVisibleEdgesSiblingSection(edges, previous_painter);
+ MergeVisibleEdges(edges);
+ LayoutTable* table = layout_table_section_->Table();
+ // populate <table>
+ GetVisibleEdgesTable(edges, layout_table_section_->Table());
+ MergeVisibleEdges(edges);
+ // populate <colgroup>
+ Vector<const LayoutTableCol*> colgroups;
+ for (LayoutTableCol* colgroup = table->FirstColumn(); colgroup;
+ colgroup = colgroup->NextColumn())
+ colgroups.push_back(colgroup);
+ for (auto col_group = colgroups.rbegin(); col_group != colgroups.rend();
+ col_group++) {
+ if ((*col_group)->IsTableColumnGroup()) {
+ GetVisibleEdgesColgroup(edges, *col_group);
+ 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 = dirty_rows.end() - 1; r >= (int)dirty_rows.Start(); r--) {
+ const LayoutTableRow* row = layout_table_section_->RowLayoutObjectAt(r);
+ if (row) {
+ GetVisibleEdgesRow(edges, row);
+ MergeVisibleEdges(edges);
+ }
+ }
+ // populate <td>, iterate in reverse
+ TextDirection direction = layout_table_section_->Style()->Direction();
+ for (int r = dirty_rows.end() - 1; r >= (int)dirty_rows.Start(); r--) {
+ for (int c = dirty_columns.end() - 1; c >= (int)dirty_columns.Start();
+ c--) {
+ // HashSet for primary cell to eliminate duplicates
+ const LayoutTableCell* cell = layout_table_section_->PrimaryCellAt(r, c);
+ if (cell) {
+ GetVisibleEdgesCell(edges, cell, direction);
+ MergeVisibleEdges(edges);
+ }
+ }
+ }
+ // ShowEdges();
+}
+
+// Gets edges of the neighboring section
+void TableCollapsedBorderPainter::GetVisibleEdgesSiblingSection(
+ VisibleEdgeContainer& edges,
+ const TableCollapsedBorderPainter& previous_painter) {
+ edges.clear();
+ bool is_above =
+ previous_painter.layout_table_section_ ==
+ layout_table_section_->Table()->SectionAbove(layout_table_section_);
+ bool is_below =
+ previous_painter.layout_table_section_ ==
+ layout_table_section_->Table()->SectionBelow(layout_table_section_);
+ if (!is_above && !is_below) {
+ // Happens if we are the first section
+ // LOG(INFO) << "not sure where sibling section is";
+ return;
+ }
+ unsigned siblingRow = is_above ? previous_painter.num_rows_ : 0;
+ unsigned my_row = is_above ? 0 : num_rows_;
+ for (unsigned c = previous_painter.start_visible_column_;
+ c <= previous_painter.end_visible_column_; c++) {
+ auto sibling_index = previous_painter.EdgeToIndex(siblingRow, c, East);
+ auto my_index = EdgeToIndex(my_row, c, East);
+ if (sibling_index != npos && my_index != npos) {
+ EdgeRecord sibling_edge = previous_painter.edges_[sibling_index];
+ edges.push_back(VisibleEdgeRecord(my_row, c, East, sibling_edge.border_,
+ sibling_edge.precedence_,
+ sibling_edge.resolved_color_));
+ }
+ }
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesTable(
+ VisibleEdgeContainer& edges,
+ const LayoutTable* table) {
+ edges.clear();
+ Color resolved_color = table->ResolveColor(CSSPropertyColor);
+ const ComputedStyle* border_style = table->Style();
+ const BorderValue* top_border = &border_style->BorderTop();
+ const BorderValue* right_border = &border_style->BorderRight();
+ const BorderValue* bottom_border = &border_style->BorderBottom();
+ const BorderValue* left_border = &border_style->BorderLeft();
+ RotateBorders(layout_table_section_->Style()->Direction(),
+ table->Style()->GetWritingMode(), &top_border, &right_border,
+ &bottom_border, &left_border);
+ // top row, only if we abut table top
+ if (this->layout_table_section_ == table->TopNonEmptySection()) {
+ FillHorizontalEdges(edges, 0, 0, num_effective_columns_, *top_border,
+ kBorderPrecedenceTable, resolved_color);
+ }
+ // bottom row, only if we abut table bottom
+ if (this->layout_table_section_ == table->BottomNonEmptySection()) {
+ FillHorizontalEdges(edges, num_rows_, 0, num_effective_columns_,
+ *bottom_border, kBorderPrecedenceTable, resolved_color);
+ }
+ FillVerticalEdges(edges, 0, 0, num_rows_, *left_border,
+ kBorderPrecedenceTable, resolved_color);
+ FillVerticalEdges(edges, num_effective_columns_, 0, num_rows_, *right_border,
+ kBorderPrecedenceTable, resolved_color);
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesSection(
+ VisibleEdgeContainer& edges) {
+ edges.clear();
+ Color resolved_color = layout_table_section_->ResolveColor(CSSPropertyColor);
+ // Traverse all the section edges, and assign them
+ FillVisibleRect(edges, 0, 0, num_rows_, num_effective_columns_,
+ layout_table_section_->Style(), TextDirection::kLtr,
+ layout_table_section_->Style()->GetWritingMode(),
+ resolved_color, kBorderPrecedenceRowGroup);
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesColgroup(
+ VisibleEdgeContainer& edges,
+ const LayoutTableCol* colgroup) {
+ edges.clear();
+ Color resolved_color = colgroup->ResolveColor(CSSPropertyColor);
+ Vector<unsigned> indexes = colgroup->GetEffectiveColumnIndexes();
+ if (indexes.size() > 0) {
+ unsigned start_col_index = indexes[0];
+ unsigned end_col_index = indexes.back() + 1;
+ FillVisibleRect(edges, 0, start_col_index, num_rows_, end_col_index,
+ colgroup->Style(), colgroup->Style()->Direction(),
+ colgroup->Style()->GetWritingMode(), resolved_color,
+ kBorderPrecedenceColumnGroup);
+ }
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesCol(
+ VisibleEdgeContainer& edges,
+ const LayoutTableCol* col) {
+ edges.clear();
+ Color resolved_color = col->ResolveColor(CSSPropertyColor);
+ LayoutTable* table = layout_table_section_->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, num_rows_, *idx + 1, col->Style(),
+ col->Style()->Direction(), col->Style()->GetWritingMode(),
+ resolved_color, kBorderPrecedenceColumn);
+ }
+ }
+ // unsigned startAbsoluteColIndex = table->colElementToAbsoluteColumn(col);
+ // unsigned endAbsoluteColIndex = startAbsoluteColIndex + col->span();
+ // unsigned start_col_index =
+ // table->absolute_columnToEffectiveColumn(startAbsoluteColIndex); unsigned
+ // end_col_index =
+ // table->absolute_columnToEffectiveColumn(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 = end_col_index; tmpEnd > start_col_index;
+ // tmpEnd--) {
+ // FillVisibleRect(edges, 0, tmpEnd - 1, num_rows_, tmpEnd, col->Style(),
+ // col->Style()->Direction(), col->Style()->GetWritingMode(),
+ // resolved_color, BorderPrecedenceColumn);
+ // }
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesRow(
+ VisibleEdgeContainer& edges,
+ const LayoutTableRow* row) {
+ edges.clear();
+ Color resolved_color = 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, num_effective_columns_,
+ row->Style(), layout_table_section_->Style()->Direction(),
+ row->Style()->GetWritingMode(), resolved_color,
+ kBorderPrecedenceRow);
+}
+
+void TableCollapsedBorderPainter::GetVisibleEdgesCell(
+ VisibleEdgeContainer& edges,
+ const LayoutTableCell* cell,
+ TextDirection direction) {
+ edges.clear();
+ Color resolved_color = cell->ResolveColor(CSSPropertyColor);
+ if (!cell->Parent()) {
+ LOG(INFO) << "DETACHED CELL";
+ return;
+ }
+ const LayoutTable* table = layout_table_section_->Table();
+ unsigned start_row = cell->Parent() ? cell->RowIndex() : 65000;
+ unsigned absolute_col = cell->AbsoluteColumnIndex();
+ unsigned eff_start_col = table->AbsoluteColumnToEffectiveColumn(absolute_col);
+ unsigned eff_end_col =
+ table->AbsoluteColumnToEffectiveColumn(absolute_col + cell->ColSpan());
+ unsigned end_row = std::min(start_row + cell->RowSpan(), num_rows_);
+ // LOG(INFO) << "m_columns: " << table->effective_columns().size()
+ // << " start_row: " << start_row << " end_row: " << end_row
+ // << " startCol: " << eff_start_col << " endCol:" << eff_end_col
+ // << " abscol:" << absolute_col;
+ // Fill border
+ // cell's direction is LTR because we do not rotate cell's borders
+ FillVisibleRect(edges, start_row, eff_start_col, end_row, eff_end_col,
+ cell->Style(), direction, cell->Style()->GetWritingMode(),
+ resolved_color, kBorderPrecedenceCell);
+ // Hide row inner edges
+ for (unsigned row = start_row + 1; row < end_row; row++) {
+ if (row >= start_visible_row_ && row < end_visible_row_) {
+ for (unsigned effCol = eff_start_col; effCol < eff_end_col; effCol++) {
+ if (effCol >= start_visible_column_ && effCol < end_visible_column_) {
+ edges.push_back(VisibleEdgeRecord(row, effCol, East, hidden_border_,
+ kBorderPrecedenceCell,
+ resolved_color, true));
+ }
+ }
+ }
+ }
+ // Hide col inner edges
+ for (unsigned effCol = eff_start_col + 1; effCol < eff_end_col; effCol++) {
+ if (effCol >= start_visible_column_ && effCol < end_visible_column_) {
+ for (unsigned row = start_row; row < end_row; row++) {
+ if (row >= start_visible_row_ && row < end_visible_row_) {
+ edges.push_back(VisibleEdgeRecord(row, effCol, South, hidden_border_,
+ kBorderPrecedenceCell,
+ resolved_color, true));
+ }
+ }
+ }
+ }
+ // ShowVisibleEdges(edges);
+}
+
+void TableCollapsedBorderPainter::RotateBorders(
+ TextDirection text_direction,
+ WritingMode writing_mode,
+ const BorderValue** top,
+ const BorderValue** right,
+ const BorderValue** bottom,
+ const BorderValue** left) const {
+ bool is_ltr = blink::IsLeftToRightDirection(text_direction);
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ if (is_ltr) {
+ } // do nothing
+ else {
+ std::swap(*right, *left);
+ }
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ if (is_ltr) {
+ auto tmp = *top;
+ *top = *right;
+ *right = *bottom;
+ *bottom = *left;
+ *left = tmp;
+ } else {
+ std::swap(*top, *right);
+ std::swap(*bottom, *left);
+ }
+ } else { // IsFlippedLinesWritingMode
+ if (is_ltr) {
+ 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 start_row,
+ unsigned start_column,
+ unsigned end_row,
+ unsigned end_column,
+ const ComputedStyle* border_style,
+ TextDirection text_direction,
+ WritingMode writing_mode,
+ Color resolved_color,
+ EBorderPrecedence precedence) {
+ const BorderValue* top_border = &border_style->BorderTop();
+ const BorderValue* bottom_border = &border_style->BorderBottom();
+ const BorderValue* right_border = &border_style->BorderRight();
+ const BorderValue* left_border = &border_style->BorderLeft();
+ RotateBorders(text_direction, writing_mode, &top_border, &right_border,
+ &bottom_border, &left_border);
+ FillHorizontalEdges(edges, start_row, start_column, end_column, *top_border,
+ precedence, resolved_color);
+ FillHorizontalEdges(edges, end_row, start_column, end_column, *bottom_border,
+ precedence, resolved_color);
+ FillVerticalEdges(edges, start_column, start_row, end_row, *left_border,
+ precedence, resolved_color);
+ FillVerticalEdges(edges, end_column, start_row, end_row, *right_border,
+ precedence, resolved_color);
+}
+
+void TableCollapsedBorderPainter::FillHorizontalEdges(
+ VisibleEdgeContainer& edges,
+ unsigned row,
+ unsigned start_column,
+ unsigned end_column,
+ const BorderValue& border,
+ EBorderPrecedence precedence,
+ Color resolved_color) {
+ if (border.Style() == kBorderStyleNone)
+ return;
+ if (row >= start_visible_row_ && row < end_visible_row_) {
+ for (unsigned c = start_column; c < end_column; c++) {
+ // FIXME: modify for loop instead of comparing guard
+ if (c >= start_visible_column_ && c < end_visible_column_) {
+ edges.push_back(VisibleEdgeRecord(row, c, East, border, precedence,
+ resolved_color));
+ }
+ }
+ }
+}
+
+void TableCollapsedBorderPainter::FillVerticalEdges(
+ VisibleEdgeContainer& edges,
+ unsigned column,
+ unsigned start_row,
+ unsigned end_row,
+ const BorderValue& border,
+ EBorderPrecedence precedence,
+ Color resolved_color) {
+ if (border.Style() == kBorderStyleNone)
+ return;
+ if (column >= start_visible_column_ && column < end_visible_column_) {
+ for (unsigned r = start_row; r < end_row; r++) {
+ // FIXME: modify for loop instead of comparing guard
+ if (r >= start_visible_row_ && r < end_visible_row_) {
+ edges.push_back(VisibleEdgeRecord(r, column, South, border, precedence,
+ resolved_color));
+ }
+ }
+ }
+}
+
+void TableCollapsedBorderPainter::MergeVisibleEdges(
+ VisibleEdgeContainer& edges) {
+ for (auto edge = edges.begin(); edge < edges.end(); edge++) {
+ unsigned idx =
+ EdgeToIndex((*edge).m_row, (*edge).m_column, (*edge).direction_);
+ DCHECK(idx != npos);
+ EdgeRecord edge_record((*edge).border_, (*edge).precedence_,
+ (*edge).resolved_color_);
+ if ((*edge).force_removal_)
+ edges_[idx] = edge_record;
+ // if both edges are same priority, new edge wins
+ if (CompareEdges(edges_[idx], edge_record) != 1)
+ edges_[idx] = edge_record;
+ }
+}
+
+static EBorderStyle CollapsedBorderStyle(EBorderStyle style) {
+ if (style == kBorderStyleOutset)
+ return kBorderStyleGroove;
+ if (style == kBorderStyleInset)
+ return kBorderStyleRidge;
+ return style;
+}
+
+void TableCollapsedBorderPainter::PaintEdges(const PaintInfo& paint_info,
+ const LayoutPoint& paint_offset,
+ const CellSpan& rows,
+ const CellSpan& cols) {
+ // see TableCellPainter::paintCollapsedBorders
+ LayoutRect paint_rect = layout_table_section_->GetCellPhysicalPosition(
+ rows.Start(), cols.Start());
+ paint_rect.Unite(layout_table_section_->GetCellPhysicalPosition(
+ rows.end() > 0 ? rows.end() - 1 : 0,
+ cols.end() > 0 ? cols.end() - 1 : 0));
+ // paint_rect size calculation is not exact, we expand by maxborder, instead
+ // of exact border
+ paint_rect.Expand(LayoutSize(LayoutUnit(max_intersection_width_ * 2),
+ LayoutUnit(max_intersection_height_ * 2)));
+ paint_rect.MoveBy(paint_offset);
+ LayoutPoint location = layout_table_section_->Location();
+ paint_rect.MoveBy(location);
+ if (LayoutObjectDrawingRecorder::UseCachedDrawingIfPossible(
+ paint_info.context, *layout_table_section_,
+ DisplayItem::kTableCollapsedBorderBase))
+ return;
+ LayoutObjectDrawingRecorder recorder(
+ paint_info.context, *layout_table_section_,
+ DisplayItem::kTableCollapsedBorderBase, paint_rect);
+ // Paint edges carefully to get them all
+ // Every row paint : ┌┌┌┌┌, then |
+ // Bottom border paint: _____
+ WritingMode writing_mode = layout_table_section_->Style()->GetWritingMode();
+ TextDirection text_direction = layout_table_section_->Style()->Direction();
+ for (unsigned r = rows.Start(); r < rows.end(); r++) {
+ // const LayoutTableRow * row = layout_table_section_->rowLayoutObjectAt(r);
+ // if (row)
+ // text_direction = row->Style()->Direction();
+ for (unsigned c = cols.Start(); c < cols.end(); c++) {
+ PaintOneEdge(paint_info, paint_offset, r, c, East, writing_mode,
+ text_direction);
+ PaintOneEdge(paint_info, paint_offset, r, c, South, writing_mode,
+ text_direction);
+ }
+ // paint the last column on edge
+ PaintOneEdge(paint_info, paint_offset, r, cols.end(), South, writing_mode,
+ text_direction);
+ }
+ // Paint the last row
+ for (unsigned c = cols.Start(); c < cols.end(); c++) {
+ PaintOneEdge(paint_info, paint_offset, rows.end(), c, East, writing_mode,
+ text_direction);
+ }
+}
+
+// Returns a rect that encloses 'direction' border of the cell_rect
+LayoutRect TableCollapsedBorderPainter::CellRectAsBorder(
+ const LayoutRect& cell_rect,
+ BoxSide side,
+ unsigned border_width,
+ WritingMode writing_mode,
+ TextDirection text_direction) {
+ LayoutUnit small_half(border_width / 2); // C++ always rounds down
+ // unsigned bigHalf = border_width - small_half;
+ LayoutUnit top_offset;
+ LayoutUnit left_offset;
+ /* 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 is_ltr = blink::IsLeftToRightDirection(text_direction);
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ if (is_ltr) {
+ top_offset = small_half;
+ left_offset = small_half;
+ } else {
+ top_offset = small_half;
+ left_offset = small_half;
+ }
+ } else { // vertical
+ if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ if (is_ltr) {
+ top_offset = small_half;
+ left_offset = small_half;
+ } else {
+ top_offset = small_half;
+ left_offset = small_half;
+ }
+ } else { // flippedLines
+ if (is_ltr) {
+ top_offset = small_half;
+ left_offset = small_half;
+ } else {
+ top_offset = small_half;
+ left_offset = small_half;
+ }
+ }
+ }
+ switch (side) {
+ case kBSTop:
+ return LayoutRect(
+ LayoutPoint(cell_rect.X(), cell_rect.Y() - top_offset),
+ LayoutSize(cell_rect.Width(), LayoutUnit(border_width)));
+ case kBSBottom:
+ return LayoutRect(
+ LayoutPoint(cell_rect.X(), cell_rect.MaxY() - top_offset),
+ LayoutSize(cell_rect.Width(), LayoutUnit(border_width)));
+ case kBSLeft:
+ return LayoutRect(
+ LayoutPoint(cell_rect.X() - left_offset, cell_rect.Y()),
+ LayoutSize(LayoutUnit(border_width), cell_rect.Height()));
+ case kBSRight:
+ return LayoutRect(
+ LayoutPoint(cell_rect.MaxX() - left_offset, cell_rect.Y()),
+ LayoutSize(LayoutUnit(border_width), cell_rect.Height()));
+ }
+}
+
+//
+LayoutRect TableCollapsedBorderPainter::EdgePaintPosition(
+ unsigned row,
+ unsigned col,
+ unsigned border_width,
+ EdgeDirection direction,
+ WritingMode writing_mode,
+ TextDirection text_direction) {
+ // Compute edge's size from size of the cell
+ LayoutRect position;
+ bool is_ltr = blink::IsLeftToRightDirection(text_direction);
+ BoxSide side = kBSLeft;
+ LayoutRect cell_rect;
+ // If this is the last row intersection, get size from cell in next to last
+ if (row == num_rows_) {
+ DCHECK(direction == East);
+ cell_rect = GetCellPhysicalPosition(row - 1, col);
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ side = kBSBottom;
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ side = kBSLeft;
+ } else {
+ side = kBSRight;
+ }
+ } else if (col == num_effective_columns_) {
+ // If this is the last column, get size from the cell before
+ DCHECK(direction == South);
+ cell_rect = GetCellPhysicalPosition(row, col - 1);
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ side = is_ltr ? kBSRight : kBSLeft;
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ side = is_ltr ? kBSBottom : kBSTop;
+ } else {
+ side = is_ltr ? kBSBottom : kBSTop;
+ }
+ } else {
+ cell_rect = GetCellPhysicalPosition(row, col);
+ if (direction == East) {
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ side = kBSTop;
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ side = kBSRight;
+ } else {
+ side = kBSLeft;
+ }
+ } else { // direction == South
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ side = is_ltr ? kBSLeft : kBSRight;
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ side = is_ltr ? kBSTop : kBSBottom;
+ } else { // flippedLines
+ side = is_ltr ? kBSTop : kBSBottom;
+ }
+ }
+ }
+ return CellRectAsBorder(cell_rect, side, border_width, writing_mode,
+ text_direction);
+}
+
+// Paints an edge starting from intersection[row, col] in direction
+void TableCollapsedBorderPainter::PaintOneEdge(const PaintInfo& paint_info,
+ const LayoutPoint& paint_offset,
+ unsigned row,
+ unsigned col,
+ EdgeDirection direction,
+ WritingMode writing_mode,
+ TextDirection text_direction) {
+ DCHECK(direction == South || direction == East);
+ unsigned idx = EdgeToIndex(row, col, direction);
+ DCHECK(idx != npos);
+ const BorderValue& border = edges_[idx].border_;
+ if (border.Style() == kBorderStyleHidden || !border.NonZero())
+ return;
+ LayoutTableSection* section_above =
+ layout_table_section_->Table()->SectionAbove(layout_table_section_,
+ kSkipEmptySections);
+ if (!row && direction == East && section_above) {
+ return;
+ }
+ // EdgeRecord edge = edges_[idx];
+ // ShowEdge(row, col, direction, edge.precedence_, edge.border_,
+ // border.Color().Resolve(edges_[idx].resolved_color_));
+ LayoutRect position = EdgePaintPosition(row, col, border.Width(), direction,
+ writing_mode, text_direction);
+ AdjustForIntersections(position, row, col, direction, writing_mode,
+ text_direction);
+ // Offset by section_location
+ LayoutPoint section_location = layout_table_section_->Location();
+ position.MoveBy(section_location);
+ position.MoveBy(paint_offset);
+ BoxSide side;
+ if (blink::IsHorizontalWritingMode(writing_mode))
+ side = direction == East ? kBSTop : kBSLeft;
+ else
+ side = direction == East ? kBSLeft : kBSTop;
+ ObjectPainter::DrawLineForBoxSide(
+ paint_info.context, position.X(), position.Y(), position.MaxX(),
+ position.MaxY(), // left, top, right, bottom,
+ side, border.GetColor().Resolve(edges_[idx].resolved_color_),
+ CollapsedBorderStyle(border.Style()), 0, 0, true);
+}
+
+void TableCollapsedBorderPainter::AdjustForIntersections(
+ LayoutRect& position,
+ unsigned row,
+ unsigned col,
+ EdgeDirection direction,
+ WritingMode writing_mode,
+ TextDirection text_direction) {
+ DCHECK(direction == South || direction == East);
+ const Intersection* start_intersection = nullptr;
+ const Intersection* end_intersection = nullptr;
+ bool winner_start = false;
+ bool winner_end = false;
+ // START intersection
+ unsigned index = IntersectionToIndex(row, col);
+ if (index != npos) {
+ start_intersection = &intersections_[index];
+ winner_start = start_intersection->direction_ == direction;
+ }
+ // END intersection
+ if (direction == South)
+ row += 1;
+ else
+ col += 1;
+ index = IntersectionToIndex(row, col);
+ if (index != npos) {
+ end_intersection = &intersections_[index];
+ if (direction == South)
+ winner_end = end_intersection->direction_ == North;
+ else
+ winner_end = end_intersection->direction_ == West;
+ }
+ bool is_ltr = blink::IsLeftToRightDirection(text_direction);
+ if (blink::IsHorizontalWritingMode(writing_mode)) {
+ AdjustForIntersectionsHorizontal(position, direction, is_ltr,
+ start_intersection, winner_start,
+ end_intersection, winner_end);
+ } else if (blink::IsFlippedBlocksWritingMode(writing_mode)) {
+ AdjustForIntersectionsFlippedBlocks(position, direction, is_ltr,
+ start_intersection, winner_start,
+ end_intersection, winner_end);
+ } else if (blink::IsFlippedLinesWritingMode(writing_mode)) {
+ AdjustForIntersectionsFlippedLines(position, direction, is_ltr,
+ start_intersection, winner_start,
+ end_intersection, winner_end);
+ } else {
+ DCHECK(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 is_ltr,
+ const Intersection* start_intersection,
+ bool winner_start,
+ const Intersection* end_intersection,
+ bool winner_end) {
+ if (start_intersection) {
+ if (direction == South) {
+ unsigned small_delta = start_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(small_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(big_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // East
+ unsigned small_delta = start_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta));
+ }
+ } else { // RTL
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(small_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(big_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ }
+ }
+ }
+ if (end_intersection) {
+ if (direction == South) {
+ unsigned small_delta = end_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta), LayoutUnit(0));
+ }
+ } else { // East
+ unsigned small_delta = end_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(big_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ } else { // loser
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(small_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // RTL
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta));
+ }
+ }
+ }
+ }
+}
+
+/*
+ * 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 is_ltr,
+ const Intersection* start_intersection,
+ bool winner_start,
+ const Intersection* end_intersection,
+ bool winner_end) {
+ if (start_intersection) {
+ if (direction == South) {
+ unsigned small_delta = start_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(small_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(big_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // East
+ unsigned small_delta = start_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(small_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(big_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // RTL
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta), LayoutUnit(0));
+ }
+ }
+ }
+ }
+ if (end_intersection) {
+ if (direction == South) {
+ unsigned small_delta = end_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta));
+ }
+ } else { // East
+ unsigned small_delta = end_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta), LayoutUnit(0));
+ } else { // loser
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta), LayoutUnit(0));
+ }
+ } else { // RTL
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(big_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(small_delta), 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 is_ltr,
+ const Intersection* start_intersection,
+ bool winner_start,
+ const Intersection* end_intersection,
+ bool winner_end) {
+ if (start_intersection) {
+ if (direction == South) {
+ unsigned small_delta = start_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta));
+ }
+ } else { // East
+ unsigned small_delta = start_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta =
+ start_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(small_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(big_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // RTL
+ if (winner_start) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta), LayoutUnit(0));
+ }
+ }
+ }
+ }
+ if (end_intersection) {
+ if (direction == South) {
+ unsigned small_delta = end_intersection->height_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->height_.ToUnsigned() - small_delta;
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(big_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(small_delta),
+ LayoutUnit(0), LayoutUnit(0));
+ }
+ } else { // East
+ unsigned small_delta = end_intersection->width_.ToUnsigned() / 2;
+ unsigned big_delta = end_intersection->width_.ToUnsigned() - small_delta;
+ if (is_ltr) {
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(big_delta), LayoutUnit(0));
+ } else { // loser
+ position.ContractEdges(LayoutUnit(0), LayoutUnit(0),
+ LayoutUnit(small_delta), LayoutUnit(0));
+ }
+ } else { // RTL
+ if (winner_end) {
+ position.ExpandEdges(LayoutUnit(big_delta), LayoutUnit(0),
+ LayoutUnit(0), LayoutUnit(0));
+ } else {
+ position.ContractEdges(LayoutUnit(small_delta), 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 intersection_row,
+ unsigned intersection_column,
+ EdgeDirection direction) const {
+ /* Edges map to 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:
+ intersection_row -= 1;
+ direction = South;
+ break;
+ case West:
+ intersection_column -= 1;
+ direction = East;
+ break;
+ case South:
+ case East:
+ case None:
+ break;
+ }
+ // Check if we are inside the covered area
+ if (intersection_row < start_visible_row_ ||
+ intersection_column < start_visible_column_ ||
+ intersection_row >= end_visible_row_ ||
+ intersection_column >= end_visible_column_) {
+ // LOG(ERROR) << "GOT AN NPOS";
+ return TableCollapsedBorderPainter::npos;
+ }
+ unsigned index = (intersection_row - start_visible_row_) *
+ (end_visible_column_ - start_visible_column_) * 2;
+ index += (intersection_column - start_visible_column_) * 2;
+ return direction == East ? index
+ : index + 1; // each vertex holds [East, South]
+}
+
+unsigned TableCollapsedBorderPainter::IntersectionToIndex(
+ unsigned row,
+ unsigned column) const {
+ if (row < start_visible_row_ || column < start_visible_column_ ||
+ row >= end_visible_row_ || column >= end_visible_column_)
+ return TableCollapsedBorderPainter::npos;
+ return (row - start_visible_row_) *
+ (end_visible_column_ - start_visible_column_) +
+ column;
+}
+}
+// blink

Powered by Google App Engine
This is Rietveld 408576698