
These operations should still apply even if they are off screen, because they affect painting of things outside of their bounding rectangles. This commit makes us always apply these, regardless of if they are in the visible region. However, if they are outside that region, we replace them with simple clip-rect commands, which have the same effect (not painting anything) but are cheaper than computing a full mask bitmap.
132 lines
5.8 KiB
C++
132 lines
5.8 KiB
C++
/*
|
|
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibWeb/Painting/DisplayList.h>
|
|
|
|
namespace Web::Painting {
|
|
|
|
void DisplayList::append(Command&& command, Optional<i32> scroll_frame_id)
|
|
{
|
|
m_commands.append({ scroll_frame_id, move(command) });
|
|
}
|
|
|
|
static Optional<Gfx::IntRect> command_bounding_rectangle(Command const& command)
|
|
{
|
|
return command.visit(
|
|
[&](auto const& command) -> Optional<Gfx::IntRect> {
|
|
if constexpr (requires { command.bounding_rect(); })
|
|
return command.bounding_rect();
|
|
else
|
|
return {};
|
|
});
|
|
}
|
|
|
|
static bool command_is_clip_or_mask(Command const& command)
|
|
{
|
|
return command.visit(
|
|
[&](auto const& command) -> bool {
|
|
if constexpr (requires { command.is_clip_or_mask(); })
|
|
return command.is_clip_or_mask();
|
|
else
|
|
return false;
|
|
});
|
|
}
|
|
|
|
void DisplayListPlayer::execute(DisplayList& display_list)
|
|
{
|
|
auto const& commands = display_list.commands();
|
|
auto const& scroll_state = display_list.scroll_state();
|
|
auto device_pixels_per_css_pixel = display_list.device_pixels_per_css_pixel();
|
|
|
|
size_t next_command_index = 0;
|
|
while (next_command_index < commands.size()) {
|
|
auto scroll_frame_id = commands[next_command_index].scroll_frame_id;
|
|
auto command = commands[next_command_index++].command;
|
|
|
|
if (command.has<PaintScrollBar>()) {
|
|
auto& paint_scroll_bar = command.get<PaintScrollBar>();
|
|
auto scroll_offset = scroll_state.own_offset_for_frame_with_id(paint_scroll_bar.scroll_frame_id);
|
|
if (paint_scroll_bar.vertical) {
|
|
auto offset = scroll_offset.y() * paint_scroll_bar.scroll_size;
|
|
paint_scroll_bar.rect.translate_by(0, -offset.to_int() * device_pixels_per_css_pixel);
|
|
} else {
|
|
auto offset = scroll_offset.x() * paint_scroll_bar.scroll_size;
|
|
paint_scroll_bar.rect.translate_by(-offset.to_int() * device_pixels_per_css_pixel, 0);
|
|
}
|
|
}
|
|
|
|
if (scroll_frame_id.has_value()) {
|
|
auto cumulative_offset = scroll_state.cumulative_offset_for_frame_with_id(scroll_frame_id.value());
|
|
auto scroll_offset = cumulative_offset.to_type<double>().scaled(device_pixels_per_css_pixel).to_type<int>();
|
|
command.visit(
|
|
[&](auto& command) {
|
|
if constexpr (requires { command.translate_by(scroll_offset); }) {
|
|
command.translate_by(scroll_offset);
|
|
}
|
|
});
|
|
}
|
|
|
|
auto bounding_rect = command_bounding_rectangle(command);
|
|
if (bounding_rect.has_value() && (bounding_rect->is_empty() || would_be_fully_clipped_by_painter(*bounding_rect))) {
|
|
// Any clip or mask that's located outside of the visible region is equivalent to a simple clip-rect,
|
|
// so replace it with one to avoid doing unnecessary work.
|
|
if (command_is_clip_or_mask(command)) {
|
|
if (command.has<AddClipRect>()) {
|
|
add_clip_rect(command.get<AddClipRect>());
|
|
} else {
|
|
add_clip_rect({ bounding_rect.release_value() });
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
#define HANDLE_COMMAND(command_type, executor_method) \
|
|
if (command.has<command_type>()) { \
|
|
executor_method(command.get<command_type>()); \
|
|
}
|
|
|
|
// clang-format off
|
|
HANDLE_COMMAND(DrawGlyphRun, draw_glyph_run)
|
|
else HANDLE_COMMAND(FillRect, fill_rect)
|
|
else HANDLE_COMMAND(DrawPaintingSurface, draw_painting_surface)
|
|
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)
|
|
else HANDLE_COMMAND(Translate, translate)
|
|
else HANDLE_COMMAND(PushStackingContext, push_stacking_context)
|
|
else HANDLE_COMMAND(PopStackingContext, pop_stacking_context)
|
|
else HANDLE_COMMAND(PaintLinearGradient, paint_linear_gradient)
|
|
else HANDLE_COMMAND(PaintRadialGradient, paint_radial_gradient)
|
|
else HANDLE_COMMAND(PaintConicGradient, paint_conic_gradient)
|
|
else HANDLE_COMMAND(PaintOuterBoxShadow, paint_outer_box_shadow)
|
|
else HANDLE_COMMAND(PaintInnerBoxShadow, paint_inner_box_shadow)
|
|
else HANDLE_COMMAND(PaintTextShadow, paint_text_shadow)
|
|
else HANDLE_COMMAND(FillRectWithRoundedCorners, fill_rect_with_rounded_corners)
|
|
else HANDLE_COMMAND(FillPathUsingColor, fill_path_using_color)
|
|
else HANDLE_COMMAND(FillPathUsingPaintStyle, fill_path_using_paint_style)
|
|
else HANDLE_COMMAND(StrokePathUsingColor, stroke_path_using_color)
|
|
else HANDLE_COMMAND(StrokePathUsingPaintStyle, stroke_path_using_paint_style)
|
|
else HANDLE_COMMAND(DrawEllipse, draw_ellipse)
|
|
else HANDLE_COMMAND(FillEllipse, fill_ellipse)
|
|
else HANDLE_COMMAND(DrawLine, draw_line)
|
|
else HANDLE_COMMAND(ApplyBackdropFilter, apply_backdrop_filter)
|
|
else HANDLE_COMMAND(DrawRect, draw_rect)
|
|
else HANDLE_COMMAND(DrawTriangleWave, draw_triangle_wave)
|
|
else HANDLE_COMMAND(AddRoundedRectClip, add_rounded_rect_clip)
|
|
else HANDLE_COMMAND(AddMask, add_mask)
|
|
else HANDLE_COMMAND(PaintScrollBar, paint_scrollbar)
|
|
else HANDLE_COMMAND(PaintNestedDisplayList, paint_nested_display_list)
|
|
else HANDLE_COMMAND(ApplyOpacity, apply_opacity)
|
|
else HANDLE_COMMAND(ApplyTransform, apply_transform)
|
|
else HANDLE_COMMAND(ApplyMaskBitmap, apply_mask_bitmap)
|
|
else VERIFY_NOT_REACHED();
|
|
// clang-format on
|
|
}
|
|
}
|
|
|
|
}
|