diff options
author | Sergey Bugaev <bugaevc@gmail.com> | 2019-09-04 23:47:03 +0300 |
---|---|---|
committer | Andreas Kling <awesomekling@gmail.com> | 2019-09-05 16:37:39 +0200 |
commit | 84bc6a92a745fbaa6f54dbd54f0c0668f3f1a385 (patch) | |
tree | b7b5a24aabc62f2d3659e8736293fefbceece465 /Libraries/LibDraw | |
parent | 27380b9d2b8409c091ce66aad3bcacc3543cc8ff (diff) | |
download | serenity-84bc6a92a745fbaa6f54dbd54f0c0668f3f1a385.zip |
LibDraw: Add emoji drawing support to Painter
From here on, all strings displayed to the user are expected to
be encoded as UTF-8. The next few commits will deal with a few
existing places where this requirement is currently violated.
https://github.com/SerenityOS/serenity/issues/490
Diffstat (limited to 'Libraries/LibDraw')
-rw-r--r-- | Libraries/LibDraw/Painter.cpp | 85 | ||||
-rw-r--r-- | Libraries/LibDraw/Painter.h | 6 |
2 files changed, 67 insertions, 24 deletions
diff --git a/Libraries/LibDraw/Painter.cpp b/Libraries/LibDraw/Painter.cpp index c043f5bb41..a3e974f77e 100644 --- a/Libraries/LibDraw/Painter.cpp +++ b/Libraries/LibDraw/Painter.cpp @@ -1,6 +1,7 @@ #include "Painter.h" #include "Font.h" #include "GraphicsBitmap.h" +#include "Emoji.h" #include <AK/Assertions.h> #include <AK/StdLibExtras.h> #include <AK/StringBuilder.h> @@ -8,6 +9,7 @@ #include <math.h> #include <stdio.h> #include <unistd.h> +#include <AK/Utf8View.h> #pragma GCC optimize("O3") @@ -554,34 +556,69 @@ void Painter::draw_scaled_bitmap(const Rect& a_dst_rect, const GraphicsBitmap& s draw_bitmap(point, font.glyph_bitmap(ch), color); } -void Painter::draw_text_line(const Rect& a_rect, const StringView& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +void Painter::draw_emoji(const Point& point, const Emoji& emoji, const Font& font) +{ + if (!font.is_fixed_width()) + blit(point, emoji.bitmap(), emoji.bitmap().rect()); + else { + Rect dst_rect { + point.x(), + point.y(), + font.glyph_width('x'), + font.glyph_height() + }; + draw_scaled_bitmap(dst_rect, emoji.bitmap(), emoji.bitmap().rect()); + } +} + +void Painter::draw_glyph_or_emoji(const Point& point, u32 codepoint, const Font& font, Color color) +{ + if (codepoint < 256) { + // This looks like a regular character. + draw_glyph(point, (char)codepoint, font, color); + return; + } + + // Perhaps it's an emoji? + const Emoji* emoji = Emoji::emoji_for_codepoint(codepoint); + if (emoji == nullptr) { + dbg() << "Failed to find an emoji for codepoint " << codepoint; + draw_glyph(point, '?', font, color); + return; + } + + draw_emoji(point, *emoji, font); +} + +void Painter::draw_text_line(const Rect& a_rect, const Utf8View& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) { auto rect = a_rect; - StringView final_text(text); + Utf8View final_text(text); String 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_length = 0; + int byte_offset = 0; int new_width = font.width("..."); if (new_width < text_width) { - for (int i = 0; i < final_text.length(); ++i) { - int glyph_width = font.glyph_width(final_text.characters_without_null_termination()[i]); + for (auto it = final_text.begin(); it != final_text.end(); ++it) { + u32 codepoint = *it; + int glyph_width = font.glyph_or_emoji_width(codepoint); // 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_length; + byte_offset = final_text.byte_offset_of(it); new_width += glyph_width + glyph_spacing; } StringBuilder builder; - builder.append(StringView(final_text.characters_without_null_termination(), new_length)); + builder.append(final_text.substring_view(0, byte_offset).as_string()); builder.append("..."); elided_text = builder.to_string(); - final_text = elided_text; + final_text = Utf8View { elided_text }; } } } @@ -604,14 +641,13 @@ void Painter::draw_text_line(const Rect& a_rect, const StringView& text, const F auto point = rect.location(); int space_width = font.glyph_width(' ') + font.glyph_spacing(); - for (ssize_t i = 0; i < final_text.length(); ++i) { - char ch = final_text.characters_without_null_termination()[i]; - if (ch == ' ') { + for (u32 codepoint : final_text) { + if (codepoint == ' ') { point.move_by(space_width, 0); continue; } - draw_glyph(point, ch, font, color); - point.move_by(font.glyph_width(ch) + font.glyph_spacing(), 0); + draw_glyph_or_emoji(point, codepoint, font, color); + point.move_by(font.glyph_or_emoji_width(codepoint) + font.glyph_spacing(), 0); } } @@ -620,22 +656,25 @@ void Painter::draw_text(const Rect& rect, const StringView& text, TextAlignment draw_text(rect, text, font(), alignment, color, elision); } -void Painter::draw_text(const Rect& rect, const StringView& text, const Font& font, TextAlignment alignment, Color color, TextElision elision) +void Painter::draw_text(const Rect& rect, const StringView& raw_text, const Font& font, TextAlignment alignment, Color color, TextElision elision) { - Vector<StringView, 32> lines; + Utf8View text { raw_text }; + Vector<Utf8View, 32> lines; int start_of_current_line = 0; - for (int i = 0; i < text.length(); ++i) { - auto ch = text[i]; - if (ch == '\n') { - lines.append(text.substring_view(start_of_current_line, i - start_of_current_line)); - start_of_current_line = i + 1; - continue; + for (auto it = text.begin(); it != text.end(); ++it) { + u32 codepoint = *it; + if (codepoint == '\n') { + int byte_offset = text.byte_offset_of(it); + Utf8View line = text.substring_view(start_of_current_line, byte_offset - start_of_current_line); + lines.append(line); + start_of_current_line = byte_offset + 1; } } - if (start_of_current_line != text.length()) { - lines.append(text.substring_view(start_of_current_line, text.length() - start_of_current_line)); + if (start_of_current_line != text.byte_length()) { + Utf8View line = text.substring_view(start_of_current_line, text.byte_length() - start_of_current_line); + lines.append(line); } static const int line_spacing = 4; diff --git a/Libraries/LibDraw/Painter.h b/Libraries/LibDraw/Painter.h index 3d16905b20..0366b1ca5c 100644 --- a/Libraries/LibDraw/Painter.h +++ b/Libraries/LibDraw/Painter.h @@ -7,11 +7,13 @@ #include <AK/AKString.h> #include <LibDraw/TextAlignment.h> #include <LibDraw/TextElision.h> +#include <AK/Utf8View.h> class CharacterBitmap; class GlyphBitmap; class GraphicsBitmap; class Font; +class Emoji; class Painter { public: @@ -34,6 +36,8 @@ public: void draw_text(const Rect&, const StringView&, TextAlignment = TextAlignment::TopLeft, Color = Color::Black, TextElision = TextElision::None); void draw_glyph(const Point&, char, Color); void draw_glyph(const Point&, char, const Font&, Color); + void draw_emoji(const Point&, const Emoji&, const Font&); + void draw_glyph_or_emoji(const Point&, u32 codepoint, const Font&, Color); const Font& font() const { return *state().font; } void set_font(const Font& font) { state().font = &font; } @@ -70,7 +74,7 @@ protected: void blit_with_opacity(const Point&, const GraphicsBitmap&, const Rect& src_rect, float opacity); void draw_pixel(const Point&, Color, int thickness = 1); - void draw_text_line(const Rect&, const StringView&, const Font&, TextAlignment, Color, TextElision); + void draw_text_line(const Rect&, const Utf8View&, const Font&, TextAlignment, Color, TextElision); struct State { const Font* font; |