Pārlūkot izejas kodu

LibGfx: Introduce provisional font interface

Old font functionality has been moved into BitmapFont
and an abstract Font interface has been introduced to
faciliate further development of TTF font integration.
Stephan Unverwerth 4 gadi atpakaļ
vecāks
revīzija
bb27b212de

+ 2 - 2
Applications/FontEditor/FontEditor.cpp

@@ -38,11 +38,11 @@
 #include <LibGUI/SpinBox.h>
 #include <LibGUI/TextBox.h>
 #include <LibGUI/Window.h>
-#include <LibGfx/Font.h>
+#include <LibGfx/BitmapFont.h>
 #include <LibGfx/Palette.h>
 #include <stdlib.h>
 
-FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::Font>&& edited_font)
+FontEditorWidget::FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&& edited_font)
     : m_edited_font(move(edited_font))
     , m_path(path)
 {

+ 3 - 2
Applications/FontEditor/FontEditor.h

@@ -28,6 +28,7 @@
 
 #include <AK/Function.h>
 #include <LibGUI/Widget.h>
+#include <LibGfx/BitmapFont.h>
 
 class GlyphEditorWidget;
 class GlyphMapWidget;
@@ -45,8 +46,8 @@ public:
     const String& path() { return m_path; }
 
 private:
-    FontEditorWidget(const String& path, RefPtr<Gfx::Font>&&);
-    RefPtr<Gfx::Font> m_edited_font;
+    FontEditorWidget(const String& path, RefPtr<Gfx::BitmapFont>&&);
+    RefPtr<Gfx::BitmapFont> m_edited_font;
 
     RefPtr<GlyphMapWidget> m_glyph_map_widget;
     RefPtr<GlyphEditorWidget> m_glyph_editor_widget;

+ 2 - 2
Applications/FontEditor/GlyphEditorWidget.cpp

@@ -26,10 +26,10 @@
 
 #include "GlyphEditorWidget.h"
 #include <LibGUI/Painter.h>
-#include <LibGfx/Font.h>
+#include <LibGfx/BitmapFont.h>
 #include <LibGfx/Palette.h>
 
-GlyphEditorWidget::GlyphEditorWidget(Gfx::Font& mutable_font)
+GlyphEditorWidget::GlyphEditorWidget(Gfx::BitmapFont& mutable_font)
     : m_font(mutable_font)
 {
     set_relative_rect({ 0, 0, preferred_width(), preferred_height() });

+ 5 - 4
Applications/FontEditor/GlyphEditorWidget.h

@@ -28,6 +28,7 @@
 
 #include <AK/Function.h>
 #include <LibGUI/Frame.h>
+#include <LibGfx/BitmapFont.h>
 
 class GlyphEditorWidget final : public GUI::Frame {
     C_OBJECT(GlyphEditorWidget)
@@ -40,20 +41,20 @@ public:
     int preferred_width() const;
     int preferred_height() const;
 
-    Gfx::Font& font() { return *m_font; }
-    const Gfx::Font& font() const { return *m_font; }
+    Gfx::BitmapFont& font() { return *m_font; }
+    const Gfx::BitmapFont& font() const { return *m_font; }
 
     Function<void(u8)> on_glyph_altered;
 
 private:
-    GlyphEditorWidget(Gfx::Font&);
+    GlyphEditorWidget(Gfx::BitmapFont&);
     virtual void paint_event(GUI::PaintEvent&) override;
     virtual void mousedown_event(GUI::MouseEvent&) override;
     virtual void mousemove_event(GUI::MouseEvent&) override;
 
     void draw_at_mouse(const GUI::MouseEvent&);
 
-    RefPtr<Gfx::Font> m_font;
+    RefPtr<Gfx::BitmapFont> m_font;
     int m_glyph { 0 };
     int m_scale { 10 };
 };

+ 2 - 2
Applications/FontEditor/GlyphMapWidget.cpp

@@ -26,10 +26,10 @@
 
 #include "GlyphMapWidget.h"
 #include <LibGUI/Painter.h>
-#include <LibGfx/Font.h>
+#include <LibGfx/BitmapFont.h>
 #include <LibGfx/Palette.h>
 
-GlyphMapWidget::GlyphMapWidget(Gfx::Font& mutable_font)
+GlyphMapWidget::GlyphMapWidget(Gfx::BitmapFont& mutable_font)
     : m_font(mutable_font)
 {
     m_glyph_count = mutable_font.glyph_count();

+ 5 - 4
Applications/FontEditor/GlyphMapWidget.h

@@ -29,6 +29,7 @@
 #include <AK/Function.h>
 #include <AK/StdLibExtras.h>
 #include <LibGUI/Frame.h>
+#include <LibGfx/BitmapFont.h>
 
 class GlyphMapWidget final : public GUI::Frame {
     C_OBJECT(GlyphMapWidget)
@@ -44,22 +45,22 @@ public:
     int preferred_width() const;
     int preferred_height() const;
 
-    Gfx::Font& font() { return *m_font; }
-    const Gfx::Font& font() const { return *m_font; }
+    Gfx::BitmapFont& font() { return *m_font; }
+    const Gfx::BitmapFont& font() const { return *m_font; }
 
     void update_glyph(int);
 
     Function<void(int)> on_glyph_selected;
 
 private:
-    explicit GlyphMapWidget(Gfx::Font&);
+    explicit GlyphMapWidget(Gfx::BitmapFont&);
     virtual void paint_event(GUI::PaintEvent&) override;
     virtual void mousedown_event(GUI::MouseEvent&) override;
     virtual void keydown_event(GUI::KeyEvent&) override;
 
     Gfx::IntRect get_outer_rect(int glyph) const;
 
-    RefPtr<Gfx::Font> m_font;
+    RefPtr<Gfx::BitmapFont> m_font;
     int m_glyph_count;
     int m_columns { 32 };
     int m_horizontal_spacing { 2 };

+ 6 - 6
Applications/FontEditor/main.cpp

@@ -36,7 +36,7 @@
 #include <LibGUI/MessageBox.h>
 #include <LibGUI/Window.h>
 #include <LibGfx/Bitmap.h>
-#include <LibGfx/Font.h>
+#include <LibGfx/BitmapFont.h>
 #include <LibGfx/FontDatabase.h>
 #include <LibGfx/Point.h>
 #include <stdio.h>
@@ -60,12 +60,12 @@ int main(int argc, char** argv)
     args_parser.add_positional_argument(path, "The font file for editing.", "file", Core::ArgsParser::Required::No);
     args_parser.parse(argc, argv);
 
-    RefPtr<Gfx::Font> edited_font;
+    RefPtr<Gfx::BitmapFont> edited_font;
     if (path == nullptr) {
         path = "/tmp/saved.font";
-        edited_font = Gfx::FontDatabase::default_font().clone();
+        edited_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::FontDatabase::default_font().clone());
     } else {
-        edited_font = Gfx::Font::load_from_file(path)->clone();
+        edited_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::Font::load_from_file(path)->clone());
         if (!edited_font) {
             String message = String::formatted("Couldn't load font: {}\n", path);
             GUI::MessageBox::show(nullptr, message, "Font Editor", GUI::MessageBox::Type::Error);
@@ -78,7 +78,7 @@ int main(int argc, char** argv)
     auto window = GUI::Window::construct();
     window->set_icon(app_icon.bitmap_for_size(16));
 
-    auto set_edited_font = [&](const String& path, RefPtr<Gfx::Font>&& font, Gfx::IntPoint point) {
+    auto set_edited_font = [&](const String& path, RefPtr<Gfx::BitmapFont>&& font, Gfx::IntPoint point) {
         // Convert 256 char font to 384 char font.
         if (font->type() == Gfx::FontTypes::Default)
             font->set_type(Gfx::FontTypes::LatinExtendedA);
@@ -97,7 +97,7 @@ int main(int argc, char** argv)
         if (!open_path.has_value())
             return;
 
-        RefPtr<Gfx::Font> new_font = Gfx::Font::load_from_file(open_path.value())->clone();
+        RefPtr<Gfx::BitmapFont> new_font = static_ptr_cast<Gfx::BitmapFont>(Gfx::Font::load_from_file(open_path.value())->clone());
         if (!new_font) {
             String message = String::formatted("Couldn't load font: {}\n", open_path.value());
             GUI::MessageBox::show(window, message, "Font Editor", GUI::MessageBox::Type::Error);

+ 325 - 0
Libraries/LibGfx/BitmapFont.cpp

@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "BitmapFont.h"
+#include "Bitmap.h"
+#include "Emoji.h"
+#include <AK/MappedFile.h>
+#include <AK/StdLibExtras.h>
+#include <AK/StringBuilder.h>
+#include <AK/Utf32View.h>
+#include <AK/Utf8View.h>
+#include <AK/Vector.h>
+#include <AK/kmalloc.h>
+#include <LibCore/FileStream.h>
+#include <LibGfx/FontDatabase.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace Gfx {
+
+struct [[gnu::packed]] FontFileHeader {
+    char magic[4];
+    u8 glyph_width;
+    u8 glyph_height;
+    u8 type;
+    u8 is_variable_width;
+    u8 glyph_spacing;
+    u8 baseline;
+    u8 mean_line;
+    u8 presentation_size;
+    u16 weight;
+    char name[32];
+    char family[32];
+};
+
+NonnullRefPtr<Font> BitmapFont::clone() const
+{
+    size_t bytes_per_glyph = sizeof(u32) * glyph_height();
+    auto* new_rows = static_cast<unsigned*>(malloc(bytes_per_glyph * m_glyph_count));
+    memcpy(new_rows, m_rows, bytes_per_glyph * m_glyph_count);
+    auto* new_widths = static_cast<u8*>(malloc(m_glyph_count));
+    if (m_glyph_widths)
+        memcpy(new_widths, m_glyph_widths, m_glyph_count);
+    else
+        memset(new_widths, m_glyph_width, m_glyph_count);
+    return adopt(*new BitmapFont(m_name, m_family, new_rows, new_widths, m_fixed_width, m_glyph_width, m_glyph_height, m_glyph_spacing, m_type, m_baseline, m_mean_line, m_presentation_size, m_weight, true));
+}
+
+NonnullRefPtr<BitmapFont> BitmapFont::create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type)
+{
+    size_t bytes_per_glyph = sizeof(u32) * glyph_height;
+    size_t count = glyph_count_by_type(type);
+    auto* new_rows = static_cast<unsigned*>(malloc(bytes_per_glyph * count));
+    memset(new_rows, 0, bytes_per_glyph * count);
+    auto* new_widths = static_cast<u8*>(malloc(count));
+    memset(new_widths, glyph_width, count);
+    return adopt(*new BitmapFont("Untitled", "Untitled", new_rows, new_widths, fixed, glyph_width, glyph_height, 1, type, 0, 0, 0, 400, true));
+}
+
+BitmapFont::BitmapFont(String name, String family, unsigned* rows, u8* widths, bool is_fixed_width, u8 glyph_width, u8 glyph_height, u8 glyph_spacing, FontTypes type, u8 baseline, u8 mean_line, u8 presentation_size, u16 weight, bool owns_arrays)
+    : m_name(name)
+    , m_family(family)
+    , m_type(type)
+    , m_rows(rows)
+    , m_glyph_widths(widths)
+    , m_glyph_width(glyph_width)
+    , m_glyph_height(glyph_height)
+    , m_min_glyph_width(glyph_width)
+    , m_max_glyph_width(glyph_width)
+    , m_glyph_spacing(glyph_spacing)
+    , m_baseline(baseline)
+    , m_mean_line(mean_line)
+    , m_presentation_size(presentation_size)
+    , m_weight(weight)
+    , m_fixed_width(is_fixed_width)
+    , m_owns_arrays(owns_arrays)
+{
+    update_x_height();
+
+    m_glyph_count = glyph_count_by_type(m_type);
+
+    if (!m_fixed_width) {
+        u8 maximum = 0;
+        u8 minimum = 255;
+        for (size_t i = 0; i < m_glyph_count; ++i) {
+            minimum = min(minimum, m_glyph_widths[i]);
+            maximum = max(maximum, m_glyph_widths[i]);
+        }
+        m_min_glyph_width = minimum;
+        m_max_glyph_width = maximum;
+    }
+}
+
+BitmapFont::~BitmapFont()
+{
+    if (m_owns_arrays) {
+        free(m_glyph_widths);
+        free(m_rows);
+    }
+}
+
+RefPtr<BitmapFont> BitmapFont::load_from_memory(const u8* data)
+{
+    auto& header = *reinterpret_cast<const FontFileHeader*>(data);
+    if (memcmp(header.magic, "!Fnt", 4)) {
+        dbgprintf("header.magic != '!Fnt', instead it's '%c%c%c%c'\n", header.magic[0], header.magic[1], header.magic[2], header.magic[3]);
+        return nullptr;
+    }
+    if (header.name[sizeof(header.name) - 1] != '\0') {
+        dbgprintf("Font name not fully null-terminated\n");
+        return nullptr;
+    }
+
+    if (header.family[sizeof(header.family) - 1] != '\0') {
+        dbgprintf("Font family not fully null-terminated\n");
+        return nullptr;
+    }
+
+    FontTypes type;
+    if (header.type == 0)
+        type = FontTypes::Default;
+    else if (header.type == 1)
+        type = FontTypes::LatinExtendedA;
+    else
+        ASSERT_NOT_REACHED();
+
+    size_t count = glyph_count_by_type(type);
+    size_t bytes_per_glyph = sizeof(unsigned) * header.glyph_height;
+
+    auto* rows = const_cast<unsigned*>((const unsigned*)(data + sizeof(FontFileHeader)));
+    u8* widths = nullptr;
+    if (header.is_variable_width)
+        widths = (u8*)(rows) + count * bytes_per_glyph;
+    return adopt(*new BitmapFont(String(header.name), String(header.family), rows, widths, !header.is_variable_width, header.glyph_width, header.glyph_height, header.glyph_spacing, type, header.baseline, header.mean_line, header.presentation_size, header.weight));
+}
+
+size_t BitmapFont::glyph_count_by_type(FontTypes type)
+{
+    if (type == FontTypes::Default)
+        return 256;
+
+    if (type == FontTypes::LatinExtendedA)
+        return 384;
+
+    dbg() << "Unknown font type:" << type;
+    ASSERT_NOT_REACHED();
+}
+
+RefPtr<BitmapFont> BitmapFont::load_from_file(const StringView& path)
+{
+    MappedFile mapped_file(path);
+    if (!mapped_file.is_valid())
+        return nullptr;
+
+    auto font = load_from_memory((const u8*)mapped_file.data());
+    if (!font)
+        return nullptr;
+
+    font->m_mapped_file = move(mapped_file);
+    return font;
+}
+
+bool BitmapFont::write_to_file(const StringView& path)
+{
+    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 = m_type;
+    header.baseline = m_baseline;
+    header.mean_line = m_mean_line;
+    header.is_variable_width = !m_fixed_width;
+    header.glyph_spacing = m_glyph_spacing;
+    header.presentation_size = m_presentation_size;
+    header.weight = m_weight;
+    memcpy(header.name, m_name.characters(), min(m_name.length(), sizeof(header.name) - 1));
+    memcpy(header.family, m_family.characters(), min(m_family.length(), sizeof(header.family) - 1));
+
+    size_t bytes_per_glyph = sizeof(unsigned) * m_glyph_height;
+    size_t count = glyph_count_by_type(m_type);
+
+    auto stream_result = Core::OutputFileStream::open_buffered(path);
+    if (stream_result.is_error())
+        return false;
+    auto& stream = stream_result.value();
+
+    stream << ReadonlyBytes { &header, sizeof(header) };
+    stream << ReadonlyBytes { m_rows, count * bytes_per_glyph };
+    stream << ReadonlyBytes { m_glyph_widths, count };
+
+    stream.flush();
+    if (stream.handle_any_error())
+        return false;
+
+    return true;
+}
+
+GlyphBitmap BitmapFont::glyph_bitmap(u32 code_point) const
+{
+    return GlyphBitmap(&m_rows[code_point * m_glyph_height], { glyph_width(code_point), m_glyph_height });
+}
+
+int BitmapFont::glyph_or_emoji_width(u32 code_point) const
+{
+    if (code_point < m_glyph_count)
+        return glyph_width(code_point);
+
+    if (m_fixed_width)
+        return m_glyph_width;
+
+    auto* emoji = Emoji::emoji_for_code_point(code_point);
+    if (emoji == nullptr)
+        return glyph_width('?');
+    return emoji->size().width();
+}
+
+int BitmapFont::width(const StringView& string) const
+{
+    Utf8View utf8 { string };
+    return width(utf8);
+}
+
+int BitmapFont::width(const Utf8View& utf8) const
+{
+    bool first = true;
+    int width = 0;
+
+    for (u32 code_point : utf8) {
+        if (!first)
+            width += glyph_spacing();
+        first = false;
+        width += glyph_or_emoji_width(code_point);
+    }
+
+    return width;
+}
+
+int BitmapFont::width(const Utf32View& view) const
+{
+    if (view.length() == 0)
+        return 0;
+    int width = (view.length() - 1) * glyph_spacing();
+    for (size_t i = 0; i < view.length(); ++i)
+        width += glyph_or_emoji_width(view.code_points()[i]);
+    return width;
+}
+
+void BitmapFont::set_type(FontTypes type)
+{
+    if (type == m_type)
+        return;
+
+    if (type == FontTypes::Default)
+        return;
+
+    size_t new_glyph_count = glyph_count_by_type(type);
+    if (new_glyph_count <= m_glyph_count) {
+        m_glyph_count = new_glyph_count;
+        return;
+    }
+
+    int item_count_to_copy = min(m_glyph_count, new_glyph_count);
+
+    size_t bytes_per_glyph = sizeof(u32) * glyph_height();
+
+    auto* new_rows = static_cast<unsigned*>(kmalloc(bytes_per_glyph * new_glyph_count));
+    memset(new_rows, (unsigned)0, bytes_per_glyph * new_glyph_count);
+    memcpy(new_rows, m_rows, bytes_per_glyph * item_count_to_copy);
+
+    auto* new_widths = static_cast<u8*>(kmalloc(new_glyph_count));
+    memset(new_widths, (u8)0, new_glyph_count);
+    memcpy(new_widths, m_glyph_widths, item_count_to_copy);
+
+    kfree(m_rows);
+    kfree(m_glyph_widths);
+
+    m_type = type;
+    m_glyph_count = new_glyph_count;
+    m_rows = new_rows;
+    m_glyph_widths = new_widths;
+}
+
+String BitmapFont::qualified_name() const
+{
+    return String::formatted("{} {} {}", family(), presentation_size(), weight());
+}
+
+const Font& BitmapFont::bold_variant() const
+{
+    if (m_bold_variant)
+        return *m_bold_variant;
+    m_bold_variant = Gfx::FontDatabase::the().get(m_family, m_presentation_size, 700);
+    if (!m_bold_variant)
+        m_bold_variant = this;
+    return *m_bold_variant;
+}
+
+}

+ 150 - 0
Libraries/LibGfx/BitmapFont.h

@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <AK/MappedFile.h>
+#include <AK/RefCounted.h>
+#include <AK/RefPtr.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+#include <LibGfx/Font.h>
+#include <LibGfx/Size.h>
+
+namespace Gfx {
+
+enum FontTypes {
+    Default = 0,
+    LatinExtendedA = 1
+};
+
+class BitmapFont : public Font {
+public:
+    NonnullRefPtr<Font> clone() const;
+    static NonnullRefPtr<BitmapFont> create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type);
+
+    static RefPtr<BitmapFont> load_from_file(const StringView& path);
+    bool write_to_file(const StringView& path);
+
+    ~BitmapFont();
+
+    u8 presentation_size() const { return m_presentation_size; }
+    void set_presentation_size(u8 size) { m_presentation_size = size; }
+
+    u16 weight() const { return m_weight; }
+    void set_weight(u16 weight) { m_weight = weight; }
+
+    GlyphBitmap glyph_bitmap(u32 code_point) const;
+
+    u8 glyph_width(size_t ch) const { return m_fixed_width ? m_glyph_width : m_glyph_widths[ch]; }
+    int glyph_or_emoji_width(u32 code_point) const;
+    u8 glyph_height() const { return m_glyph_height; }
+    int x_height() const { return m_x_height; }
+
+    u8 min_glyph_width() const { return m_min_glyph_width; }
+    u8 max_glyph_width() const { return m_max_glyph_width; }
+    u8 glyph_fixed_width() const { return m_glyph_width; }
+
+    u8 baseline() const { return m_baseline; }
+    void set_baseline(u8 baseline)
+    {
+        m_baseline = baseline;
+        update_x_height();
+    }
+
+    u8 mean_line() const { return m_mean_line; }
+    void set_mean_line(u8 mean_line)
+    {
+        m_mean_line = mean_line;
+        update_x_height();
+    }
+
+    int width(const StringView&) const;
+    int width(const Utf8View&) const;
+    int width(const Utf32View&) const;
+
+    const String& name() const { return m_name; }
+    void set_name(String name) { m_name = move(name); }
+
+    bool is_fixed_width() const { return m_fixed_width; }
+    void set_fixed_width(bool b) { m_fixed_width = b; }
+
+    u8 glyph_spacing() const { return m_glyph_spacing; }
+    void set_glyph_spacing(u8 spacing) { m_glyph_spacing = spacing; }
+
+    void set_glyph_width(size_t ch, u8 width)
+    {
+        ASSERT(m_glyph_widths);
+        m_glyph_widths[ch] = width;
+    }
+
+    int glyph_count() const { return m_glyph_count; }
+
+    FontTypes type() { return m_type; }
+    void set_type(FontTypes type);
+
+    const String& family() const { return m_family; }
+    void set_family(String family) { m_family = move(family); }
+
+    String qualified_name() const;
+
+    const Font& bold_variant() const;
+
+private:
+    BitmapFont(String name, String family, unsigned* rows, u8* widths, bool is_fixed_width, u8 glyph_width, u8 glyph_height, u8 glyph_spacing, FontTypes type, u8 baseline, u8 mean_line, u8 presentation_size, u16 weight, bool owns_arrays = false);
+
+    static RefPtr<BitmapFont> load_from_memory(const u8*);
+    static size_t glyph_count_by_type(FontTypes type);
+
+    void update_x_height() { m_x_height = m_baseline - m_mean_line; };
+
+    String m_name;
+    String m_family;
+    FontTypes m_type;
+    size_t m_glyph_count { 256 };
+
+    unsigned* m_rows { nullptr };
+    u8* m_glyph_widths { nullptr };
+    MappedFile m_mapped_file;
+
+    u8 m_glyph_width { 0 };
+    u8 m_glyph_height { 0 };
+    u8 m_x_height { 0 };
+    u8 m_min_glyph_width { 0 };
+    u8 m_max_glyph_width { 0 };
+    u8 m_glyph_spacing { 0 };
+    u8 m_baseline { 0 };
+    u8 m_mean_line { 0 };
+    u8 m_presentation_size { 0 };
+    u16 m_weight { 0 };
+
+    bool m_fixed_width { false };
+    bool m_owns_arrays { false };
+
+    mutable RefPtr<Gfx::Font> m_bold_variant;
+};
+
+}

+ 1 - 0
Libraries/LibGfx/CMakeLists.txt

@@ -1,6 +1,7 @@
 set(SOURCES
     AffineTransform.cpp
     Bitmap.cpp
+    BitmapFont.cpp
     BMPLoader.cpp
     BMPWriter.cpp
     CharacterBitmap.cpp

+ 7 - 292
Libraries/LibGfx/Font.cpp

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -24,302 +24,17 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include "Font.h"
-#include "Bitmap.h"
-#include "Emoji.h"
-#include <AK/MappedFile.h>
-#include <AK/StdLibExtras.h>
-#include <AK/StringBuilder.h>
-#include <AK/Utf32View.h>
-#include <AK/Utf8View.h>
-#include <AK/Vector.h>
-#include <AK/kmalloc.h>
-#include <LibCore/FileStream.h>
-#include <LibGfx/FontDatabase.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <unistd.h>
+#include <LibGfx/BitmapFont.h>
+#include <LibGfx/Font.h>
 
 namespace Gfx {
 
-struct [[gnu::packed]] FontFileHeader {
-    char magic[4];
-    u8 glyph_width;
-    u8 glyph_height;
-    u8 type;
-    u8 is_variable_width;
-    u8 glyph_spacing;
-    u8 baseline;
-    u8 mean_line;
-    u8 presentation_size;
-    u16 weight;
-    char name[32];
-    char family[32];
-};
-
-NonnullRefPtr<Font> Font::clone() const
-{
-    size_t bytes_per_glyph = sizeof(u32) * glyph_height();
-    auto* new_rows = static_cast<unsigned*>(malloc(bytes_per_glyph * m_glyph_count));
-    memcpy(new_rows, m_rows, bytes_per_glyph * m_glyph_count);
-    auto* new_widths = static_cast<u8*>(malloc(m_glyph_count));
-    if (m_glyph_widths)
-        memcpy(new_widths, m_glyph_widths, m_glyph_count);
-    else
-        memset(new_widths, m_glyph_width, m_glyph_count);
-    return adopt(*new Font(m_name, m_family, new_rows, new_widths, m_fixed_width, m_glyph_width, m_glyph_height, m_glyph_spacing, m_type, m_baseline, m_mean_line, m_presentation_size, m_weight, true));
-}
-
-NonnullRefPtr<Font> Font::create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type)
-{
-    size_t bytes_per_glyph = sizeof(u32) * glyph_height;
-    size_t count = glyph_count_by_type(type);
-    auto* new_rows = static_cast<unsigned*>(malloc(bytes_per_glyph * count));
-    memset(new_rows, 0, bytes_per_glyph * count);
-    auto* new_widths = static_cast<u8*>(malloc(count));
-    memset(new_widths, glyph_width, count);
-    return adopt(*new Font("Untitled", "Untitled", new_rows, new_widths, fixed, glyph_width, glyph_height, 1, type, 0, 0, 0, 400, true));
-}
-
-Font::Font(String name, String family, unsigned* rows, u8* widths, bool is_fixed_width, u8 glyph_width, u8 glyph_height, u8 glyph_spacing, FontTypes type, u8 baseline, u8 mean_line, u8 presentation_size, u16 weight, bool owns_arrays)
-    : m_name(name)
-    , m_family(family)
-    , m_type(type)
-    , m_rows(rows)
-    , m_glyph_widths(widths)
-    , m_glyph_width(glyph_width)
-    , m_glyph_height(glyph_height)
-    , m_min_glyph_width(glyph_width)
-    , m_max_glyph_width(glyph_width)
-    , m_glyph_spacing(glyph_spacing)
-    , m_baseline(baseline)
-    , m_mean_line(mean_line)
-    , m_presentation_size(presentation_size)
-    , m_weight(weight)
-    , m_fixed_width(is_fixed_width)
-    , m_owns_arrays(owns_arrays)
-{
-    update_x_height();
-
-    m_glyph_count = glyph_count_by_type(m_type);
-
-    if (!m_fixed_width) {
-        u8 maximum = 0;
-        u8 minimum = 255;
-        for (size_t i = 0; i < m_glyph_count; ++i) {
-            minimum = min(minimum, m_glyph_widths[i]);
-            maximum = max(maximum, m_glyph_widths[i]);
-        }
-        m_min_glyph_width = minimum;
-        m_max_glyph_width = maximum;
-    }
-}
-
-Font::~Font()
-{
-    if (m_owns_arrays) {
-        free(m_glyph_widths);
-        free(m_rows);
-    }
-}
-
-RefPtr<Font> Font::load_from_memory(const u8* data)
-{
-    auto& header = *reinterpret_cast<const FontFileHeader*>(data);
-    if (memcmp(header.magic, "!Fnt", 4)) {
-        dbgprintf("header.magic != '!Fnt', instead it's '%c%c%c%c'\n", header.magic[0], header.magic[1], header.magic[2], header.magic[3]);
-        return nullptr;
-    }
-    if (header.name[sizeof(header.name) - 1] != '\0') {
-        dbgprintf("Font name not fully null-terminated\n");
-        return nullptr;
-    }
-
-    if (header.family[sizeof(header.family) - 1] != '\0') {
-        dbgprintf("Font family not fully null-terminated\n");
-        return nullptr;
-    }
-
-    FontTypes type;
-    if (header.type == 0)
-        type = FontTypes::Default;
-    else if (header.type == 1)
-        type = FontTypes::LatinExtendedA;
-    else
-        ASSERT_NOT_REACHED();
-
-    size_t count = glyph_count_by_type(type);
-    size_t bytes_per_glyph = sizeof(unsigned) * header.glyph_height;
-
-    auto* rows = const_cast<unsigned*>((const unsigned*)(data + sizeof(FontFileHeader)));
-    u8* widths = nullptr;
-    if (header.is_variable_width)
-        widths = (u8*)(rows) + count * bytes_per_glyph;
-    return adopt(*new Font(String(header.name), String(header.family), rows, widths, !header.is_variable_width, header.glyph_width, header.glyph_height, header.glyph_spacing, type, header.baseline, header.mean_line, header.presentation_size, header.weight));
-}
-
-size_t Font::glyph_count_by_type(FontTypes type)
-{
-    if (type == FontTypes::Default)
-        return 256;
-
-    if (type == FontTypes::LatinExtendedA)
-        return 384;
-
-    dbg() << "Unknown font type:" << type;
-    ASSERT_NOT_REACHED();
-}
-
 RefPtr<Font> Font::load_from_file(const StringView& path)
 {
-    MappedFile mapped_file(path);
-    if (!mapped_file.is_valid())
-        return nullptr;
-
-    auto font = load_from_memory((const u8*)mapped_file.data());
-    if (!font)
-        return nullptr;
-
-    font->m_mapped_file = move(mapped_file);
-    return font;
-}
-
-bool Font::write_to_file(const StringView& path)
-{
-    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 = m_type;
-    header.baseline = m_baseline;
-    header.mean_line = m_mean_line;
-    header.is_variable_width = !m_fixed_width;
-    header.glyph_spacing = m_glyph_spacing;
-    header.presentation_size = m_presentation_size;
-    header.weight = m_weight;
-    memcpy(header.name, m_name.characters(), min(m_name.length(), sizeof(header.name) - 1));
-    memcpy(header.family, m_family.characters(), min(m_family.length(), sizeof(header.family) - 1));
-
-    size_t bytes_per_glyph = sizeof(unsigned) * m_glyph_height;
-    size_t count = glyph_count_by_type(m_type);
-
-    auto stream_result = Core::OutputFileStream::open_buffered(path);
-    if (stream_result.is_error())
-        return false;
-    auto& stream = stream_result.value();
-
-    stream << ReadonlyBytes { &header, sizeof(header) };
-    stream << ReadonlyBytes { m_rows, count * bytes_per_glyph };
-    stream << ReadonlyBytes { m_glyph_widths, count };
-
-    stream.flush();
-    if (stream.handle_any_error())
-        return false;
-
-    return true;
-}
-
-GlyphBitmap Font::glyph_bitmap(u32 code_point) const
-{
-    return GlyphBitmap(&m_rows[code_point * m_glyph_height], { glyph_width(code_point), m_glyph_height });
-}
-
-int Font::glyph_or_emoji_width(u32 code_point) const
-{
-    if (code_point < m_glyph_count)
-        return glyph_width(code_point);
-
-    if (m_fixed_width)
-        return m_glyph_width;
-
-    auto* emoji = Emoji::emoji_for_code_point(code_point);
-    if (emoji == nullptr)
-        return glyph_width('?');
-    return emoji->size().width();
-}
-
-int Font::width(const StringView& string) const
-{
-    Utf8View utf8 { string };
-    return width(utf8);
-}
-
-int Font::width(const Utf8View& utf8) const
-{
-    bool first = true;
-    int width = 0;
-
-    for (u32 code_point : utf8) {
-        if (!first)
-            width += glyph_spacing();
-        first = false;
-        width += glyph_or_emoji_width(code_point);
-    }
-
-    return width;
-}
-
-int Font::width(const Utf32View& view) const
-{
-    if (view.length() == 0)
-        return 0;
-    int width = (view.length() - 1) * glyph_spacing();
-    for (size_t i = 0; i < view.length(); ++i)
-        width += glyph_or_emoji_width(view.code_points()[i]);
-    return width;
-}
-
-void Font::set_type(FontTypes type)
-{
-    if (type == m_type)
-        return;
-
-    if (type == FontTypes::Default)
-        return;
-
-    size_t new_glyph_count = glyph_count_by_type(type);
-    if (new_glyph_count <= m_glyph_count) {
-        m_glyph_count = new_glyph_count;
-        return;
+    if (path.ends_with(".font")) {
+        return BitmapFont::load_from_file(path);
     }
-
-    int item_count_to_copy = min(m_glyph_count, new_glyph_count);
-
-    size_t bytes_per_glyph = sizeof(u32) * glyph_height();
-
-    auto* new_rows = static_cast<unsigned*>(kmalloc(bytes_per_glyph * new_glyph_count));
-    memset(new_rows, (unsigned)0, bytes_per_glyph * new_glyph_count);
-    memcpy(new_rows, m_rows, bytes_per_glyph * item_count_to_copy);
-
-    auto* new_widths = static_cast<u8*>(kmalloc(new_glyph_count));
-    memset(new_widths, (u8)0, new_glyph_count);
-    memcpy(new_widths, m_glyph_widths, item_count_to_copy);
-
-    kfree(m_rows);
-    kfree(m_glyph_widths);
-
-    m_type = type;
-    m_glyph_count = new_glyph_count;
-    m_rows = new_rows;
-    m_glyph_widths = new_widths;
-}
-
-String Font::qualified_name() const
-{
-    return String::formatted("{} {} {}", family(), presentation_size(), weight());
+    return {};
 }
 
-const Font& Font::bold_variant() const
-{
-    if (m_bold_variant)
-        return *m_bold_variant;
-    m_bold_variant = Gfx::FontDatabase::the().get(m_family, m_presentation_size, 700);
-    if (!m_bold_variant)
-        m_bold_variant = this;
-    return *m_bold_variant;
-}
-
-}
+}

+ 26 - 94
Libraries/LibGfx/Font.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -35,14 +35,9 @@
 
 namespace Gfx {
 
-enum FontTypes {
-    Default = 0,
-    LatinExtendedA = 1
-};
-
 // FIXME: Make a MutableGlyphBitmap buddy class for FontEditor instead?
 class GlyphBitmap {
-    friend class Font;
+    friend class BitmapFont;
 
 public:
     const unsigned* rows() const { return m_rows; }
@@ -75,108 +70,45 @@ private:
 
 class Font : public RefCounted<Font> {
 public:
-    NonnullRefPtr<Font> clone() const;
-    static NonnullRefPtr<Font> create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type);
-
     static RefPtr<Font> load_from_file(const StringView& path);
-    bool write_to_file(const StringView& path);
-
-    ~Font();
-
-    u8 presentation_size() const { return m_presentation_size; }
-    void set_presentation_size(u8 size) { m_presentation_size = size; }
-
-    u16 weight() const { return m_weight; }
-    void set_weight(u16 weight) { m_weight = weight; }
-
-    GlyphBitmap glyph_bitmap(u32 code_point) const;
-
-    u8 glyph_width(size_t ch) const { return m_fixed_width ? m_glyph_width : m_glyph_widths[ch]; }
-    int glyph_or_emoji_width(u32 code_point) const;
-    u8 glyph_height() const { return m_glyph_height; }
-    int x_height() const { return m_x_height; }
-
-    u8 min_glyph_width() const { return m_min_glyph_width; }
-    u8 max_glyph_width() const { return m_max_glyph_width; }
-    u8 glyph_fixed_width() const { return m_glyph_width; }
-
-    u8 baseline() const { return m_baseline; }
-    void set_baseline(u8 baseline)
-    {
-        m_baseline = baseline;
-        update_x_height();
-    }
-
-    u8 mean_line() const { return m_mean_line; }
-    void set_mean_line(u8 mean_line)
-    {
-        m_mean_line = mean_line;
-        update_x_height();
-    }
 
-    int width(const StringView&) const;
-    int width(const Utf8View&) const;
-    int width(const Utf32View&) const;
+    virtual NonnullRefPtr<Font> clone() const = 0;
+    virtual ~Font() {};
 
-    const String& name() const { return m_name; }
-    void set_name(String name) { m_name = move(name); }
+    virtual u8 presentation_size() const = 0;
 
-    bool is_fixed_width() const { return m_fixed_width; }
-    void set_fixed_width(bool b) { m_fixed_width = b; }
+    virtual u16 weight() const = 0;
+    virtual GlyphBitmap glyph_bitmap(u32 code_point) const = 0;
 
-    u8 glyph_spacing() const { return m_glyph_spacing; }
-    void set_glyph_spacing(u8 spacing) { m_glyph_spacing = spacing; }
+    virtual u8 glyph_width(size_t ch) const = 0;
+    virtual int glyph_or_emoji_width(u32 code_point) const = 0;
+    virtual u8 glyph_height() const = 0;
+    virtual int x_height() const = 0;
 
-    void set_glyph_width(size_t ch, u8 width)
-    {
-        ASSERT(m_glyph_widths);
-        m_glyph_widths[ch] = width;
-    }
+    virtual u8 min_glyph_width() const = 0;
+    virtual u8 max_glyph_width() const = 0;
+    virtual u8 glyph_fixed_width() const = 0;
 
-    int glyph_count() const { return m_glyph_count; }
+    virtual u8 baseline() const = 0;
+    virtual u8 mean_line() const = 0;
 
-    FontTypes type() { return m_type; }
-    void set_type(FontTypes type);
-
-    const String& family() const { return m_family; }
-    void set_family(String family) { m_family = move(family); }
-
-    String qualified_name() const;
-
-    const Font& bold_variant() const;
-
-private:
-    Font(String name, String family, unsigned* rows, u8* widths, bool is_fixed_width, u8 glyph_width, u8 glyph_height, u8 glyph_spacing, FontTypes type, u8 baseline, u8 mean_line, u8 presentation_size, u16 weight, bool owns_arrays = false);
+    virtual int width(const StringView&) const = 0;
+    virtual int width(const Utf8View&) const = 0;
+    virtual int width(const Utf32View&) const = 0;
 
-    static RefPtr<Font> load_from_memory(const u8*);
-    static size_t glyph_count_by_type(FontTypes type);
+    virtual const String& name() const = 0;
 
-    void update_x_height() { m_x_height = m_baseline - m_mean_line; };
+    virtual bool is_fixed_width() const = 0;
 
-    String m_name;
-    String m_family;
-    FontTypes m_type;
-    size_t m_glyph_count { 256 };
+    virtual u8 glyph_spacing() const = 0;
 
-    unsigned* m_rows { nullptr };
-    u8* m_glyph_widths { nullptr };
-    MappedFile m_mapped_file;
+    virtual int glyph_count() const = 0;
 
-    u8 m_glyph_width { 0 };
-    u8 m_glyph_height { 0 };
-    u8 m_x_height { 0 };
-    u8 m_min_glyph_width { 0 };
-    u8 m_max_glyph_width { 0 };
-    u8 m_glyph_spacing { 0 };
-    u8 m_baseline { 0 };
-    u8 m_mean_line { 0 };
-    u8 m_presentation_size { 0 };
-    u16 m_weight { 0 };
+    virtual const String& family() const = 0;
 
-    bool m_fixed_width { false };
-    bool m_owns_arrays { false };
+    virtual String qualified_name() const = 0;
 
-    mutable RefPtr<Gfx::Font> m_bold_variant;
+    virtual const Font& bold_variant() const = 0;
 };
 
 }

+ 8 - 8
Userland/test-gfx-font.cpp

@@ -24,7 +24,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-#include <LibGfx/Font.h>
+#include <LibGfx/BitmapFont.h>
 #include <LibGfx/FontDatabase.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -72,7 +72,7 @@ static void test_clone()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
 
     auto new_font = font->clone();
     assert(!new_font->name().is_null());
@@ -85,7 +85,7 @@ static void test_set_name()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
 
     const char* name = "my newly created font";
     font->set_name(name);
@@ -98,7 +98,7 @@ static void test_set_type()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
 
     auto type = Gfx::FontTypes::Default;
     font->set_type(type);
@@ -110,7 +110,7 @@ static void test_width()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
 
     assert(font->width("A") == glyph_width);
 }
@@ -119,7 +119,7 @@ static void test_glyph_or_emoji_width()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
     font->set_type(Gfx::FontTypes::Default);
 
     assert(font->glyph_or_emoji_width(0));
@@ -127,7 +127,7 @@ static void test_glyph_or_emoji_width()
 
 static void test_load_from_file()
 {
-    auto font = Gfx::Font::load_from_file("/res/fonts/PebbletonBold14.font");
+    auto font = Gfx::BitmapFont::load_from_file("/res/fonts/PebbletonBold14.font");
     assert(!font->name().is_null());
 }
 
@@ -135,7 +135,7 @@ static void test_write_to_file()
 {
     u8 glyph_height = 1;
     u8 glyph_width = 1;
-    auto font = Gfx::Font::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
+    auto font = Gfx::BitmapFont::create(glyph_height, glyph_width, true, Gfx::FontTypes::Default);
 
     char path[] = "/tmp/new.font.XXXXXX";
     assert(mkstemp(path) != -1);