From 8709369d4358d6fc6d5da1dec139f29ea5d716db Mon Sep 17 00:00:00 2001 From: Tobias Christiansen Date: Tue, 1 Aug 2023 20:07:34 +0200 Subject: [PATCH] LibWeb: Add object-fit support to HTMLImageElement This patch adds handling of the 'object-fit' CSS property to the painting of HTML Image Elements. This is achieved by first calculating the rect which the image would need if it were to fully expand into open space and then adequately cropping it to fit into the image's box. scale-down is not supported for now. --- .../LibWeb/Painting/ImagePaintable.cpp | 67 ++++++++++++++++++- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/Userland/Libraries/LibWeb/Painting/ImagePaintable.cpp b/Userland/Libraries/LibWeb/Painting/ImagePaintable.cpp index 7629c51961..a19917acb6 100644 --- a/Userland/Libraries/LibWeb/Painting/ImagePaintable.cpp +++ b/Userland/Libraries/LibWeb/Painting/ImagePaintable.cpp @@ -64,8 +64,71 @@ void ImagePaintable::paint(PaintContext& context, PaintPhase phase) const context.painter().draw_text(enclosing_rect, alt, Gfx::TextAlignment::Center, computed_values().color(), Gfx::TextElision::Right); } else if (auto bitmap = layout_box().image_provider().current_image_bitmap(image_rect.size().to_type())) { ScopedCornerRadiusClip corner_clip { context, context.painter(), image_rect, normalized_border_radii_data(ShrinkRadiiForBorders::Yes) }; - auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), bitmap->rect(), image_rect.to_type()); - context.painter().draw_scaled_bitmap(image_rect.to_type(), *bitmap, bitmap->rect(), 1.f, scaling_mode); + auto image_int_rect = image_rect.to_type(); + auto bitmap_rect = bitmap->rect(); + auto scaling_mode = to_gfx_scaling_mode(computed_values().image_rendering(), bitmap_rect, image_int_rect); + auto& dom_element = verify_cast(*dom_node()); + auto object_fit = dom_element.computed_css_values()->object_fit(); + auto bitmap_aspect_ratio = bitmap_rect.height() / bitmap_rect.width(); + auto image_aspect_ratio = (float)image_rect.height().value() / image_rect.width().value(); + + auto scale_x = 0.0f; + auto scale_y = 0.0f; + Gfx::IntRect bitmap_intersect = bitmap_rect; + + auto object_fit_value = CSS::InitialValues::object_fit(); + + if (object_fit.has_value()) + object_fit_value = object_fit.value(); + + switch (object_fit_value) { + case CSS::ObjectFit::Fill: + scale_x = (float)image_int_rect.width() / bitmap_rect.width(); + scale_y = (float)image_int_rect.height() / bitmap_rect.height(); + bitmap_intersect = bitmap_rect; + break; + case CSS::ObjectFit::Contain: + if (bitmap_aspect_ratio >= image_aspect_ratio) { + scale_x = (float)image_int_rect.height() / bitmap_rect.height(); + scale_y = scale_x; + } else { + scale_x = (float)image_int_rect.width() / bitmap_rect.width(); + scale_y = scale_x; + } + break; + case CSS::ObjectFit::Cover: + if (bitmap_aspect_ratio >= image_aspect_ratio) { + scale_x = (float)image_int_rect.width() / bitmap_rect.width(); + scale_y = scale_x; + bitmap_intersect.set_height(bitmap_rect.width() * image_aspect_ratio); + } else { + scale_x = (float)image_int_rect.height() / bitmap_rect.height(); + scale_y = scale_x; + bitmap_intersect.set_width(bitmap_rect.height() / image_aspect_ratio); + } + break; + case CSS::ObjectFit::ScaleDown: + // FIXME: Implement + case CSS::ObjectFit::None: + scale_x = 1; + scale_y = 1; + bitmap_intersect.set_size(image_int_rect.size()); + } + + bitmap_intersect.set_x((bitmap_rect.width() - bitmap_intersect.width()) / 2); + bitmap_intersect.set_y((bitmap_rect.height() - bitmap_intersect.height()) / 2); + + auto offset_x = (image_int_rect.width() - bitmap_rect.width() * scale_x) / 2; + auto offset_y = (image_int_rect.height() - bitmap_rect.height() * scale_y) / 2; + + Gfx::IntRect draw_rect = { + image_int_rect.x() + offset_x, + image_int_rect.y() + offset_y, + bitmap_rect.width() * scale_x, + bitmap_rect.height() * scale_y + }; + + context.painter().draw_scaled_bitmap(draw_rect.intersected(image_int_rect), *bitmap, bitmap_rect.intersected(bitmap_intersect), 1.f, scaling_mode); } } }