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 |