diff --git a/Tests/LibWeb/Ref/reference/images/object-fit-position.png b/Tests/LibWeb/Ref/reference/images/object-fit-position.png index 72dd070741..7e28d43164 100644 Binary files a/Tests/LibWeb/Ref/reference/images/object-fit-position.png and b/Tests/LibWeb/Ref/reference/images/object-fit-position.png differ diff --git a/Tests/LibWeb/Ref/reference/images/text-shadow-ref.png b/Tests/LibWeb/Ref/reference/images/text-shadow-ref.png index 4f494f7a53..9edd4e3757 100644 Binary files a/Tests/LibWeb/Ref/reference/images/text-shadow-ref.png and b/Tests/LibWeb/Ref/reference/images/text-shadow-ref.png differ diff --git a/Userland/Libraries/LibGfx/Painter.cpp b/Userland/Libraries/LibGfx/Painter.cpp index 5e4fbdbed4..fde4c74f39 100644 --- a/Userland/Libraries/LibGfx/Painter.cpp +++ b/Userland/Libraries/LibGfx/Painter.cpp @@ -1422,7 +1422,7 @@ void Painter::draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIterator& it, F draw_glyph(glyph.position, glyph.code_point, *glyph.font, color); } else { auto& emoji = draw_glyph_or_emoji.get(); - draw_emoji(emoji.position, *emoji.emoji, *emoji.font); + draw_emoji(emoji.position.to_type(), *emoji.emoji, *emoji.font); } } @@ -2445,7 +2445,7 @@ void Painter::draw_text_run(FloatPoint baseline_start, Utf8View const& string, F draw_glyph(glyph.position, glyph.code_point, *glyph.font, color); } else { auto& emoji = glyph_or_emoji.get(); - draw_emoji(emoji.position, *emoji.emoji, *emoji.font); + draw_emoji(emoji.position.to_type(), *emoji.emoji, *emoji.font); } }); } diff --git a/Userland/Libraries/LibGfx/TextLayout.cpp b/Userland/Libraries/LibGfx/TextLayout.cpp index 4cb50a544b..9ba2c9b601 100644 --- a/Userland/Libraries/LibGfx/TextLayout.cpp +++ b/Userland/Libraries/LibGfx/TextLayout.cpp @@ -231,7 +231,7 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter // If we didn't find a text glyph, or have an emoji variation selector or regional indicator, try to draw an emoji glyph. if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it)) { return DrawEmoji { - .position = point.to_type(), + .position = point, .emoji = emoji, .font = &font, }; @@ -255,15 +255,4 @@ DrawGlyphOrEmoji prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIter }; } -Vector get_glyph_run(FloatPoint baseline_start, Utf8View const& string, Font const& font, IncludeLeftBearing include_left_bearing) -{ - Vector glyphs_or_emojis; - for_each_glyph_position( - baseline_start, string, font, [&](auto glyph_or_emoji) { - glyphs_or_emojis.append(glyph_or_emoji); - }, - include_left_bearing); - return glyphs_or_emojis; -} - } diff --git a/Userland/Libraries/LibGfx/TextLayout.h b/Userland/Libraries/LibGfx/TextLayout.h index 68dbc88d64..a694cad1a5 100644 --- a/Userland/Libraries/LibGfx/TextLayout.h +++ b/Userland/Libraries/LibGfx/TextLayout.h @@ -82,7 +82,7 @@ struct DrawGlyph { }; struct DrawEmoji { - IntPoint position; + FloatPoint position; Gfx::Bitmap const* emoji; Font const* font; }; @@ -92,7 +92,7 @@ using DrawGlyphOrEmoji = Variant; Variant prepare_draw_glyph_or_emoji(FloatPoint point, Utf8CodePointIterator& it, Font const& font); template -void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Font const& font, Callback callback, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No) +void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Font const& font, Callback callback, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No, Optional width = {}) { float space_width = font.glyph_width(' ') + font.glyph_spacing(); @@ -127,8 +127,9 @@ void for_each_glyph_position(FloatPoint baseline_start, Utf8View string, Font co point.translate_by(glyph_width, 0); last_code_point = code_point; } + + if (width.has_value()) + *width = point.x() - font.glyph_spacing(); } -Vector get_glyph_run(FloatPoint baseline_start, Utf8View const& string, Font const& font, IncludeLeftBearing include_left_bearing = IncludeLeftBearing::No); - } diff --git a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp index ca1a916541..98c2584a89 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineFormattingContext.cpp @@ -326,7 +326,8 @@ void InlineFormattingContext::generate_line_boxes(LayoutMode layout_mode) item.margin_start, item.margin_end, item.width, - text_node.line_height()); + text_node.line_height(), + item.glyph_run); break; } } diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp index 7d84974d3a..e33902e845 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.cpp @@ -187,12 +187,19 @@ Optional InlineLevelIterator::next_without_lookahead( }; } - CSSPixels chunk_width; + Vector glyph_run; + float glyph_run_width = 0; + Gfx::for_each_glyph_position( + { 0, 0 }, chunk.view, text_node.font(), [&](Gfx::DrawGlyphOrEmoji const& glyph_or_emoji) { + glyph_run.append(glyph_or_emoji); + return IterationDecision::Continue; + }, + Gfx::IncludeLeftBearing::No, glyph_run_width); - if (m_text_node_context->is_last_chunk) - chunk_width = CSSPixels::nearest_value_for(text_node.font().width(chunk.view)); - else - chunk_width = CSSPixels::nearest_value_for(text_node.font().width(chunk.view) + text_node.font().glyph_spacing()); + if (!m_text_node_context->is_last_chunk) + glyph_run_width += text_node.font().glyph_spacing(); + + CSSPixels chunk_width = CSSPixels::nearest_value_for(glyph_run_width); // NOTE: We never consider `content: ""` to be collapsible whitespace. bool is_generated_empty_string = text_node.is_generated() && chunk.length == 0; @@ -200,6 +207,7 @@ Optional InlineLevelIterator::next_without_lookahead( Item item { .type = Item::Type::Text, .node = &text_node, + .glyph_run = move(glyph_run), .offset_in_node = chunk.start, .length_in_node = chunk.length, .width = chunk_width, diff --git a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h index 0f194923c0..ce369e2646 100644 --- a/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h +++ b/Userland/Libraries/LibWeb/Layout/InlineLevelIterator.h @@ -32,6 +32,7 @@ public: }; Type type {}; JS::GCPtr node {}; + Vector glyph_run {}; size_t offset_in_node { 0 }; size_t length_in_node { 0 }; CSSPixels width { 0.0f }; diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.cpp b/Userland/Libraries/LibWeb/Layout/LineBox.cpp index 5ba5aa99a8..e5d8452ec8 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBox.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBox.cpp @@ -15,18 +15,24 @@ 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) +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, Span 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) { + 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) { + glyph.visit([&](auto& glyph) { glyph.position.translate_by(fragment_width.to_float(), 0); }); + m_fragments.last().m_glyph_run.append(glyph); + } } else { + Vector glyph_run_copy { glyph_run }; CSSPixels x_offset = leading_margin + leading_size + m_width; CSSPixels y_offset = 0; - m_fragments.append(LineBoxFragment { layout_node, start, length, CSSPixelPoint(x_offset, y_offset), CSSPixelSize(content_width, content_height), border_box_top, border_box_bottom }); + m_fragments.append(LineBoxFragment { layout_node, start, length, CSSPixelPoint(x_offset, y_offset), CSSPixelSize(content_width, content_height), border_box_top, border_box_bottom, move(glyph_run_copy) }); } m_width += leading_margin + leading_size + content_width + trailing_size + trailing_margin; m_height = max(m_height, content_height + border_box_top + border_box_bottom); diff --git a/Userland/Libraries/LibWeb/Layout/LineBox.h b/Userland/Libraries/LibWeb/Layout/LineBox.h index 2ac2e144a0..465f1eeeb8 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBox.h +++ b/Userland/Libraries/LibWeb/Layout/LineBox.h @@ -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); + 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, Span = {}); Vector const& fragments() const { return m_fragments; } Vector& fragments() { return m_fragments; } diff --git a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h index 96a8ae8e8e..ac9ad0dc18 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h +++ b/Userland/Libraries/LibWeb/Layout/LineBoxFragment.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -17,7 +18,7 @@ class LineBoxFragment { friend class LineBox; public: - LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, CSSPixels border_box_bottom) + LineBoxFragment(Node const& layout_node, int start, int length, CSSPixelPoint offset, CSSPixelSize size, CSSPixels border_box_top, CSSPixels border_box_bottom, Vector glyph_run = {}) : m_layout_node(layout_node) , m_start(start) , m_length(length) @@ -25,6 +26,7 @@ public: , m_size(size) , m_border_box_top(border_box_top) , m_border_box_bottom(border_box_bottom) + , m_glyph_run(move(glyph_run)) { } @@ -71,6 +73,8 @@ public: bool is_atomic_inline() const; + Vector const& glyph_run() const { return m_glyph_run; } + private: JS::NonnullGCPtr m_layout_node; int m_start { 0 }; @@ -80,6 +84,7 @@ private: CSSPixels m_border_box_top { 0 }; CSSPixels m_border_box_bottom { 0 }; CSSPixels m_baseline { 0 }; + Vector m_glyph_run; }; } diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp index 43e3c22034..8b40aec436 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.cpp @@ -97,9 +97,9 @@ 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) +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, Span 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); + 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, glyph_run); m_max_height_on_current_line = max(m_max_height_on_current_line, content_height); } diff --git a/Userland/Libraries/LibWeb/Layout/LineBuilder.h b/Userland/Libraries/LibWeb/Layout/LineBuilder.h index 17fe13a732..279f8eff4a 100644 --- a/Userland/Libraries/LibWeb/Layout/LineBuilder.h +++ b/Userland/Libraries/LibWeb/Layout/LineBuilder.h @@ -25,7 +25,7 @@ public: void break_line(ForcedBreak, Optional 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); + 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, Span); // Returns whether a line break occurred. bool break_if_needed(CSSPixels next_item_width) diff --git a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp index 5699ca0597..d42c5404e7 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintableBox.cpp @@ -645,18 +645,24 @@ static void paint_text_fragment(PaintContext& context, Layout::TextNode const& t 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()) }; - Utf8View view { text.code_points().substring_view(fragment.start(), fragment.length()) }; - - auto& scaled_font = fragment.layout_node().scaled_font(context); - - painter.draw_text_run(baseline_start.to_type(), view, scaled_font, text_node.computed_values().color(), fragment_absolute_device_rect.to_type()); + auto const& scaled_font = fragment.layout_node().scaled_font(context); + Vector scaled_glyph_run; + scaled_glyph_run.ensure_capacity(fragment.glyph_run().size()); + for (auto glyph : fragment.glyph_run()) { + glyph.visit([&](auto& glyph) { + glyph.font = &scaled_font; + 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(), scaled_glyph_run, text_node.computed_values().color(), fragment_absolute_device_rect.to_type()); auto selection_rect = context.enclosing_device_rect(fragment.selection_rect(text_node.font())).to_type(); if (!selection_rect.is_empty()) { painter.fill_rect(selection_rect, CSS::SystemColor::highlight()); RecordingPainterStateSaver saver(painter); painter.add_clip_rect(selection_rect); - painter.draw_text_run(baseline_start.to_type(), view, scaled_font, CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type()); + painter.draw_text_run(baseline_start.to_type(), fragment.glyph_run(), CSS::SystemColor::highlight_text(), fragment_absolute_device_rect.to_type()); } paint_text_decoration(context, text_node, fragment); diff --git a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorCPU.cpp b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorCPU.cpp index 884e070a94..d93a48c9d0 100644 --- a/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorCPU.cpp +++ b/Userland/Libraries/LibWeb/Painting/PaintingCommandExecutorCPU.cpp @@ -33,7 +33,7 @@ CommandResult PaintingCommandExecutorCPU::draw_glyph_run(Vector(); - painter.draw_emoji(emoji.position, *emoji.emoji, *emoji.font); + painter.draw_emoji(emoji.position.to_type(), *emoji.emoji, *emoji.font); } } return CommandResult::Continue; diff --git a/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp b/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp index 6261ea2b19..360134dd2b 100644 --- a/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp +++ b/Userland/Libraries/LibWeb/Painting/RecordingPainter.cpp @@ -214,11 +214,17 @@ void RecordingPainter::draw_signed_distance_field(Gfx::IntRect const& dst_rect, }); } -void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Utf8View string, Gfx::Font const& font, Color color, Gfx::IntRect const& rect) +void RecordingPainter::draw_text_run(Gfx::IntPoint baseline_start, Span glyph_run, Color color, Gfx::IntRect const& rect) { - auto glyph_run = Gfx::get_glyph_run(state().translation.map(baseline_start).to_type(), string, font); + auto transformed_baseline_start = state().translation.map(baseline_start).to_type(); + Vector translated_glyph_run; + translated_glyph_run.ensure_capacity(glyph_run.size()); + for (auto glyph : glyph_run) { + glyph.visit([&](auto& glyph) { glyph.position.translate_by(transformed_baseline_start); }); + translated_glyph_run.append(glyph); + } push_command(DrawGlyphRun { - .glyph_run = glyph_run, + .glyph_run = move(translated_glyph_run), .color = color, .rect = state().translation.map(rect), }); diff --git a/Userland/Libraries/LibWeb/Painting/RecordingPainter.h b/Userland/Libraries/LibWeb/Painting/RecordingPainter.h index 89cb70bbcc..ef2dfe3a43 100644 --- a/Userland/Libraries/LibWeb/Painting/RecordingPainter.h +++ b/Userland/Libraries/LibWeb/Painting/RecordingPainter.h @@ -460,7 +460,7 @@ public: 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. - void draw_text_run(Gfx::IntPoint baseline_start, Utf8View string, Gfx::Font const& font, Color color, Gfx::IntRect const& rect); + void draw_text_run(Gfx::IntPoint baseline_start, Span glyph_run, Color color, Gfx::IntRect const& rect); void add_clip_rect(Gfx::IntRect const& rect);