diff --git a/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp b/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp index ad3b91f07b..40401cc887 100644 --- a/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/ShadowPainting.cpp @@ -54,9 +54,13 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B painter.fill_rect(rect, color); }; + // Our blur cannot handle radii over 255 so there's no point trying (255 is silly big anyway) + auto blur_radius = clamp(box_shadow_data.blur_radius, 0, 255); + // If there's no blurring, nor rounded corners, we can save a lot of effort. - if (box_shadow_data.blur_radius == 0 && !border_radii.has_any_radius()) { - fill_rect_masked(painter, content_rect.inflated(box_shadow_data.spread_distance, box_shadow_data.spread_distance, box_shadow_data.spread_distance, box_shadow_data.spread_distance).translated(box_shadow_data.offset_x, box_shadow_data.offset_y), content_rect, box_shadow_data.color); + auto non_blurred_shadow_rect = content_rect.inflated(box_shadow_data.spread_distance, box_shadow_data.spread_distance, box_shadow_data.spread_distance, box_shadow_data.spread_distance); + if (blur_radius == 0 && !border_radii.has_any_radius()) { + fill_rect_masked(painter, non_blurred_shadow_rect.translated(box_shadow_data.offset_x, box_shadow_data.offset_y), content_rect, box_shadow_data.color); continue; } @@ -77,7 +81,7 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B spread_corner(bottom_right_shadow_corner); spread_corner(bottom_left_shadow_corner); - auto expansion = box_shadow_data.spread_distance - (box_shadow_data.blur_radius * 2); + auto expansion = box_shadow_data.spread_distance - (blur_radius * 2); Gfx::IntRect inner_bounding_rect = { content_rect.x() + box_shadow_data.offset_x - expansion, content_rect.y() + box_shadow_data.offset_y - expansion, @@ -89,8 +93,8 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B // all vertical strips of the shadow are identical, and the same goes for horizontal ones. // So instead, we generate a shadow bitmap that is just large enough to include the corners and 1px of // non-corner, and then we repeatedly blit sections of it. This is similar to a NinePatch on Android. - auto double_radius = box_shadow_data.blur_radius * 2; - auto blurred_edge_thickness = box_shadow_data.blur_radius * 4; + auto double_radius = blur_radius * 2; + auto blurred_edge_thickness = blur_radius * 4; auto default_corner_size = Gfx::IntSize { double_radius, double_radius }; auto top_left_corner_size = top_left_shadow_corner ? top_left_shadow_corner.as_rect().size() : default_corner_size; @@ -98,6 +102,25 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B auto bottom_left_corner_size = bottom_left_shadow_corner ? bottom_left_shadow_corner.as_rect().size() : default_corner_size; auto bottom_right_corner_size = bottom_right_shadow_corner ? bottom_right_shadow_corner.as_rect().size() : default_corner_size; + auto max_edge_width = non_blurred_shadow_rect.width() / 2; + auto max_edge_height = non_blurred_shadow_rect.height() / 2; + auto extra_edge_width = non_blurred_shadow_rect.width() % 2; + auto extra_edge_height = non_blurred_shadow_rect.height() % 2; + + auto clip_corner_size = [&](auto& size, int x_bonus = 0, int y_bonus = 0) { + size.set_width(min(size.width(), max_edge_width + x_bonus)); + size.set_height(min(size.height(), max_edge_height + y_bonus)); + }; + + if (!top_left_corner) + clip_corner_size(top_left_corner_size, extra_edge_width, extra_edge_height); + if (!top_right_corner) + clip_corner_size(top_right_corner_size, 0, extra_edge_height); + if (!bottom_left_corner) + clip_corner_size(bottom_left_corner_size, extra_edge_width); + if (!bottom_right_corner) + clip_corner_size(bottom_right_corner_size); + auto shadow_bitmap_rect = Gfx::IntRect( 0, 0, max( @@ -131,10 +154,15 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B bottom_left_corner_size.height() + double_radius }; - Gfx::IntRect left_edge_rect { 0, top_left_corner_rect.height(), blurred_edge_thickness, 1 }; - Gfx::IntRect right_edge_rect { shadow_bitmap_rect.width() - blurred_edge_thickness, top_right_corner_rect.height(), blurred_edge_thickness, 1 }; - Gfx::IntRect top_edge_rect { top_left_corner_rect.width(), 0, 1, blurred_edge_thickness }; - Gfx::IntRect bottom_edge_rect { bottom_left_corner_rect.width(), shadow_bitmap_rect.height() - blurred_edge_thickness, 1, blurred_edge_thickness }; + auto horizontal_edge_width = min(max_edge_height, double_radius) + double_radius; + auto vertical_edge_width = min(max_edge_width, double_radius) + double_radius; + auto horizontal_top_edge_width = min(max_edge_height + extra_edge_height, double_radius) + double_radius; + auto vertical_left_edge_width = min(max_edge_width + extra_edge_width, double_radius) + double_radius; + + Gfx::IntRect left_edge_rect { 0, top_left_corner_rect.height(), vertical_left_edge_width, 1 }; + Gfx::IntRect right_edge_rect { shadow_bitmap_rect.width() - vertical_edge_width, top_right_corner_rect.height(), vertical_edge_width, 1 }; + Gfx::IntRect top_edge_rect { top_left_corner_rect.width(), 0, 1, horizontal_top_edge_width }; + Gfx::IntRect bottom_edge_rect { bottom_left_corner_rect.width(), shadow_bitmap_rect.height() - horizontal_edge_width, 1, horizontal_edge_width }; auto shadows_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, shadow_bitmap_rect.size()); if (shadows_bitmap.is_error()) { @@ -147,7 +175,7 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B aa_corner_painter.fill_rect_with_rounded_corners(shadow_bitmap_rect.shrunken(double_radius, double_radius, double_radius, double_radius), box_shadow_data.color, top_left_shadow_corner, top_right_shadow_corner, bottom_right_shadow_corner, bottom_left_shadow_corner); Gfx::StackBlurFilter filter(*shadow_bitmap); - filter.process_rgba(box_shadow_data.blur_radius, box_shadow_data.color); + filter.process_rgba(blur_radius, box_shadow_data.color); auto paint_shadow_infill = [&] { if (!border_radii.has_any_radius()) @@ -201,9 +229,9 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B }; auto left_start = inner_bounding_rect.left() - blurred_edge_thickness; - auto right_start = inner_bounding_rect.left() + inner_bounding_rect.width(); + auto right_start = inner_bounding_rect.left() + inner_bounding_rect.width() + (blurred_edge_thickness - vertical_edge_width); auto top_start = inner_bounding_rect.top() - blurred_edge_thickness; - auto bottom_start = inner_bounding_rect.top() + inner_bounding_rect.height(); + auto bottom_start = inner_bounding_rect.top() + inner_bounding_rect.height() + (blurred_edge_thickness - horizontal_edge_width); // Note: The +1s in a few of the following translations are due to the -1s Gfx::Rect::right() and Gfx::Rect::bottom(). auto top_left_corner_blit_pos = inner_bounding_rect.top_left().translated(-blurred_edge_thickness, -blurred_edge_thickness);