1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:17:44 +00:00

LibWeb: Remove glyph run allocation in paint_text_fragment()

Instead of allocating a new glyph run to scale glyph positions and
fonts, a scale factor could be encoded in a paint command and applied
later during command execution.
This commit is contained in:
Aliaksandr Kalenik 2024-03-01 14:14:47 +01:00 committed by Andreas Kling
parent aeb5a0d9e8
commit cf6999f5f3
11 changed files with 25 additions and 27 deletions

View file

@ -11,12 +11,8 @@ namespace Web::Painting {
void DrawGlyphRun::translate_by(Gfx::IntPoint const& offset) 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); rect.translate_by(offset);
translation.translate_by(offset.to_type<float>());
} }
Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const Gfx::IntRect PaintOuterBoxShadow::bounding_rect() const

View file

@ -42,6 +42,7 @@ struct DrawGlyphRun {
Color color; Color color;
Gfx::IntRect rect; Gfx::IntRect rect;
Gfx::FloatPoint translation; Gfx::FloatPoint translation;
double scale { 1 };
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; } [[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }

View file

@ -24,12 +24,15 @@ CommandExecutorCPU::CommandExecutorCPU(Gfx::Bitmap& bitmap)
.scaling_mode = {} }); .scaling_mode = {} });
} }
CommandResult CommandExecutorCPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color, Gfx::FloatPoint translation) CommandResult CommandExecutorCPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color, Gfx::FloatPoint translation, double scale)
{ {
auto& painter = this->painter(); auto& painter = this->painter();
for (auto& glyph_or_emoji : glyph_run) { for (auto& glyph_or_emoji : glyph_run) {
auto transformed_glyph = glyph_or_emoji; auto transformed_glyph = glyph_or_emoji;
transformed_glyph.visit([&](auto& glyph) { glyph.position.translate_by(translation); }); transformed_glyph.visit([&](auto& glyph) {
glyph.position = glyph.position.scaled(scale).translated(translation);
glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(scale));
});
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) { if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
auto& glyph = transformed_glyph.get<Gfx::DrawGlyph>(); auto& glyph = transformed_glyph.get<Gfx::DrawGlyph>();
painter.draw_glyph(glyph.position, glyph.code_point, *glyph.font, color); painter.draw_glyph(glyph.position, glyph.code_point, *glyph.font, color);

View file

@ -13,7 +13,7 @@ namespace Web::Painting {
class CommandExecutorCPU : public CommandExecutor { class CommandExecutorCPU : public CommandExecutor {
public: public:
CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation) override; CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override;
CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;

View file

@ -31,13 +31,16 @@ CommandExecutorGPU::~CommandExecutorGPU()
painter().flush(m_target_bitmap); painter().flush(m_target_bitmap);
} }
CommandResult CommandExecutorGPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color, Gfx::FloatPoint translation) CommandResult CommandExecutorGPU::draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const& color, Gfx::FloatPoint translation, double scale)
{ {
Vector<Gfx::DrawGlyphOrEmoji> transformed_glyph_run; Vector<Gfx::DrawGlyphOrEmoji> transformed_glyph_run;
transformed_glyph_run.ensure_capacity(glyph_run.size()); transformed_glyph_run.ensure_capacity(glyph_run.size());
for (auto& glyph : glyph_run) { for (auto& glyph : glyph_run) {
auto transformed_glyph = glyph; auto transformed_glyph = glyph;
transformed_glyph.visit([&](auto& glyph) { glyph.position.translate_by(translation); }); transformed_glyph.visit([&](auto& glyph) {
glyph.position = glyph.position.scaled(scale).translated(translation);
glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(scale));
});
transformed_glyph_run.append(transformed_glyph); transformed_glyph_run.append(transformed_glyph);
} }
painter().draw_glyph_run(transformed_glyph_run, color); painter().draw_glyph_run(transformed_glyph_run, color);

View file

@ -14,7 +14,7 @@ namespace Web::Painting {
class CommandExecutorGPU : public CommandExecutor { class CommandExecutorGPU : public CommandExecutor {
public: public:
CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation) override; CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) override;
CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override; CommandResult draw_text(Gfx::IntRect const& rect, String const& raw_text, Gfx::TextAlignment alignment, Color const&, Gfx::TextElision, Gfx::TextWrapping, Optional<NonnullRefPtr<Gfx::Font>> const&) override;
CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override; CommandResult fill_rect(Gfx::IntRect const& rect, Color const&) override;
CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override; CommandResult draw_scaled_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) override;

View file

@ -48,10 +48,12 @@ void CommandList::execute(CommandExecutor& executor)
for (auto& command_with_scroll_id : m_commands) { for (auto& command_with_scroll_id : m_commands) {
auto& command = command_with_scroll_id.command; auto& command = command_with_scroll_id.command;
if (command.has<DrawGlyphRun>()) { if (command.has<DrawGlyphRun>()) {
auto scale = command.get<DrawGlyphRun>().scale;
for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) { for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run) {
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) { if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>(); auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
unique_glyphs.ensure(glyph.font, [] { return HashTable<u32> {}; }).set(glyph.code_point); auto const& font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(scale));
unique_glyphs.ensure(&font, [] { return HashTable<u32> {}; }).set(glyph.code_point);
} }
} }
} }
@ -87,7 +89,7 @@ void CommandList::execute(CommandExecutor& executor)
auto result = command.visit( auto result = command.visit(
[&](DrawGlyphRun const& command) { [&](DrawGlyphRun const& command) {
return executor.draw_glyph_run(command.glyph_run, command.color, command.translation); return executor.draw_glyph_run(command.glyph_run, command.color, command.translation, command.scale);
}, },
[&](DrawText const& command) { [&](DrawText const& command) {
return executor.draw_text(command.rect, command.raw_text, command.alignment, command.color, return executor.draw_text(command.rect, command.raw_text, command.alignment, command.color,

View file

@ -47,7 +47,7 @@ class CommandExecutor {
public: public:
virtual ~CommandExecutor() = default; virtual ~CommandExecutor() = default;
virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation) = 0; virtual CommandResult draw_glyph_run(Vector<Gfx::DrawGlyphOrEmoji> const& glyph_run, Color const&, Gfx::FloatPoint translation, double scale) = 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 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 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_bitmap(Gfx::IntRect const& dst_rect, Gfx::Bitmap const& bitmap, Gfx::IntRect const& src_rect, Gfx::Painter::ScalingMode scaling_mode) = 0;

View file

@ -668,23 +668,15 @@ void paint_text_fragment(PaintContext& context, Layout::TextNode const& text_nod
auto text = text_node.text_for_rendering(); auto text = text_node.text_for_rendering();
DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) }; DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) };
Vector<Gfx::DrawGlyphOrEmoji> scaled_glyph_run; auto scale = context.device_pixels_per_css_pixel();
scaled_glyph_run.ensure_capacity(fragment.glyph_run().size()); painter.draw_text_run(baseline_start.to_type<int>(), fragment.glyph_run(), text_node.computed_values().color(), fragment_absolute_device_rect.to_type<int>(), scale);
for (auto glyph : fragment.glyph_run()) {
glyph.visit([&](auto& glyph) {
glyph.font = *glyph.font->with_size(glyph.font->point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
glyph.position = glyph.position.scaled(context.device_pixels_per_css_pixel());
});
scaled_glyph_run.append(move(glyph));
}
painter.draw_text_run(baseline_start.to_type<int>(), scaled_glyph_run, text_node.computed_values().color(), fragment_absolute_device_rect.to_type<int>());
auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.first_available_font())).to_type<int>(); auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.first_available_font())).to_type<int>();
if (!selection_rect.is_empty()) { if (!selection_rect.is_empty()) {
painter.fill_rect(selection_rect, CSS::SystemColor::highlight()); painter.fill_rect(selection_rect, CSS::SystemColor::highlight());
RecordingPainterStateSaver saver(painter); RecordingPainterStateSaver saver(painter);
painter.add_clip_rect(selection_rect); painter.add_clip_rect(selection_rect);
painter.draw_text_run(baseline_start.to_type<int>(), scaled_glyph_run, CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type<int>()); painter.draw_text_run(baseline_start.to_type<int>(), fragment.glyph_run(), CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type<int>(), scale);
} }
paint_text_decoration(context, text_node, fragment); paint_text_decoration(context, text_node, fragment);

