1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:17:44 +00:00

LibWeb: Convert shadow painting to new pixel units

This commit is contained in:
Sam Atkins 2022-10-27 16:22:19 +01:00 committed by Linus Groh
parent 9d1f30b533
commit fb43a71981
4 changed files with 110 additions and 100 deletions

View file

@ -63,13 +63,13 @@ void InlinePaintable::paint(PaintContext& context, Painting::PaintPhase phase) c
for (auto const& layer : computed_box_shadow) { for (auto const& layer : computed_box_shadow) {
resolved_box_shadow_data.empend( resolved_box_shadow_data.empend(
layer.color, layer.color,
static_cast<int>(layer.offset_x.to_px(layout_node())), layer.offset_x.to_px(layout_node()),
static_cast<int>(layer.offset_y.to_px(layout_node())), layer.offset_y.to_px(layout_node()),
static_cast<int>(layer.blur_radius.to_px(layout_node())), layer.blur_radius.to_px(layout_node()),
static_cast<int>(layer.spread_distance.to_px(layout_node())), layer.spread_distance.to_px(layout_node()),
layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer : Painting::ShadowPlacement::Inner); layer.placement == CSS::ShadowPlacement::Outer ? Painting::ShadowPlacement::Outer : Painting::ShadowPlacement::Inner);
} }
Painting::paint_box_shadow(context, enclosing_int_rect(absolute_fragment_rect), border_radii_data, resolved_box_shadow_data); Painting::paint_box_shadow(context, absolute_fragment_rect.to_type<CSSPixels>(), border_radii_data, resolved_box_shadow_data);
} }
return IterationDecision::Continue; return IterationDecision::Continue;

View file

@ -112,8 +112,8 @@ Gfx::FloatRect PaintableBox::compute_absolute_paint_rect() const
for (auto const& shadow : resolved_box_shadow_data) { for (auto const& shadow : resolved_box_shadow_data) {
if (shadow.placement == ShadowPlacement::Inner) if (shadow.placement == ShadowPlacement::Inner)
continue; continue;
auto inflate = shadow.spread_distance + shadow.blur_radius; auto inflate = shadow.spread_distance.value() + shadow.blur_radius.value();
auto shadow_rect = rect.inflated(inflate, inflate, inflate, inflate).translated(shadow.offset_x, shadow.offset_y); auto shadow_rect = rect.inflated(inflate, inflate, inflate, inflate).translated(shadow.offset_x.value(), shadow.offset_y.value());
rect = rect.united(shadow_rect); rect = rect.united(shadow_rect);
} }
return rect; return rect;
@ -280,10 +280,10 @@ Vector<ShadowData> PaintableBox::resolve_box_shadow_data() const
for (auto const& layer : box_shadow_data) { for (auto const& layer : box_shadow_data) {
resolved_box_shadow_data.empend( resolved_box_shadow_data.empend(
layer.color, layer.color,
static_cast<int>(layer.offset_x.to_px(layout_box())), layer.offset_x.to_px(layout_box()),
static_cast<int>(layer.offset_y.to_px(layout_box())), layer.offset_y.to_px(layout_box()),
static_cast<int>(layer.blur_radius.to_px(layout_box())), layer.blur_radius.to_px(layout_box()),
static_cast<int>(layer.spread_distance.to_px(layout_box())), layer.spread_distance.to_px(layout_box()),
layer.placement == CSS::ShadowPlacement::Outer ? ShadowPlacement::Outer : ShadowPlacement::Inner); layer.placement == CSS::ShadowPlacement::Outer ? ShadowPlacement::Outer : ShadowPlacement::Inner);
} }
@ -295,7 +295,7 @@ void PaintableBox::paint_box_shadow(PaintContext& context) const
auto resolved_box_shadow_data = resolve_box_shadow_data(); auto resolved_box_shadow_data = resolve_box_shadow_data();
if (resolved_box_shadow_data.is_empty()) if (resolved_box_shadow_data.is_empty())
return; return;
Painting::paint_box_shadow(context, absolute_border_box_rect().to_rounded<int>(), normalized_border_radii_data(), resolved_box_shadow_data); Painting::paint_box_shadow(context, absolute_border_box_rect().to_type<CSSPixels>(), normalized_border_radii_data(), resolved_box_shadow_data);
} }
BorderRadiiData PaintableBox::normalized_border_radii_data(ShrinkRadiiForBorders shrink) const BorderRadiiData PaintableBox::normalized_border_radii_data(ShrinkRadiiForBorders shrink) const
@ -577,10 +577,10 @@ void PaintableWithLines::paint(PaintContext& context, PaintPhase phase) const
for (auto const& layer : text_shadow) { for (auto const& layer : text_shadow) {
resolved_shadow_data.empend( resolved_shadow_data.empend(
layer.color, layer.color,
static_cast<int>(layer.offset_x.to_px(layout_box())), layer.offset_x.to_px(layout_box()),
static_cast<int>(layer.offset_y.to_px(layout_box())), layer.offset_y.to_px(layout_box()),
static_cast<int>(layer.blur_radius.to_px(layout_box())), layer.blur_radius.to_px(layout_box()),
static_cast<int>(layer.spread_distance.to_px(layout_box())), layer.spread_distance.to_px(layout_box()),
ShadowPlacement::Outer); ShadowPlacement::Outer);
} }
context.painter().set_font(fragment.layout_node().font()); context.painter().set_font(fragment.layout_node().font());

