LibWeb: Draw text vertically, for fragments with vertical writing-mode

For fragments with a vertical `writing-mode`. Rotate the text, so that
it is rendered on its side. This makes it fit into its layout box.
This commit is contained in:
BenJilks 2024-10-29 14:29:00 +00:00 committed by Alexander Kalenik
commit 02276360e9
Notes: github-actions[bot] 2024-11-03 16:02:57 +00:00
9 changed files with 41 additions and 7 deletions

View file

@ -53,6 +53,7 @@ public:
bool is_atomic_inline() const;
RefPtr<Gfx::GlyphRun> glyph_run() const { return m_glyph_run; }
CSS::WritingMode writing_mode() const { return m_writing_mode; }
void append_glyph_run(RefPtr<Gfx::GlyphRun> const&, CSSPixels run_width);
private:

View file

@ -31,7 +31,7 @@ static RefPtr<DisplayList> compute_text_clip_paths(PaintContext& context, Painta
DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) };
auto scale = context.device_pixels_per_css_pixel();
display_list_recorder.draw_text_run(baseline_start.to_type<int>(), *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type<int>(), scale);
display_list_recorder.draw_text_run(baseline_start.to_type<int>(), *glyph_run, Gfx::Color::Black, fragment_absolute_device_rect.to_type<int>(), scale, fragment.orientation());
};
paintable.for_each_in_inclusive_subtree([&](auto& paintable) {

View file

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

View file

@ -474,7 +474,20 @@ void DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
SkPaint paint;
paint.setColor(to_skia_color(command.color));
surface().canvas().drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint);
auto& canvas = surface().canvas();
switch (command.orientation) {
case Gfx::Orientation::Horizontal:
canvas.drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint);
break;
case Gfx::Orientation::Vertical:
canvas.save();
canvas.translate(command.rect.width(), 0);
canvas.rotate(90, command.rect.top_left().x(), command.rect.top_left().y());
canvas.drawGlyphs(glyphs.size(), glyphs.data(), positions.data(), to_skia_point(command.translation), sk_font, paint);
canvas.restore();
break;
}
}
void DisplayListPlayerSkia::fill_rect(FillRect const& command)

View file

@ -234,10 +234,10 @@ void DisplayListRecorder::draw_text(Gfx::IntRect const& rect, String raw_text, G
}
auto metrics = font.pixel_metrics();
float baseline_y = static_cast<float>(rect.y()) + metrics.ascent + (static_cast<float>(rect.height()) - (metrics.ascent + metrics.descent)) / 2.0f;
draw_text_run(Gfx::IntPoint(roundf(baseline_x), roundf(baseline_y)), *glyph_run, color, rect, 1.0);
draw_text_run(Gfx::IntPoint(roundf(baseline_x), roundf(baseline_y)), *glyph_run, color, rect, 1.0, Orientation::Horizontal);
}
void DisplayListRecorder::draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale)
void DisplayListRecorder::draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale, Orientation orientation)
{
if (rect.is_empty())
return;
@ -247,6 +247,7 @@ void DisplayListRecorder::draw_text_run(Gfx::IntPoint baseline_start, Gfx::Glyph
.rect = rect,
.translation = baseline_start.to_type<float>(),
.scale = scale,
.orientation = orientation,
});
}

View file

@ -97,7 +97,7 @@ public:
void draw_text(Gfx::IntRect const&, String, Gfx::Font const&, Gfx::TextAlignment, Color);
// Streamlined text drawing routine that does no wrapping/elision/alignment.
void draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale);
void draw_text_run(Gfx::IntPoint baseline_start, Gfx::GlyphRun const& glyph_run, Color color, Gfx::IntRect const& rect, double scale, Gfx::Orientation);
void add_clip_rect(Gfx::IntRect const& rect);

View file

@ -670,14 +670,14 @@ void paint_text_fragment(PaintContext& context, TextPaintable const& paintable,
DevicePixelPoint baseline_start { fragment_absolute_device_rect.x(), fragment_absolute_device_rect.y() + context.rounded_device_pixels(fragment.baseline()) };
auto scale = context.device_pixels_per_css_pixel();
painter.draw_text_run(baseline_start.to_type<int>(), *glyph_run, paintable.computed_values().webkit_text_fill_color(), fragment_absolute_device_rect.to_type<int>(), scale);
painter.draw_text_run(baseline_start.to_type<int>(), *glyph_run, paintable.computed_values().webkit_text_fill_color(), fragment_absolute_device_rect.to_type<int>(), scale, fragment.orientation());
auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(paintable.layout_node().first_available_font())).to_type<int>();
if (!selection_rect.is_empty()) {
painter.fill_rect(selection_rect, CSS::SystemColor::highlight());
DisplayListRecorderStateSaver saver(painter);
painter.add_clip_rect(selection_rect);
painter.draw_text_run(baseline_start.to_type<int>(), *glyph_run, CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type<int>(), scale);
painter.draw_text_run(baseline_start.to_type<int>(), *glyph_run, CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type<int>(), scale, fragment.orientation());
}
paint_text_decoration(context, paintable, fragment);

View file

@ -22,6 +22,7 @@ PaintableFragment::PaintableFragment(Layout::LineBoxFragment const& fragment)
, m_start(fragment.start())
, m_length(fragment.length())
, m_glyph_run(fragment.glyph_run())
, m_writing_mode(fragment.writing_mode())
{
}
@ -129,6 +130,21 @@ CSSPixelRect PaintableFragment::range_rect(Gfx::Font const& font, size_t start_o
return {};
}
Gfx::Orientation PaintableFragment::orientation() const
{
switch (m_writing_mode) {
case CSS::WritingMode::HorizontalTb:
return Gfx::Orientation::Horizontal;
case CSS::WritingMode::VerticalRl:
case CSS::WritingMode::VerticalLr:
case CSS::WritingMode::SidewaysRl:
case CSS::WritingMode::SidewaysLr:
return Gfx::Orientation::Vertical;
default:
VERIFY_NOT_REACHED();
}
}
CSSPixelRect PaintableFragment::selection_rect(Gfx::Font const& font) const
{
if (!paintable().is_selected())

View file

@ -44,6 +44,7 @@ public:
CSSPixelRect const absolute_rect() const;
RefPtr<Gfx::GlyphRun> glyph_run() const { return m_glyph_run; }
Gfx::Orientation orientation() const;
CSSPixelRect selection_rect(Gfx::Font const&) const;
CSSPixelRect range_rect(Gfx::Font const&, size_t start_offset, size_t end_offset) const;
@ -64,6 +65,7 @@ private:
int m_length;
Painting::BorderRadiiData m_border_radii_data;
RefPtr<Gfx::GlyphRun> m_glyph_run;
CSS::WritingMode m_writing_mode;
Vector<ShadowData> m_shadows;
ResolvedBackground m_resolved_background;
};