ladybird/Libraries/LibWeb/Painting/DisplayList.cpp

264 lines
11 KiB
C++
Raw Normal View History

/*
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
* Copyright (c) 2024-2026, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/TemporaryChange.h>
#include <LibWeb/Painting/DevicePixelConverter.h>
#include <LibWeb/Painting/DisplayList.h>
#include <LibWeb/Painting/ResolvedCSSFilter.h>
namespace Web::Painting {
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
void DisplayList::append(DisplayListCommand&& command, RefPtr<AccumulatedVisualContext const> context)
{
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
m_commands.append({ move(context), move(command) });
}
static Optional<Gfx::IntRect> command_bounding_rectangle(DisplayListCommand 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(DisplayListCommand 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, ScrollStateSnapshotByDisplayList&& scroll_state_snapshot_by_display_list, RefPtr<Gfx::PaintingSurface> surface)
{
TemporaryChange change { m_scroll_state_snapshots_by_display_list, move(scroll_state_snapshot_by_display_list) };
if (surface) {
surface->lock_context();
}
auto scroll_state_snapshot = m_scroll_state_snapshots_by_display_list.get(display_list).value_or({});
execute_impl(display_list, scroll_state_snapshot, surface);
if (surface) {
surface->unlock_context();
}
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
static RefPtr<AccumulatedVisualContext const> find_common_ancestor(RefPtr<AccumulatedVisualContext const> a, RefPtr<AccumulatedVisualContext const> b)
{
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
if (!a || !b)
return {};
while (a->depth() > b->depth())
a = a->parent();
while (b->depth() > a->depth())
b = b->parent();
while (a != b) {
a = a->parent();
b = b->parent();
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
return a;
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
static Gfx::FloatMatrix4x4 scale_matrix_translation(Gfx::FloatMatrix4x4 matrix, float scale)
{
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
matrix[0, 3] *= scale;
matrix[1, 3] *= scale;
matrix[2, 3] *= scale;
return matrix;
}
void DisplayListPlayer::execute_impl(DisplayList& display_list, ScrollStateSnapshot const& scroll_state, RefPtr<Gfx::PaintingSurface> surface)
{
if (surface)
m_surfaces.append(*surface);
ScopeGuard guard = [&surfaces = m_surfaces, pop_surface_from_stack = !!surface] {
if (pop_surface_from_stack)
(void)surfaces.take_last();
};
auto const& commands = display_list.commands();
auto device_pixels_per_css_pixel = display_list.device_pixels_per_css_pixel();
DevicePixelConverter device_pixel_converter { device_pixels_per_css_pixel };
VERIFY(!m_surfaces.is_empty());
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto for_each_node_from_common_ancestor_to_target = [](this auto const& self, RefPtr<AccumulatedVisualContext const> common_ancestor, RefPtr<AccumulatedVisualContext const> node, auto&& callback) -> void {
if (!node || node == common_ancestor)
return;
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
self(common_ancestor, node->parent(), callback);
callback(*node);
};
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto apply_accumulated_visual_context = [&](AccumulatedVisualContext const& node) {
node.data().visit(
[&](EffectsData const& effects) {
Optional<Gfx::Filter> gfx_filter;
if (effects.filter.has_filters())
gfx_filter = to_gfx_filter(effects.filter, device_pixels_per_css_pixel);
apply_effects({ .opacity = effects.opacity, .compositing_and_blending_operator = effects.blend_mode, .filter = gfx_filter });
},
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
[&](PerspectiveData const& perspective) {
save({});
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto matrix = scale_matrix_translation(perspective.matrix, static_cast<float>(device_pixels_per_css_pixel));
apply_transform({ 0, 0 }, matrix);
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
},
[&](ScrollData const& scroll) {
save({});
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto own_offset = scroll_state.own_offset_for_frame_with_id(scroll.scroll_frame_id);
if (!own_offset.is_zero()) {
auto scroll_offset = own_offset.to_type<double>().scaled(device_pixels_per_css_pixel).to_type<int>();
translate({ .delta = scroll_offset });
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
},
[&](TransformData const& transform) {
save({});
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto origin = transform.origin.to_type<double>().scaled(device_pixels_per_css_pixel).to_type<float>();
auto matrix = scale_matrix_translation(transform.matrix, static_cast<float>(device_pixels_per_css_pixel));
apply_transform(origin, matrix);
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
},
[&](ClipData const& clip) {
save({});
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
auto device_rect = device_pixel_converter.rounded_device_rect(clip.rect).to_type<int>();
auto corner_radii = clip.corner_radii.as_corners(device_pixel_converter);
if (corner_radii.has_any_radius())
add_rounded_rect_clip({ .corner_radii = corner_radii, .border_rect = device_rect, .corner_clip = CornerClip::Outside });
else
add_clip_rect({ .rect = device_rect });
},
[&](ClipPathData const& clip_path) {
save({});
auto transformed_path = clip_path.path.copy_transformed(Gfx::AffineTransform {}.set_scale(static_cast<float>(device_pixels_per_css_pixel), static_cast<float>(device_pixels_per_css_pixel)));
add_clip_path(transformed_path);
});
};
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
RefPtr<AccumulatedVisualContext const> applied_context;
size_t applied_depth = 0;
auto switch_to_context = [&](RefPtr<AccumulatedVisualContext const> const& target_context) {
if (applied_context == target_context)
return;
auto common_ancestor = find_common_ancestor(applied_context, target_context);
auto common_ancestor_depth = common_ancestor ? common_ancestor->depth() : 0;
while (applied_depth > common_ancestor_depth) {
restore({});
applied_depth--;
}
for_each_node_from_common_ancestor_to_target(common_ancestor, target_context, [&](AccumulatedVisualContext const& node) {
apply_accumulated_visual_context(node);
applied_depth++;
});
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
applied_context = target_context;
};
2025-03-24 06:47:14 -03:00
for (size_t command_index = 0; command_index < commands.size(); command_index++) {
auto const& [context, command] = commands[command_index];
auto bounding_rect = command_bounding_rectangle(command);
// OPTIMIZATION: If the leaf context is an effect and we're switching to a new context,
// check culling before applying it. Effects (opacity, filters, blend modes) don't affect
// clip state, so would_be_fully_clipped_by_painter() returns the same result before and after
// applying effects.
// This avoids expensive saveLayer/restore cycles for off-screen elements with effects like blur.
// NOTE: We must not do this for consecutive commands with the same context, as that would incorrectly restore
// and re-apply the effect layer, breaking blend mode compositing.
if (context && applied_context != context && context->is_effect() && bounding_rect.has_value()) {
switch_to_context(context->parent());
if (bounding_rect->is_empty() || would_be_fully_clipped_by_painter(*bounding_rect))
continue;
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
switch_to_context(context);
if (command.has<PaintScrollBar>()) {
auto translated_command = command;
auto& paint_scroll_bar = translated_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.thumb_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.thumb_rect.translate_by(-offset.to_int() * device_pixels_per_css_pixel, 0);
}
paint_scrollbar(paint_scroll_bar);
continue;
}
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(SaveLayer, save_layer)
else HANDLE_COMMAND(Restore, restore)
else HANDLE_COMMAND(Translate, translate)
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(FillPath, fill_path)
else HANDLE_COMMAND(StrokePath, stroke_path)
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(AddRoundedRectClip, add_rounded_rect_clip)
else HANDLE_COMMAND(AddMask, add_mask)
else HANDLE_COMMAND(PaintNestedDisplayList, paint_nested_display_list)
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
else HANDLE_COMMAND(ApplyEffects, apply_effects)
else VERIFY_NOT_REACHED();
// clang-format on
}
LibWeb: Integrate AccumulatedVisualContext with display list Integrate AccumulatedVisualContext with display list recording and playback. This is the main commit of the refactoring that delivers the architectural improvements enabled by AccumulatedVisualContext. Recording changes: Each display list command now stores a single RefPtr<AccumulatedVisualContext> instead of separate scroll_frame_id and ClipFrame. The recorder simply captures the current accumulated context when appending commands. The before_paint()/after_paint() hooks that pushed/popped scroll frame IDs are replaced by directly setting accumulated_visual_context on the recorder before painting each element. Playback changes: The display list player now uses LCA (Lowest Common Ancestor) based traversal to switch between visual contexts efficiently. When transitioning from context A to context B: 1. Find the LCA of A and B in the context tree 2. Pop (restore) states back to the LCA depth 3. Push (save + apply) states from LCA down to B This approach minimizes redundant save/restore operations. For example, when rendering siblings that share a common scroll container, the player keeps that scroll state applied and only switches the divergent parts of their context chains. Key deletions: - Remove translate_by() from all 45 display list commands - commands are now immutable - Remove transform/perspective fields from PushStackingContext - transforms are tracked via AccumulatedVisualContext - Remove push_scroll_frame_id()/pop_scroll_frame_id() from DisplayListRecorder - Remove before_paint()/after_paint() hooks from Paintable - Merge ApplyOpacity, ApplyCompositeAndBlendingOperator, ApplyFilter into single ApplyEffects command Stacking context painting changes: The StackingContext::paint() method is significantly simplified. Instead of building a PushStackingContextParams struct with transform matrices and pushing/popping stacking contexts, it now: 1. Sets the accumulated visual context (which already contains transforms) 2. Applies effects (opacity, blend mode, filters) if needed 3. Applies clip path if needed 4. Paints the content 5. Restores state The visual state management that was interleaved throughout the painting code is now handled uniformly by the context tree.
2026-01-13 16:44:09 -03:00
while (applied_depth > 0) {
restore({});
applied_depth--;
}
if (surface)
flush();
}
}