mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-14 05:22:24 +00:00
Everywhere: Limit layout text fragments to use one font for all glyphs
The ChunkIterator now limits a chunk to using only one font (before, it was possible to have a chunk with >1 font, when `unicode-range` CSS property is used). This change allows us to reduce some complexity in the text shaping and painting code and makes us compatible with the APIs in Skia and HarfBuzz.
This commit is contained in:
parent
b95c05b611
commit
7181c3f2ea
Notes:
sideshowbarker
2024-07-17 06:35:16 +09:00
Author: https://github.com/kalenikaliaksandr
Commit: 7181c3f2ea
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/320
Reviewed-by: https://github.com/tcl3
25 changed files with 98 additions and 83 deletions
|
@ -407,7 +407,7 @@ void Painter::draw_scaled_bitmap(Gfx::FloatRect const& dst_rect, Gfx::Bitmap con
|
|||
GL::delete_texture(texture);
|
||||
}
|
||||
|
||||
void Painter::draw_glyph_run(Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color const& color)
|
||||
void Painter::draw_glyph_run(Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Gfx::Font const& font, Color const& color)
|
||||
{
|
||||
bind_target_canvas();
|
||||
|
||||
|
@ -420,7 +420,6 @@ void Painter::draw_glyph_run(Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color
|
|||
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
|
||||
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
|
||||
auto const& font = *glyph.font;
|
||||
auto code_point = glyph.code_point;
|
||||
auto point = glyph.position;
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ public:
|
|||
void draw_scaled_immutable_bitmap(Gfx::IntRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::IntRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor);
|
||||
void draw_scaled_immutable_bitmap(Gfx::FloatRect const& dst_rect, Gfx::ImmutableBitmap const&, Gfx::FloatRect const& src_rect, ScalingMode = ScalingMode::NearestNeighbor);
|
||||
|
||||
void draw_glyph_run(Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color const& color);
|
||||
void draw_glyph_run(Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Gfx::Font const&, Color const& color);
|
||||
|
||||
void set_clip_rect(Gfx::IntRect);
|
||||
void clear_clip_rect();
|
||||
|
|
|
@ -120,6 +120,8 @@ public:
|
|||
virtual Optional<Glyph> glyph(u32 code_point, GlyphSubpixelOffset) const = 0;
|
||||
virtual bool contains_glyph(u32 code_point) const = 0;
|
||||
|
||||
virtual bool append_glyph_path_to(Gfx::Path&, u32 glyph_id) const = 0;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
||||
virtual float glyph_left_bearing(u32 code_point) const = 0;
|
||||
virtual float glyph_width(u32 code_point) const = 0;
|
||||
virtual float glyph_or_emoji_width(Utf8CodePointIterator&) const = 0;
|
||||
|
|
|
@ -24,11 +24,9 @@ struct GlyphIndexWithSubpixelOffset {
|
|||
class ScaledFont final : public Gfx::Font {
|
||||
public:
|
||||
ScaledFont(NonnullRefPtr<Typeface>, float point_width, float point_height, unsigned dpi_x = DEFAULT_DPI, unsigned dpi_y = DEFAULT_DPI);
|
||||
u32 glyph_id_for_code_point(u32 code_point) const { return m_font->glyph_id_for_code_point(code_point); }
|
||||
ScaledFontMetrics metrics() const { return m_font->metrics(m_x_scale, m_y_scale); }
|
||||
ScaledGlyphMetrics glyph_metrics(u32 glyph_id) const { return m_font->glyph_metrics(glyph_id, m_x_scale, m_y_scale, m_point_width, m_point_height); }
|
||||
RefPtr<Gfx::Bitmap> rasterize_glyph(u32 glyph_id, GlyphSubpixelOffset) const;
|
||||
bool append_glyph_path_to(Gfx::Path&, u32 glyph_id) const;
|
||||
|
||||
// ^Gfx::Font
|
||||
virtual float point_size() const override;
|
||||
|
@ -44,6 +42,8 @@ public:
|
|||
virtual float glyph_width(u32 code_point) const override;
|
||||
virtual float glyph_or_emoji_width(Utf8CodePointIterator&) const override;
|
||||
virtual float glyphs_horizontal_kerning(u32 left_code_point, u32 right_code_point) const override;
|
||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override { return m_font->glyph_id_for_code_point(code_point); }
|
||||
virtual bool append_glyph_path_to(Gfx::Path&, u32 glyph_id) const override;
|
||||
virtual float preferred_line_height() const override { return metrics().height() + metrics().line_gap; }
|
||||
virtual int x_height() const override { return m_point_height; } // FIXME: Read from font
|
||||
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
||||
|
|
|
@ -167,10 +167,8 @@ void Path::text(Utf8View text, Font const& font)
|
|||
}
|
||||
|
||||
auto& scaled_font = static_cast<ScaledFont const&>(font);
|
||||
auto font_list = Gfx::FontCascadeList::create();
|
||||
font_list->add(scaled_font);
|
||||
for_each_glyph_position(
|
||||
last_point(), text, font_list, [&](DrawGlyphOrEmoji glyph_or_emoji) {
|
||||
last_point(), text, scaled_font, [&](DrawGlyphOrEmoji glyph_or_emoji) {
|
||||
if (glyph_or_emoji.has<DrawGlyph>()) {
|
||||
auto& glyph = glyph_or_emoji.get<DrawGlyph>();
|
||||
move_to(glyph.position);
|
||||
|
@ -208,13 +206,10 @@ Path Path::place_text_along(Utf8View text, Font const& font) const
|
|||
return lines[line_index].a();
|
||||
};
|
||||
|
||||
auto font_list = Gfx::FontCascadeList::create();
|
||||
font_list->add(font);
|
||||
auto& scaled_font = static_cast<Gfx::ScaledFont const&>(font);
|
||||
|
||||
Gfx::Path result_path;
|
||||
Gfx::for_each_glyph_position(
|
||||
{}, text, font_list, [&](Gfx::DrawGlyphOrEmoji glyph_or_emoji) {
|
||||
{}, text, font, [&](Gfx::DrawGlyphOrEmoji glyph_or_emoji) {
|
||||
auto* glyph = glyph_or_emoji.get_pointer<Gfx::DrawGlyph>();
|
||||
if (!glyph)
|
||||
return;
|
||||
|
|
|
@ -37,7 +37,6 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter
|
|||
return DrawGlyph {
|
||||
.position = point,
|
||||
.code_point = code_point,
|
||||
.font = font,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -46,7 +45,6 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter
|
|||
return DrawEmoji {
|
||||
.position = point,
|
||||
.emoji = emoji,
|
||||
.font = font,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -55,7 +53,6 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter
|
|||
return DrawGlyph {
|
||||
.position = point,
|
||||
.code_point = code_point,
|
||||
.font = font,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -64,7 +61,6 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter
|
|||
return DrawGlyph {
|
||||
.position = point,
|
||||
.code_point = 0xFFFD,
|
||||
.font = font,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ enum class IncludeLeftBearing {
|
|||
struct DrawGlyph {
|
||||
FloatPoint position;
|
||||
u32 code_point;
|
||||
NonnullRefPtr<Font const> font;
|
||||
|
||||
void translate_by(FloatPoint const& delta)
|
||||
{
|
||||
|
@ -44,7 +43,6 @@ struct DrawGlyph {
|
|||
struct DrawEmoji {
|
||||
FloatPoint position;
|
||||
Gfx::Bitmap const* emoji;
|
||||
NonnullRefPtr<Font const> font;
|
||||
|
||||
void translate_by(FloatPoint const& delta)
|
||||
{
|
||||
|
@ -56,28 +54,30 @@ using DrawGlyphOrEmoji = Variant<DrawGlyph, DrawEmoji>;
|
|||
|
||||
class GlyphRun : public RefCounted<GlyphRun> {
|
||||
public:
|
||||
GlyphRun() = default;
|
||||
GlyphRun(Vector<Gfx::DrawGlyphOrEmoji>&& glyphs)
|
||||
GlyphRun(Vector<Gfx::DrawGlyphOrEmoji>&& glyphs, NonnullRefPtr<Font> font)
|
||||
: m_glyphs(move(glyphs))
|
||||
, m_font(move(font))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] Font const& font() const { return m_font; }
|
||||
[[nodiscard]] Vector<Gfx::DrawGlyphOrEmoji> const& glyphs() const { return m_glyphs; }
|
||||
[[nodiscard]] Vector<Gfx::DrawGlyphOrEmoji>& glyphs() { return m_glyphs; }
|
||||
[[nodiscard]] bool is_empty() const { return m_glyphs.is_empty(); }
|
||||
|
||||
void append(Gfx::DrawGlyphOrEmoji glyph) { m_glyphs.append(glyph); }
|
||||
|
||||
private:
|
||||
Vector<Gfx::DrawGlyphOrEmoji> m_glyphs;
|
||||
NonnullRefPtr<Font> m_font;
|
||||
};
|
||||
|
||||
Variant<DrawGlyph, DrawEmoji> prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIterator& it, Font const& font);
|
||||
|
||||
template<typename Callback>
|
||||
void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, FontCascadeList const& font_list, Callback callback, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No, Optional<float&> width = {})
|
||||
void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Gfx::Font const& font, Callback callback, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No, Optional<float&> width = {})
|
||||
{
|
||||
auto const& space_glyph_font = font_list.font_for_code_point(' ');
|
||||
float space_width = space_glyph_font.glyph_width(' ');
|
||||
auto space_width = font.glyph_width(' ');
|
||||
|
||||
u32 last_code_point = 0;
|
||||
|
||||
|
@ -85,9 +85,8 @@ void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, FontCas
|
|||
for (auto code_point_iterator = string.begin(); code_point_iterator != string.end(); ++code_point_iterator) {
|
||||
auto it = code_point_iterator; // The callback function will advance the iterator, so create a copy for this lookup.
|
||||
auto code_point = *code_point_iterator;
|
||||
RefPtr<Gfx::Font const> font = font_list.font_for_code_point(code_point);
|
||||
|
||||
point.set_y(baseline_start.y() - font->pixel_metrics().ascent);
|
||||
point.set_y(baseline_start.y() - font.pixel_metrics().ascent);
|
||||
|
||||
if (should_paint_as_space(code_point)) {
|
||||
point.translate_by(space_width, 0);
|
||||
|
@ -95,15 +94,15 @@ void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, FontCas
|
|||
continue;
|
||||
}
|
||||
|
||||
auto kerning = font->glyphs_horizontal_kerning(last_code_point, code_point);
|
||||
auto kerning = font.glyphs_horizontal_kerning(last_code_point, code_point);
|
||||
if (kerning != 0.0f)
|
||||
point.translate_by(kerning, 0);
|
||||
|
||||
auto glyph_width = font->glyph_or_emoji_width(it);
|
||||
auto glyph_or_emoji = prepare_draw_glyph_or_emoji(point, code_point_iterator, *font);
|
||||
auto glyph_width = font.glyph_or_emoji_width(it);
|
||||
auto glyph_or_emoji = prepare_draw_glyph_or_emoji(point, code_point_iterator, font);
|
||||
if (include_left_bearing == IncludeLeftBearing::Yes) {
|
||||
if (glyph_or_emoji.has<DrawGlyph>())
|
||||
glyph_or_emoji.get<DrawGlyph>().position += FloatPoint(font->glyph_left_bearing(code_point), 0);
|
||||
glyph_or_emoji.get<DrawGlyph>().position += FloatPoint(font.glyph_left_bearing(code_point), 0);
|
||||
}
|
||||
|
||||
callback(glyph_or_emoji);
|
||||
|
|
|
@ -198,7 +198,7 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead(
|
|||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run;
|
||||
float glyph_run_width = 0;
|
||||
Gfx::for_each_glyph_position(
|
||||
{ 0, 0 }, chunk.view, text_node.computed_values().font_list(), [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) {
|
||||
{ 0, 0 }, chunk.view, chunk.font, [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) {
|
||||
glyph_run.append(glyph_or_emoji);
|
||||
return IterationDecision::Continue;
|
||||
},
|
||||
|
@ -212,7 +212,7 @@ Optional<InlineLevelIterator::Item> InlineLevelIterator::next_without_lookahead(
|
|||
Item item {
|
||||
.type = Item::Type::Text,
|
||||
.node = &text_node,
|
||||
.glyph_run = move(glyph_run),
|
||||
.glyph_run = adopt_ref(*new Gfx::GlyphRun(move(glyph_run), chunk.font)),
|
||||
.offset_in_node = chunk.start,
|
||||
.length_in_node = chunk.length,
|
||||
.width = chunk_width,
|
||||
|
@ -321,7 +321,7 @@ void InlineLevelIterator::enter_text_node(Layout::TextNode const& text_node)
|
|||
.do_respect_linebreaks = do_respect_linebreaks,
|
||||
.is_first_chunk = true,
|
||||
.is_last_chunk = false,
|
||||
.chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), do_wrap_lines, do_respect_linebreaks },
|
||||
.chunk_iterator = TextNode::ChunkIterator { text_node.text_for_rendering(), do_wrap_lines, do_respect_linebreaks, text_node.computed_values().font_list() },
|
||||
};
|
||||
m_text_node_context->next_chunk = m_text_node_context->chunk_iterator.next();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
};
|
||||
Type type {};
|
||||
JS::GCPtr<Layout::Node const> node {};
|
||||
Vector<Gfx::DrawGlyphOrEmoji> glyph_run {};
|
||||
RefPtr<Gfx::GlyphRun> glyph_run {};
|
||||
size_t offset_in_node { 0 };
|
||||
size_t length_in_node { 0 };
|
||||
CSSPixels width { 0.0f };
|
||||
|
|
|
@ -15,16 +15,16 @@
|
|||
|
||||
namespace Web::Layout {
|
||||
|
||||
void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, Vector<Gfx::DrawGlyphOrEmoji> glyph_run)
|
||||
void LineBox::add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, RefPtr<Gfx::GlyphRun> glyph_run)
|
||||
{
|
||||
bool text_align_is_justify = layout_node.computed_values().text_align() == CSS::TextAlign::Justify;
|
||||
if (!text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node) {
|
||||
if (glyph_run && !text_align_is_justify && !m_fragments.is_empty() && &m_fragments.last().layout_node() == &layout_node && &m_fragments.last().m_glyph_run->font() == &glyph_run->font()) {
|
||||
auto const fragment_width = m_fragments.last().width();
|
||||
// The fragment we're adding is from the last Layout::Node on the line.
|
||||
// Expand the last fragment instead of adding a new one with the same Layout::Node.
|
||||
m_fragments.last().m_length = (start - m_fragments.last().m_start) + length;
|
||||
m_fragments.last().set_width(m_fragments.last().width() + content_width);
|
||||
for (auto& glyph : glyph_run) {
|
||||
for (auto& glyph : glyph_run->glyphs()) {
|
||||
glyph.visit([&](auto& glyph) { glyph.position.translate_by(fragment_width.to_float(), 0); });
|
||||
m_fragments.last().m_glyph_run->append(glyph);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public:
|
|||
CSSPixels bottom() const { return m_bottom; }
|
||||
CSSPixels baseline() const { return m_baseline; }
|
||||
|
||||
void add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, Vector<Gfx::DrawGlyphOrEmoji> = {});
|
||||
void add_fragment(Node const& layout_node, int start, int length, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, CSSPixels border_box_top, CSSPixels border_box_bottom, RefPtr<Gfx::GlyphRun> glyph_run = {});
|
||||
|
||||
Vector<LineBoxFragment> const& fragments() const { return m_fragments; }
|
||||
Vector<LineBoxFragment>& fragments() { return m_fragments; }
|
||||
|
|
|
@ -19,14 +19,14 @@ class LineBoxFragment {
|
|||
friend class LineBox;
|
||||
|
||||
public:
|
||||
LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, Vector<Gfx::DrawGlyphOrEmoji> glyphs)
|
||||
LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, RefPtr<Gfx::GlyphRun> glyph_run)
|
||||
: m_layout_node(layout_node)
|
||||
, m_start(start)
|
||||
, m_length(length)
|
||||
, m_offset(offset)
|
||||
, m_size(size)
|
||||
, m_border_box_top(border_box_top)
|
||||
, m_glyph_run(adopt_ref(*new Gfx::GlyphRun(move(glyphs))))
|
||||
, m_glyph_run(move(glyph_run))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public:
|
|||
|
||||
bool is_atomic_inline() const;
|
||||
|
||||
Gfx::GlyphRun const& glyph_run() const { return *m_glyph_run; }
|
||||
RefPtr<Gfx::GlyphRun> glyph_run() const { return m_glyph_run; }
|
||||
|
||||
private:
|
||||
JS::NonnullGCPtr<Node const> m_layout_node;
|
||||
|
@ -69,7 +69,7 @@ private:
|
|||
CSSPixelSize m_size;
|
||||
CSSPixels m_border_box_top { 0 };
|
||||
CSSPixels m_baseline { 0 };
|
||||
NonnullRefPtr<Gfx::GlyphRun> m_glyph_run;
|
||||
RefPtr<Gfx::GlyphRun> m_glyph_run;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ void LineBuilder::append_box(Box const& box, CSSPixels leading_size, CSSPixels t
|
|||
};
|
||||
}
|
||||
|
||||
void LineBuilder::append_text_chunk(TextNode const& text_node, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, Vector<Gfx::DrawGlyphOrEmoji> glyph_run)
|
||||
void LineBuilder::append_text_chunk(TextNode const& text_node, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, RefPtr<Gfx::GlyphRun> glyph_run)
|
||||
{
|
||||
ensure_last_line_box().add_fragment(text_node, offset_in_node, length_in_node, leading_size, trailing_size, leading_margin, trailing_margin, content_width, content_height, 0, 0, move(glyph_run));
|
||||
m_max_height_on_current_line = max(m_max_height_on_current_line, content_height);
|
||||
|
|
|
@ -25,7 +25,7 @@ public:
|
|||
|
||||
void break_line(ForcedBreak, Optional<CSSPixels> next_item_width = {});
|
||||
void append_box(Box const&, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin);
|
||||
void append_text_chunk(TextNode const&, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, Vector<Gfx::DrawGlyphOrEmoji>);
|
||||
void append_text_chunk(TextNode const&, size_t offset_in_node, size_t length_in_node, CSSPixels leading_size, CSSPixels trailing_size, CSSPixels leading_margin, CSSPixels trailing_margin, CSSPixels content_width, CSSPixels content_height, RefPtr<Gfx::GlyphRun>);
|
||||
|
||||
// Returns whether a line break occurred.
|
||||
bool break_if_needed(CSSPixels next_item_width)
|
||||
|
|
|
@ -391,11 +391,12 @@ void TextNode::compute_text_for_rendering()
|
|||
m_text_for_rendering = MUST(builder.to_string());
|
||||
}
|
||||
|
||||
TextNode::ChunkIterator::ChunkIterator(StringView text, bool wrap_lines, bool respect_linebreaks)
|
||||
TextNode::ChunkIterator::ChunkIterator(StringView text, bool wrap_lines, bool respect_linebreaks, Gfx::FontCascadeList const& font_cascade_list)
|
||||
: m_wrap_lines(wrap_lines)
|
||||
, m_respect_linebreaks(respect_linebreaks)
|
||||
, m_utf8_view(text)
|
||||
, m_iterator(m_utf8_view.begin())
|
||||
, m_font_cascade_list(font_cascade_list)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -406,16 +407,22 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::next()
|
|||
|
||||
auto start_of_chunk = m_iterator;
|
||||
|
||||
Gfx::Font const& font = m_font_cascade_list.font_for_code_point(*m_iterator);
|
||||
while (m_iterator != m_utf8_view.end()) {
|
||||
if (&font != &m_font_cascade_list.font_for_code_point(*m_iterator)) {
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false, font); result.has_value())
|
||||
return result.release_value();
|
||||
}
|
||||
|
||||
if (m_respect_linebreaks && *m_iterator == '\n') {
|
||||
// Newline encountered, and we're supposed to preserve them.
|
||||
// If we have accumulated some code points in the current chunk, commit them now and continue with the newline next time.
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false); result.has_value())
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false, font); result.has_value())
|
||||
return result.release_value();
|
||||
|
||||
// Otherwise, commit the newline!
|
||||
++m_iterator;
|
||||
auto result = try_commit_chunk(start_of_chunk, m_iterator, true);
|
||||
auto result = try_commit_chunk(start_of_chunk, m_iterator, true, font);
|
||||
VERIFY(result.has_value());
|
||||
return result.release_value();
|
||||
}
|
||||
|
@ -424,12 +431,12 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::next()
|
|||
if (is_ascii_space(*m_iterator)) {
|
||||
// Whitespace encountered, and we're allowed to break on whitespace.
|
||||
// If we have accumulated some code points in the current chunk, commit them now and continue with the whitespace next time.
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false); result.has_value())
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false, font); result.has_value())
|
||||
return result.release_value();
|
||||
|
||||
// Otherwise, commit the whitespace!
|
||||
++m_iterator;
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false); result.has_value())
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_iterator, false, font); result.has_value())
|
||||
return result.release_value();
|
||||
continue;
|
||||
}
|
||||
|
@ -440,14 +447,14 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::next()
|
|||
|
||||
if (start_of_chunk != m_utf8_view.end()) {
|
||||
// Try to output whatever's left at the end of the text node.
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_utf8_view.end(), false); result.has_value())
|
||||
if (auto result = try_commit_chunk(start_of_chunk, m_utf8_view.end(), false, font); result.has_value())
|
||||
return result.release_value();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
Optional<TextNode::Chunk> TextNode::ChunkIterator::try_commit_chunk(Utf8View::Iterator const& start, Utf8View::Iterator const& end, bool has_breaking_newline) const
|
||||
Optional<TextNode::Chunk> TextNode::ChunkIterator::try_commit_chunk(Utf8View::Iterator const& start, Utf8View::Iterator const& end, bool has_breaking_newline, Gfx::Font const& font) const
|
||||
{
|
||||
auto byte_offset = m_utf8_view.byte_offset_of(start);
|
||||
auto byte_length = m_utf8_view.byte_offset_of(end) - byte_offset;
|
||||
|
@ -456,6 +463,7 @@ Optional<TextNode::Chunk> TextNode::ChunkIterator::try_commit_chunk(Utf8View::It
|
|||
auto chunk_view = m_utf8_view.substring_view(byte_offset, byte_length);
|
||||
return Chunk {
|
||||
.view = chunk_view,
|
||||
.font = font,
|
||||
.start = byte_offset,
|
||||
.length = byte_length,
|
||||
.has_breaking_newline = has_breaking_newline,
|
||||
|
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
struct Chunk {
|
||||
Utf8View view;
|
||||
NonnullRefPtr<Gfx::Font> font;
|
||||
size_t start { 0 };
|
||||
size_t length { 0 };
|
||||
bool has_breaking_newline { false };
|
||||
|
@ -36,16 +37,17 @@ public:
|
|||
|
||||
class ChunkIterator {
|
||||
public:
|
||||
ChunkIterator(StringView text, bool wrap_lines, bool respect_linebreaks);
|
||||
ChunkIterator(StringView text, bool wrap_lines, bool respect_linebreaks, Gfx::FontCascadeList const&);
|
||||
Optional<Chunk> next();
|
||||
|
||||
private:
|
||||
Optional<Chunk> try_commit_chunk(Utf8View::Iterator const& start, Utf8View::Iterator const& end, bool has_breaking_newline) const;
|
||||
Optional<Chunk> try_commit_chunk(Utf8View::Iterator const& start, Utf8View::Iterator const& end, bool has_breaking_newline, Gfx::Font const&) const;
|
||||
|
||||
bool const m_wrap_lines;
|
||||
bool const m_respect_linebreaks;
|
||||
Utf8View m_utf8_view;
|
||||
Utf8View::Iterator m_iterator;
|
||||
Gfx::FontCascadeList const& m_font_cascade_list;
|
||||
};
|
||||
|
||||
void invalidate_text_for_rendering();
|
||||
|
|
|
@ -62,11 +62,15 @@ static Vector<Gfx::Path> compute_text_clip_paths(PaintContext& context, Paintabl
|
|||
{
|
||||
Vector<Gfx::Path> text_clip_paths;
|
||||
auto add_text_clip_path = [&](PaintableFragment const& fragment) {
|
||||
auto glyph_run = fragment.glyph_run();
|
||||
if (!glyph_run || glyph_run->glyphs().is_empty())
|
||||
return;
|
||||
// Scale to the device pixels.
|
||||
Gfx::Path glyph_run_path;
|
||||
for (auto glyph : fragment.glyph_run().glyphs()) {
|
||||
auto const& font = fragment.glyph_run()->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(context.device_pixels_per_css_pixel()));
|
||||
for (auto glyph : fragment.glyph_run()->glyphs()) {
|
||||
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());
|
||||
});
|
||||
|
||||
|
@ -75,13 +79,12 @@ static Vector<Gfx::Path> compute_text_clip_paths(PaintContext& context, Paintabl
|
|||
|
||||
// Get the path for the glyph.
|
||||
Gfx::Path glyph_path;
|
||||
auto const& scaled_font = static_cast<Gfx::ScaledFont const&>(*draw_glyph.font);
|
||||
auto glyph_id = scaled_font.glyph_id_for_code_point(draw_glyph.code_point);
|
||||
scaled_font.append_glyph_path_to(glyph_path, glyph_id);
|
||||
auto glyph_id = scaled_font->glyph_id_for_code_point(draw_glyph.code_point);
|
||||
scaled_font->append_glyph_path_to(glyph_path, glyph_id);
|
||||
|
||||
// Transform the path to the fragment's position.
|
||||
// FIXME: Record glyphs and use Painter::draw_glyphs() instead to avoid this duplicated code.
|
||||
auto top_left = draw_glyph.position + Gfx::FloatPoint(scaled_font.glyph_left_bearing(draw_glyph.code_point), 0);
|
||||
auto top_left = draw_glyph.position + Gfx::FloatPoint(scaled_font->glyph_left_bearing(draw_glyph.code_point), 0);
|
||||
auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(top_left);
|
||||
auto transform = Gfx::AffineTransform {}.translate(glyph_position.blit_position.to_type<float>());
|
||||
glyph_run_path.append_path(glyph_path.copy_transformed(transform));
|
||||
|
|
|
@ -86,11 +86,12 @@ void DisplayList::execute(DisplayListPlayer& executor)
|
|||
auto& command = command_with_scroll_id.command;
|
||||
if (command.has<DrawGlyphRun>()) {
|
||||
auto scale = command.get<DrawGlyphRun>().scale;
|
||||
auto const& font = command.get<DrawGlyphRun>().glyph_run->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(scale));
|
||||
for (auto const& glyph_or_emoji : command.get<DrawGlyphRun>().glyph_run->glyphs()) {
|
||||
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
|
||||
auto const& glyph = glyph_or_emoji.get<Gfx::DrawGlyph>();
|
||||
auto font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(scale));
|
||||
unique_glyphs.ensure(font, [] { return HashTable<u32> {}; }).set(glyph.code_point);
|
||||
unique_glyphs.ensure(scaled_font, [] { return HashTable<u32> {}; }).set(glyph.code_point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,18 +30,19 @@ CommandResult DisplayListPlayerCPU::draw_glyph_run(DrawGlyphRun const& command)
|
|||
{
|
||||
auto& painter = this->painter();
|
||||
auto const& glyphs = command.glyph_run->glyphs();
|
||||
for (auto& glyph_or_emoji : glyphs) {
|
||||
auto const& font = command.glyph_run->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(command.scale));
|
||||
for (auto const& glyph_or_emoji : glyphs) {
|
||||
auto transformed_glyph = glyph_or_emoji;
|
||||
transformed_glyph.visit([&](auto& glyph) {
|
||||
glyph.position = glyph.position.scaled(command.scale).translated(command.translation);
|
||||
glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(command.scale));
|
||||
});
|
||||
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
|
||||
auto& glyph = transformed_glyph.get<Gfx::DrawGlyph>();
|
||||
painter.draw_glyph(glyph.position, glyph.code_point, *glyph.font, command.color);
|
||||
painter.draw_glyph(glyph.position, glyph.code_point, *scaled_font, command.color);
|
||||
} else {
|
||||
auto& emoji = transformed_glyph.get<Gfx::DrawEmoji>();
|
||||
painter.draw_emoji(emoji.position.to_type<int>(), *emoji.emoji, *emoji.font);
|
||||
painter.draw_emoji(emoji.position.to_type<int>(), *emoji.emoji, *scaled_font);
|
||||
}
|
||||
}
|
||||
return CommandResult::Continue;
|
||||
|
@ -273,18 +274,19 @@ CommandResult DisplayListPlayerCPU::paint_text_shadow(PaintTextShadow const& com
|
|||
Gfx::IntPoint const baseline_start(command.text_rect.x(), command.text_rect.y());
|
||||
shadow_painter.translate(baseline_start);
|
||||
auto const& glyphs = command.glyph_run->glyphs();
|
||||
auto const& font = command.glyph_run->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(command.glyph_run_scale));
|
||||
for (auto const& glyph_or_emoji : glyphs) {
|
||||
auto transformed_glyph = glyph_or_emoji;
|
||||
transformed_glyph.visit([&](auto& glyph) {
|
||||
glyph.position = glyph.position.scaled(command.glyph_run_scale);
|
||||
glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(command.glyph_run_scale));
|
||||
});
|
||||
if (glyph_or_emoji.has<Gfx::DrawGlyph>()) {
|
||||
auto& glyph = transformed_glyph.get<Gfx::DrawGlyph>();
|
||||
shadow_painter.draw_glyph(glyph.position, glyph.code_point, *glyph.font, command.color);
|
||||
shadow_painter.draw_glyph(glyph.position, glyph.code_point, *scaled_font, command.color);
|
||||
} else {
|
||||
auto& emoji = transformed_glyph.get<Gfx::DrawEmoji>();
|
||||
shadow_painter.draw_emoji(emoji.position.to_type<int>(), *emoji.emoji, *emoji.font);
|
||||
shadow_painter.draw_emoji(emoji.position.to_type<int>(), *emoji.emoji, *scaled_font);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,15 +36,16 @@ CommandResult DisplayListPlayerGPU::draw_glyph_run(DrawGlyphRun const& command)
|
|||
Vector<Gfx::DrawGlyphOrEmoji> transformed_glyph_run;
|
||||
auto const& glyphs = command.glyph_run->glyphs();
|
||||
transformed_glyph_run.ensure_capacity(glyphs.size());
|
||||
auto const& font = command.glyph_run->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(command.scale));
|
||||
for (auto& glyph : glyphs) {
|
||||
auto transformed_glyph = glyph;
|
||||
transformed_glyph.visit([&](auto& glyph) {
|
||||
glyph.position = glyph.position.scaled(command.scale).translated(command.translation);
|
||||
glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(command.scale));
|
||||
});
|
||||
transformed_glyph_run.append(transformed_glyph);
|
||||
}
|
||||
painter().draw_glyph_run(transformed_glyph_run, command.color);
|
||||
painter().draw_glyph_run(transformed_glyph_run, scaled_font, command.color);
|
||||
return CommandResult::Continue;
|
||||
}
|
||||
|
||||
|
@ -192,7 +193,7 @@ CommandResult DisplayListPlayerGPU::paint_text_shadow(PaintTextShadow const& com
|
|||
Gfx::FloatRect const shadow_location { command.draw_location, command.shadow_bounding_rect.size() };
|
||||
Gfx::IntPoint const baseline_start(command.text_rect.x(), command.text_rect.y());
|
||||
text_shadow_painter->translate(baseline_start.to_type<float>());
|
||||
text_shadow_painter->draw_glyph_run(command.glyph_run->glyphs(), command.color);
|
||||
text_shadow_painter->draw_glyph_run(command.glyph_run->glyphs(), command.glyph_run->font(), command.color);
|
||||
if (command.blur_radius == 0) {
|
||||
painter().blit_canvas(shadow_location, *text_shadow_canvas);
|
||||
return CommandResult::Continue;
|
||||
|
|
|
@ -289,19 +289,20 @@ CommandResult DisplayListPlayerSkia::draw_glyph_run(DrawGlyphRun const& command)
|
|||
SkPaint paint;
|
||||
paint.setColorFilter(SkColorFilters::Blend(to_skia_color(command.color), SkBlendMode::kSrcIn));
|
||||
auto const& glyphs = command.glyph_run->glyphs();
|
||||
auto const& font = command.glyph_run->font();
|
||||
auto scaled_font = font.with_size(font.point_size() * static_cast<float>(command.scale));
|
||||
for (auto const& glyph_or_emoji : glyphs) {
|
||||
auto transformed_glyph = glyph_or_emoji;
|
||||
transformed_glyph.visit([&](auto& glyph) {
|
||||
glyph.position = glyph.position.scaled(command.scale).translated(command.translation);
|
||||
glyph.font = glyph.font->with_size(glyph.font->point_size() * static_cast<float>(command.scale));
|
||||
});
|
||||
if (transformed_glyph.has<Gfx::DrawGlyph>()) {
|
||||
auto& glyph = transformed_glyph.get<Gfx::DrawGlyph>();
|
||||
auto const& point = glyph.position;
|
||||
auto const& code_point = glyph.code_point;
|
||||
auto top_left = point + Gfx::FloatPoint(glyph.font->glyph_left_bearing(code_point), 0);
|
||||
auto top_left = point + Gfx::FloatPoint(scaled_font->glyph_left_bearing(code_point), 0);
|
||||
auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(top_left);
|
||||
auto maybe_font_glyph = glyph.font->glyph(code_point, glyph_position.subpixel_offset);
|
||||
auto maybe_font_glyph = scaled_font->glyph(code_point, glyph_position.subpixel_offset);
|
||||
if (!maybe_font_glyph.has_value())
|
||||
continue;
|
||||
if (maybe_font_glyph->is_color_bitmap()) {
|
||||
|
|
|
@ -227,12 +227,10 @@ void DisplayListRecorder::draw_text(Gfx::IntRect const& rect, String raw_text, G
|
|||
if (rect.is_empty())
|
||||
return;
|
||||
|
||||
auto glyph_run = adopt_ref(*new Gfx::GlyphRun);
|
||||
auto font_cascade_list = Gfx::FontCascadeList::create();
|
||||
font_cascade_list->add(font);
|
||||
auto glyph_run = adopt_ref(*new Gfx::GlyphRun({}, font));
|
||||
float glyph_run_width = 0;
|
||||
Gfx::for_each_glyph_position(
|
||||
{ 0, 0 }, raw_text.code_points(), font_cascade_list, [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) {
|
||||
{ 0, 0 }, raw_text.code_points(), font, [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) {
|
||||
glyph_run->append(glyph_or_emoji);
|
||||
return IterationDecision::Continue;
|
||||
},
|
||||
|
|
|
@ -663,16 +663,20 @@ void paint_text_fragment(PaintContext& context, TextPaintable const& paintable,
|
|||
|
||||
auto text = paintable.text_for_rendering();
|
||||
|
||||
auto glyph_run = fragment.glyph_run();
|
||||
if (!glyph_run)
|
||||
return;
|
||||
|
||||
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>(), fragment.glyph_run(), paintable.computed_values().color(), fragment_absolute_device_rect.to_type<int>(), scale);
|
||||
painter.draw_text_run(baseline_start.to_type<int>(), *glyph_run, paintable.computed_values().color(), fragment_absolute_device_rect.to_type<int>(), scale);
|
||||
|
||||
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>(), fragment.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);
|
||||
}
|
||||
|
||||
paint_text_decoration(context, paintable, fragment);
|
||||
|
|
|
@ -38,7 +38,7 @@ public:
|
|||
|
||||
CSSPixelRect const absolute_rect() const;
|
||||
|
||||
Gfx::GlyphRun const& glyph_run() const { return *m_glyph_run; }
|
||||
RefPtr<Gfx::GlyphRun> glyph_run() const { return m_glyph_run; }
|
||||
|
||||
CSSPixelRect selection_rect(Gfx::Font const&) const;
|
||||
|
||||
|
@ -57,7 +57,7 @@ private:
|
|||
int m_start;
|
||||
int m_length;
|
||||
Painting::BorderRadiiData m_border_radii_data;
|
||||
NonnullRefPtr<Gfx::GlyphRun> m_glyph_run;
|
||||
RefPtr<Gfx::GlyphRun> m_glyph_run;
|
||||
Vector<ShadowData> m_shadows;
|
||||
};
|
||||
|
||||
|
|
|
@ -579,7 +579,11 @@ void paint_box_shadow(PaintContext& context,
|
|||
|
||||
void paint_text_shadow(PaintContext& context, PaintableFragment const& fragment, Vector<ShadowData> const& shadow_layers)
|
||||
{
|
||||
if (shadow_layers.is_empty() || fragment.glyph_run().is_empty())
|
||||
if (shadow_layers.is_empty())
|
||||
return;
|
||||
|
||||
auto glyph_run = fragment.glyph_run();
|
||||
if (!glyph_run || glyph_run->glyphs().is_empty())
|
||||
return;
|
||||
|
||||
auto fragment_width = context.enclosing_device_pixels(fragment.width()).value();
|
||||
|
@ -610,7 +614,7 @@ void paint_text_shadow(PaintContext& context, PaintableFragment const& fragment,
|
|||
draw_rect.y() + offset_y - margin
|
||||
};
|
||||
|
||||
context.display_list_recorder().paint_text_shadow(blur_radius, bounding_rect, text_rect.translated(0, fragment_baseline), fragment.glyph_run(), context.device_pixels_per_css_pixel(), layer.color, draw_location);
|
||||
context.display_list_recorder().paint_text_shadow(blur_radius, bounding_rect, text_rect.translated(0, fragment_baseline), *glyph_run, context.device_pixels_per_css_pixel(), layer.color, draw_location);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue