/src/serenity/Userland/Libraries/LibWeb/Painting/TableBordersPainting.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2023, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #include <AK/HashMap.h> |
8 | | #include <AK/QuickSort.h> |
9 | | #include <AK/Traits.h> |
10 | | #include <LibWeb/Layout/TableFormattingContext.h> |
11 | | #include <LibWeb/Painting/PaintableBox.h> |
12 | | #include <LibWeb/Painting/TableBordersPainting.h> |
13 | | |
14 | | struct CellCoordinates { |
15 | | size_t row_index; |
16 | | size_t column_index; |
17 | | |
18 | | bool operator==(CellCoordinates const& other) const |
19 | 0 | { |
20 | 0 | return row_index == other.row_index && column_index == other.column_index; |
21 | 0 | } |
22 | | }; |
23 | | |
24 | | namespace AK { |
25 | | template<> |
26 | | struct Traits<CellCoordinates> : public DefaultTraits<CellCoordinates> { |
27 | 0 | static unsigned hash(CellCoordinates const& key) { return pair_int_hash(key.row_index, key.column_index); } |
28 | | }; |
29 | | } |
30 | | |
31 | | namespace Web::Painting { |
32 | | |
33 | | static void collect_cell_boxes(Vector<PaintableBox const&>& cell_boxes, PaintableBox const& table_paintable) |
34 | 0 | { |
35 | 0 | table_paintable.for_each_child_of_type<PaintableBox>([&](auto& child) { |
36 | 0 | if (child.display().is_table_cell()) { |
37 | 0 | cell_boxes.append(child); |
38 | 0 | } else { |
39 | 0 | collect_cell_boxes(cell_boxes, child); |
40 | 0 | } |
41 | 0 | return IterationDecision::Continue; |
42 | 0 | }); |
43 | 0 | } |
44 | | |
45 | | enum class EdgeDirection { |
46 | | Horizontal, |
47 | | Vertical, |
48 | | }; |
49 | | |
50 | | struct DeviceBorderData { |
51 | | Color color { Color::Transparent }; |
52 | | CSS::LineStyle line_style { CSS::LineStyle::None }; |
53 | | DevicePixels width { 0 }; |
54 | | }; |
55 | | |
56 | | struct DeviceBorderDataWithElementKind { |
57 | | DeviceBorderData border_data; |
58 | | Painting::PaintableBox::ConflictingElementKind element_kind { Painting::PaintableBox::ConflictingElementKind::Cell }; |
59 | | }; |
60 | | |
61 | | struct DeviceBordersDataWithElementKind { |
62 | | DeviceBorderDataWithElementKind top; |
63 | | DeviceBorderDataWithElementKind right; |
64 | | DeviceBorderDataWithElementKind bottom; |
65 | | DeviceBorderDataWithElementKind left; |
66 | | }; |
67 | | |
68 | | struct BorderEdgePaintingInfo { |
69 | | DevicePixelRect rect; |
70 | | DeviceBorderDataWithElementKind border_data_with_element_kind; |
71 | | EdgeDirection direction; |
72 | | Optional<size_t> row; |
73 | | Optional<size_t> column; |
74 | | }; |
75 | | |
76 | | static Optional<size_t> row_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind) |
77 | 0 | { |
78 | 0 | switch (element_kind) { |
79 | 0 | case Painting::PaintableBox::ConflictingElementKind::Cell: |
80 | 0 | case Painting::PaintableBox::ConflictingElementKind::Row: |
81 | 0 | case Painting::PaintableBox::ConflictingElementKind::RowGroup: { |
82 | 0 | return index; |
83 | 0 | } |
84 | 0 | default: |
85 | 0 | return {}; |
86 | 0 | } |
87 | 0 | } |
88 | | |
89 | | static Optional<size_t> column_index_for_element_kind(size_t index, Painting::PaintableBox::ConflictingElementKind element_kind) |
90 | 0 | { |
91 | 0 | switch (element_kind) { |
92 | 0 | case Painting::PaintableBox::ConflictingElementKind::Cell: |
93 | 0 | case Painting::PaintableBox::ConflictingElementKind::Column: |
94 | 0 | case Painting::PaintableBox::ConflictingElementKind::ColumnGroup: { |
95 | 0 | return index; |
96 | 0 | } |
97 | 0 | default: |
98 | 0 | return {}; |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | static DevicePixels half_ceil(DevicePixels width) |
103 | 0 | { |
104 | 0 | return ceil(static_cast<double>(width.value()) / 2); |
105 | 0 | } |
106 | | |
107 | | static DevicePixels half_floor(DevicePixels width) |
108 | 0 | { |
109 | 0 | return floor(static_cast<double>(width.value()) / 2); |
110 | 0 | } |
111 | | |
112 | | static BorderEdgePaintingInfo make_right_cell_edge( |
113 | | DevicePixelRect const& right_cell_rect, |
114 | | DevicePixelRect const& cell_rect, |
115 | | DeviceBordersDataWithElementKind const& borders_data, |
116 | | CellCoordinates const& coordinates) |
117 | 0 | { |
118 | 0 | auto connect_top_offset = half_ceil(borders_data.top.border_data.width); |
119 | 0 | auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width); |
120 | 0 | DevicePixelRect right_border_rect = { |
121 | 0 | right_cell_rect.x() - half_ceil(borders_data.right.border_data.width), |
122 | 0 | cell_rect.y() - connect_top_offset, |
123 | 0 | borders_data.right.border_data.width, |
124 | 0 | max(cell_rect.height(), right_cell_rect.height()) + connect_excess_height, |
125 | 0 | }; |
126 | 0 | return BorderEdgePaintingInfo { |
127 | 0 | .rect = right_border_rect, |
128 | 0 | .border_data_with_element_kind = borders_data.right, |
129 | 0 | .direction = EdgeDirection::Vertical, |
130 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind), |
131 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind), |
132 | 0 | }; |
133 | 0 | } |
134 | | |
135 | | static BorderEdgePaintingInfo make_down_cell_edge( |
136 | | DevicePixelRect const& down_cell_rect, |
137 | | DevicePixelRect const& cell_rect, |
138 | | DeviceBordersDataWithElementKind const& borders_data, |
139 | | CellCoordinates const& coordinates) |
140 | 0 | { |
141 | 0 | auto connect_left_offset = half_ceil(borders_data.left.border_data.width); |
142 | 0 | auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width); |
143 | 0 | DevicePixelRect down_border_rect = { |
144 | 0 | cell_rect.x() - connect_left_offset, |
145 | 0 | down_cell_rect.y() - half_ceil(borders_data.bottom.border_data.width), |
146 | 0 | max(cell_rect.width(), down_cell_rect.width()) + connect_excess_width, |
147 | 0 | borders_data.bottom.border_data.width, |
148 | 0 | }; |
149 | 0 | return BorderEdgePaintingInfo { |
150 | 0 | .rect = down_border_rect, |
151 | 0 | .border_data_with_element_kind = borders_data.bottom, |
152 | 0 | .direction = EdgeDirection::Horizontal, |
153 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind), |
154 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind), |
155 | 0 | }; |
156 | 0 | } |
157 | | |
158 | | static BorderEdgePaintingInfo make_first_row_top_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates) |
159 | 0 | { |
160 | 0 | auto connect_left_offset = half_ceil(borders_data.left.border_data.width.value()); |
161 | 0 | auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width.value()); |
162 | 0 | DevicePixelRect top_border_rect = { |
163 | 0 | cell_rect.x() - connect_left_offset, |
164 | 0 | cell_rect.y() - half_ceil(borders_data.top.border_data.width.value()), |
165 | 0 | cell_rect.width() + connect_excess_width, |
166 | 0 | borders_data.top.border_data.width, |
167 | 0 | }; |
168 | 0 | return BorderEdgePaintingInfo { |
169 | 0 | .rect = top_border_rect, |
170 | 0 | .border_data_with_element_kind = borders_data.top, |
171 | 0 | .direction = EdgeDirection::Horizontal, |
172 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.top.element_kind), |
173 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.top.element_kind), |
174 | 0 | }; |
175 | 0 | } |
176 | | |
177 | | static BorderEdgePaintingInfo make_last_row_bottom_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates) |
178 | 0 | { |
179 | 0 | auto connect_left_offset = half_ceil(borders_data.left.border_data.width); |
180 | 0 | auto connect_excess_width = connect_left_offset + half_floor(borders_data.right.border_data.width); |
181 | 0 | DevicePixelRect bottom_border_rect = { |
182 | 0 | cell_rect.x() - connect_left_offset, |
183 | 0 | cell_rect.y() + cell_rect.height() - half_ceil(borders_data.bottom.border_data.width), |
184 | 0 | cell_rect.width() + connect_excess_width, |
185 | 0 | borders_data.bottom.border_data.width, |
186 | 0 | }; |
187 | 0 | return BorderEdgePaintingInfo { |
188 | 0 | .rect = bottom_border_rect, |
189 | 0 | .border_data_with_element_kind = borders_data.bottom, |
190 | 0 | .direction = EdgeDirection::Horizontal, |
191 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.bottom.element_kind), |
192 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.bottom.element_kind), |
193 | 0 | }; |
194 | 0 | } |
195 | | |
196 | | static BorderEdgePaintingInfo make_first_column_left_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates) |
197 | 0 | { |
198 | 0 | auto connect_top_offset = half_ceil(borders_data.top.border_data.width); |
199 | 0 | auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width); |
200 | 0 | DevicePixelRect left_border_rect = { |
201 | 0 | cell_rect.x() - half_ceil(borders_data.left.border_data.width), |
202 | 0 | cell_rect.y() - connect_top_offset, |
203 | 0 | borders_data.left.border_data.width, |
204 | 0 | cell_rect.height() + connect_excess_height, |
205 | 0 | }; |
206 | 0 | return BorderEdgePaintingInfo { |
207 | 0 | .rect = left_border_rect, |
208 | 0 | .border_data_with_element_kind = borders_data.left, |
209 | 0 | .direction = EdgeDirection::Vertical, |
210 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.left.element_kind), |
211 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.left.element_kind), |
212 | 0 | }; |
213 | 0 | } |
214 | | |
215 | | static BorderEdgePaintingInfo make_last_column_right_cell_edge(DevicePixelRect const& cell_rect, DeviceBordersDataWithElementKind const& borders_data, CellCoordinates const& coordinates) |
216 | 0 | { |
217 | 0 | auto connect_top_offset = half_ceil(borders_data.top.border_data.width); |
218 | 0 | auto connect_excess_height = connect_top_offset + half_floor(borders_data.bottom.border_data.width); |
219 | 0 | DevicePixelRect right_border_rect = { |
220 | 0 | cell_rect.x() + cell_rect.width() - half_ceil(borders_data.right.border_data.width), |
221 | 0 | cell_rect.y() - connect_top_offset, |
222 | 0 | borders_data.right.border_data.width, |
223 | 0 | cell_rect.height() + connect_excess_height, |
224 | 0 | }; |
225 | 0 | return BorderEdgePaintingInfo { |
226 | 0 | .rect = right_border_rect, |
227 | 0 | .border_data_with_element_kind = borders_data.right, |
228 | 0 | .direction = EdgeDirection::Vertical, |
229 | 0 | .row = row_index_for_element_kind(coordinates.row_index, borders_data.right.element_kind), |
230 | 0 | .column = column_index_for_element_kind(coordinates.column_index, borders_data.right.element_kind), |
231 | 0 | }; |
232 | 0 | } |
233 | | |
234 | | static CSS::BorderData css_border_data_from_device_border_data(DeviceBorderData const& device_border_data) |
235 | 0 | { |
236 | 0 | return CSS::BorderData { |
237 | 0 | .color = device_border_data.color, |
238 | 0 | .line_style = device_border_data.line_style, |
239 | 0 | .width = device_border_data.width.value(), |
240 | 0 | }; |
241 | 0 | } |
242 | | |
243 | | static void paint_collected_edges(PaintContext& context, Vector<BorderEdgePaintingInfo>& border_edge_painting_info_list) |
244 | 0 | { |
245 | | // This sorting step isn't part of the specification, but it matches the behavior of other browsers at border intersections, which aren't |
246 | | // part of border conflict resolution in the specification but it's still desirable to handle them in a way which is consistent with it. |
247 | | // See https://www.w3.org/TR/CSS22/tables.html#border-conflict-resolution for reference. |
248 | 0 | quick_sort(border_edge_painting_info_list, [](auto const& a, auto const& b) { |
249 | 0 | auto const& a_border_data = a.border_data_with_element_kind.border_data; |
250 | 0 | auto const& b_border_data = b.border_data_with_element_kind.border_data; |
251 | 0 | if (a_border_data.line_style == b_border_data.line_style && a_border_data.width == b_border_data.width) { |
252 | 0 | if (b.border_data_with_element_kind.element_kind < a.border_data_with_element_kind.element_kind) { |
253 | 0 | return true; |
254 | 0 | } else if (b.border_data_with_element_kind.element_kind > a.border_data_with_element_kind.element_kind) { |
255 | 0 | return false; |
256 | 0 | } |
257 | | // Here the element kind is the same, thus the coordinates are either both set or not set. |
258 | 0 | VERIFY(a.column.has_value() == b.column.has_value()); |
259 | 0 | VERIFY(a.row.has_value() == b.row.has_value()); |
260 | 0 | if (a.column.has_value()) { |
261 | 0 | if (b.column.value() < a.column.value()) { |
262 | 0 | return true; |
263 | 0 | } else if (b.column.value() > a.column.value()) { |
264 | 0 | return false; |
265 | 0 | } |
266 | 0 | } |
267 | 0 | return a.row.has_value() ? b.row.value() < a.row.value() : false; |
268 | 0 | } |
269 | 0 | return Layout::TableFormattingContext::border_is_less_specific( |
270 | 0 | css_border_data_from_device_border_data(a_border_data), |
271 | 0 | css_border_data_from_device_border_data(b_border_data)); |
272 | 0 | }); |
273 | |
|
274 | 0 | for (auto const& border_edge_painting_info : border_edge_painting_info_list) { |
275 | 0 | auto const& border_data_with_element_kind = border_edge_painting_info.border_data_with_element_kind; |
276 | 0 | auto width = border_data_with_element_kind.border_data.width; |
277 | 0 | if (width <= 0) |
278 | 0 | continue; |
279 | 0 | auto color = border_data_with_element_kind.border_data.color; |
280 | 0 | auto border_style = border_data_with_element_kind.border_data.line_style; |
281 | 0 | auto p1 = border_edge_painting_info.rect.top_left(); |
282 | 0 | auto p2 = border_edge_painting_info.direction == EdgeDirection::Horizontal |
283 | 0 | ? border_edge_painting_info.rect.top_right() |
284 | 0 | : border_edge_painting_info.rect.bottom_left(); |
285 | |
|
286 | 0 | if (border_style == CSS::LineStyle::Dotted) { |
287 | 0 | context.display_list_recorder().draw_line(p1.to_type<int>(), p2.to_type<int>(), color, width.value(), Gfx::LineStyle::Dotted); |
288 | 0 | } else if (border_style == CSS::LineStyle::Dashed) { |
289 | 0 | context.display_list_recorder().draw_line(p1.to_type<int>(), p2.to_type<int>(), color, width.value(), Gfx::LineStyle::Dashed); |
290 | 0 | } else { |
291 | | // FIXME: Support the remaining line styles instead of rendering them as solid. |
292 | 0 | context.display_list_recorder().fill_rect(Gfx::IntRect(border_edge_painting_info.rect.location(), border_edge_painting_info.rect.size()), color); |
293 | 0 | } |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | | static HashMap<CellCoordinates, DevicePixelRect> snap_cells_to_device_coordinates(HashMap<CellCoordinates, PaintableBox const*> const& cell_coordinates_to_box, size_t row_count, size_t column_count, PaintContext const& context) |
298 | 0 | { |
299 | 0 | Vector<DevicePixels> y_line_start_coordinates; |
300 | 0 | Vector<DevicePixels> y_line_end_coordinates; |
301 | 0 | y_line_start_coordinates.resize(row_count + 1); |
302 | 0 | y_line_end_coordinates.resize(row_count + 1); |
303 | 0 | Vector<DevicePixels> x_line_start_coordinates; |
304 | 0 | Vector<DevicePixels> x_line_end_coordinates; |
305 | 0 | x_line_start_coordinates.resize(column_count + 1); |
306 | 0 | x_line_end_coordinates.resize(column_count + 1); |
307 | 0 | for (auto const& kv : cell_coordinates_to_box) { |
308 | 0 | auto const& cell_box = kv.value; |
309 | 0 | auto start_row_index = cell_box->table_cell_coordinates()->row_index; |
310 | 0 | auto end_row_index = start_row_index + cell_box->table_cell_coordinates()->row_span; |
311 | 0 | auto cell_rect = cell_box->absolute_border_box_rect(); |
312 | 0 | y_line_start_coordinates[start_row_index] = max(context.rounded_device_pixels(cell_rect.y()), y_line_start_coordinates[start_row_index]); |
313 | 0 | y_line_end_coordinates[end_row_index] = max(context.rounded_device_pixels(cell_rect.y() + cell_rect.height()), y_line_end_coordinates[end_row_index]); |
314 | 0 | auto start_column_index = cell_box->table_cell_coordinates()->column_index; |
315 | 0 | auto end_column_index = start_column_index + cell_box->table_cell_coordinates()->column_span; |
316 | 0 | x_line_start_coordinates[start_column_index] = max(context.rounded_device_pixels(cell_rect.x()), x_line_start_coordinates[start_column_index]); |
317 | 0 | x_line_end_coordinates[end_column_index] = max(context.rounded_device_pixels(cell_rect.x() + cell_rect.width()), x_line_end_coordinates[end_column_index]); |
318 | 0 | } |
319 | 0 | HashMap<CellCoordinates, DevicePixelRect> cell_coordinates_to_device_rect; |
320 | 0 | for (auto const& kv : cell_coordinates_to_box) { |
321 | 0 | auto const& cell_box = kv.value; |
322 | 0 | auto start_row_index = cell_box->table_cell_coordinates()->row_index; |
323 | 0 | auto end_row_index = start_row_index + cell_box->table_cell_coordinates()->row_span; |
324 | 0 | auto height = y_line_end_coordinates[end_row_index] - y_line_start_coordinates[start_row_index]; |
325 | 0 | auto start_column_index = cell_box->table_cell_coordinates()->column_index; |
326 | 0 | auto end_column_index = start_column_index + cell_box->table_cell_coordinates()->column_span; |
327 | 0 | auto width = x_line_end_coordinates[end_column_index] - x_line_start_coordinates[start_column_index]; |
328 | 0 | cell_coordinates_to_device_rect.set(kv.key, DevicePixelRect { x_line_start_coordinates[start_column_index], y_line_start_coordinates[start_row_index], width, height }); |
329 | 0 | } |
330 | 0 | return cell_coordinates_to_device_rect; |
331 | 0 | } |
332 | | |
333 | | static DeviceBorderDataWithElementKind device_border_data_from_css_border_data(Painting::PaintableBox::BorderDataWithElementKind const& border_data_with_element_kind, PaintContext const& context) |
334 | 0 | { |
335 | 0 | return DeviceBorderDataWithElementKind { |
336 | 0 | .border_data = { |
337 | 0 | .color = border_data_with_element_kind.border_data.color, |
338 | 0 | .line_style = border_data_with_element_kind.border_data.line_style, |
339 | 0 | .width = context.rounded_device_pixels(border_data_with_element_kind.border_data.width), |
340 | 0 | }, |
341 | 0 | .element_kind = border_data_with_element_kind.element_kind, |
342 | 0 | }; |
343 | 0 | } |
344 | | |
345 | | static void paint_separate_cell_borders(PaintableBox const& cell_box, HashMap<CellCoordinates, DevicePixelRect> const& cell_coordinates_to_device_rect, PaintContext& context) |
346 | 0 | { |
347 | 0 | auto borders_data = cell_box.override_borders_data().has_value() ? PaintableBox::remove_element_kind_from_borders_data(cell_box.override_borders_data().value()) : BordersData { |
348 | 0 | .top = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(), |
349 | 0 | .right = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(), |
350 | 0 | .bottom = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(), |
351 | 0 | .left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(), |
352 | 0 | }; |
353 | 0 | auto cell_rect = cell_coordinates_to_device_rect.get({ cell_box.table_cell_coordinates()->row_index, cell_box.table_cell_coordinates()->column_index }).value(); |
354 | 0 | paint_all_borders(context.display_list_recorder(), cell_rect, cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context)); |
355 | 0 | } |
356 | | |
357 | | void paint_table_borders(PaintContext& context, PaintableBox const& table_paintable) |
358 | 0 | { |
359 | | // Partial implementation of painting according to the collapsing border model: |
360 | | // https://www.w3.org/TR/CSS22/tables.html#collapsing-borders |
361 | 0 | Vector<PaintableBox const&> cell_boxes; |
362 | 0 | collect_cell_boxes(cell_boxes, table_paintable); |
363 | 0 | Vector<BorderEdgePaintingInfo> border_edge_painting_info_list; |
364 | 0 | HashMap<CellCoordinates, PaintableBox const*> cell_coordinates_to_box; |
365 | 0 | size_t row_count = 0; |
366 | 0 | size_t column_count = 0; |
367 | 0 | for (auto const& cell_box : cell_boxes) { |
368 | 0 | cell_coordinates_to_box.set(CellCoordinates { |
369 | 0 | .row_index = cell_box.table_cell_coordinates()->row_index, |
370 | 0 | .column_index = cell_box.table_cell_coordinates()->column_index }, |
371 | 0 | &cell_box); |
372 | 0 | row_count = max(row_count, cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span); |
373 | 0 | column_count = max(column_count, cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span); |
374 | 0 | } |
375 | 0 | auto cell_coordinates_to_device_rect = snap_cells_to_device_coordinates(cell_coordinates_to_box, row_count, column_count, context); |
376 | 0 | for (auto const& cell_box : cell_boxes) { |
377 | 0 | if (cell_box.computed_values().border_collapse() == CSS::BorderCollapse::Separate) { |
378 | 0 | paint_separate_cell_borders(cell_box, cell_coordinates_to_device_rect, context); |
379 | 0 | continue; |
380 | 0 | } |
381 | 0 | auto css_borders_data = cell_box.override_borders_data().has_value() ? cell_box.override_borders_data().value() : PaintableBox::BordersDataWithElementKind { |
382 | 0 | .top = { .border_data = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(), .element_kind = PaintableBox::ConflictingElementKind::Cell }, |
383 | 0 | .right = { .border_data = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(), .element_kind = PaintableBox::ConflictingElementKind::Cell }, |
384 | 0 | .bottom = { .border_data = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(), .element_kind = PaintableBox::ConflictingElementKind::Cell }, |
385 | 0 | .left = { .border_data = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(), .element_kind = PaintableBox::ConflictingElementKind::Cell }, |
386 | 0 | }; |
387 | 0 | DeviceBordersDataWithElementKind borders_data = { |
388 | 0 | .top = device_border_data_from_css_border_data(css_borders_data.top, context), |
389 | 0 | .right = device_border_data_from_css_border_data(css_borders_data.right, context), |
390 | 0 | .bottom = device_border_data_from_css_border_data(css_borders_data.bottom, context), |
391 | 0 | .left = device_border_data_from_css_border_data(css_borders_data.left, context), |
392 | 0 | }; |
393 | 0 | auto cell_rect = cell_coordinates_to_device_rect.get({ cell_box.table_cell_coordinates()->row_index, cell_box.table_cell_coordinates()->column_index }).value(); |
394 | 0 | CellCoordinates right_cell_coordinates { |
395 | 0 | .row_index = cell_box.table_cell_coordinates()->row_index, |
396 | 0 | .column_index = cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span |
397 | 0 | }; |
398 | 0 | auto maybe_right_cell = cell_coordinates_to_device_rect.get(right_cell_coordinates); |
399 | 0 | CellCoordinates down_cell_coordinates { |
400 | 0 | .row_index = cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span, |
401 | 0 | .column_index = cell_box.table_cell_coordinates()->column_index |
402 | 0 | }; |
403 | 0 | auto maybe_down_cell = cell_coordinates_to_device_rect.get(down_cell_coordinates); |
404 | 0 | if (maybe_right_cell.has_value()) |
405 | 0 | border_edge_painting_info_list.append(make_right_cell_edge(maybe_right_cell.value(), cell_rect, borders_data, right_cell_coordinates)); |
406 | 0 | if (maybe_down_cell.has_value()) |
407 | 0 | border_edge_painting_info_list.append(make_down_cell_edge(maybe_down_cell.value(), cell_rect, borders_data, down_cell_coordinates)); |
408 | 0 | if (cell_box.table_cell_coordinates()->row_index == 0) |
409 | 0 | border_edge_painting_info_list.append(make_first_row_top_cell_edge(cell_rect, borders_data, |
410 | 0 | { .row_index = 0, .column_index = cell_box.table_cell_coordinates()->column_index })); |
411 | 0 | if (cell_box.table_cell_coordinates()->row_index + cell_box.table_cell_coordinates()->row_span == row_count) |
412 | 0 | border_edge_painting_info_list.append(make_last_row_bottom_cell_edge(cell_rect, borders_data, |
413 | 0 | { .row_index = row_count - 1, .column_index = cell_box.table_cell_coordinates()->column_index })); |
414 | 0 | if (cell_box.table_cell_coordinates()->column_index == 0) |
415 | 0 | border_edge_painting_info_list.append(make_first_column_left_cell_edge(cell_rect, borders_data, |
416 | 0 | { .row_index = cell_box.table_cell_coordinates()->row_index, .column_index = 0 })); |
417 | 0 | if (cell_box.table_cell_coordinates()->column_index + cell_box.table_cell_coordinates()->column_span == column_count) |
418 | 0 | border_edge_painting_info_list.append(make_last_column_right_cell_edge(cell_rect, borders_data, |
419 | 0 | { .row_index = cell_box.table_cell_coordinates()->row_index, .column_index = column_count - 1 })); |
420 | 0 | } |
421 | |
|
422 | 0 | paint_collected_edges(context, border_edge_painting_info_list); |
423 | |
|
424 | 0 | for (auto const& cell_box : cell_boxes) { |
425 | 0 | auto const& border_radii_data = cell_box.normalized_border_radii_data(); |
426 | 0 | auto top_left = border_radii_data.top_left.as_corner(context); |
427 | 0 | auto top_right = border_radii_data.top_right.as_corner(context); |
428 | 0 | auto bottom_right = border_radii_data.bottom_right.as_corner(context); |
429 | 0 | auto bottom_left = border_radii_data.bottom_left.as_corner(context); |
430 | 0 | if (!top_left && !top_right && !bottom_left && !bottom_right) { |
431 | 0 | continue; |
432 | 0 | } else { |
433 | 0 | auto borders_data = cell_box.override_borders_data().has_value() ? PaintableBox::remove_element_kind_from_borders_data(cell_box.override_borders_data().value()) : BordersData { |
434 | 0 | .top = cell_box.box_model().border.top == 0 ? CSS::BorderData() : cell_box.computed_values().border_top(), |
435 | 0 | .right = cell_box.box_model().border.right == 0 ? CSS::BorderData() : cell_box.computed_values().border_right(), |
436 | 0 | .bottom = cell_box.box_model().border.bottom == 0 ? CSS::BorderData() : cell_box.computed_values().border_bottom(), |
437 | 0 | .left = cell_box.box_model().border.left == 0 ? CSS::BorderData() : cell_box.computed_values().border_left(), |
438 | 0 | }; |
439 | 0 | paint_all_borders(context.display_list_recorder(), context.rounded_device_rect(cell_box.absolute_border_box_rect()), cell_box.normalized_border_radii_data().as_corners(context), borders_data.to_device_pixels(context)); |
440 | 0 | } |
441 | 0 | } |
442 | 0 | } |
443 | | } |