View file

@ -18,19 +18,20 @@
namespace Web::Painting { namespace Web::Painting {
void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, BorderRadiiData const& border_radii, Vector<ShadowData> const& box_shadow_layers) void paint_box_shadow(PaintContext& context, CSSPixelRect const& content_rect, BorderRadiiData const& border_radii, Vector<ShadowData> const& box_shadow_layers)
{ {
if (box_shadow_layers.is_empty()) if (box_shadow_layers.is_empty())
return; return;
auto& painter = context.painter(); auto& painter = context.painter();
auto device_content_rect = context.rounded_device_rect(content_rect);
auto top_left_corner = border_radii.top_left.as_corner(context); auto top_left_corner = border_radii.top_left.as_corner(context);
auto top_right_corner = border_radii.top_right.as_corner(context); auto top_right_corner = border_radii.top_right.as_corner(context);
auto bottom_right_corner = border_radii.bottom_right.as_corner(context); auto bottom_right_corner = border_radii.bottom_right.as_corner(context);
auto bottom_left_corner = border_radii.bottom_left.as_corner(context); auto bottom_left_corner = border_radii.bottom_left.as_corner(context);
ScopedCornerRadiusClip corner_clipper { context, painter, content_rect.to_type<DevicePixels>(), border_radii, CornerClip::Inside }; ScopedCornerRadiusClip corner_clipper { context, painter, device_content_rect, border_radii, CornerClip::Inside };
// Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse // Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse
for (auto& box_shadow_data : box_shadow_layers.in_reverse()) { for (auto& box_shadow_data : box_shadow_layers.in_reverse()) {
@ -38,21 +39,26 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
if (box_shadow_data.placement != ShadowPlacement::Outer) if (box_shadow_data.placement != ShadowPlacement::Outer)
continue; continue;
DevicePixels offset_x = context.rounded_device_pixels(box_shadow_data.offset_x);
DevicePixels offset_y = context.rounded_device_pixels(box_shadow_data.offset_y);
DevicePixels blur_radius = context.rounded_device_pixels(box_shadow_data.blur_radius);
DevicePixels spread_distance = context.rounded_device_pixels(box_shadow_data.spread_distance);
auto fill_rect_masked = [](auto& painter, auto fill_rect, auto mask_rect, auto color) { auto fill_rect_masked = [](auto& painter, auto fill_rect, auto mask_rect, auto color) {
Gfx::DisjointIntRectSet rect_set; Gfx::DisjointRectSet<DevicePixels> rect_set;
rect_set.add(fill_rect); rect_set.add(fill_rect);
auto shattered = rect_set.shatter(mask_rect); auto shattered = rect_set.shatter(mask_rect);
for (auto& rect : shattered.rects()) for (auto& rect : shattered.rects())
painter.fill_rect(rect, color); painter.fill_rect(rect.template to_type<int>(), color);
}; };
// Our blur cannot handle radii over 255 so there's no point trying (255 is silly big anyway) // 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); blur_radius = clamp(blur_radius, 0, 255);
// If there's no blurring, nor rounded corners, we can save a lot of effort. // If there's no blurring, nor rounded corners, we can save a lot of effort.
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); auto non_blurred_shadow_rect = device_content_rect.inflated(spread_distance, spread_distance, spread_distance, spread_distance);
if (blur_radius == 0 && !border_radii.has_any_radius()) { 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); fill_rect_masked(painter, non_blurred_shadow_rect.translated(offset_x, offset_y), device_content_rect, box_shadow_data.color);
continue; continue;
} }
@ -63,8 +69,8 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
auto spread_corner = [&](auto& corner) { auto spread_corner = [&](auto& corner) {
if (corner) { if (corner) {
corner.horizontal_radius += box_shadow_data.spread_distance; corner.horizontal_radius += spread_distance.value();
corner.vertical_radius += box_shadow_data.spread_distance; corner.vertical_radius += spread_distance.value();
} }
}; };
@ -73,12 +79,12 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
spread_corner(bottom_right_shadow_corner); spread_corner(bottom_right_shadow_corner);
spread_corner(bottom_left_shadow_corner); spread_corner(bottom_left_shadow_corner);
auto expansion = box_shadow_data.spread_distance - (blur_radius * 2); auto expansion = spread_distance - (blur_radius * 2);
Gfx::IntRect inner_bounding_rect = { DevicePixelRect inner_bounding_rect = {
content_rect.x() + box_shadow_data.offset_x - expansion, device_content_rect.x() + offset_x - expansion,
content_rect.y() + box_shadow_data.offset_y - expansion, device_content_rect.y() + offset_y - expansion,
content_rect.width() + 2 * expansion, device_content_rect.width() + 2 * expansion,
content_rect.height() + 2 * expansion device_content_rect.height() + 2 * expansion
}; };
// Calculating and blurring the box-shadow full size is expensive, and wasteful - aside from the corners, // Calculating and blurring the box-shadow full size is expensive, and wasteful - aside from the corners,
@ -89,21 +95,21 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
auto blurred_edge_thickness = blur_radius * 4; auto blurred_edge_thickness = blur_radius * 4;
auto default_corner_size = Gfx::IntSize { double_radius, double_radius }; 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; auto top_left_corner_size = (top_left_shadow_corner ? top_left_shadow_corner.as_rect().size() : default_corner_size).to_type<DevicePixels>();
auto top_right_corner_size = top_right_shadow_corner ? top_right_shadow_corner.as_rect().size() : default_corner_size; auto top_right_corner_size = (top_right_shadow_corner ? top_right_shadow_corner.as_rect().size() : default_corner_size).to_type<DevicePixels>();
auto bottom_left_corner_size = bottom_left_shadow_corner ? bottom_left_shadow_corner.as_rect().size() : default_corner_size; auto bottom_left_corner_size = (bottom_left_shadow_corner ? bottom_left_shadow_corner.as_rect().size() : default_corner_size).to_type<DevicePixels>();
auto bottom_right_corner_size = bottom_right_shadow_corner ? bottom_right_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).to_type<DevicePixels>();
auto max_edge_width = non_blurred_shadow_rect.width() / 2; auto max_edge_width = non_blurred_shadow_rect.width() / 2;
auto max_edge_height = non_blurred_shadow_rect.height() / 2; auto max_edge_height = non_blurred_shadow_rect.height() / 2;
auto extra_edge_width = non_blurred_shadow_rect.width() % 2; auto extra_edge_width = non_blurred_shadow_rect.width() % 2;
auto extra_edge_height = non_blurred_shadow_rect.height() % 2; auto extra_edge_height = non_blurred_shadow_rect.height() % 2;
auto clip_corner_size = [&](auto& size, auto const& corner, int x_bonus = 0, int y_bonus = 0) { auto clip_corner_size = [&](auto& size, auto const& corner, DevicePixels x_bonus = 0, DevicePixels y_bonus = 0) {
auto max_x = max_edge_width + x_bonus; auto max_x = (max_edge_width + x_bonus).value();
auto max_y = max_edge_height + y_bonus; auto max_y = (max_edge_height + y_bonus).value();
auto min_x = max(corner.horizontal_radius, min(double_radius, max_x)); auto min_x = max(corner.horizontal_radius, min(double_radius, max_x).value());
auto min_y = max(corner.vertical_radius, min(double_radius, max_y)); auto min_y = max(corner.vertical_radius, min(double_radius, max_y).value());
if (min_x <= max_x) if (min_x <= max_x)
size.set_width(clamp(size.width(), min_x, max_x)); size.set_width(clamp(size.width(), min_x, max_x));
if (min_y <= max_y) if (min_y <= max_y)
@ -115,7 +121,7 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
clip_corner_size(bottom_left_corner_size, bottom_left_corner, extra_edge_width); clip_corner_size(bottom_left_corner_size, bottom_left_corner, extra_edge_width);
clip_corner_size(bottom_right_corner_size, bottom_right_corner); clip_corner_size(bottom_right_corner_size, bottom_right_corner);
auto shadow_bitmap_rect = Gfx::IntRect( auto shadow_bitmap_rect = DevicePixelRect(
0, 0, 0, 0,
max( max(
top_left_corner_size.width() + top_right_corner_size.width(), top_left_corner_size.width() + top_right_corner_size.width(),
@ -126,23 +132,23 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
top_right_corner_size.height() + bottom_right_corner_size.height()) top_right_corner_size.height() + bottom_right_corner_size.height())
+ 1 + blurred_edge_thickness); + 1 + blurred_edge_thickness);
auto top_left_corner_rect = Gfx::IntRect { auto top_left_corner_rect = DevicePixelRect {
0, 0, 0, 0,
top_left_corner_size.width() + double_radius, top_left_corner_size.width() + double_radius,
top_left_corner_size.height() + double_radius top_left_corner_size.height() + double_radius
}; };
auto top_right_corner_rect = Gfx::IntRect { auto top_right_corner_rect = DevicePixelRect {
shadow_bitmap_rect.width() - (top_right_corner_size.width() + double_radius), 0, shadow_bitmap_rect.width() - (top_right_corner_size.width() + double_radius), 0,
top_right_corner_size.width() + double_radius, top_right_corner_size.width() + double_radius,
top_right_corner_size.height() + double_radius top_right_corner_size.height() + double_radius
}; };
auto bottom_right_corner_rect = Gfx::IntRect { auto bottom_right_corner_rect = DevicePixelRect {
shadow_bitmap_rect.width() - (bottom_right_corner_size.width() + double_radius), shadow_bitmap_rect.width() - (bottom_right_corner_size.width() + double_radius),
shadow_bitmap_rect.height() - (bottom_right_corner_size.height() + double_radius), shadow_bitmap_rect.height() - (bottom_right_corner_size.height() + double_radius),
bottom_right_corner_size.width() + double_radius, bottom_right_corner_size.width() + double_radius,
bottom_right_corner_size.height() + double_radius bottom_right_corner_size.height() + double_radius
}; };
auto bottom_left_corner_rect = Gfx::IntRect { auto bottom_left_corner_rect = DevicePixelRect {
0, shadow_bitmap_rect.height() - (bottom_left_corner_size.height() + double_radius), 0, shadow_bitmap_rect.height() - (bottom_left_corner_size.height() + double_radius),
bottom_left_corner_size.width() + double_radius, bottom_left_corner_size.width() + double_radius,
bottom_left_corner_size.height() + double_radius bottom_left_corner_size.height() + double_radius
@ -153,12 +159,12 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
auto horizontal_top_edge_width = min(max_edge_height + extra_edge_height, 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; 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 }; DevicePixelRect 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 }; DevicePixelRect 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 }; DevicePixelRect 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 }; DevicePixelRect 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()); auto shadows_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, shadow_bitmap_rect.size().to_type<int>());
if (shadows_bitmap.is_error()) { if (shadows_bitmap.is_error()) {
dbgln("Unable to allocate temporary bitmap {} for box-shadow rendering: {}", shadow_bitmap_rect, shadows_bitmap.error()); dbgln("Unable to allocate temporary bitmap {} for box-shadow rendering: {}", shadow_bitmap_rect, shadows_bitmap.error());
return; return;
@ -167,13 +173,13 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
Gfx::Painter corner_painter { *shadow_bitmap }; Gfx::Painter corner_painter { *shadow_bitmap };
Gfx::AntiAliasingPainter aa_corner_painter { corner_painter }; Gfx::AntiAliasingPainter aa_corner_painter { corner_painter };
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); aa_corner_painter.fill_rect_with_rounded_corners(shadow_bitmap_rect.shrunken(double_radius, double_radius, double_radius, double_radius).to_type<int>(), 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); Gfx::StackBlurFilter filter(*shadow_bitmap);
filter.process_rgba(blur_radius, box_shadow_data.color); filter.process_rgba(blur_radius.value(), box_shadow_data.color);
auto paint_shadow_infill = [&] { auto paint_shadow_infill = [&] {
if (!border_radii.has_any_radius()) if (!border_radii.has_any_radius())
return painter.fill_rect(inner_bounding_rect, box_shadow_data.color); return painter.fill_rect(inner_bounding_rect.to_type<int>(), box_shadow_data.color);
auto top_left_inner_width = top_left_corner_rect.width() - blurred_edge_thickness; auto top_left_inner_width = top_left_corner_rect.width() - blurred_edge_thickness;
auto top_left_inner_height = top_left_corner_rect.height() - blurred_edge_thickness; auto top_left_inner_height = top_left_corner_rect.height() - blurred_edge_thickness;
@ -184,42 +190,42 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
auto bottom_left_inner_width = bottom_left_corner_rect.width() - blurred_edge_thickness; auto bottom_left_inner_width = bottom_left_corner_rect.width() - blurred_edge_thickness;
auto bottom_left_inner_height = bottom_left_corner_rect.height() - blurred_edge_thickness; auto bottom_left_inner_height = bottom_left_corner_rect.height() - blurred_edge_thickness;
Gfx::IntRect top_rect { DevicePixelRect top_rect {
inner_bounding_rect.x() + top_left_inner_width, inner_bounding_rect.x() + top_left_inner_width,
inner_bounding_rect.y(), inner_bounding_rect.y(),
inner_bounding_rect.width() - top_left_inner_width - top_right_inner_width, inner_bounding_rect.width() - top_left_inner_width - top_right_inner_width,
top_left_inner_height top_left_inner_height
}; };
Gfx::IntRect right_rect { DevicePixelRect right_rect {
inner_bounding_rect.x() + inner_bounding_rect.width() - top_right_inner_width, inner_bounding_rect.x() + inner_bounding_rect.width() - top_right_inner_width,
inner_bounding_rect.y() + top_right_inner_height, inner_bounding_rect.y() + top_right_inner_height,
top_right_inner_width, top_right_inner_width,
inner_bounding_rect.height() - top_right_inner_height - bottom_right_inner_height inner_bounding_rect.height() - top_right_inner_height - bottom_right_inner_height
}; };
Gfx::IntRect bottom_rect { DevicePixelRect bottom_rect {
inner_bounding_rect.x() + bottom_left_inner_width, inner_bounding_rect.x() + bottom_left_inner_width,
inner_bounding_rect.y() + inner_bounding_rect.height() - bottom_right_inner_height, inner_bounding_rect.y() + inner_bounding_rect.height() - bottom_right_inner_height,
inner_bounding_rect.width() - bottom_left_inner_width - bottom_right_inner_width, inner_bounding_rect.width() - bottom_left_inner_width - bottom_right_inner_width,
bottom_right_inner_height bottom_right_inner_height
}; };
Gfx::IntRect left_rect { DevicePixelRect left_rect {
inner_bounding_rect.x(), inner_bounding_rect.x(),
inner_bounding_rect.y() + top_left_inner_height, inner_bounding_rect.y() + top_left_inner_height,
bottom_left_inner_width, bottom_left_inner_width,
inner_bounding_rect.height() - top_left_inner_height - bottom_left_inner_height inner_bounding_rect.height() - top_left_inner_height - bottom_left_inner_height
}; };
Gfx::IntRect inner = { DevicePixelRect inner = {
left_rect.x() + left_rect.width(), left_rect.x() + left_rect.width(),
left_rect.y(), left_rect.y(),
inner_bounding_rect.width() - left_rect.width() - right_rect.width(), inner_bounding_rect.width() - left_rect.width() - right_rect.width(),
inner_bounding_rect.height() - top_rect.height() - bottom_rect.height() inner_bounding_rect.height() - top_rect.height() - bottom_rect.height()
}; };
painter.fill_rect(top_rect, box_shadow_data.color); painter.fill_rect(top_rect.to_type<int>(), box_shadow_data.color);
painter.fill_rect(right_rect, box_shadow_data.color); painter.fill_rect(right_rect.to_type<int>(), box_shadow_data.color);
painter.fill_rect(bottom_rect, box_shadow_data.color); painter.fill_rect(bottom_rect.to_type<int>(), box_shadow_data.color);
painter.fill_rect(left_rect, box_shadow_data.color); painter.fill_rect(left_rect.to_type<int>(), box_shadow_data.color);
painter.fill_rect(inner, box_shadow_data.color); painter.fill_rect(inner.to_type<int>(), box_shadow_data.color);
}; };
auto left_start = inner_bounding_rect.left() - blurred_edge_thickness; auto left_start = inner_bounding_rect.left() - blurred_edge_thickness;
@ -233,29 +239,29 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
auto bottom_left_corner_blit_pos = inner_bounding_rect.bottom_left().translated(-blurred_edge_thickness, -bottom_left_corner_size.height() + 1 + double_radius); auto bottom_left_corner_blit_pos = inner_bounding_rect.bottom_left().translated(-blurred_edge_thickness, -bottom_left_corner_size.height() + 1 + double_radius);
auto bottom_right_corner_blit_pos = inner_bounding_rect.bottom_right().translated(-bottom_right_corner_size.width() + 1 + double_radius, -bottom_right_corner_size.height() + 1 + double_radius); auto bottom_right_corner_blit_pos = inner_bounding_rect.bottom_right().translated(-bottom_right_corner_size.width() + 1 + double_radius, -bottom_right_corner_size.height() + 1 + double_radius);
auto paint_shadow = [&](Gfx::IntRect clip_rect) { auto paint_shadow = [&](DevicePixelRect clip_rect) {
Gfx::PainterStateSaver save { painter }; Gfx::PainterStateSaver save { painter };
painter.add_clip_rect(clip_rect); painter.add_clip_rect(clip_rect.to_type<int>());
paint_shadow_infill(); paint_shadow_infill();
// Corners // Corners
painter.blit(top_left_corner_blit_pos, shadow_bitmap, top_left_corner_rect); painter.blit(top_left_corner_blit_pos.to_type<int>(), shadow_bitmap, top_left_corner_rect.to_type<int>());
painter.blit(top_right_corner_blit_pos, shadow_bitmap, top_right_corner_rect); painter.blit(top_right_corner_blit_pos.to_type<int>(), shadow_bitmap, top_right_corner_rect.to_type<int>());
painter.blit(bottom_left_corner_blit_pos, shadow_bitmap, bottom_left_corner_rect); painter.blit(bottom_left_corner_blit_pos.to_type<int>(), shadow_bitmap, bottom_left_corner_rect.to_type<int>());
painter.blit(bottom_right_corner_blit_pos, shadow_bitmap, bottom_right_corner_rect); painter.blit(bottom_right_corner_blit_pos.to_type<int>(), shadow_bitmap, bottom_right_corner_rect.to_type<int>());
// Horizontal edges // Horizontal edges
for (auto x = inner_bounding_rect.left() + (bottom_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (bottom_right_corner_size.width() - double_radius); ++x) for (auto x = inner_bounding_rect.left() + (bottom_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (bottom_right_corner_size.width() - double_radius); ++x)
painter.blit({ x, bottom_start }, shadow_bitmap, bottom_edge_rect); painter.blit({ x, bottom_start }, shadow_bitmap, bottom_edge_rect.to_type<int>());
for (auto x = inner_bounding_rect.left() + (top_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (top_right_corner_size.width() - double_radius); ++x) for (auto x = inner_bounding_rect.left() + (top_left_corner_size.width() - double_radius); x <= inner_bounding_rect.right() - (top_right_corner_size.width() - double_radius); ++x)
painter.blit({ x, top_start }, shadow_bitmap, top_edge_rect); painter.blit({ x, top_start }, shadow_bitmap, top_edge_rect.to_type<int>());
// Vertical edges // Vertical edges
for (auto y = inner_bounding_rect.top() + (top_right_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_right_corner_size.height() - double_radius); ++y) for (auto y = inner_bounding_rect.top() + (top_right_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_right_corner_size.height() - double_radius); ++y)
painter.blit({ right_start, y }, shadow_bitmap, right_edge_rect); painter.blit({ right_start, y }, shadow_bitmap, right_edge_rect.to_type<int>());
for (auto y = inner_bounding_rect.top() + (top_left_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_left_corner_size.height() - double_radius); ++y) for (auto y = inner_bounding_rect.top() + (top_left_corner_size.height() - double_radius); y <= inner_bounding_rect.bottom() - (bottom_left_corner_size.height() - double_radius); ++y)
painter.blit({ left_start, y }, shadow_bitmap, left_edge_rect); painter.blit({ left_start, y }, shadow_bitmap, left_edge_rect.to_type<int>());
}; };
// FIXME: Painter only lets us define a clip-rect which discards drawing outside of it, whereas here we want // FIXME: Painter only lets us define a clip-rect which discards drawing outside of it, whereas here we want
@ -290,38 +296,38 @@ void paint_box_shadow(PaintContext& context, Gfx::IntRect const& content_rect, B
constexpr auto really_large_number = NumericLimits<int>::max() / 2; constexpr auto really_large_number = NumericLimits<int>::max() / 2;
// Everything above content_rect, including sides // Everything above content_rect, including sides
paint_shadow({ 0, 0, really_large_number, content_rect.top() }); paint_shadow({ 0, 0, really_large_number, device_content_rect.top() });
// Everything below content_rect, including sides // Everything below content_rect, including sides
paint_shadow({ 0, content_rect.bottom() + 1, really_large_number, really_large_number }); paint_shadow({ 0, device_content_rect.bottom() + 1, really_large_number, really_large_number });
// Everything directly to the left of content_rect // Everything directly to the left of content_rect
paint_shadow({ 0, content_rect.top(), content_rect.left(), content_rect.height() }); paint_shadow({ 0, device_content_rect.top(), device_content_rect.left(), device_content_rect.height() });
// Everything directly to the right of content_rect // Everything directly to the right of content_rect
paint_shadow({ content_rect.right() + 1, content_rect.top(), really_large_number, content_rect.height() }); paint_shadow({ device_content_rect.right() + 1, device_content_rect.top(), really_large_number, device_content_rect.height() });
if (top_left_corner) { if (top_left_corner) {
// Inside the top left corner (the part outside the border radius) // Inside the top left corner (the part outside the border radius)
auto top_left = top_left_corner.as_rect().translated(content_rect.top_left()); auto top_left = top_left_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.top_left());
paint_shadow(top_left); paint_shadow(top_left);
} }
if (top_right_corner) { if (top_right_corner) {
// Inside the top right corner (the part outside the border radius) // Inside the top right corner (the part outside the border radius)
auto top_right = top_right_corner.as_rect().translated(content_rect.top_right().translated(-top_right_corner.horizontal_radius + 1, 0)); auto top_right = top_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.top_right().translated(-top_right_corner.horizontal_radius + 1, 0));
paint_shadow(top_right); paint_shadow(top_right);
} }
if (bottom_right_corner) { if (bottom_right_corner) {
// Inside the bottom right corner (the part outside the border radius) // Inside the bottom right corner (the part outside the border radius)
auto bottom_right = bottom_right_corner.as_rect().translated(content_rect.bottom_right().translated(-bottom_right_corner.horizontal_radius + 1, -bottom_right_corner.vertical_radius + 1)); auto bottom_right = bottom_right_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_right().translated(-bottom_right_corner.horizontal_radius + 1, -bottom_right_corner.vertical_radius + 1));
paint_shadow(bottom_right); paint_shadow(bottom_right);
} }
if (bottom_left_corner) { if (bottom_left_corner) {
// Inside the bottom left corner (the part outside the border radius) // Inside the bottom left corner (the part outside the border radius)
auto bottom_left = bottom_left_corner.as_rect().translated(content_rect.bottom_left().translated(0, -bottom_left_corner.vertical_radius + 1)); auto bottom_left = bottom_left_corner.as_rect().to_type<DevicePixels>().translated(device_content_rect.bottom_left().translated(0, -bottom_left_corner.vertical_radius + 1));
paint_shadow(bottom_left); paint_shadow(bottom_left);
} }
} }
@ -336,22 +342,26 @@ void paint_text_shadow(PaintContext& context, Layout::LineBoxFragment const& fra
// Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse // Note: Box-shadow layers are ordered front-to-back, so we paint them in reverse
for (auto& layer : shadow_layers.in_reverse()) { for (auto& layer : shadow_layers.in_reverse()) {
DevicePixels offset_x = context.rounded_device_pixels(layer.offset_x);
DevicePixels offset_y = context.rounded_device_pixels(layer.offset_y);
DevicePixels blur_radius = context.rounded_device_pixels(layer.blur_radius);
DevicePixels fragment_width = context.enclosing_device_pixels(fragment.width());
DevicePixels fragment_height = context.enclosing_device_pixels(fragment.height());
// Space around the painted text to allow it to blur. // Space around the painted text to allow it to blur.
// FIXME: Include spread in this once we use that. // FIXME: Include spread in this once we use that.
auto margin = layer.blur_radius * 2; DevicePixels margin = blur_radius * 2;
Gfx::IntRect text_rect { DevicePixelRect text_rect {
margin, margin, margin, margin,
static_cast<int>(ceilf(fragment.width())), fragment_width, fragment_height
static_cast<int>(ceilf(fragment.height()))
}; };
Gfx::IntRect bounding_rect { DevicePixelRect bounding_rect {
0, 0, 0, 0,
text_rect.width() + margin + margin, text_rect.width() + margin + margin,
text_rect.height() + margin + margin text_rect.height() + margin + margin
}; };
// FIXME: Figure out the maximum bitmap size for all shadows and then allocate it once and reuse it? // FIXME: Figure out the maximum bitmap size for all shadows and then allocate it once and reuse it?
auto maybe_shadow_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, bounding_rect.size()); auto maybe_shadow_bitmap = Gfx::Bitmap::try_create(Gfx::BitmapFormat::BGRA8888, bounding_rect.size().to_type<int>());
if (maybe_shadow_bitmap.is_error()) { if (maybe_shadow_bitmap.is_error()) {
dbgln("Unable to allocate temporary bitmap {} for text-shadow rendering: {}", bounding_rect.size(), maybe_shadow_bitmap.error()); dbgln("Unable to allocate temporary bitmap {} for text-shadow rendering: {}", bounding_rect.size(), maybe_shadow_bitmap.error());
return; return;
@ -361,19 +371,19 @@ void paint_text_shadow(PaintContext& context, Layout::LineBoxFragment const& fra
Gfx::Painter shadow_painter { *shadow_bitmap }; Gfx::Painter shadow_painter { *shadow_bitmap };
shadow_painter.set_font(context.painter().font()); shadow_painter.set_font(context.painter().font());
// FIXME: "Spread" the shadow somehow. // FIXME: "Spread" the shadow somehow.
Gfx::FloatPoint baseline_start(text_rect.x(), text_rect.y() + fragment.baseline()); DevicePixelPoint baseline_start(text_rect.x(), text_rect.y() + fragment.baseline());
shadow_painter.draw_text_run(baseline_start, Utf8View(fragment.text()), context.painter().font(), layer.color); shadow_painter.draw_text_run(baseline_start.to_type<int>(), Utf8View(fragment.text()), context.painter().font(), layer.color);
// Blur // Blur
Gfx::StackBlurFilter filter(*shadow_bitmap); Gfx::StackBlurFilter filter(*shadow_bitmap);
filter.process_rgba(layer.blur_radius, layer.color); filter.process_rgba(blur_radius.value(), layer.color);
auto draw_rect = Gfx::enclosing_int_rect(fragment.absolute_rect()); auto draw_rect = context.enclosing_device_rect(fragment.absolute_rect().to_type<CSSPixels>());
Gfx::IntPoint draw_location { DevicePixelPoint draw_location {
draw_rect.x() + layer.offset_x - margin, draw_rect.x() + offset_x - margin,
draw_rect.y() + layer.offset_y - margin draw_rect.y() + offset_y - margin
}; };
painter.blit(draw_location, *shadow_bitmap, bounding_rect); painter.blit(draw_location.to_type<int>(), *shadow_bitmap, bounding_rect.to_type<int>());
} }
} }

View file

@ -19,14 +19,14 @@ enum class ShadowPlacement {
struct ShadowData { struct ShadowData {
Gfx::Color color; Gfx::Color color;
int offset_x; CSSPixels offset_x;
int offset_y; CSSPixels offset_y;
int blur_radius; CSSPixels blur_radius;
int spread_distance; CSSPixels spread_distance;
ShadowPlacement placement; ShadowPlacement placement;
}; };
void paint_box_shadow(PaintContext&, Gfx::IntRect const&, BorderRadiiData const&, Vector<ShadowData> const&); void paint_box_shadow(PaintContext&, CSSPixelRect const&, BorderRadiiData const&, Vector<ShadowData> const&);
void paint_text_shadow(PaintContext&, Layout::LineBoxFragment const&, Vector<ShadowData> const&); void paint_text_shadow(PaintContext&, Layout::LineBoxFragment const&, Vector<ShadowData> const&);
} }