From 668c4c8abab1b0af8a8e0eca9bb4567b1c7b364e Mon Sep 17 00:00:00 2001 From: Oriol Brufau Date: Thu, 27 Apr 2023 01:40:29 +0200 Subject: [PATCH] Round text decoration thickness to >= 1 device pixels Previously the thickness coming from font metrics could be something like 0.7px, so with 1dppx it would be painted as either 1 or 0 device pixels. Enforcing at least 1 device pixel ensures that the decoration will be visible, and rounding to an integral amount of device pixels ensures that the thickness won't vary depending on the position. The specification requires this behavior when text-decoration-thickness is set to a length or percentage. It's not clear if it should also happen by default, but this seems to match other browsers (except for WebKit rounding up instead of to the nearest integer). The test text-decoration-thickness-from-zero-sized-font.html is now failing because of #29675. --- components/layout/display_list/builder.rs | 18 +++++++++++++++--- components/layout_2020/display_list/mod.rs | 11 ++++++++--- ...ion-thickness-from-zero-sized-font.html.ini | 2 ++ ...ion-thickness-from-zero-sized-font.html.ini | 2 ++ 4 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 tests/wpt/metadata-layout-2020/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini create mode 100644 tests/wpt/metadata/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini diff --git a/components/layout/display_list/builder.rs b/components/layout/display_list/builder.rs index 17f6a974a50..4b6aa8e7371 100644 --- a/components/layout/display_list/builder.rs +++ b/components/layout/display_list/builder.rs @@ -2064,6 +2064,15 @@ impl Fragment { // Create display items for text decorations. let text_decorations = self.style().get_inherited_text().text_decorations_in_effect; + let dppx = state + .layout_context + .style_context + .device_pixel_ratio() + .get(); + let round_to_nearest_device_pixel = |value: Au| -> Au { + // Round to the nearest integer device pixel, ensuring at least one device pixel. + Au::from_f32_px((value.to_f32_px() * dppx).round().max(1.0) / dppx) + }; let logical_stacking_relative_content_box = LogicalRect::from_physical( self.style.writing_mode, @@ -2077,7 +2086,8 @@ impl Fragment { stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset; - stacking_relative_box.size.block = metrics.underline_size; + stacking_relative_box.size.block = + round_to_nearest_device_pixel(metrics.underline_size); self.build_display_list_for_text_decoration( state, &text_color, @@ -2089,7 +2099,8 @@ impl Fragment { // Overline if text_decorations.overline { let mut stacking_relative_box = logical_stacking_relative_content_box; - stacking_relative_box.size.block = metrics.underline_size; + stacking_relative_box.size.block = + round_to_nearest_device_pixel(metrics.underline_size); self.build_display_list_for_text_decoration( state, &text_color, @@ -2144,7 +2155,8 @@ impl Fragment { let mut stacking_relative_box = logical_stacking_relative_content_box; stacking_relative_box.start.b = stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset; - stacking_relative_box.size.block = metrics.strikeout_size; + stacking_relative_box.size.block = + round_to_nearest_device_pixel(metrics.strikeout_size); self.build_display_list_for_text_decoration( state, &text_color, diff --git a/components/layout_2020/display_list/mod.rs b/components/layout_2020/display_list/mod.rs index ae34c5ad952..e042517ea42 100644 --- a/components/layout_2020/display_list/mod.rs +++ b/components/layout_2020/display_list/mod.rs @@ -212,6 +212,11 @@ impl Fragment { let color = fragment.parent_style.clone_color(); let font_metrics = &fragment.font_metrics; + let dppx = builder.context.style_context.device_pixel_ratio().get(); + let round_to_nearest_device_pixel = |value: Length| -> Length { + // Round to the nearest integer device pixel, ensuring at least one device pixel. + Length::new((value.px() * dppx).round().max(1.0) / dppx) + }; // Underline. if fragment @@ -220,7 +225,7 @@ impl Fragment { { let mut rect = rect; rect.origin.y = rect.origin.y + font_metrics.ascent - font_metrics.underline_offset; - rect.size.height = font_metrics.underline_size; + rect.size.height = round_to_nearest_device_pixel(font_metrics.underline_size); self.build_display_list_for_text_decoration(fragment, builder, &rect, color); } @@ -230,7 +235,7 @@ impl Fragment { .contains(TextDecorationLine::OVERLINE) { let mut rect = rect; - rect.size.height = font_metrics.underline_size; + rect.size.height = round_to_nearest_device_pixel(font_metrics.underline_size); self.build_display_list_for_text_decoration(fragment, builder, &rect, color); } @@ -252,7 +257,7 @@ impl Fragment { let mut rect = rect; rect.origin.y = rect.origin.y + font_metrics.ascent - font_metrics.strikeout_offset; // XXX(ferjm) This does not work on MacOS #942 - rect.size.height = font_metrics.strikeout_size; + rect.size.height = round_to_nearest_device_pixel(font_metrics.strikeout_size); self.build_display_list_for_text_decoration(fragment, builder, &rect, color); } } diff --git a/tests/wpt/metadata-layout-2020/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini b/tests/wpt/metadata-layout-2020/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini new file mode 100644 index 00000000000..ba4be2d12c7 --- /dev/null +++ b/tests/wpt/metadata-layout-2020/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini @@ -0,0 +1,2 @@ +[text-decoration-thickness-from-zero-sized-font.html] + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini b/tests/wpt/metadata/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini new file mode 100644 index 00000000000..ba4be2d12c7 --- /dev/null +++ b/tests/wpt/metadata/css/css-text-decor/text-decoration-thickness-from-zero-sized-font.html.ini @@ -0,0 +1,2 @@ +[text-decoration-thickness-from-zero-sized-font.html] + expected: FAIL