From 7f91aec25cd2b517e8a4bacc2b35e08a0556e9d4 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Sat, 2 Feb 2019 23:13:12 +0100 Subject: [PATCH] Support font files. This only works with the userspace build of SharedGraphics so far. It's also very slow at loading fonts, but that's easy to fix. Let's put fonts in /res/fonts/. --- AK/BufferStream.h | 18 ++++- Base/res/fonts/Liza8x10.font | Bin 0 -> 10318 bytes FontEditor/FontEditor.cpp | 16 ++++- FontEditor/FontEditor.h | 2 + SharedGraphics/Font.cpp | 132 +++++++++++++++++++++++++++++++++-- SharedGraphics/Font.h | 10 ++- SharedGraphics/Liza8x10.h | 2 + 7 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 Base/res/fonts/Liza8x10.font diff --git a/AK/BufferStream.h b/AK/BufferStream.h index b5abab5a2b..d2f99c9ac2 100644 --- a/AK/BufferStream.h +++ b/AK/BufferStream.h @@ -16,6 +16,11 @@ public: m_buffer[m_offset++] = value & 0xffu; } + void operator<<(char value) + { + m_buffer[m_offset++] = (byte)value; + } + void operator<<(word value) { m_buffer[m_offset++] = value & 0xffu; @@ -43,9 +48,20 @@ public: m_buffer[m_offset++] = value[i]; } + void operator<<(const ByteBuffer& value) + { + for (size_t i = 0; i < value.size(); ++i) + m_buffer[m_offset++] = value[i]; + } + + bool at_end() const + { + return m_offset == m_buffer.size(); + } + void fill_to_end(byte ch) { - while (m_offset < m_buffer.size()) + while (!at_end()) m_buffer[m_offset++] = ch; } diff --git a/Base/res/fonts/Liza8x10.font b/Base/res/fonts/Liza8x10.font new file mode 100644 index 0000000000000000000000000000000000000000..f6e22189728a4b820c09ec8f0236ff1f1eb54131 GIT binary patch literal 10318 zcmY#%%PZmFV&SN`!XzNWz`$^19iu?wOcoB7^^5}C3=9k)djCBZjt37OJg~XSBmlx7 zF%dyl4h9AWhLrV;0`Kp!fY~A-fl)jf0;3@?8UmvsfF}eb7#J9^VUQM>IEZb=z`y{) zLLd$Stir&+0K#=p8pH-+WIo6U1qKEN8wLi3AO;2okhvhX2Ll6x7gU@Dq=I6o0|Ns{ z4ag1X7$gpo2kC>cL4HBUAT=O)kUC@x60?JvkFTZzsRLn{S`ZuA99(QzIN_29nTJab zn>Z-EKo}+m^C!rEAPjOB2*bh&qz)99pl}DpEeM0+6of%>3Bn+GkUEe)P<(Ph!vn-e z#~^he^-c^73?K}$A7(GeP9_Eh22k1psRPl-@*sT<3=9k)4AKkYgVcfe2@DJj0SpWb zpfu+KH3K9L!yt7a^FZdqFevO`^2p|a%mRfG%sn7=AbA*u*$XlcrXSgzAbl_lvKOQt zrWZtG<6~1-1NAG&Y><1{K#I^XNF0Pg!XO%iL2PskN=G0J5(m-P)Pcl7@da`_D2zZf zhz}A6=|jdKzk~QN8pKA&AoqgwgW?zD2M`V7gTz5R_$ zkUK%_EpLagaPnog4!L1E_pL zwg<)riG$=p>Ok&6HV0%cG9M(5O&v%-vN{kOqz+_1$Q>X)2!p}{W-mw_Bo9&t6NAwp zdthuB4N?b+8$uYQ4iw&?_`-%k{sooSAax+KL3~hs04fte@eZOve2_Ru9;6QBc6=D* zUXZ#b1_p*>3=9mb7#J8PF)%QI_~;m<4rDF}gUTjQIsuhgpz;eO2E!nApzr~O6S^57 zK1duS4^jt;Czw7E4dSD_7o-lP9;6SX9~pz{Es*#QkRk>K29SP`IWP?3BZmn{FGwkJ zdIsf15FZrgAbp^84&sB<)#E8|Vetu42hxWNBb$e=1|$c=AUi=AWFE+!=ol0)p!fmB z6G#rE&JmhFK=mWcJs=vy2c-j$Ji0oN97qj12B`z32T-{S3JX}70tz2koPgv(>Okof zB#sM%x(A7Y)FES#UXVPo4KfD#4P+h+gZLmff%JpShhb1& zgQ-ELVP=8i9K;6E*f2;Agh6sJ8pKA&pmq?EhN%J3u=oJ!N5&vIWDIf($X%dt1?2@08zcr(14|zu^I&Wk z4N6Nebub#F9~pz(334M$9!7)06ox@+G#D5dKyd?;htV)KptykHH!w*g8r1gzm8GCK z2VoE!B!;8}%tUq@h>e_{!3v-RDF1-UBaoXw{som|ATf|Mj1Lk6VNm#jXcz{`gVcdA zh>Z_})PVFMtAW`G69dVE)S+V^1_lNrbiq;nXb6mkz-S1JhQMeDjE2By2#kinXb6mk fz-S1JhQMeDjE2By2#kinXb6mkz-S0CFfafB1-z69 literal 0 HcmV?d00001 diff --git a/FontEditor/FontEditor.cpp b/FontEditor/FontEditor.cpp index 011368d6db..57dc476678 100644 --- a/FontEditor/FontEditor.cpp +++ b/FontEditor/FontEditor.cpp @@ -1,18 +1,28 @@ #include "FontEditor.h" #include +#include #include FontEditorWidget::FontEditorWidget(GWidget* parent) : GWidget(parent) { - auto mutable_font = Font::default_font().clone(); + m_edited_font = Font::load_from_file("/saved.font"); + if (!m_edited_font) + m_edited_font = Font::default_font().clone(); - m_glyph_map_widget = new GlyphMapWidget(*mutable_font, this); + m_glyph_map_widget = new GlyphMapWidget(*m_edited_font, this); m_glyph_map_widget->move_to({ 90, 5 }); - m_glyph_editor_widget = new GlyphEditorWidget(*mutable_font, this); + m_glyph_editor_widget = new GlyphEditorWidget(*m_edited_font, this); m_glyph_editor_widget->move_to({ 5, 5 }); + auto* save_button = new GButton(this); + save_button->set_caption("Save"); + save_button->set_relative_rect({ 5, 135, 140, 20 }); + save_button->on_click = [this] (GButton&) { + m_edited_font->write_to_file("/saved.font"); + }; + auto* label = new GLabel(this); label->set_relative_rect({ 5, 110, 140, 20 }); diff --git a/FontEditor/FontEditor.h b/FontEditor/FontEditor.h index b4a1f547ee..7786edceb2 100644 --- a/FontEditor/FontEditor.h +++ b/FontEditor/FontEditor.h @@ -12,6 +12,8 @@ public: virtual ~FontEditorWidget() override; private: + RetainPtr m_edited_font; + GlyphMapWidget* m_glyph_map_widget { nullptr }; GlyphEditorWidget* m_glyph_editor_widget { nullptr }; }; diff --git a/SharedGraphics/Font.cpp b/SharedGraphics/Font.cpp index a667fd8c1a..7dee9bc68c 100644 --- a/SharedGraphics/Font.cpp +++ b/SharedGraphics/Font.cpp @@ -2,6 +2,15 @@ #include "Peanut8x10.h" #include "Liza8x10.h" #include +#include +#include + +#ifdef USERLAND +#include +#include +#include +#include +#endif #define DEFAULT_FONT_NAME Liza8x10 @@ -29,8 +38,15 @@ void Font::initialize() Font& Font::default_font() { - if (!s_default_font) - s_default_font = adopt(*new Font(DEFAULT_FONT_NAME::glyphs, DEFAULT_FONT_NAME::glyph_width, DEFAULT_FONT_NAME::glyph_height, DEFAULT_FONT_NAME::first_glyph, DEFAULT_FONT_NAME::last_glyph)).leak_ref(); + if (!s_default_font) { +#ifdef USERLAND + s_default_font = Font::load_from_file("/res/fonts/Liza8x10.font").leak_ref(); + ASSERT(s_default_font); +#else + + s_default_font = adopt(*new Font(DEFAULT_FONT_NAME::name, DEFAULT_FONT_NAME::glyphs, DEFAULT_FONT_NAME::glyph_width, DEFAULT_FONT_NAME::glyph_height, DEFAULT_FONT_NAME::first_glyph, DEFAULT_FONT_NAME::last_glyph)).leak_ref(); +#endif + } return *s_default_font; } @@ -47,11 +63,12 @@ RetainPtr Font::clone() const memset(new_glyphs[i], ' ', bytes_per_glyph); } } - return adopt(*new Font(new_glyphs, m_glyph_width, m_glyph_height, 0, 255)); + return adopt(*new Font(m_name, new_glyphs, m_glyph_width, m_glyph_height, 0, 255)); } -Font::Font(const char* const* glyphs, byte glyph_width, byte glyph_height, byte first_glyph, byte last_glyph) - : m_glyphs(glyphs) +Font::Font(const String& name, const char* const* glyphs, byte glyph_width, byte glyph_height, byte first_glyph, byte last_glyph) + : m_name(name) + , m_glyphs(glyphs) , m_glyph_width(glyph_width) , m_glyph_height(glyph_height) , m_first_glyph(first_glyph) @@ -73,3 +90,108 @@ Font::Font(const char* const* glyphs, byte glyph_width, byte glyph_height, byte Font::~Font() { } + +#ifdef USERLAND +struct FontFileHeader { + char magic[4]; + byte glyph_width; + byte glyph_height; + byte type; + byte unused[7]; + char name[64]; +} PACKED; + +RetainPtr Font::load_from_file(const String& path) +{ + int fd = open(path.characters(), O_RDONLY, 0644); + if (fd < 0) { + dbgprintf("open(%s) got fd=%d, failed: %s\n", path.characters(), fd, strerror(errno)); + perror("open"); + return nullptr; + } + + FontFileHeader header; + ssize_t nread = read(fd, &header, sizeof(FontFileHeader)); + if (nread != sizeof(FontFileHeader)) { + dbgprintf("nread != sizeof(FontFileHeader)=%u\n", sizeof(FontFileHeader)); + return nullptr; + } + + if (memcmp(header.magic, "!Fnt", 4)) { + dbgprintf("header.magic != '!Fnt'\n"); + return nullptr; + } + + if (header.name[63] != '\0') { + dbgprintf("Font name not fully null-terminated\n"); + return nullptr; + } + + char** new_glyphs = static_cast(kmalloc(sizeof(char*) * 256)); + for (unsigned glyph_index = 0; glyph_index < 256; ++glyph_index) { + new_glyphs[glyph_index] = static_cast(kmalloc(header.glyph_width * header.glyph_height)); + char* bitptr = new_glyphs[glyph_index]; + for (unsigned y = 0; y < header.glyph_height; ++y) { + unsigned pattern; + ssize_t nread = read(fd, &pattern, sizeof(unsigned)); + if (nread != sizeof(unsigned)) { + // FIXME: free() things and return nullptr. + ASSERT_NOT_REACHED(); + } + for (unsigned x = 0; x < header.glyph_width; ++x) { + if (pattern & (1u << x)) { + *(bitptr++) = '#'; + } else { + *(bitptr++) = ' '; + } + } + } + } + + return adopt(*new Font(String(header.name), new_glyphs, header.glyph_width, header.glyph_height, 0, 255)); +} + +bool Font::write_to_file(const String& path) +{ + int fd = open(path.characters(), O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + perror("open"); + return false; + } + + FontFileHeader header; + memset(&header, 0, sizeof(FontFileHeader)); + memcpy(header.magic, "!Fnt", 4); + header.glyph_width = m_glyph_width; + header.glyph_height = m_glyph_height; + header.type = 0; + memcpy(header.name, m_name.characters(), min(m_name.length(), 63u)); + + size_t bytes_per_glyph = sizeof(unsigned) * m_glyph_height; + + auto buffer = ByteBuffer::create_uninitialized(sizeof(FontFileHeader) + (256 * bytes_per_glyph)); + BufferStream stream(buffer); + + stream << ByteBuffer::wrap((byte*)&header, sizeof(FontFileHeader)); + + for (unsigned glyph_index = 0; glyph_index < 256; ++glyph_index) { + auto* glyph_bits = (byte*)m_glyphs[glyph_index]; + for (unsigned y = 0; y < m_glyph_height; ++y) { + unsigned pattern = 0; + for (unsigned x = 0; x < m_glyph_width; ++x) { + if (glyph_bits[y * m_glyph_width + x] == '#') { + pattern |= 1 << x; + } + } + stream << pattern; + } + } + + ASSERT(stream.at_end()); + ssize_t nwritten = write(fd, buffer.pointer(), buffer.size()); + ASSERT(nwritten == (ssize_t)buffer.size()); + int rc = close(fd); + ASSERT(rc == 0); + return true; +} +#endif diff --git a/SharedGraphics/Font.h b/SharedGraphics/Font.h index e97402e1e4..847a368533 100644 --- a/SharedGraphics/Font.h +++ b/SharedGraphics/Font.h @@ -3,6 +3,7 @@ #include "CharacterBitmap.h" #include #include +#include #include class Font : public Retainable { @@ -11,6 +12,11 @@ public: RetainPtr clone() const; +#ifdef USERLAND + static RetainPtr load_from_file(const String& path); + bool write_to_file(const String& path); +#endif + ~Font(); const CharacterBitmap& glyph_bitmap(char ch) const { return *m_bitmaps[(byte)ch]; } @@ -21,7 +27,9 @@ public: static void initialize(); private: - Font(const char* const* glyphs, byte glyph_width, byte glyph_height, byte first_glyph, byte last_glyph); + Font(const String& name, const char* const* glyphs, byte glyph_width, byte glyph_height, byte first_glyph, byte last_glyph); + + String m_name; const char* const* m_glyphs { nullptr }; mutable RetainPtr m_bitmaps[256]; diff --git a/SharedGraphics/Liza8x10.h b/SharedGraphics/Liza8x10.h index b5b0072473..ba3cee22ba 100644 --- a/SharedGraphics/Liza8x10.h +++ b/SharedGraphics/Liza8x10.h @@ -2,6 +2,8 @@ namespace Liza8x10 { +static constexpr char name[64] = "Old Liza"; + static constexpr char first_glyph = '!'; static constexpr char last_glyph = '~'; static constexpr byte glyph_width = 8;