mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 03:17:35 +00:00
LibWeb: Introduce RecordingPainter to serialize painting commands
This modification introduces a new layer to the painting process. The stacking context traversal no longer immediately calls the Gfx::Painter methods. Instead, it writes serialized painting commands into newly introduced RecordingPainter. Created list of commands is executed later to produce resulting bitmap. Producing painting command list will make it easier to add new optimizations: - It's simpler to check if the painting result is not visible in the viewport at the command level rather than during stacking context traversal. - Run painting in a separate thread. The painting thread can process serialized painting commands, while the main thread can work on the next paintable tree and safely invalidate the previous one. - As we consider GPU-accelerated painting support, it would be easier to back each painting command rather than constructing an alternative for the entire Gfx::Painter API.
This commit is contained in:
parent
8ebb4e8047
commit
063e66cae9
49 changed files with 1970 additions and 441 deletions
|
@ -11,7 +11,6 @@
|
|||
#include <AK/StringBuilder.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Matrix4x4.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibWeb/CSS/ComputedValues.h>
|
||||
#include <LibWeb/CSS/StyleValues/TransformationStyleValue.h>
|
||||
|
@ -290,10 +289,7 @@ Gfx::AffineTransform StackingContext::affine_transform_matrix() const
|
|||
|
||||
void StackingContext::paint(PaintContext& context) const
|
||||
{
|
||||
Gfx::PainterStateSaver saver(context.painter());
|
||||
if (paintable_box().is_fixed_position()) {
|
||||
context.painter().translate(-context.painter().translation());
|
||||
}
|
||||
RecordingPainterStateSaver saver(context.painter());
|
||||
|
||||
auto opacity = paintable_box().computed_values().opacity();
|
||||
if (opacity == 0.0f)
|
||||
|
@ -305,18 +301,12 @@ void StackingContext::paint(PaintContext& context) const
|
|||
if (masking_area->is_empty())
|
||||
return;
|
||||
auto paint_rect = context.enclosing_device_rect(*masking_area);
|
||||
auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, paint_rect.size().to_type<int>());
|
||||
if (bitmap_or_error.is_error())
|
||||
return;
|
||||
auto bitmap = bitmap_or_error.release_value();
|
||||
{
|
||||
Gfx::Painter painter(bitmap);
|
||||
painter.translate(-paint_rect.location().to_type<int>());
|
||||
auto paint_context = context.clone(painter);
|
||||
paint_internal(paint_context);
|
||||
}
|
||||
paintable_box().apply_mask(context, bitmap, *masking_area);
|
||||
context.painter().blit(paint_rect.location().to_type<int>(), *bitmap, bitmap->rect(), opacity);
|
||||
context.painter().push_stacking_context_with_mask(paint_rect);
|
||||
paint_internal(context);
|
||||
|
||||
auto mask_bitmap = paintable_box().calculate_mask(context, *masking_area);
|
||||
auto mask_type = paintable_box().get_mask_type();
|
||||
context.painter().pop_stacking_context_with_mask(mask_bitmap, *mask_type, paint_rect, opacity);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -324,52 +314,40 @@ void StackingContext::paint(PaintContext& context) const
|
|||
auto translation = context.rounded_device_point(affine_transform.translation().to_type<CSSPixels>()).to_type<int>().to_type<float>();
|
||||
affine_transform.set_translation(translation);
|
||||
|
||||
auto transform_origin = this->transform_origin();
|
||||
auto source_rect = context.enclosing_device_rect(paintable_box().absolute_paint_rect()).to_type<int>().to_type<float>().translated(-transform_origin);
|
||||
auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform_origin);
|
||||
auto destination_rect = transformed_destination_rect.to_rounded<int>();
|
||||
|
||||
RecordingPainter::PushStackingContextParams push_stacking_context_params {
|
||||
.semitransparent_or_has_non_identity_transform = false,
|
||||
.has_fixed_position = false,
|
||||
.opacity = opacity,
|
||||
.source_rect = source_rect,
|
||||
.transformed_destination_rect = transformed_destination_rect,
|
||||
.painter_location = context.rounded_device_point(-paintable_box().absolute_paint_rect().location())
|
||||
};
|
||||
|
||||
RecordingPainter::PopStackingContextParams pop_stacking_context_params {
|
||||
.semitransparent_or_has_non_identity_transform = false,
|
||||
.scaling_mode = CSS::to_gfx_scaling_mode(paintable_box().computed_values().image_rendering(), destination_rect, destination_rect)
|
||||
};
|
||||
|
||||
if (paintable_box().is_fixed_position()) {
|
||||
push_stacking_context_params.has_fixed_position = true;
|
||||
}
|
||||
|
||||
if (opacity < 1.0f || !affine_transform.is_identity_or_translation()) {
|
||||
auto transform_origin = this->transform_origin();
|
||||
auto source_rect = context.enclosing_device_rect(paintable_box().absolute_paint_rect()).to_type<int>().to_type<float>().translated(-transform_origin);
|
||||
auto transformed_destination_rect = affine_transform.map(source_rect).translated(transform_origin);
|
||||
auto destination_rect = transformed_destination_rect.to_rounded<int>();
|
||||
|
||||
// FIXME: We should find a way to scale the paintable, rather than paint into a separate bitmap,
|
||||
// then scale it. This snippet now copies the background at the destination, then scales it down/up
|
||||
// to the size of the source (which could add some artefacts, though just scaling the bitmap already does that).
|
||||
// We need to copy the background at the destination because a bunch of our rendering effects now rely on
|
||||
// being able to sample the painter (see border radii, shadows, filters, etc).
|
||||
Gfx::FloatPoint destination_clipped_fixup {};
|
||||
auto try_get_scaled_destination_bitmap = [&]() -> ErrorOr<NonnullRefPtr<Gfx::Bitmap>> {
|
||||
Gfx::IntRect actual_destination_rect;
|
||||
auto bitmap = TRY(context.painter().get_region_bitmap(destination_rect, Gfx::BitmapFormat::BGRA8888, actual_destination_rect));
|
||||
// get_region_bitmap() may clip to a smaller region if the requested rect goes outside the painter, so we need to account for that.
|
||||
destination_clipped_fixup = Gfx::FloatPoint { destination_rect.location() - actual_destination_rect.location() };
|
||||
destination_rect = actual_destination_rect;
|
||||
if (source_rect.size() != transformed_destination_rect.size()) {
|
||||
auto sx = static_cast<float>(source_rect.width()) / transformed_destination_rect.width();
|
||||
auto sy = static_cast<float>(source_rect.height()) / transformed_destination_rect.height();
|
||||
bitmap = TRY(bitmap->scaled(sx, sy));
|
||||
destination_clipped_fixup.scale_by(sx, sy);
|
||||
}
|
||||
return bitmap;
|
||||
};
|
||||
|
||||
auto bitmap_or_error = try_get_scaled_destination_bitmap();
|
||||
if (bitmap_or_error.is_error())
|
||||
return;
|
||||
auto bitmap = bitmap_or_error.release_value_but_fixme_should_propagate_errors();
|
||||
Gfx::Painter painter(bitmap);
|
||||
painter.translate(context.rounded_device_point(-paintable_box().absolute_paint_rect().location() + destination_clipped_fixup.to_type<CSSPixels>()).to_type<int>());
|
||||
auto paint_context = context.clone(painter);
|
||||
paint_internal(paint_context);
|
||||
|
||||
if (destination_rect.size() == bitmap->size()) {
|
||||
context.painter().blit(destination_rect.location(), *bitmap, bitmap->rect(), opacity);
|
||||
} else {
|
||||
auto scaling_mode = CSS::to_gfx_scaling_mode(paintable_box().computed_values().image_rendering(), bitmap->rect(), destination_rect);
|
||||
context.painter().draw_scaled_bitmap(destination_rect, *bitmap, bitmap->rect(), opacity, scaling_mode);
|
||||
}
|
||||
push_stacking_context_params.semitransparent_or_has_non_identity_transform = true;
|
||||
pop_stacking_context_params.semitransparent_or_has_non_identity_transform = true;
|
||||
context.painter().push_stacking_context(push_stacking_context_params);
|
||||
paint_internal(context);
|
||||
context.painter().pop_stacking_context(pop_stacking_context_params);
|
||||
} else {
|
||||
Gfx::PainterStateSaver saver(context.painter());
|
||||
context.painter().push_stacking_context(push_stacking_context_params);
|
||||
context.painter().translate(affine_transform.translation().to_rounded<int>());
|
||||
paint_internal(context);
|
||||
context.painter().pop_stacking_context(pop_stacking_context_params);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue