1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:27:46 +00:00

LibWeb: Apply scroll boxes offsets after painting commands recording

With this change, instead of applying scroll offsets during the
recording of the painting command list, we do the following:
1. Collect all boxes with scrollable overflow into a PaintContext,
   each with an id and the total amount of scrolling offset accumulated
   from ancestor scrollable boxes.
2. During the recording phase assign a corresponding scroll_frame_id to
   each command that paints content within a scrollable box.
3. Before executing the recorded commands, translate each command that
   has a scroll_frame_id by the accumulated scroll offset.

This approach has following advantages:
- Implementing nested scrollables becomes much simpler, as the
  recording phase only requires the correct assignment of the nearest
  scrollable's scroll_frame_id, while the accumulated offset from
  ancestors is applied subsequently.
- The recording of painting commands is not tied to a specific offset
  within scrollable boxes, which means in the future, it will be
  possible to update the scrolling offset and repaint without the need
  to re-record painting commands.
This commit is contained in:
Aliaksandr Kalenik 2023-12-29 06:10:32 +01:00 committed by Andreas Kling
parent d3025668a4
commit ac6b3c989d
12 changed files with 307 additions and 24 deletions

View file

@ -9,11 +9,31 @@
namespace Web::Painting {
void DrawGlyphRun::translate_by(Gfx::IntPoint const& offset)
{
for (auto& glyph : glyph_run) {
glyph.visit([&](auto& glyph) {
glyph.translate_by(offset.to_type<float>());
});
}
rect.translate_by(offset);
}
Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const
{
return get_outer_box_shadow_bounding_rect(outer_box_shadow_params);
}
void PaintOuterBoxShadow::translate_by(Gfx::IntPoint const& offset)
{
outer_box_shadow_params.device_content_rect.translate_by(offset.to_type<DevicePixels>());
}
void PaintInnerBoxShadow::translate_by(Gfx::IntPoint const& offset)
{
outer_box_shadow_params.device_content_rect.translate_by(offset.to_type<DevicePixels>());
}
Gfx::IntRect SampleUnderCorners::bounding_rect() const
{
return border_rect;
@ -303,8 +323,8 @@ void RecordingPainter::push_stacking_context(PushStackingContextParams params)
void RecordingPainter::pop_stacking_context()
{
push_command(PopStackingContext {});
m_state_stack.take_last();
push_command(PopStackingContext {});
}
void RecordingPainter::paint_frame(Gfx::IntRect rect, Palette palette, Gfx::FrameStyle style)
@ -405,11 +425,27 @@ static Optional<Gfx::IntRect> command_bounding_rectangle(PaintingCommand const&
});
}
void RecordingPainter::apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id)
{
for (auto& command_with_scroll_id : m_painting_commands) {
if (command_with_scroll_id.scroll_frame_id.has_value()) {
auto const& scroll_frame_id = command_with_scroll_id.scroll_frame_id.value();
auto const& scroll_offset = offsets_by_frame_id[scroll_frame_id];
command_with_scroll_id.command.visit(
[&](auto& command) {
if constexpr (requires { command.translate_by(scroll_offset); })
command.translate_by(scroll_offset);
});
}
}
}
void RecordingPainter::execute(PaintingCommandExecutor& executor)
{
if (executor.needs_prepare_glyphs_texture()) {
HashMap<Gfx::Font const*, HashTable<u32>> unique_glyphs;
for (auto& command : m_painting_commands) {
for (auto& command_with_scroll_id : m_painting_commands) {
auto& command = command_with_scroll_id.command;
if (command.has<DrawGlyphRun>()) {
for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) {
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
@ -424,7 +460,8 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor)
if (executor.needs_update_immutable_bitmap_texture_cache()) {
HashMap<u32, Gfx::ImmutableBitmap const*> immutable_bitmaps;
for (auto const& command : m_painting_commands) {
for (auto const& command_with_scroll_id : m_painting_commands) {
auto& command = command_with_scroll_id.command;
if (command.has<DrawScaledImmutableBitmap>()) {
auto const& immutable_bitmap = command.get<DrawScaledImmutableBitmap>().bitmap;
immutable_bitmaps.set(immutable_bitmap->id(), immutable_bitmap.ptr());
@ -436,7 +473,8 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor)
HashTable<u32> skipped_sample_corner_commands;
size_t next_command_index = 0;
while (next_command_index < m_painting_commands.size()) {
auto& command = m_painting_commands[next_command_index++];
auto& command_with_scroll_id = m_painting_commands[next_command_index++];
auto& command = command_with_scroll_id.command;
auto bounding_rect = command_bounding_rectangle(command);
if (bounding_rect.has_value() && (bounding_rect->is_empty() || executor.would_be_fully_clipped_by_painter(*bounding_rect))) {
if (command.has<SampleUnderCorners>()) {
@ -555,9 +593,9 @@ void RecordingPainter::execute(PaintingCommandExecutor& executor)
if (result == CommandResult::SkipStackingContext) {
auto stacking_context_nesting_level = 1;
while (next_command_index < m_painting_commands.size()) {
if (m_painting_commands[next_command_index].has<PushStackingContext>()) {
if (m_painting_commands[next_command_index].command.has<PushStackingContext>()) {
stacking_context_nesting_level++;
} else if (m_painting_commands[next_command_index].has<PopStackingContext>()) {
} else if (m_painting_commands[next_command_index].command.has<PopStackingContext>()) {
stacking_context_nesting_level--;
}