View file

@ -206,7 +206,7 @@ void RecordingPainter::draw_signed_distance_field(Gfx::IntRect const& dst_rect,
}); });
} }
void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect) void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect, double scale)
{ {
auto transformed_baseline_start = state().translation.map(baseline_start).to_type<float>(); auto transformed_baseline_start = state().translation.map(baseline_start).to_type<float>();
append(DrawGlyphRun { append(DrawGlyphRun {
@ -214,6 +214,7 @@ void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::Dra
.color = color, .color = color,
.rect = state().translation.map(rect), .rect = state().translation.map(rect),
.translation = transformed_baseline_start, .translation = transformed_baseline_start,
.scale = scale,
}); });
} }

View file

@ -100,7 +100,7 @@ public:
void draw_signed_distance_field(Gfx::IntRect const& dst_rect, Color color, Gfx::GrayscaleBitmap const& sdf, float smoothing); void draw_signed_distance_field(Gfx::IntRect const& dst_rect, Color color, Gfx::GrayscaleBitmap const& sdf, float smoothing);
// Streamlined text drawing routine that does no wrapping/elision/alignment. // Streamlined text drawing routine that does no wrapping/elision/alignment.
void draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect); void draw_text_run(Gfx::IntPoint baseline_start, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, Gfx::IntRect const& rect, double scale);
void add_clip_rect(Gfx::IntRect const& rect); void add_clip_rect(Gfx::IntRect const& rect);