diff --git a/Tests/LibWeb/Screenshot/images/border-radius-ref.png b/Tests/LibWeb/Screenshot/images/border-radius-ref.png index 7d40159cf83..d2306b30733 100644 Binary files a/Tests/LibWeb/Screenshot/images/border-radius-ref.png and b/Tests/LibWeb/Screenshot/images/border-radius-ref.png differ diff --git a/Tests/LibWeb/Screenshot/images/css-background-clip-text.png b/Tests/LibWeb/Screenshot/images/css-background-clip-text.png index d3a18a4e250..69ffed3e36d 100644 Binary files a/Tests/LibWeb/Screenshot/images/css-background-clip-text.png and b/Tests/LibWeb/Screenshot/images/css-background-clip-text.png differ diff --git a/Tests/LibWeb/Screenshot/images/css-backgrounds-ref.png b/Tests/LibWeb/Screenshot/images/css-backgrounds-ref.png index 1e50022d12c..c55f9510649 100644 Binary files a/Tests/LibWeb/Screenshot/images/css-backgrounds-ref.png and b/Tests/LibWeb/Screenshot/images/css-backgrounds-ref.png differ diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp index 389f2dd690a..37e1dfa493e 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.cpp @@ -141,6 +141,11 @@ void ImageStyleValue::paint(PaintContext& context, DevicePixelRect const& dest_r } } +Gfx::ImmutableBitmap const* ImageStyleValue::current_frame_bitmap(DevicePixelRect const& dest_rect) const +{ + return bitmap(m_current_frame_index, dest_rect.size().to_type()); +} + JS::GCPtr ImageStyleValue::image_data() const { if (!m_image_request) diff --git a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h index 79c9656b444..d77d8c6ac8a 100644 --- a/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h +++ b/Userland/Libraries/LibWeb/CSS/StyleValues/ImageStyleValue.h @@ -48,6 +48,7 @@ public: void paint(PaintContext& context, DevicePixelRect const& dest_rect, CSS::ImageRendering image_rendering, Vector const& clip_paths = {}) const override; virtual Optional color_if_single_pixel_bitmap() const override; + Gfx::ImmutableBitmap const* current_frame_bitmap(DevicePixelRect const& dest_rect) const; Function on_animate; diff --git a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp index edbaa15a923..664149ac770 100644 --- a/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp +++ b/Userland/Libraries/LibWeb/Painting/BackgroundPainting.cpp @@ -351,6 +351,8 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet // Repetition bool repeat_x = false; bool repeat_y = false; + bool repeat_x_has_gap = false; + bool repeat_y_has_gap = false; CSSPixels x_step = 0; CSSPixels y_step = 0; @@ -368,6 +370,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet auto space = fmod(background_positioning_area.width().to_double(), image_rect.width().to_double()); x_step = image_rect.width() + CSSPixels::nearest_value_for(space / static_cast(whole_images - 1)); repeat_x = true; + repeat_x_has_gap = true; } break; } @@ -399,6 +402,7 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet auto space = fmod(background_positioning_area.height().to_float(), image_rect.height().to_float()); y_step = image_rect.height() + CSSPixels::nearest_value_for(static_cast(space) / static_cast(whole_images - 1)); repeat_y = true; + repeat_y_has_gap = true; } break; } @@ -456,6 +460,13 @@ void paint_background(PaintContext& context, Layout::NodeWithStyleAndBoxModelMet } }); display_list_recorder.fill_rect(fill_rect->to_type(), color.value(), clip_paths); + } else if (is(image) && repeat_x && repeat_y && !repeat_x_has_gap && !repeat_y_has_gap) { + // Use a dedicated painting command for repeated images instead of recording a separate command for each instance + // of a repeated background, so the painter has the opportunity to optimize the painting of repeated images. + auto dest_rect = context.rounded_device_rect(image_rect); + auto const* bitmap = static_cast(image).current_frame_bitmap(dest_rect); + auto scaling_mode = to_gfx_scaling_mode(image_rendering, bitmap->rect(), dest_rect.to_type()); + context.display_list_recorder().draw_repeated_immutable_bitmap(dest_rect.to_type(), *bitmap, scaling_mode, { .x = repeat_x, .y = repeat_y }, clip_paths); } else { for_each_image_device_rect([&](auto const& image_device_rect) { image.paint(context, image_device_rect, image_rendering, clip_paths); diff --git a/Userland/Libraries/LibWeb/Painting/Command.h b/Userland/Libraries/LibWeb/Painting/Command.h index aaedf1d9af7..a453ac608f6 100644 --- a/Userland/Libraries/LibWeb/Painting/Command.h +++ b/Userland/Libraries/LibWeb/Painting/Command.h @@ -75,6 +75,21 @@ struct DrawScaledImmutableBitmap { void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } }; +struct DrawRepeatedImmutableBitmap { + struct Repeat { + bool x { false }; + bool y { false }; + }; + + Gfx::IntRect dst_rect; + NonnullRefPtr bitmap; + Gfx::ScalingMode scaling_mode; + Repeat repeat; + Vector clip_paths; + + void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); } +}; + struct Save { }; struct Restore { }; @@ -352,6 +367,7 @@ using Command = Variant< FillRect, DrawScaledBitmap, DrawScaledImmutableBitmap, + DrawRepeatedImmutableBitmap, Save, Restore, AddClipRect, diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp index 9f852501b24..7a981ff3f09 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayList.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayList.cpp @@ -152,6 +152,7 @@ void DisplayList::execute(DisplayListPlayer& executor) else HANDLE_COMMAND(FillRect, fill_rect) else HANDLE_COMMAND(DrawScaledBitmap, draw_scaled_bitmap) else HANDLE_COMMAND(DrawScaledImmutableBitmap, draw_scaled_immutable_bitmap) + else HANDLE_COMMAND(DrawRepeatedImmutableBitmap, draw_repeated_immutable_bitmap) else HANDLE_COMMAND(AddClipRect, add_clip_rect) else HANDLE_COMMAND(Save, save) else HANDLE_COMMAND(Restore, restore) diff --git a/Userland/Libraries/LibWeb/Painting/DisplayList.h b/Userland/Libraries/LibWeb/Painting/DisplayList.h index 91fc2432762..5872d0e53ad 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayList.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayList.h @@ -45,6 +45,7 @@ public: virtual CommandResult fill_rect(FillRect const&) = 0; virtual CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) = 0; virtual CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) = 0; + virtual CommandResult draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) = 0; virtual CommandResult save(Save const&) = 0; virtual CommandResult restore(Restore const&) = 0; virtual CommandResult add_clip_rect(AddClipRect const&) = 0; diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp index 0be010a6906..c3cb7317188 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.cpp @@ -459,6 +459,31 @@ CommandResult DisplayListPlayerSkia::draw_scaled_immutable_bitmap(DrawScaledImmu return CommandResult::Continue; } +CommandResult DisplayListPlayerSkia::draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const& command) +{ + APPLY_PATH_CLIP_IF_NEEDED + + auto bitmap = to_skia_bitmap(command.bitmap->bitmap()); + auto image = SkImages::RasterFromBitmap(bitmap); + + SkMatrix matrix; + auto dst_rect = command.dst_rect.to_type(); + auto src_size = command.bitmap->size().to_type(); + matrix.setScale(dst_rect.width() / src_size.width(), dst_rect.height() / src_size.height()); + matrix.postTranslate(dst_rect.x(), dst_rect.y()); + auto sampling_options = to_skia_sampling_options(command.scaling_mode); + + auto tile_mode_x = command.repeat.x ? SkTileMode::kRepeat : SkTileMode::kDecal; + auto tile_mode_y = command.repeat.y ? SkTileMode::kRepeat : SkTileMode::kDecal; + auto shader = image->makeShader(tile_mode_x, tile_mode_y, sampling_options, matrix); + + SkPaint paint; + paint.setShader(shader); + auto& canvas = surface().canvas(); + canvas.drawPaint(paint); + return CommandResult::Continue; +} + CommandResult DisplayListPlayerSkia::add_clip_rect(AddClipRect const& command) { auto& canvas = surface().canvas(); diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h index 965d3c0c8fa..79a8554790d 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayListPlayerSkia.h @@ -37,6 +37,7 @@ public: CommandResult fill_rect(FillRect const&) override; CommandResult draw_scaled_bitmap(DrawScaledBitmap const&) override; CommandResult draw_scaled_immutable_bitmap(DrawScaledImmutableBitmap const&) override; + CommandResult draw_repeated_immutable_bitmap(DrawRepeatedImmutableBitmap const&) override; CommandResult add_clip_rect(AddClipRect const&) override; CommandResult save(Save const&) override; CommandResult restore(Restore const&) override; diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp index 62cf752fa05..ea16e8e18ae 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp +++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.cpp @@ -210,6 +210,17 @@ void DisplayListRecorder::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_r }); } +void DisplayListRecorder::draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat repeat, Vector const& clip_paths) +{ + append(DrawRepeatedImmutableBitmap { + .dst_rect = dst_rect, + .bitmap = move(bitmap), + .scaling_mode = scaling_mode, + .repeat = repeat, + .clip_paths = clip_paths, + }); +} + void DisplayListRecorder::draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness, Gfx::LineStyle style, Color alternate_color) { append(DrawLine { diff --git a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h index 10162eba41e..6c52fde069d 100644 --- a/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h +++ b/Userland/Libraries/LibWeb/Painting/DisplayListRecorder.h @@ -89,6 +89,8 @@ public: void draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor); void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::ScalingMode scaling_mode = Gfx::ScalingMode::NearestNeighbor, Vector const& clip_paths = {}); + void draw_repeated_immutable_bitmap(Gfx::IntRect dst_rect, NonnullRefPtr bitmap, Gfx::ScalingMode scaling_mode, DrawRepeatedImmutableBitmap::Repeat, Vector const& clip_paths = {}); + void draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness = 1, Gfx::LineStyle style = Gfx::LineStyle::Solid, Color alternate_color = Color::Transparent); void draw_text(Gfx::IntRect const&, String, Gfx::Font const&, Gfx::TextAlignment, Color);