diff options
author | Tom <tomut@yahoo.com> | 2020-10-20 09:51:04 -0600 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-10-22 15:23:45 +0200 |
commit | f239fbaa58aa80ff615a3e66bcf35bbf878271b2 (patch) | |
tree | a849952c5985f92377f6e90f822da896a8fb57f7 /Libraries/LibGfx | |
parent | 25e7225782d133e463ca26892bfe732720ccf9f7 (diff) | |
download | serenity-f239fbaa58aa80ff615a3e66bcf35bbf878271b2.zip |
LibGfx: Reduce code duplication in Painter::draw_text
Use the same logic for all variants for Painter::draw_text. Also,
add an overload that allows taking a callback function for custom
gylph drawing. This allows drawing some glyphs differently in the
correct location when drawing more complex strings (e.g. multi-line,
elisions, etc).
Diffstat (limited to 'Libraries/LibGfx')
-rw-r--r-- | Libraries/LibGfx/Painter.cpp | 242 | ||||
-rw-r--r-- | Libraries/LibGfx/Painter.h | 6 |
2 files changed, 104 insertions, 144 deletions
diff --git a/Libraries/LibGfx/Painter.cpp b/Libraries/LibGfx/Painter.cpp index b6a044b7be..fb4294a5b5 100644 --- a/Libraries/LibGfx/Painter.cpp +++ b/Libraries/LibGfx/Painter.cpp @@ -854,20 +854,52 @@ void Painter::draw_glyph_or_emoji(const IntPoint& point, u32 code_point, const F draw_emoji(point, *emoji, font); } -void Painter::draw_text_line(const IntRect& a_rect, const Utf8View& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +static void apply_elision(Utf8View& final_text, String& elided_text, size_t offset) +{ + StringBuilder builder; + builder.append(final_text.substring_view(0, offset).as_string()); + builder.append("..."); + elided_text = builder.to_string(); + final_text = Utf8View { elided_text }; +} + +static void apply_elision(Utf32View& final_text, Vector<u32>& elided_text, size_t offset) +{ + elided_text.append(final_text.code_points(), offset); + elided_text.append('.'); + elided_text.append('.'); + elided_text.append('.'); + final_text = Utf32View { elided_text.data(), elided_text.size() }; +} + +template<typename TextType> +struct ElidedText { +}; + +template<> +struct ElidedText<Utf8View> { + typedef String Type; +}; + +template<> +struct ElidedText<Utf32View> { + typedef Vector<u32> Type; +}; + +template<typename TextType, typename DrawGlyphFunction> +void draw_text_line(const IntRect& a_rect, const TextType& text, const Font& font, TextAlignment alignment, TextElision elision, DrawGlyphFunction draw_glyph) { auto rect = a_rect; - Utf8View final_text(text); - String elided_text; + TextType final_text(text); + typename ElidedText<TextType>::Type elided_text; if (elision == TextElision::Right) { int text_width = font.width(final_text); if (font.width(final_text) > rect.width()) { int glyph_spacing = font.glyph_spacing(); - int byte_offset = 0; int new_width = font.width("..."); if (new_width < text_width) { - for (auto it = final_text.begin(); it != final_text.end(); ++it) { - u32 code_point = *it; + size_t offset = 0; + for (auto code_point : text) { int glyph_width = font.glyph_or_emoji_width(code_point); // NOTE: Glyph spacing should not be added after the last glyph on the line, // but since we are here because the last glyph does not actually fit on the line, @@ -875,14 +907,10 @@ void Painter::draw_text_line(const IntRect& a_rect, const Utf8View& text, const int width_with_this_glyph_included = new_width + glyph_width + glyph_spacing; if (width_with_this_glyph_included > rect.width()) break; - byte_offset = final_text.byte_offset_of(it); new_width += glyph_width + glyph_spacing; + offset++; } - StringBuilder builder; - builder.append(final_text.substring_view(0, byte_offset).as_string()); - builder.append("..."); - elided_text = builder.to_string(); - final_text = Utf8View { elided_text }; + apply_elision(final_text, elided_text, offset); } } } @@ -920,105 +948,50 @@ void Painter::draw_text_line(const IntRect& a_rect, const Utf8View& text, const point.move_by(space_width, 0); continue; } - draw_glyph_or_emoji(point, code_point, font, color); - point.move_by(font.glyph_or_emoji_width(code_point) + font.glyph_spacing(), 0); + IntSize glyph_size(font.glyph_or_emoji_width(code_point) + font.glyph_spacing(), font.glyph_height()); + draw_glyph({ point, glyph_size }, code_point); + point.move_by(glyph_size.width(), 0); } } -void Painter::draw_text_line(const IntRect& a_rect, const Utf32View& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +static inline size_t draw_text_iterator_offset(const Utf8View& text, const Utf8View::Iterator& it) { - auto rect = a_rect; - Utf32View final_text(text); - Vector<u32> elided_text; - if (elision == TextElision::Right) { - int text_width = font.width(final_text); - if (font.width(final_text) > rect.width()) { - int glyph_spacing = font.glyph_spacing(); - int new_width = font.width("..."); - if (new_width < text_width) { - size_t i = 0; - for (; i < text.length(); ++i) { - u32 code_point = text.code_points()[i]; - int glyph_width = font.glyph_or_emoji_width(code_point); - // NOTE: Glyph spacing should not be added after the last glyph on the line, - // but since we are here because the last glyph does not actually fit on the line, - // we don't have to worry about spacing. - int width_with_this_glyph_included = new_width + glyph_width + glyph_spacing; - if (width_with_this_glyph_included > rect.width()) - break; - new_width += glyph_width + glyph_spacing; - } - elided_text.clear(); - elided_text.append(final_text.code_points(), i); - elided_text.append('.'); - elided_text.append('.'); - elided_text.append('.'); - final_text = Utf32View { elided_text.data(), elided_text.size() }; - } - } - } - - switch (alignment) { - case TextAlignment::TopLeft: - case TextAlignment::CenterLeft: - break; - case TextAlignment::TopRight: - case TextAlignment::CenterRight: - rect.set_x(rect.right() - font.width(final_text)); - break; - case TextAlignment::Center: { - auto shrunken_rect = rect; - shrunken_rect.set_width(font.width(final_text)); - shrunken_rect.center_within(rect); - rect = shrunken_rect; - break; - } - default: - ASSERT_NOT_REACHED(); - } - - auto point = rect.location(); - int space_width = font.glyph_width(' ') + font.glyph_spacing(); + return text.byte_offset_of(it); +} - for (size_t i = 0; i < final_text.length(); ++i) { - auto code_point = final_text.code_points()[i]; - if (code_point == ' ') { - point.move_by(space_width, 0); - continue; - } - draw_glyph_or_emoji(point, code_point, font, color); - point.move_by(font.glyph_or_emoji_width(code_point) + font.glyph_spacing(), 0); - } +static inline size_t draw_text_iterator_offset(const Utf32View& text, const Utf32View::Iterator& it) +{ + return it - text.begin(); } -void Painter::draw_text(const IntRect& rect, const StringView& text, TextAlignment alignment, Color color, TextElision elision) +static inline size_t draw_text_get_length(const Utf8View& text) { - draw_text(rect, text, font(), alignment, color, elision); + return text.byte_length(); } -void Painter::draw_text(const IntRect& rect, const Utf32View& text, TextAlignment alignment, Color color, TextElision elision) +static inline size_t draw_text_get_length(const Utf32View& text) { - draw_text(rect, text, font(), alignment, color, elision); + return text.length(); } -void Painter::draw_text(const IntRect& rect, const StringView& raw_text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +template<typename TextType, typename DrawGlyphFunction> +void do_draw_text(const IntRect& rect, const TextType& text, const Font& font, TextAlignment alignment, TextElision elision, DrawGlyphFunction draw_glyph) { - Utf8View text { raw_text }; - Vector<Utf8View, 32> lines; + Vector<TextType, 32> lines; - int start_of_current_line = 0; + size_t start_of_current_line = 0; for (auto it = text.begin(); it != text.end(); ++it) { u32 code_point = *it; if (code_point == '\n') { - int byte_offset = text.byte_offset_of(it); - Utf8View line = text.substring_view(start_of_current_line, byte_offset - start_of_current_line); + auto offset = draw_text_iterator_offset(text, it); + TextType line = text.substring_view(start_of_current_line, offset - start_of_current_line); lines.append(line); - start_of_current_line = byte_offset + 1; + start_of_current_line = offset + 1; } } - if (start_of_current_line != text.byte_length()) { - Utf8View line = text.substring_view(start_of_current_line, text.byte_length() - start_of_current_line); + if (start_of_current_line != draw_text_get_length(text)) { + TextType line = text.substring_view(start_of_current_line, draw_text_get_length(text) - start_of_current_line); lines.append(line); } @@ -1048,9 +1021,6 @@ void Painter::draw_text(const IntRect& rect, const StringView& raw_text, const F case TextAlignment::Center: bounding_rect.center_within(rect); break; - case TextAlignment::BottomRight: - bounding_rect.set_location({ (rect.right() + 1) - bounding_rect.width(), (rect.bottom() + 1) - bounding_rect.height() }); - break; default: ASSERT_NOT_REACHED(); } @@ -1059,65 +1029,55 @@ void Painter::draw_text(const IntRect& rect, const StringView& raw_text, const F auto& line = lines[i]; IntRect line_rect { bounding_rect.x(), bounding_rect.y() + static_cast<int>(i) * line_height, bounding_rect.width(), line_height }; line_rect.intersect(rect); - draw_text_line(line_rect, line, font, alignment, color, elision); + draw_text_line(line_rect, line, font, alignment, elision, draw_glyph); } } -void Painter::draw_text(const IntRect& rect, const Utf32View& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +void Painter::draw_text(const IntRect& rect, const StringView& text, TextAlignment alignment, Color color, TextElision elision) { - Vector<Utf32View, 32> lines; + draw_text(rect, text, font(), alignment, color, elision); +} - size_t start_of_current_line = 0; - for (size_t i = 0; i < text.length(); ++i) { - u32 code_point = text.code_points()[i]; - if (code_point == '\n') { - Utf32View line = text.substring_view(start_of_current_line, i - start_of_current_line); - lines.append(line); - start_of_current_line = i + 1; - } - } +void Painter::draw_text(const IntRect& rect, const Utf32View& text, TextAlignment alignment, Color color, TextElision elision) +{ + draw_text(rect, text, font(), alignment, color, elision); +} - if (start_of_current_line != text.length()) { - Utf32View line = text.substring_view(start_of_current_line, text.length() - start_of_current_line); - lines.append(line); - } +void Painter::draw_text(const IntRect& rect, const StringView& raw_text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +{ + Utf8View text { raw_text }; + do_draw_text(rect, text, font, alignment, elision, [&](const IntRect& r, u32 code_point) { + draw_glyph_or_emoji(r.location(), code_point, font, color); + }); +} - static const int line_spacing = 4; - int line_height = font.glyph_height() + line_spacing; - IntRect bounding_rect { 0, 0, 0, (static_cast<int>(lines.size()) * line_height) - line_spacing }; +void Painter::draw_text(const IntRect& rect, const Utf32View& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +{ + do_draw_text(rect, text, font, alignment, elision, [&](const IntRect& r, u32 code_point) { + draw_glyph_or_emoji(r.location(), code_point, font, color); + }); +} - for (auto& line : lines) { - auto line_width = font.width(line); - if (line_width > bounding_rect.width()) - bounding_rect.set_width(line_width); - } +void Painter::draw_text(Function<void(const IntRect&, u32)> draw_one_glyph, const IntRect& rect, const StringView& raw_text, const Font& font, TextAlignment alignment, TextElision elision) +{ + Utf8View text { raw_text }; + do_draw_text(rect, text, font, alignment, elision, [&](const IntRect& r, u32 code_point) { + draw_one_glyph(r, code_point); + }); +} - switch (alignment) { - case TextAlignment::TopLeft: - bounding_rect.set_location(rect.location()); - break; - case TextAlignment::TopRight: - bounding_rect.set_location({ (rect.right() + 1) - bounding_rect.width(), rect.y() }); - break; - case TextAlignment::CenterLeft: - bounding_rect.set_location({ rect.x(), rect.center().y() - (bounding_rect.height() / 2) }); - break; - case TextAlignment::CenterRight: - bounding_rect.set_location({ (rect.right() + 1) - bounding_rect.width(), rect.center().y() - (bounding_rect.height() / 2) }); - break; - case TextAlignment::Center: - bounding_rect.center_within(rect); - break; - default: - ASSERT_NOT_REACHED(); - } +void Painter::draw_text(Function<void(const IntRect&, u32)> draw_one_glyph, const IntRect& rect, const Utf8View& text, const Font& font, TextAlignment alignment, TextElision elision) +{ + do_draw_text(rect, text, font, alignment, elision, [&](const IntRect& r, u32 code_point) { + draw_one_glyph(r, code_point); + }); +} - for (size_t i = 0; i < lines.size(); ++i) { - auto& line = lines[i]; - IntRect line_rect { bounding_rect.x(), bounding_rect.y() + static_cast<int>(i) * line_height, bounding_rect.width(), line_height }; - line_rect.intersect(rect); - draw_text_line(line_rect, line, font, alignment, color, elision); - } +void Painter::draw_text(Function<void(const IntRect&, u32)> draw_one_glyph, const IntRect& rect, const Utf32View& text, const Font& font, TextAlignment alignment, TextElision elision) +{ + do_draw_text(rect, text, font, alignment, elision, [&](const IntRect& r, u32 code_point) { + draw_one_glyph(r, code_point); + }); } void Painter::set_pixel(const IntPoint& p, Color color) diff --git a/Libraries/LibGfx/Painter.h b/Libraries/LibGfx/Painter.h index cb3271214b..4983f0ef8b 100644 --- a/Libraries/LibGfx/Painter.h +++ b/Libraries/LibGfx/Painter.h @@ -79,6 +79,9 @@ public: void draw_text(const IntRect&, const StringView&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None); void draw_text(const IntRect&, const Utf32View&, const Font&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None); void draw_text(const IntRect&, const Utf32View&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None); + void draw_text(Function<void(const IntRect&, u32)>, const IntRect&, const StringView&, const Font&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None); + void draw_text(Function<void(const IntRect&, u32)>, const IntRect&, const Utf8View&, const Font&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None); + void draw_text(Function<void(const IntRect&, u32)>, const IntRect&, const Utf32View&, const Font&, TextAlignment = TextAlignment::TopLeft, TextElision = TextElision::None); void draw_glyph(const IntPoint&, u32, Color); void draw_glyph(const IntPoint&, u32, const Font&, Color); void draw_emoji(const IntPoint&, const Gfx::Bitmap&, const Font&); @@ -135,9 +138,6 @@ protected: void blit_with_opacity(const IntPoint&, const Gfx::Bitmap&, const IntRect& src_rect, float opacity); void draw_pixel(const IntPoint&, Color, int thickness = 1); - void draw_text_line(const IntRect&, const Utf8View&, const Font&, TextAlignment, Color, TextElision); - void draw_text_line(const IntRect&, const Utf32View&, const Font&, TextAlignment, Color, TextElision); - struct State { const Font* font; IntPoint translation; |