From c790cf21945cd68e03a86c4ea128c498d618b435 Mon Sep 17 00:00:00 2001 From: InvalidUsernameException Date: Mon, 6 Jan 2025 23:44:53 +0100 Subject: [PATCH] LibWeb: Prevent paths thinner than 1px from disappearing SVGs are rendered with subpixel precision. As such it can happen that paths are rendered with less than 1px width or height and that they can have a bounding box thinner than 1px. Due to an optimization such paths were ignored when painting because their bounding box was incorrectly calculated to be empty. As a result horizontal or vertical lines inside SVGs were missing if: * The SVG is displayed at viewbox size but the lines are defined with less than 1px. * The SVG contians 1px-thin lines, but is displayed at a size smaller than viewbox size. To prevent this, the bounding box of the path is now enlarged to contain all pixels that are partially affected. --- .../LibWeb/Painting/DisplayListRecorder.cpp | 28 +++++---- ...nal-directions-less-than-1px-wide-ref.html | 12 ++++ ...inal-directions-less-than-1px-wide-ref.png | Bin 0 -> 200 bytes ...ardinal-directions-less-than-1px-wide.html | 57 ++++++++++++++++++ 4 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 Tests/LibWeb/Screenshot/expected/svg-paths-cardinal-directions-less-than-1px-wide-ref.html create mode 100644 Tests/LibWeb/Screenshot/images/svg-paths-cardinal-directions-less-than-1px-wide-ref.png create mode 100644 Tests/LibWeb/Screenshot/input/svg-paths-cardinal-directions-less-than-1px-wide.html diff --git a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index e8bd4536000..82971c4bddb 100644 --- a/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -49,11 +49,12 @@ void DisplayListRecorder::fill_rect(Gfx::IntRect const& rect, Color color) void DisplayListRecorder::fill_path(FillPathUsingColorParams params) { auto aa_translation = params.translation.value_or(Gfx::FloatPoint {}); - auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type(); - if (path_bounding_rect.is_empty()) + auto path_bounding_rect = params.path.bounding_box().translated(aa_translation); + auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect); + if (path_bounding_int_rect.is_empty()) return; append(FillPathUsingColor { - .path_bounding_rect = path_bounding_rect, + .path_bounding_rect = path_bounding_int_rect, .path = move(params.path), .color = params.color, .winding_rule = params.winding_rule, @@ -64,11 +65,12 @@ void DisplayListRecorder::fill_path(FillPathUsingColorParams params) void DisplayListRecorder::fill_path(FillPathUsingPaintStyleParams params) { auto aa_translation = params.translation.value_or(Gfx::FloatPoint {}); - auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type(); - if (path_bounding_rect.is_empty()) + auto path_bounding_rect = params.path.bounding_box().translated(aa_translation); + auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect); + if (path_bounding_int_rect.is_empty()) return; append(FillPathUsingPaintStyle { - .path_bounding_rect = path_bounding_rect, + .path_bounding_rect = path_bounding_int_rect, .path = move(params.path), .paint_style = params.paint_style, .winding_rule = params.winding_rule, @@ -80,10 +82,11 @@ void DisplayListRecorder::fill_path(FillPathUsingPaintStyleParams params) void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params) { auto aa_translation = params.translation.value_or(Gfx::FloatPoint {}); - auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type(); + auto path_bounding_rect = params.path.bounding_box().translated(aa_translation); // Increase path bounding box by `thickness` to account for stroke. path_bounding_rect.inflate(params.thickness, params.thickness); - if (path_bounding_rect.is_empty()) + auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect); + if (path_bounding_int_rect.is_empty()) return; append(StrokePathUsingColor { .cap_style = params.cap_style, @@ -91,7 +94,7 @@ void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params) .miter_limit = params.miter_limit, .dash_array = move(params.dash_array), .dash_offset = params.dash_offset, - .path_bounding_rect = path_bounding_rect, + .path_bounding_rect = path_bounding_int_rect, .path = move(params.path), .color = params.color, .thickness = params.thickness, @@ -102,10 +105,11 @@ void DisplayListRecorder::stroke_path(StrokePathUsingColorParams params) void DisplayListRecorder::stroke_path(StrokePathUsingPaintStyleParams params) { auto aa_translation = params.translation.value_or(Gfx::FloatPoint {}); - auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type(); + auto path_bounding_rect = params.path.bounding_box().translated(aa_translation); // Increase path bounding box by `thickness` to account for stroke. path_bounding_rect.inflate(params.thickness, params.thickness); - if (path_bounding_rect.is_empty()) + auto path_bounding_int_rect = enclosing_int_rect(path_bounding_rect); + if (path_bounding_int_rect.is_empty()) return; append(StrokePathUsingPaintStyle { .cap_style = params.cap_style, @@ -113,7 +117,7 @@ void DisplayListRecorder::stroke_path(StrokePathUsingPaintStyleParams params) .miter_limit = params.miter_limit, .dash_array = move(params.dash_array), .dash_offset = params.dash_offset, - .path_bounding_rect = path_bounding_rect, + .path_bounding_rect = path_bounding_int_rect, .path = move(params.path), .paint_style = params.paint_style, .thickness = params.thickness, diff --git a/Tests/LibWeb/Screenshot/expected/svg-paths-cardinal-directions-less-than-1px-wide-ref.html b/Tests/LibWeb/Screenshot/expected/svg-paths-cardinal-directions-less-than-1px-wide-ref.html new file mode 100644 index 00000000000..8c9a97aabfa --- /dev/null +++ b/Tests/LibWeb/Screenshot/expected/svg-paths-cardinal-directions-less-than-1px-wide-ref.html @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/Tests/LibWeb/Screenshot/images/svg-paths-cardinal-directions-less-than-1px-wide-ref.png b/Tests/LibWeb/Screenshot/images/svg-paths-cardinal-directions-less-than-1px-wide-ref.png new file mode 100644 index 0000000000000000000000000000000000000000..74051eb06eb0f1d5e7ceffb0d8512c0067900080 GIT binary patch literal 200 zcmeAS@N?(olHy`uVBq!ia0y~yV8~-&U?^l^W?*1A_O-i>fq_9Zz$e7DzP`Spp<%&- z1^f5!KXBl{g9i`(|NmbX{zwy~!_&nvB;xSfYa2NaC + + + + + + + + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + +
+ + + + + + + + + + + +