mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 08:24:58 +00:00
LibWeb+WebContent: Separate painting command list from RecordingPainter
Separating the recorder list from the painter will allow us to save it for later execution without carrying along the painter's state. This will be useful once we have a separate thread for executing painting commands, to which we will have to transfer commands from the main thread. Preparation for https://github.com/SerenityOS/serenity/pull/23108
This commit is contained in:
parent
ce9ad3a236
commit
11d746a67f
11 changed files with 874 additions and 732 deletions
|
@ -492,6 +492,8 @@ set(SOURCES
|
|||
Painting/BordersData.cpp
|
||||
Painting/ButtonPaintable.cpp
|
||||
Painting/CanvasPaintable.cpp
|
||||
Painting/Command.cpp
|
||||
Painting/CommandList.cpp
|
||||
Painting/CheckBoxPaintable.cpp
|
||||
Painting/GradientPainting.cpp
|
||||
Painting/FilterPainting.cpp
|
||||
|
|
|
@ -2127,7 +2127,7 @@ void Navigable::paint(Painting::RecordingPainter& recording_painter, PaintConfig
|
|||
auto scroll_offset = context.rounded_device_point(scrollable_frame->offset).to_type<int>();
|
||||
scroll_offsets_by_frame_id[scrollable_frame->id] = scroll_offset;
|
||||
}
|
||||
recording_painter.apply_scroll_offsets(scroll_offsets_by_frame_id);
|
||||
recording_painter.commands_list().apply_scroll_offsets(scroll_offsets_by_frame_id);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
Userland/Libraries/LibWeb/Painting/Command.cpp
Normal file
37
Userland/Libraries/LibWeb/Painting/Command.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Painting/Command.h>
|
||||
#include <LibWeb/Painting/ShadowPainting.h>
|
||||
|
||||
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>());
|
||||
}
|
||||
|
||||
}
|
431
Userland/Libraries/LibWeb/Painting/Command.h
Normal file
431
Userland/Libraries/LibWeb/Painting/Command.h
Normal file
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/SegmentedVector.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Gradients.h>
|
||||
#include <LibGfx/GrayscaleBitmap.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibGfx/TextAlignment.h>
|
||||
#include <LibGfx/TextDirection.h>
|
||||
#include <LibGfx/TextElision.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/TextWrapping.h>
|
||||
#include <LibWeb/CSS/Enums.h>
|
||||
#include <LibWeb/Painting/BorderRadiiData.h>
|
||||
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
|
||||
#include <LibWeb/Painting/GradientData.h>
|
||||
#include <LibWeb/Painting/PaintOuterBoxShadowParams.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
struct DrawGlyphRun {
|
||||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
|
||||
Color color;
|
||||
Gfx::IntRect rect;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct DrawText {
|
||||
Gfx::IntRect rect;
|
||||
String raw_text;
|
||||
Gfx::TextAlignment alignment;
|
||||
Color color;
|
||||
Gfx::TextElision elision;
|
||||
Gfx::TextWrapping wrapping;
|
||||
Optional<NonnullRefPtr<Gfx::Font>> font {};
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillRect {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawScaledBitmap {
|
||||
Gfx::IntRect dst_rect;
|
||||
NonnullRefPtr<Gfx::Bitmap> bitmap;
|
||||
Gfx::IntRect src_rect;
|
||||
Gfx::Painter::ScalingMode scaling_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawScaledImmutableBitmap {
|
||||
Gfx::IntRect dst_rect;
|
||||
NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
|
||||
Gfx::IntRect src_rect;
|
||||
Gfx::Painter::ScalingMode scaling_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct SetClipRect {
|
||||
Gfx::IntRect rect;
|
||||
};
|
||||
|
||||
struct ClearClipRect { };
|
||||
|
||||
struct StackingContextTransform {
|
||||
Gfx::FloatPoint origin;
|
||||
Gfx::FloatMatrix4x4 matrix;
|
||||
};
|
||||
|
||||
struct StackingContextMask {
|
||||
NonnullRefPtr<Gfx::Bitmap> mask_bitmap;
|
||||
Gfx::Bitmap::MaskKind mask_kind;
|
||||
};
|
||||
|
||||
struct PushStackingContext {
|
||||
float opacity;
|
||||
bool is_fixed_position;
|
||||
// The bounding box of the source paintable (pre-transform).
|
||||
Gfx::IntRect source_paintable_rect;
|
||||
// A translation to be applied after the stacking context has been transformed.
|
||||
Gfx::IntPoint post_transform_translation;
|
||||
CSS::ImageRendering image_rendering;
|
||||
StackingContextTransform transform;
|
||||
Optional<StackingContextMask> mask = {};
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
source_paintable_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PopStackingContext { };
|
||||
|
||||
struct PaintLinearGradient {
|
||||
Gfx::IntRect gradient_rect;
|
||||
LinearGradientData linear_gradient_data;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
gradient_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintOuterBoxShadow {
|
||||
PaintOuterBoxShadowParams outer_box_shadow_params;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const;
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct PaintInnerBoxShadow {
|
||||
PaintOuterBoxShadowParams outer_box_shadow_params;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct PaintTextShadow {
|
||||
int blur_radius;
|
||||
Gfx::IntRect shadow_bounding_rect;
|
||||
Gfx::IntRect text_rect;
|
||||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
|
||||
Color color;
|
||||
int fragment_baseline;
|
||||
Gfx::IntPoint draw_location;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location, shadow_bounding_rect.size() }; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { draw_location.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillRectWithRoundedCorners {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::AntiAliasingPainter::CornerRadius top_left_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillPathUsingColor {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
Color color;
|
||||
Gfx::Painter::WindingRule winding_rule;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct FillPathUsingPaintStyle {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
NonnullRefPtr<Gfx::PaintStyle> paint_style;
|
||||
Gfx::Painter::WindingRule winding_rule;
|
||||
float opacity;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct StrokePathUsingColor {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
Color color;
|
||||
float thickness;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct StrokePathUsingPaintStyle {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
NonnullRefPtr<Gfx::PaintStyle> paint_style;
|
||||
float thickness;
|
||||
float opacity = 1.0f;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawEllipse {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
int thickness;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct FillEllipse {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::AntiAliasingPainter::BlendMode blend_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawLine {
|
||||
Color color;
|
||||
Gfx::IntPoint from;
|
||||
Gfx::IntPoint to;
|
||||
int thickness;
|
||||
Gfx::Painter::LineStyle style;
|
||||
Color alternate_color;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
from.translate_by(offset);
|
||||
to.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawSignedDistanceField {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::GrayscaleBitmap sdf;
|
||||
float smoothing;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintFrame {
|
||||
Gfx::IntRect rect;
|
||||
Palette palette;
|
||||
Gfx::FrameStyle style;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct ApplyBackdropFilter {
|
||||
Gfx::IntRect backdrop_region;
|
||||
BorderRadiiData border_radii_data;
|
||||
CSS::ResolvedBackdropFilter backdrop_filter;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
backdrop_region.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawRect {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
bool rough;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct PaintRadialGradient {
|
||||
Gfx::IntRect rect;
|
||||
RadialGradientData radial_gradient_data;
|
||||
Gfx::IntPoint center;
|
||||
Gfx::IntSize size;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct PaintConicGradient {
|
||||
Gfx::IntRect rect;
|
||||
ConicGradientData conic_gradient_data;
|
||||
Gfx::IntPoint position;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawTriangleWave {
|
||||
Gfx::IntPoint p1;
|
||||
Gfx::IntPoint p2;
|
||||
Color color;
|
||||
int amplitude;
|
||||
int thickness;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
p1.translate_by(offset);
|
||||
p2.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct SampleUnderCorners {
|
||||
u32 id;
|
||||
CornerRadii corner_radii;
|
||||
Gfx::IntRect border_rect;
|
||||
CornerClip corner_clip;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { border_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct BlitCornerClipping {
|
||||
u32 id;
|
||||
Gfx::IntRect border_rect;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { border_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct PaintBorders {
|
||||
DevicePixelRect border_rect;
|
||||
CornerRadii corner_radii;
|
||||
BordersDataDevicePixels borders_data;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect.to_type<int>(); }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
border_rect.translate_by(offset.to_type<DevicePixels>());
|
||||
}
|
||||
};
|
||||
|
||||
using Command = Variant<
|
||||
DrawGlyphRun,
|
||||
DrawText,
|
||||
FillRect,
|
||||
DrawScaledBitmap,
|
||||
DrawScaledImmutableBitmap,
|
||||
SetClipRect,
|
||||
ClearClipRect,
|
||||
PushStackingContext,
|
||||
PopStackingContext,
|
||||
PaintLinearGradient,
|
||||
PaintRadialGradient,
|
||||
PaintConicGradient,
|
||||
PaintOuterBoxShadow,
|
||||
PaintInnerBoxShadow,
|
||||
PaintTextShadow,
|
||||
FillRectWithRoundedCorners,
|
||||
FillPathUsingColor,
|
||||
FillPathUsingPaintStyle,
|
||||
StrokePathUsingColor,
|
||||
StrokePathUsingPaintStyle,
|
||||
DrawEllipse,
|
||||
FillEllipse,
|
||||
DrawLine,
|
||||
DrawSignedDistanceField,
|
||||
PaintFrame,
|
||||
ApplyBackdropFilter,
|
||||
DrawRect,
|
||||
DrawTriangleWave,
|
||||
SampleUnderCorners,
|
||||
BlitCornerClipping,
|
||||
PaintBorders>;
|
||||
|
||||
}
|
233
Userland/Libraries/LibWeb/Painting/CommandList.cpp
Normal file
233
Userland/Libraries/LibWeb/Painting/CommandList.cpp
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Painting/CommandList.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
void CommandList::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 {};
|
||||
});
|
||||
}
|
||||
|
||||
void CommandList::apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id)
|
||||
{
|
||||
for (auto& command_with_scroll_id : m_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 CommandList::execute(PaintingCommandExecutor& executor)
|
||||
{
|
||||
executor.prepare_to_execute();
|
||||
|
||||
if (executor.needs_prepare_glyphs_texture()) {
|
||||
HashMap<Gfx::Font const*, HashTable<u32>> unique_glyphs;
|
||||
for (auto& command_with_scroll_id : m_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>()) {
|
||||
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
unique_glyphs.ensure(glyph.font, [] { return HashTable<u32> {}; }).set(glyph.code_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.prepare_glyph_texture(unique_glyphs);
|
||||
}
|
||||
|
||||
if (executor.needs_update_immutable_bitmap_texture_cache()) {
|
||||
HashMap<u32, Gfx::ImmutableBitmap const*> immutable_bitmaps;
|
||||
for (auto const& command_with_scroll_id : m_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());
|
||||
}
|
||||
}
|
||||
executor.update_immutable_bitmap_texture_cache(immutable_bitmaps);
|
||||
}
|
||||
|
||||
HashTable<u32> skipped_sample_corner_commands;
|
||||
size_t next_command_index = 0;
|
||||
while (next_command_index < m_commands.size()) {
|
||||
auto& command_with_scroll_id = m_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>()) {
|
||||
auto const& sample_under_corners = command.get<SampleUnderCorners>();
|
||||
skipped_sample_corner_commands.set(sample_under_corners.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto result = command.visit(
|
||||
[&](DrawGlyphRun const& command) {
|
||||
return executor.draw_glyph_run(command.glyph_run, command.color);
|
||||
},
|
||||
[&](DrawText const& command) {
|
||||
return executor.draw_text(command.rect, command.raw_text, command.alignment, command.color,
|
||||
command.elision, command.wrapping, command.font);
|
||||
},
|
||||
[&](FillRect const& command) {
|
||||
return executor.fill_rect(command.rect, command.color);
|
||||
},
|
||||
[&](DrawScaledBitmap const& command) {
|
||||
return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect,
|
||||
command.scaling_mode);
|
||||
},
|
||||
[&](DrawScaledImmutableBitmap const& command) {
|
||||
return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect,
|
||||
command.scaling_mode);
|
||||
},
|
||||
[&](SetClipRect const& command) {
|
||||
return executor.set_clip_rect(command.rect);
|
||||
},
|
||||
[&](ClearClipRect const&) {
|
||||
return executor.clear_clip_rect();
|
||||
},
|
||||
[&](PushStackingContext const& command) {
|
||||
return executor.push_stacking_context(command.opacity, command.is_fixed_position,
|
||||
command.source_paintable_rect,
|
||||
command.post_transform_translation,
|
||||
command.image_rendering, command.transform, command.mask);
|
||||
},
|
||||
[&](PopStackingContext const&) {
|
||||
return executor.pop_stacking_context();
|
||||
},
|
||||
[&](PaintLinearGradient const& command) {
|
||||
return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data);
|
||||
},
|
||||
[&](PaintRadialGradient const& command) {
|
||||
return executor.paint_radial_gradient(command.rect, command.radial_gradient_data,
|
||||
command.center, command.size);
|
||||
},
|
||||
[&](PaintConicGradient const& command) {
|
||||
return executor.paint_conic_gradient(command.rect, command.conic_gradient_data,
|
||||
command.position);
|
||||
},
|
||||
[&](PaintOuterBoxShadow const& command) {
|
||||
return executor.paint_outer_box_shadow(command.outer_box_shadow_params);
|
||||
},
|
||||
[&](PaintInnerBoxShadow const& command) {
|
||||
return executor.paint_inner_box_shadow(command.outer_box_shadow_params);
|
||||
},
|
||||
[&](PaintTextShadow const& command) {
|
||||
return executor.paint_text_shadow(command.blur_radius, command.shadow_bounding_rect,
|
||||
command.text_rect, command.glyph_run, command.color,
|
||||
command.fragment_baseline, command.draw_location);
|
||||
},
|
||||
[&](FillRectWithRoundedCorners const& command) {
|
||||
return executor.fill_rect_with_rounded_corners(command.rect, command.color,
|
||||
command.top_left_radius,
|
||||
command.top_right_radius,
|
||||
command.bottom_left_radius,
|
||||
command.bottom_right_radius);
|
||||
},
|
||||
[&](FillPathUsingColor const& command) {
|
||||
return executor.fill_path_using_color(command.path, command.color, command.winding_rule,
|
||||
command.aa_translation);
|
||||
},
|
||||
[&](FillPathUsingPaintStyle const& command) {
|
||||
return executor.fill_path_using_paint_style(command.path, command.paint_style,
|
||||
command.winding_rule, command.opacity,
|
||||
command.aa_translation);
|
||||
},
|
||||
[&](StrokePathUsingColor const& command) {
|
||||
return executor.stroke_path_using_color(command.path, command.color, command.thickness,
|
||||
command.aa_translation);
|
||||
},
|
||||
[&](StrokePathUsingPaintStyle const& command) {
|
||||
return executor.stroke_path_using_paint_style(command.path, command.paint_style,
|
||||
command.thickness, command.opacity,
|
||||
command.aa_translation);
|
||||
},
|
||||
[&](DrawEllipse const& command) {
|
||||
return executor.draw_ellipse(command.rect, command.color, command.thickness);
|
||||
},
|
||||
[&](FillEllipse const& command) {
|
||||
return executor.fill_ellipse(command.rect, command.color, command.blend_mode);
|
||||
},
|
||||
[&](DrawLine const& command) {
|
||||
return executor.draw_line(command.color, command.from, command.to, command.thickness,
|
||||
command.style, command.alternate_color);
|
||||
},
|
||||
[&](DrawSignedDistanceField const& command) {
|
||||
return executor.draw_signed_distance_field(command.rect, command.color, command.sdf,
|
||||
command.smoothing);
|
||||
},
|
||||
[&](PaintFrame const& command) {
|
||||
return executor.paint_frame(command.rect, command.palette, command.style);
|
||||
},
|
||||
[&](ApplyBackdropFilter const& command) {
|
||||
return executor.apply_backdrop_filter(command.backdrop_region, command.backdrop_filter);
|
||||
},
|
||||
[&](DrawRect const& command) {
|
||||
return executor.draw_rect(command.rect, command.color, command.rough);
|
||||
},
|
||||
[&](DrawTriangleWave const& command) {
|
||||
return executor.draw_triangle_wave(command.p1, command.p2, command.color, command.amplitude,
|
||||
command.thickness);
|
||||
},
|
||||
[&](SampleUnderCorners const& command) {
|
||||
return executor.sample_under_corners(command.id, command.corner_radii, command.border_rect,
|
||||
command.corner_clip);
|
||||
},
|
||||
[&](BlitCornerClipping const& command) {
|
||||
if (skipped_sample_corner_commands.contains(command.id)) {
|
||||
// FIXME: If a sampling command falls outside the viewport and is not executed, the associated blit
|
||||
// should also be skipped if it is within the viewport. In a properly generated list of
|
||||
// painting commands, sample and blit commands should have matching rectangles, preventing
|
||||
// this discrepancy.
|
||||
dbgln("Skipping blit_corner_clipping command because the sample_under_corners command was skipped.");
|
||||
return CommandResult::Continue;
|
||||
}
|
||||
return executor.blit_corner_clipping(command.id);
|
||||
},
|
||||
[&](PaintBorders const& command) {
|
||||
return executor.paint_borders(command.border_rect, command.corner_radii, command.borders_data);
|
||||
});
|
||||
|
||||
if (result == CommandResult::SkipStackingContext) {
|
||||
auto stacking_context_nesting_level = 1;
|
||||
while (next_command_index < m_commands.size()) {
|
||||
if (m_commands[next_command_index].command.has<PushStackingContext>()) {
|
||||
stacking_context_nesting_level++;
|
||||
} else if (m_commands[next_command_index].command.has<PopStackingContext>()) {
|
||||
stacking_context_nesting_level--;
|
||||
}
|
||||
|
||||
next_command_index++;
|
||||
|
||||
if (stacking_context_nesting_level == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
110
Userland/Libraries/LibWeb/Painting/CommandList.h
Normal file
110
Userland/Libraries/LibWeb/Painting/CommandList.h
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Forward.h>
|
||||
#include <AK/NonnullRefPtr.h>
|
||||
#include <AK/SegmentedVector.h>
|
||||
#include <AK/Utf8View.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/AntiAliasingPainter.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Gradients.h>
|
||||
#include <LibGfx/GrayscaleBitmap.h>
|
||||
#include <LibGfx/ImmutableBitmap.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Palette.h>
|
||||
#include <LibGfx/Point.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibGfx/Size.h>
|
||||
#include <LibGfx/StylePainter.h>
|
||||
#include <LibGfx/TextAlignment.h>
|
||||
#include <LibGfx/TextDirection.h>
|
||||
#include <LibGfx/TextElision.h>
|
||||
#include <LibGfx/TextLayout.h>
|
||||
#include <LibGfx/TextWrapping.h>
|
||||
#include <LibWeb/CSS/Enums.h>
|
||||
#include <LibWeb/Painting/BorderRadiiData.h>
|
||||
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
|
||||
#include <LibWeb/Painting/Command.h>
|
||||
#include <LibWeb/Painting/GradientData.h>
|
||||
#include <LibWeb/Painting/PaintOuterBoxShadowParams.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
enum class CommandResult {
|
||||
Continue,
|
||||
SkipStackingContext,
|
||||
};
|
||||
|
||||
class PaintingCommandExecutor {
|
||||
public:
|
||||
virtual ~PaintingCommandExecutor() = default;
|
||||
|
||||
virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&) = 0;
|
||||
virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0;
|
||||
virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0;
|
||||
virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
|
||||
virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
|
||||
virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0;
|
||||
virtual CommandResult clear_clip_rect() = 0;
|
||||
virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0;
|
||||
virtual CommandResult pop_stacking_context() = 0;
|
||||
virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0;
|
||||
virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0;
|
||||
virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0;
|
||||
virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0;
|
||||
virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0;
|
||||
virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0;
|
||||
virtual CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color,
|
||||
Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius,
|
||||
Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius,
|
||||
Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius,
|
||||
Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius)
|
||||
= 0;
|
||||
virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult stroke_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, float thickness, float opacity, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult draw_ellipse(Gfx::IntRect const&, Color const&, int thickness) = 0;
|
||||
virtual CommandResult fill_ellipse(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::BlendMode blend_mode) = 0;
|
||||
virtual CommandResult draw_line(Color const& color, Gfx::IntPoint const& from, Gfx::IntPoint const& to, int thickness, Gfx::Painter::LineStyle, Color const& alternate_color) = 0;
|
||||
virtual CommandResult draw_signed_distance_field(Gfx::IntRect const& rect, Color const&, Gfx::GrayscaleBitmap const&, float smoothing) = 0;
|
||||
virtual CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) = 0;
|
||||
virtual CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) = 0;
|
||||
virtual CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) = 0;
|
||||
virtual CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const& color, int amplitude, int thickness) = 0;
|
||||
virtual CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) = 0;
|
||||
virtual CommandResult blit_corner_clipping(u32 id) = 0;
|
||||
virtual CommandResult paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data) = 0;
|
||||
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
|
||||
virtual bool needs_prepare_glyphs_texture() const { return false; }
|
||||
virtual void prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs) = 0;
|
||||
virtual void prepare_to_execute() { }
|
||||
virtual bool needs_update_immutable_bitmap_texture_cache() const = 0;
|
||||
virtual void update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>&) = 0;
|
||||
};
|
||||
|
||||
class CommandList {
|
||||
public:
|
||||
void append(Command&& command, Optional<i32> scroll_frame_id);
|
||||
|
||||
void apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id);
|
||||
void execute(PaintingCommandExecutor&);
|
||||
|
||||
private:
|
||||
struct CommandWithScrollFrame {
|
||||
Optional<i32> scroll_frame_id;
|
||||
Command command;
|
||||
};
|
||||
|
||||
AK::SegmentedVector<CommandWithScrollFrame, 512> m_commands;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
* Copyright (c) 2023-2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -9,44 +9,20 @@
|
|||
|
||||
namespace Web::Painting {
|
||||
|
||||
void DrawGlyphRun::translate_by(Gfx::IntPoint const& offset)
|
||||
RecordingPainter::RecordingPainter(CommandList& command_list)
|
||||
: m_command_list(command_list)
|
||||
{
|
||||
for (auto& glyph : glyph_run) {
|
||||
glyph.visit([&](auto& glyph) {
|
||||
glyph.translate_by(offset.to_type<float>());
|
||||
});
|
||||
}
|
||||
rect.translate_by(offset);
|
||||
m_state_stack.append(State());
|
||||
}
|
||||
|
||||
Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const
|
||||
void RecordingPainter::append(Command&& command)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
Gfx::IntRect BlitCornerClipping::bounding_rect() const
|
||||
{
|
||||
return border_rect;
|
||||
m_command_list.append(move(command), state().scroll_frame_id);
|
||||
}
|
||||
|
||||
void RecordingPainter::sample_under_corners(u32 id, CornerRadii corner_radii, Gfx::IntRect border_rect, CornerClip corner_clip)
|
||||
{
|
||||
push_command(SampleUnderCorners {
|
||||
append(SampleUnderCorners {
|
||||
id,
|
||||
corner_radii,
|
||||
border_rect = state().translation.map(border_rect),
|
||||
|
@ -55,12 +31,12 @@ void RecordingPainter::sample_under_corners(u32 id, CornerRadii corner_radii, Gf
|
|||
|
||||
void RecordingPainter::blit_corner_clipping(u32 id, Gfx::IntRect border_rect)
|
||||
{
|
||||
push_command(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) });
|
||||
append(BlitCornerClipping { id, border_rect = state().translation.map(border_rect) });
|
||||
}
|
||||
|
||||
void RecordingPainter::fill_rect(Gfx::IntRect const& rect, Color color)
|
||||
{
|
||||
push_command(FillRect {
|
||||
append(FillRect {
|
||||
.rect = state().translation.map(rect),
|
||||
.color = color,
|
||||
});
|
||||
|
@ -70,7 +46,7 @@ void RecordingPainter::fill_path(FillPathUsingColorParams params)
|
|||
{
|
||||
auto aa_translation = state().translation.map(params.translation.value_or(Gfx::FloatPoint {}));
|
||||
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type<int>();
|
||||
push_command(FillPathUsingColor {
|
||||
append(FillPathUsingColor {
|
||||
.path_bounding_rect = path_bounding_rect,
|
||||
.path = params.path,
|
||||
.color = params.color,
|
||||
|
@ -83,7 +59,7 @@ void RecordingPainter::fill_path(FillPathUsingPaintStyleParams params)
|
|||
{
|
||||
auto aa_translation = state().translation.map(params.translation.value_or(Gfx::FloatPoint {}));
|
||||
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type<int>();
|
||||
push_command(FillPathUsingPaintStyle {
|
||||
append(FillPathUsingPaintStyle {
|
||||
.path_bounding_rect = path_bounding_rect,
|
||||
.path = params.path,
|
||||
.paint_style = params.paint_style,
|
||||
|
@ -99,7 +75,7 @@ void RecordingPainter::stroke_path(StrokePathUsingColorParams params)
|
|||
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type<int>();
|
||||
// Increase path bounding box by `thickness` to account for stroke.
|
||||
path_bounding_rect.inflate(params.thickness, params.thickness);
|
||||
push_command(StrokePathUsingColor {
|
||||
append(StrokePathUsingColor {
|
||||
.path_bounding_rect = path_bounding_rect,
|
||||
.path = params.path,
|
||||
.color = params.color,
|
||||
|
@ -114,7 +90,7 @@ void RecordingPainter::stroke_path(StrokePathUsingPaintStyleParams params)
|
|||
auto path_bounding_rect = params.path.bounding_box().translated(aa_translation).to_type<int>();
|
||||
// Increase path bounding box by `thickness` to account for stroke.
|
||||
path_bounding_rect.inflate(params.thickness, params.thickness);
|
||||
push_command(StrokePathUsingPaintStyle {
|
||||
append(StrokePathUsingPaintStyle {
|
||||
.path_bounding_rect = path_bounding_rect,
|
||||
.path = params.path,
|
||||
.paint_style = params.paint_style,
|
||||
|
@ -126,7 +102,7 @@ void RecordingPainter::stroke_path(StrokePathUsingPaintStyleParams params)
|
|||
|
||||
void RecordingPainter::draw_ellipse(Gfx::IntRect const& a_rect, Color color, int thickness)
|
||||
{
|
||||
push_command(DrawEllipse {
|
||||
append(DrawEllipse {
|
||||
.rect = state().translation.map(a_rect),
|
||||
.color = color,
|
||||
.thickness = thickness,
|
||||
|
@ -135,7 +111,7 @@ void RecordingPainter::draw_ellipse(Gfx::IntRect const& a_rect, Color color, int
|
|||
|
||||
void RecordingPainter::fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx::AntiAliasingPainter::BlendMode blend_mode)
|
||||
{
|
||||
push_command(FillEllipse {
|
||||
append(FillEllipse {
|
||||
.rect = state().translation.map(a_rect),
|
||||
.color = color,
|
||||
.blend_mode = blend_mode,
|
||||
|
@ -144,7 +120,7 @@ void RecordingPainter::fill_ellipse(Gfx::IntRect const& a_rect, Color color, Gfx
|
|||
|
||||
void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradient_rect, LinearGradientData const& data)
|
||||
{
|
||||
push_command(PaintLinearGradient {
|
||||
append(PaintLinearGradient {
|
||||
.gradient_rect = state().translation.map(gradient_rect),
|
||||
.linear_gradient_data = data,
|
||||
});
|
||||
|
@ -152,7 +128,7 @@ void RecordingPainter::fill_rect_with_linear_gradient(Gfx::IntRect const& gradie
|
|||
|
||||
void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const& data, Gfx::IntPoint const& position)
|
||||
{
|
||||
push_command(PaintConicGradient {
|
||||
append(PaintConicGradient {
|
||||
.rect = state().translation.map(rect),
|
||||
.conic_gradient_data = data,
|
||||
.position = position });
|
||||
|
@ -160,7 +136,7 @@ void RecordingPainter::fill_rect_with_conic_gradient(Gfx::IntRect const& rect, C
|
|||
|
||||
void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const& data, Gfx::IntPoint center, Gfx::IntSize size)
|
||||
{
|
||||
push_command(PaintRadialGradient {
|
||||
append(PaintRadialGradient {
|
||||
.rect = state().translation.map(rect),
|
||||
.radial_gradient_data = data,
|
||||
.center = center,
|
||||
|
@ -169,7 +145,7 @@ void RecordingPainter::fill_rect_with_radial_gradient(Gfx::IntRect const& rect,
|
|||
|
||||
void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rough)
|
||||
{
|
||||
push_command(DrawRect {
|
||||
append(DrawRect {
|
||||
.rect = state().translation.map(rect),
|
||||
.color = color,
|
||||
.rough = rough });
|
||||
|
@ -177,7 +153,7 @@ void RecordingPainter::draw_rect(Gfx::IntRect const& rect, Color color, bool rou
|
|||
|
||||
void RecordingPainter::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
|
||||
{
|
||||
push_command(DrawScaledBitmap {
|
||||
append(DrawScaledBitmap {
|
||||
.dst_rect = state().translation.map(dst_rect),
|
||||
.bitmap = bitmap,
|
||||
.src_rect = src_rect,
|
||||
|
@ -187,7 +163,7 @@ void RecordingPainter::draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bit
|
|||
|
||||
void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode)
|
||||
{
|
||||
push_command(DrawScaledImmutableBitmap {
|
||||
append(DrawScaledImmutableBitmap {
|
||||
.dst_rect = state().translation.map(dst_rect),
|
||||
.bitmap = bitmap,
|
||||
.src_rect = src_rect,
|
||||
|
@ -197,7 +173,7 @@ void RecordingPainter::draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect
|
|||
|
||||
void RecordingPainter::draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color color, int thickness, Gfx::Painter::LineStyle style, Color alternate_color)
|
||||
{
|
||||
push_command(DrawLine {
|
||||
append(DrawLine {
|
||||
.color = color,
|
||||
.from = state().translation.map(from),
|
||||
.to = state().translation.map(to),
|
||||
|
@ -209,7 +185,7 @@ void RecordingPainter::draw_line(Gfx::IntPoint from, Gfx::IntPoint to, Color col
|
|||
|
||||
void RecordingPainter::draw_text(Gfx::IntRect const& rect, String raw_text, Gfx::Font const& font, Gfx::TextAlignment alignment, Color color, Gfx::TextElision elision, Gfx::TextWrapping wrapping)
|
||||
{
|
||||
push_command(DrawText {
|
||||
append(DrawText {
|
||||
.rect = state().translation.map(rect),
|
||||
.raw_text = move(raw_text),
|
||||
.alignment = alignment,
|
||||
|
@ -222,7 +198,7 @@ void RecordingPainter::draw_text(Gfx::IntRect const& rect, String raw_text, Gfx:
|
|||
|
||||
void RecordingPainter::draw_signed_distance_field(Gfx::IntRect const& dst_rect, Color color, Gfx::GrayscaleBitmap const& sdf, float smoothing)
|
||||
{
|
||||
push_command(DrawSignedDistanceField {
|
||||
append(DrawSignedDistanceField {
|
||||
.rect = state().translation.map(dst_rect),
|
||||
.color = color,
|
||||
.sdf = sdf,
|
||||
|
@ -239,7 +215,7 @@ void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::Dra
|
|||
glyph.visit([&](auto& glyph) { glyph.position.translate_by(transformed_baseline_start); });
|
||||
translated_glyph_run.append(glyph);
|
||||
}
|
||||
push_command(DrawGlyphRun {
|
||||
append(DrawGlyphRun {
|
||||
.glyph_run = move(translated_glyph_run),
|
||||
.color = color,
|
||||
.rect = state().translation.map(rect),
|
||||
|
@ -256,7 +232,7 @@ void RecordingPainter::add_clip_rect(Gfx::IntRect const& rect)
|
|||
}
|
||||
|
||||
if (prev_clip_rect != state().clip_rect)
|
||||
push_command(SetClipRect { .rect = *state().clip_rect });
|
||||
append(SetClipRect { .rect = *state().clip_rect });
|
||||
}
|
||||
|
||||
void RecordingPainter::translate(int dx, int dy)
|
||||
|
@ -283,15 +259,15 @@ void RecordingPainter::restore()
|
|||
|
||||
if (state().clip_rect != prev_clip_rect) {
|
||||
if (state().clip_rect.has_value())
|
||||
push_command(SetClipRect { .rect = *state().clip_rect });
|
||||
append(SetClipRect { .rect = *state().clip_rect });
|
||||
else
|
||||
push_command(ClearClipRect {});
|
||||
append(ClearClipRect {});
|
||||
}
|
||||
}
|
||||
|
||||
void RecordingPainter::push_stacking_context(PushStackingContextParams params)
|
||||
{
|
||||
push_command(PushStackingContext {
|
||||
append(PushStackingContext {
|
||||
.opacity = params.opacity,
|
||||
.is_fixed_position = params.is_fixed_position,
|
||||
.source_paintable_rect = params.source_paintable_rect,
|
||||
|
@ -311,17 +287,17 @@ void RecordingPainter::push_stacking_context(PushStackingContextParams params)
|
|||
void RecordingPainter::pop_stacking_context()
|
||||
{
|
||||
m_state_stack.take_last();
|
||||
push_command(PopStackingContext {});
|
||||
append(PopStackingContext {});
|
||||
}
|
||||
|
||||
void RecordingPainter::paint_frame(Gfx::IntRect rect, Palette palette, Gfx::FrameStyle style)
|
||||
{
|
||||
push_command(PaintFrame { state().translation.map(rect), palette, style });
|
||||
append(PaintFrame { state().translation.map(rect), palette, style });
|
||||
}
|
||||
|
||||
void RecordingPainter::apply_backdrop_filter(Gfx::IntRect const& backdrop_region, BorderRadiiData const& border_radii_data, CSS::ResolvedBackdropFilter const& backdrop_filter)
|
||||
{
|
||||
push_command(ApplyBackdropFilter {
|
||||
append(ApplyBackdropFilter {
|
||||
.backdrop_region = state().translation.map(backdrop_region),
|
||||
.border_radii_data = border_radii_data,
|
||||
.backdrop_filter = backdrop_filter,
|
||||
|
@ -331,21 +307,21 @@ void RecordingPainter::apply_backdrop_filter(Gfx::IntRect const& backdrop_region
|
|||
void RecordingPainter::paint_outer_box_shadow_params(PaintOuterBoxShadowParams params)
|
||||
{
|
||||
params.device_content_rect = state().translation.map(params.device_content_rect.to_type<int>()).to_type<DevicePixels>();
|
||||
push_command(PaintOuterBoxShadow {
|
||||
append(PaintOuterBoxShadow {
|
||||
.outer_box_shadow_params = params,
|
||||
});
|
||||
}
|
||||
|
||||
void RecordingPainter::paint_inner_box_shadow_params(PaintOuterBoxShadowParams params)
|
||||
{
|
||||
push_command(PaintInnerBoxShadow {
|
||||
append(PaintInnerBoxShadow {
|
||||
.outer_box_shadow_params = params,
|
||||
});
|
||||
}
|
||||
|
||||
void RecordingPainter::paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location)
|
||||
{
|
||||
push_command(PaintTextShadow {
|
||||
append(PaintTextShadow {
|
||||
.blur_radius = blur_radius,
|
||||
.shadow_bounding_rect = bounding_rect,
|
||||
.text_rect = text_rect,
|
||||
|
@ -362,7 +338,7 @@ void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect,
|
|||
return;
|
||||
}
|
||||
|
||||
push_command(FillRectWithRoundedCorners {
|
||||
append(FillRectWithRoundedCorners {
|
||||
.rect = state().translation.map(rect),
|
||||
.color = color,
|
||||
.top_left_radius = top_left_radius,
|
||||
|
@ -388,7 +364,7 @@ void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect
|
|||
|
||||
void RecordingPainter::draw_triangle_wave(Gfx::IntPoint a_p1, Gfx::IntPoint a_p2, Color color, int amplitude, int thickness = 1)
|
||||
{
|
||||
push_command(DrawTriangleWave {
|
||||
append(DrawTriangleWave {
|
||||
.p1 = state().translation.map(a_p1),
|
||||
.p2 = state().translation.map(a_p2),
|
||||
.color = color,
|
||||
|
@ -400,200 +376,7 @@ void RecordingPainter::paint_borders(DevicePixelRect const& border_rect, CornerR
|
|||
{
|
||||
if (borders_data.top.width == 0 && borders_data.right.width == 0 && borders_data.bottom.width == 0 && borders_data.left.width == 0)
|
||||
return;
|
||||
push_command(PaintBorders { border_rect, corner_radii, borders_data });
|
||||
}
|
||||
|
||||
static Optional<Gfx::IntRect> command_bounding_rectangle(PaintingCommand const& command)
|
||||
{
|
||||
return command.visit(
|
||||
[&](auto const& command) -> Optional<Gfx::IntRect> {
|
||||
if constexpr (requires { command.bounding_rect(); })
|
||||
return command.bounding_rect();
|
||||
else
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
executor.prepare_to_execute();
|
||||
|
||||
if (executor.needs_prepare_glyphs_texture()) {
|
||||
HashMap<Gfx::Font const*, HashTable<u32>> unique_glyphs;
|
||||
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>()) {
|
||||
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
unique_glyphs.ensure(glyph.font, [] { return HashTable<u32> {}; }).set(glyph.code_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
executor.prepare_glyph_texture(unique_glyphs);
|
||||
}
|
||||
|
||||
if (executor.needs_update_immutable_bitmap_texture_cache()) {
|
||||
HashMap<u32, Gfx::ImmutableBitmap const*> immutable_bitmaps;
|
||||
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());
|
||||
}
|
||||
}
|
||||
executor.update_immutable_bitmap_texture_cache(immutable_bitmaps);
|
||||
}
|
||||
|
||||
HashTable<u32> skipped_sample_corner_commands;
|
||||
size_t next_command_index = 0;
|
||||
while (next_command_index < m_painting_commands.size()) {
|
||||
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>()) {
|
||||
auto const& sample_under_corners = command.get<SampleUnderCorners>();
|
||||
skipped_sample_corner_commands.set(sample_under_corners.id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto result = command.visit(
|
||||
[&](DrawGlyphRun const& command) {
|
||||
return executor.draw_glyph_run(command.glyph_run, command.color);
|
||||
},
|
||||
[&](DrawText const& command) {
|
||||
return executor.draw_text(command.rect, command.raw_text, command.alignment, command.color, command.elision, command.wrapping, command.font);
|
||||
},
|
||||
[&](FillRect const& command) {
|
||||
return executor.fill_rect(command.rect, command.color);
|
||||
},
|
||||
[&](DrawScaledBitmap const& command) {
|
||||
return executor.draw_scaled_bitmap(command.dst_rect, command.bitmap, command.src_rect, command.scaling_mode);
|
||||
},
|
||||
[&](DrawScaledImmutableBitmap const& command) {
|
||||
return executor.draw_scaled_immutable_bitmap(command.dst_rect, command.bitmap, command.src_rect, command.scaling_mode);
|
||||
},
|
||||
[&](SetClipRect const& command) {
|
||||
return executor.set_clip_rect(command.rect);
|
||||
},
|
||||
[&](ClearClipRect const&) {
|
||||
return executor.clear_clip_rect();
|
||||
},
|
||||
[&](PushStackingContext const& command) {
|
||||
return executor.push_stacking_context(command.opacity, command.is_fixed_position, command.source_paintable_rect, command.post_transform_translation, command.image_rendering, command.transform, command.mask);
|
||||
},
|
||||
[&](PopStackingContext const&) {
|
||||
return executor.pop_stacking_context();
|
||||
},
|
||||
[&](PaintLinearGradient const& command) {
|
||||
return executor.paint_linear_gradient(command.gradient_rect, command.linear_gradient_data);
|
||||
},
|
||||
[&](PaintRadialGradient const& command) {
|
||||
return executor.paint_radial_gradient(command.rect, command.radial_gradient_data, command.center, command.size);
|
||||
},
|
||||
[&](PaintConicGradient const& command) {
|
||||
return executor.paint_conic_gradient(command.rect, command.conic_gradient_data, command.position);
|
||||
},
|
||||
[&](PaintOuterBoxShadow const& command) {
|
||||
return executor.paint_outer_box_shadow(command.outer_box_shadow_params);
|
||||
},
|
||||
[&](PaintInnerBoxShadow const& command) {
|
||||
return executor.paint_inner_box_shadow(command.outer_box_shadow_params);
|
||||
},
|
||||
[&](PaintTextShadow const& command) {
|
||||
return executor.paint_text_shadow(command.blur_radius, command.shadow_bounding_rect, command.text_rect, command.glyph_run, command.color, command.fragment_baseline, command.draw_location);
|
||||
},
|
||||
[&](FillRectWithRoundedCorners const& command) {
|
||||
return executor.fill_rect_with_rounded_corners(command.rect, command.color, command.top_left_radius, command.top_right_radius, command.bottom_left_radius, command.bottom_right_radius);
|
||||
},
|
||||
[&](FillPathUsingColor const& command) {
|
||||
return executor.fill_path_using_color(command.path, command.color, command.winding_rule, command.aa_translation);
|
||||
},
|
||||
[&](FillPathUsingPaintStyle const& command) {
|
||||
return executor.fill_path_using_paint_style(command.path, command.paint_style, command.winding_rule, command.opacity, command.aa_translation);
|
||||
},
|
||||
[&](StrokePathUsingColor const& command) {
|
||||
return executor.stroke_path_using_color(command.path, command.color, command.thickness, command.aa_translation);
|
||||
},
|
||||
[&](StrokePathUsingPaintStyle const& command) {
|
||||
return executor.stroke_path_using_paint_style(command.path, command.paint_style, command.thickness, command.opacity, command.aa_translation);
|
||||
},
|
||||
[&](DrawEllipse const& command) {
|
||||
return executor.draw_ellipse(command.rect, command.color, command.thickness);
|
||||
},
|
||||
[&](FillEllipse const& command) {
|
||||
return executor.fill_ellipse(command.rect, command.color, command.blend_mode);
|
||||
},
|
||||
[&](DrawLine const& command) {
|
||||
return executor.draw_line(command.color, command.from, command.to, command.thickness, command.style, command.alternate_color);
|
||||
},
|
||||
[&](DrawSignedDistanceField const& command) {
|
||||
return executor.draw_signed_distance_field(command.rect, command.color, command.sdf, command.smoothing);
|
||||
},
|
||||
[&](PaintFrame const& command) {
|
||||
return executor.paint_frame(command.rect, command.palette, command.style);
|
||||
},
|
||||
[&](ApplyBackdropFilter const& command) {
|
||||
return executor.apply_backdrop_filter(command.backdrop_region, command.backdrop_filter);
|
||||
},
|
||||
[&](DrawRect const& command) {
|
||||
return executor.draw_rect(command.rect, command.color, command.rough);
|
||||
},
|
||||
[&](DrawTriangleWave const& command) {
|
||||
return executor.draw_triangle_wave(command.p1, command.p2, command.color, command.amplitude, command.thickness);
|
||||
},
|
||||
[&](SampleUnderCorners const& command) {
|
||||
return executor.sample_under_corners(command.id, command.corner_radii, command.border_rect, command.corner_clip);
|
||||
},
|
||||
[&](BlitCornerClipping const& command) {
|
||||
if (skipped_sample_corner_commands.contains(command.id)) {
|
||||
// FIXME: If a sampling command falls outside the viewport and is not executed, the associated blit
|
||||
// should also be skipped if it is within the viewport. In a properly generated list of
|
||||
// painting commands, sample and blit commands should have matching rectangles, preventing
|
||||
// this discrepancy.
|
||||
dbgln("Skipping blit_corner_clipping command because the sample_under_corners command was skipped.");
|
||||
return CommandResult::Continue;
|
||||
}
|
||||
return executor.blit_corner_clipping(command.id);
|
||||
},
|
||||
[&](PaintBorders const& command) {
|
||||
return executor.paint_borders(command.border_rect, command.corner_radii, command.borders_data);
|
||||
});
|
||||
|
||||
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].command.has<PushStackingContext>()) {
|
||||
stacking_context_nesting_level++;
|
||||
} else if (m_painting_commands[next_command_index].command.has<PopStackingContext>()) {
|
||||
stacking_context_nesting_level--;
|
||||
}
|
||||
|
||||
next_command_index++;
|
||||
|
||||
if (stacking_context_nesting_level == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
append(PaintBorders { border_rect, corner_radii, borders_data });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
* Copyright (c) 2023-2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
@ -32,461 +32,17 @@
|
|||
#include <LibWeb/CSS/Enums.h>
|
||||
#include <LibWeb/Painting/BorderRadiiData.h>
|
||||
#include <LibWeb/Painting/BorderRadiusCornerClipper.h>
|
||||
#include <LibWeb/Painting/Command.h>
|
||||
#include <LibWeb/Painting/CommandList.h>
|
||||
#include <LibWeb/Painting/GradientData.h>
|
||||
#include <LibWeb/Painting/PaintOuterBoxShadowParams.h>
|
||||
|
||||
namespace Web::Painting {
|
||||
|
||||
enum class CommandResult {
|
||||
Continue,
|
||||
SkipStackingContext,
|
||||
};
|
||||
|
||||
struct DrawGlyphRun {
|
||||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
|
||||
Color color;
|
||||
Gfx::IntRect rect;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct DrawText {
|
||||
Gfx::IntRect rect;
|
||||
String raw_text;
|
||||
Gfx::TextAlignment alignment;
|
||||
Color color;
|
||||
Gfx::TextElision elision;
|
||||
Gfx::TextWrapping wrapping;
|
||||
Optional<NonnullRefPtr<Gfx::Font>> font {};
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillRect {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawScaledBitmap {
|
||||
Gfx::IntRect dst_rect;
|
||||
NonnullRefPtr<Gfx::Bitmap> bitmap;
|
||||
Gfx::IntRect src_rect;
|
||||
Gfx::Painter::ScalingMode scaling_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawScaledImmutableBitmap {
|
||||
Gfx::IntRect dst_rect;
|
||||
NonnullRefPtr<Gfx::ImmutableBitmap> bitmap;
|
||||
Gfx::IntRect src_rect;
|
||||
Gfx::Painter::ScalingMode scaling_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return dst_rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { dst_rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct SetClipRect {
|
||||
Gfx::IntRect rect;
|
||||
};
|
||||
|
||||
struct ClearClipRect { };
|
||||
|
||||
struct StackingContextTransform {
|
||||
Gfx::FloatPoint origin;
|
||||
Gfx::FloatMatrix4x4 matrix;
|
||||
};
|
||||
|
||||
struct StackingContextMask {
|
||||
NonnullRefPtr<Gfx::Bitmap> mask_bitmap;
|
||||
Gfx::Bitmap::MaskKind mask_kind;
|
||||
};
|
||||
|
||||
struct PushStackingContext {
|
||||
float opacity;
|
||||
bool is_fixed_position;
|
||||
// The bounding box of the source paintable (pre-transform).
|
||||
Gfx::IntRect source_paintable_rect;
|
||||
// A translation to be applied after the stacking context has been transformed.
|
||||
Gfx::IntPoint post_transform_translation;
|
||||
CSS::ImageRendering image_rendering;
|
||||
StackingContextTransform transform;
|
||||
Optional<StackingContextMask> mask = {};
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
source_paintable_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PopStackingContext { };
|
||||
|
||||
struct PaintLinearGradient {
|
||||
Gfx::IntRect gradient_rect;
|
||||
LinearGradientData linear_gradient_data;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return gradient_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
gradient_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintOuterBoxShadow {
|
||||
PaintOuterBoxShadowParams outer_box_shadow_params;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const;
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct PaintInnerBoxShadow {
|
||||
PaintOuterBoxShadowParams outer_box_shadow_params;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset);
|
||||
};
|
||||
|
||||
struct PaintTextShadow {
|
||||
int blur_radius;
|
||||
Gfx::IntRect shadow_bounding_rect;
|
||||
Gfx::IntRect text_rect;
|
||||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
|
||||
Color color;
|
||||
int fragment_baseline;
|
||||
Gfx::IntPoint draw_location;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return { draw_location, shadow_bounding_rect.size() }; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { draw_location.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillRectWithRoundedCorners {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::AntiAliasingPainter::CornerRadius top_left_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
|
||||
Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct FillPathUsingColor {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
Color color;
|
||||
Gfx::Painter::WindingRule winding_rule;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct FillPathUsingPaintStyle {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
NonnullRefPtr<Gfx::PaintStyle> paint_style;
|
||||
Gfx::Painter::WindingRule winding_rule;
|
||||
float opacity;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct StrokePathUsingColor {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
Color color;
|
||||
float thickness;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct StrokePathUsingPaintStyle {
|
||||
Gfx::IntRect path_bounding_rect;
|
||||
Gfx::Path path;
|
||||
NonnullRefPtr<Gfx::PaintStyle> paint_style;
|
||||
float thickness;
|
||||
float opacity = 1.0f;
|
||||
Gfx::FloatPoint aa_translation;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return path_bounding_rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
path_bounding_rect.translate_by(offset);
|
||||
aa_translation.translate_by(offset.to_type<float>());
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawEllipse {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
int thickness;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct FillEllipse {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::AntiAliasingPainter::BlendMode blend_mode;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawLine {
|
||||
Color color;
|
||||
Gfx::IntPoint from;
|
||||
Gfx::IntPoint to;
|
||||
int thickness;
|
||||
Gfx::Painter::LineStyle style;
|
||||
Color alternate_color;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
from.translate_by(offset);
|
||||
to.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawSignedDistanceField {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
Gfx::GrayscaleBitmap sdf;
|
||||
float smoothing;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintFrame {
|
||||
Gfx::IntRect rect;
|
||||
Palette palette;
|
||||
Gfx::FrameStyle style;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct ApplyBackdropFilter {
|
||||
Gfx::IntRect backdrop_region;
|
||||
BorderRadiiData border_radii_data;
|
||||
CSS::ResolvedBackdropFilter backdrop_filter;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return backdrop_region; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
backdrop_region.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct DrawRect {
|
||||
Gfx::IntRect rect;
|
||||
Color color;
|
||||
bool rough;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct PaintRadialGradient {
|
||||
Gfx::IntRect rect;
|
||||
RadialGradientData radial_gradient_data;
|
||||
Gfx::IntPoint center;
|
||||
Gfx::IntSize size;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct PaintConicGradient {
|
||||
Gfx::IntRect rect;
|
||||
ConicGradientData conic_gradient_data;
|
||||
Gfx::IntPoint position;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset) { rect.translate_by(offset); }
|
||||
};
|
||||
|
||||
struct DrawTriangleWave {
|
||||
Gfx::IntPoint p1;
|
||||
Gfx::IntPoint p2;
|
||||
Color color;
|
||||
int amplitude;
|
||||
int thickness;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
p1.translate_by(offset);
|
||||
p2.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct SampleUnderCorners {
|
||||
u32 id;
|
||||
CornerRadii corner_radii;
|
||||
Gfx::IntRect border_rect;
|
||||
CornerClip corner_clip;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
border_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct BlitCornerClipping {
|
||||
u32 id;
|
||||
Gfx::IntRect border_rect;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const;
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
border_rect.translate_by(offset);
|
||||
}
|
||||
};
|
||||
|
||||
struct PaintBorders {
|
||||
DevicePixelRect border_rect;
|
||||
CornerRadii corner_radii;
|
||||
BordersDataDevicePixels borders_data;
|
||||
|
||||
[[nodiscard]] Gfx::IntRect bounding_rect() const { return border_rect.to_type<int>(); }
|
||||
|
||||
void translate_by(Gfx::IntPoint const& offset)
|
||||
{
|
||||
border_rect.translate_by(offset.to_type<DevicePixels>());
|
||||
}
|
||||
};
|
||||
|
||||
using PaintingCommand = Variant<
|
||||
DrawGlyphRun,
|
||||
DrawText,
|
||||
FillRect,
|
||||
DrawScaledBitmap,
|
||||
DrawScaledImmutableBitmap,
|
||||
SetClipRect,
|
||||
ClearClipRect,
|
||||
PushStackingContext,
|
||||
PopStackingContext,
|
||||
PaintLinearGradient,
|
||||
PaintRadialGradient,
|
||||
PaintConicGradient,
|
||||
PaintOuterBoxShadow,
|
||||
PaintInnerBoxShadow,
|
||||
PaintTextShadow,
|
||||
FillRectWithRoundedCorners,
|
||||
FillPathUsingColor,
|
||||
FillPathUsingPaintStyle,
|
||||
StrokePathUsingColor,
|
||||
StrokePathUsingPaintStyle,
|
||||
DrawEllipse,
|
||||
FillEllipse,
|
||||
DrawLine,
|
||||
DrawSignedDistanceField,
|
||||
PaintFrame,
|
||||
ApplyBackdropFilter,
|
||||
DrawRect,
|
||||
DrawTriangleWave,
|
||||
SampleUnderCorners,
|
||||
BlitCornerClipping,
|
||||
PaintBorders>;
|
||||
|
||||
class PaintingCommandExecutor {
|
||||
public:
|
||||
virtual ~PaintingCommandExecutor() = default;
|
||||
|
||||
virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&) = 0;
|
||||
virtual CommandResult draw_text(Gfx::IntRect const&, String const&, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) = 0;
|
||||
virtual CommandResult fill_rect(Gfx::IntRect const&, Color const&) = 0;
|
||||
virtual CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
|
||||
virtual CommandResult draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;
|
||||
virtual CommandResult set_clip_rect(Gfx::IntRect const& rect) = 0;
|
||||
virtual CommandResult clear_clip_rect() = 0;
|
||||
virtual CommandResult push_stacking_context(float opacity, bool is_fixed_position, Gfx::IntRect const& source_paintable_rect, Gfx::IntPoint post_transform_translation, CSS::ImageRendering image_rendering, StackingContextTransform transform, Optional<StackingContextMask> mask) = 0;
|
||||
virtual CommandResult pop_stacking_context() = 0;
|
||||
virtual CommandResult paint_linear_gradient(Gfx::IntRect const&, LinearGradientData const&) = 0;
|
||||
virtual CommandResult paint_radial_gradient(Gfx::IntRect const& rect, RadialGradientData const&, Gfx::IntPoint const& center, Gfx::IntSize const& size) = 0;
|
||||
virtual CommandResult paint_conic_gradient(Gfx::IntRect const& rect, ConicGradientData const&, Gfx::IntPoint const& position) = 0;
|
||||
virtual CommandResult paint_outer_box_shadow(PaintOuterBoxShadowParams const&) = 0;
|
||||
virtual CommandResult paint_inner_box_shadow(PaintOuterBoxShadowParams const&) = 0;
|
||||
virtual CommandResult paint_text_shadow(int blur_radius, Gfx::IntRect const& shadow_bounding_rect, Gfx::IntRect const& text_rect, Span<Gfx::DrawGlyphOrEmoji const>, Color const&, int fragment_baseline, Gfx::IntPoint const& draw_location) = 0;
|
||||
virtual CommandResult fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color const& color, Gfx::AntiAliasingPainter::CornerRadius const& top_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& top_right_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_left_radius, Gfx::AntiAliasingPainter::CornerRadius const& bottom_right_radius) = 0;
|
||||
virtual CommandResult fill_path_using_color(Gfx::Path const&, Color const& color, Gfx::Painter::WindingRule, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult fill_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, Gfx::Painter::WindingRule winding_rule, float opacity, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult stroke_path_using_color(Gfx::Path const&, Color const& color, float thickness, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult stroke_path_using_paint_style(Gfx::Path const&, Gfx::PaintStyle const& paint_style, float thickness, float opacity, Gfx::FloatPoint const& aa_translation) = 0;
|
||||
virtual CommandResult draw_ellipse(Gfx::IntRect const&, Color const&, int thickness) = 0;
|
||||
virtual CommandResult fill_ellipse(Gfx::IntRect const&, Color const&, Gfx::AntiAliasingPainter::BlendMode blend_mode) = 0;
|
||||
virtual CommandResult draw_line(Color const& color, Gfx::IntPoint const& from, Gfx::IntPoint const& to, int thickness, Gfx::Painter::LineStyle, Color const& alternate_color) = 0;
|
||||
virtual CommandResult draw_signed_distance_field(Gfx::IntRect const& rect, Color const&, Gfx::GrayscaleBitmap const&, float smoothing) = 0;
|
||||
virtual CommandResult paint_frame(Gfx::IntRect const& rect, Palette const&, Gfx::FrameStyle) = 0;
|
||||
virtual CommandResult apply_backdrop_filter(Gfx::IntRect const& backdrop_region, Web::CSS::ResolvedBackdropFilter const& backdrop_filter) = 0;
|
||||
virtual CommandResult draw_rect(Gfx::IntRect const& rect, Color const&, bool rough) = 0;
|
||||
virtual CommandResult draw_triangle_wave(Gfx::IntPoint const& p1, Gfx::IntPoint const& p2, Color const& color, int amplitude, int thickness) = 0;
|
||||
virtual CommandResult sample_under_corners(u32 id, CornerRadii const&, Gfx::IntRect const&, CornerClip) = 0;
|
||||
virtual CommandResult blit_corner_clipping(u32 id) = 0;
|
||||
virtual CommandResult paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data) = 0;
|
||||
|
||||
virtual bool would_be_fully_clipped_by_painter(Gfx::IntRect) const = 0;
|
||||
|
||||
virtual bool needs_prepare_glyphs_texture() const { return false; }
|
||||
virtual void prepare_glyph_texture(HashMap<Gfx::Font const*, HashTable<u32>> const& unique_glyphs) = 0;
|
||||
|
||||
virtual void prepare_to_execute() { }
|
||||
|
||||
virtual bool needs_update_immutable_bitmap_texture_cache() const = 0;
|
||||
virtual void update_immutable_bitmap_texture_cache(HashMap<u32, Gfx::ImmutableBitmap const*>&) = 0;
|
||||
};
|
||||
|
||||
class RecordingPainter {
|
||||
AK_MAKE_NONCOPYABLE(RecordingPainter);
|
||||
AK_MAKE_NONMOVABLE(RecordingPainter);
|
||||
|
||||
public:
|
||||
void fill_rect(Gfx::IntRect const& rect, Color color);
|
||||
|
||||
|
@ -589,14 +145,11 @@ public:
|
|||
|
||||
void paint_borders(DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const& borders_data);
|
||||
|
||||
void execute(PaintingCommandExecutor&);
|
||||
RecordingPainter(CommandList& commands_list);
|
||||
|
||||
RecordingPainter()
|
||||
{
|
||||
m_state_stack.append(State());
|
||||
}
|
||||
CommandList& commands_list() { return m_command_list; }
|
||||
|
||||
void apply_scroll_offsets(Vector<Gfx::IntPoint> const& offsets_by_frame_id);
|
||||
void append(Command&& command);
|
||||
|
||||
private:
|
||||
struct State {
|
||||
|
@ -607,18 +160,8 @@ private:
|
|||
State& state() { return m_state_stack.last(); }
|
||||
State const& state() const { return m_state_stack.last(); }
|
||||
|
||||
void push_command(PaintingCommand command)
|
||||
{
|
||||
m_painting_commands.append({ state().scroll_frame_id, move(command) });
|
||||
}
|
||||
|
||||
struct PaintingCommandWithScrollFrame {
|
||||
Optional<i32> scroll_frame_id;
|
||||
PaintingCommand command;
|
||||
};
|
||||
|
||||
AK::SegmentedVector<PaintingCommandWithScrollFrame, 512> m_painting_commands;
|
||||
Vector<State> m_state_stack;
|
||||
CommandList& m_command_list;
|
||||
};
|
||||
|
||||
class RecordingPainterStateSaver {
|
||||
|
|
|
@ -92,13 +92,14 @@ RefPtr<Gfx::Bitmap> SVGGraphicsPaintable::calculate_mask(PaintContext& context,
|
|||
return {};
|
||||
mask_bitmap = mask_bitmap_or_error.release_value();
|
||||
{
|
||||
RecordingPainter painter;
|
||||
painter.translate(-mask_rect.location().to_type<int>());
|
||||
auto paint_context = context.clone(painter);
|
||||
CommandList painting_commands;
|
||||
RecordingPainter recording_painter(painting_commands);
|
||||
recording_painter.translate(-mask_rect.location().to_type<int>());
|
||||
auto paint_context = context.clone(recording_painter);
|
||||
paint_context.set_svg_transform(graphics_element.get_transform());
|
||||
StackingContext::paint_node_as_stacking_context(mask_paintable, paint_context);
|
||||
PaintingCommandExecutorCPU executor { *mask_bitmap };
|
||||
painter.execute(executor);
|
||||
painting_commands.execute(executor);
|
||||
}
|
||||
}
|
||||
return mask_bitmap;
|
||||
|
|
|
@ -129,13 +129,14 @@ RefPtr<Gfx::Bitmap> SVGDecodedImageData::render(Gfx::IntSize size) const
|
|||
m_document->navigable()->set_viewport_rect({ 0, 0, size.width(), size.height() });
|
||||
m_document->update_layout();
|
||||
|
||||
Painting::RecordingPainter recording_painter;
|
||||
Painting::CommandList painting_commands;
|
||||
Painting::RecordingPainter recording_painter(painting_commands);
|
||||
PaintContext context(recording_painter, m_page_client->palette(), m_page_client->device_pixels_per_css_pixel());
|
||||
|
||||
m_document->paintable()->paint_all_phases(context);
|
||||
|
||||
Painting::PaintingCommandExecutorCPU executor { *bitmap };
|
||||
recording_painter.execute(executor);
|
||||
painting_commands.execute(executor);
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
|
|
@ -189,7 +189,8 @@ Web::Layout::Viewport* PageClient::layout_root()
|
|||
|
||||
void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& target, Web::PaintOptions paint_options)
|
||||
{
|
||||
Web::Painting::RecordingPainter recording_painter;
|
||||
Web::Painting::CommandList painting_commands;
|
||||
Web::Painting::RecordingPainter recording_painter(painting_commands);
|
||||
|
||||
Gfx::IntRect bitmap_rect { {}, content_rect.size().to_type<int>() };
|
||||
recording_painter.fill_rect(bitmap_rect, Web::CSS::SystemColor::canvas());
|
||||
|
@ -203,7 +204,7 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta
|
|||
if (s_use_gpu_painter) {
|
||||
#ifdef HAS_ACCELERATED_GRAPHICS
|
||||
Web::Painting::PaintingCommandExecutorGPU painting_command_executor(*m_accelerated_graphics_context, target);
|
||||
recording_painter.execute(painting_command_executor);
|
||||
painting_commands.execute(painting_command_executor);
|
||||
#else
|
||||
static bool has_warned_about_configuration = false;
|
||||
|
||||
|
@ -214,7 +215,7 @@ void PageClient::paint(Web::DevicePixelRect const& content_rect, Gfx::Bitmap& ta
|
|||
#endif
|
||||
} else {
|
||||
Web::Painting::PaintingCommandExecutorCPU painting_command_executor(target);
|
||||
recording_painter.execute(painting_command_executor);
|
||||
painting_commands.execute(painting_command_executor);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue