diff --git a/Applications/FontEditor/FontEditor.cpp b/Applications/FontEditor/FontEditor.cpp index 25e3479346..56cbf3e06b 100644 --- a/Applications/FontEditor/FontEditor.cpp +++ b/Applications/FontEditor/FontEditor.cpp @@ -38,11 +38,11 @@ #include #include #include -#include +#include #include #include -FontEditorWidget::FontEditorWidget(const String& path, RefPtr&& edited_font) +FontEditorWidget::FontEditorWidget(const String& path, RefPtr&& edited_font) : m_edited_font(move(edited_font)) , m_path(path) { diff --git a/Applications/FontEditor/FontEditor.h b/Applications/FontEditor/FontEditor.h index 785af863e3..ed904e8e7d 100644 --- a/Applications/FontEditor/FontEditor.h +++ b/Applications/FontEditor/FontEditor.h @@ -28,6 +28,7 @@ #include #include +#include class GlyphEditorWidget; class GlyphMapWidget; @@ -45,8 +46,8 @@ public: const String& path() { return m_path; } private: - FontEditorWidget(const String& path, RefPtr&&); - RefPtr m_edited_font; + FontEditorWidget(const String& path, RefPtr&&); + RefPtr m_edited_font; RefPtr m_glyph_map_widget; RefPtr m_glyph_editor_widget; diff --git a/Applications/FontEditor/GlyphEditorWidget.cpp b/Applications/FontEditor/GlyphEditorWidget.cpp index 57cbed8058..6f545c1ccd 100644 --- a/Applications/FontEditor/GlyphEditorWidget.cpp +++ b/Applications/FontEditor/GlyphEditorWidget.cpp @@ -26,10 +26,10 @@ #include "GlyphEditorWidget.h" #include -#include +#include #include -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() }); diff --git a/Applications/FontEditor/GlyphEditorWidget.h b/Applications/FontEditor/GlyphEditorWidget.h index f53d78d851..a54c551642 100644 --- a/Applications/FontEditor/GlyphEditorWidget.h +++ b/Applications/FontEditor/GlyphEditorWidget.h @@ -28,6 +28,7 @@ #include #include +#include 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 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 m_font; + RefPtr m_font; int m_glyph { 0 }; int m_scale { 10 }; }; diff --git a/Applications/FontEditor/GlyphMapWidget.cpp b/Applications/FontEditor/GlyphMapWidget.cpp index 6d0561ad89..ed6a65493f 100644 --- a/Applications/FontEditor/GlyphMapWidget.cpp +++ b/Applications/FontEditor/GlyphMapWidget.cpp @@ -26,10 +26,10 @@ #include "GlyphMapWidget.h" #include -#include +#include #include -GlyphMapWidget::GlyphMapWidget(Gfx::Font& mutable_font) +GlyphMapWidget::GlyphMapWidget(Gfx::BitmapFont& mutable_font) : m_font(mutable_font) { m_glyph_count = mutable_font.glyph_count(); diff --git a/Applications/FontEditor/GlyphMapWidget.h b/Applications/FontEditor/GlyphMapWidget.h index 4c6243ef68..2c8443a251 100644 --- a/Applications/FontEditor/GlyphMapWidget.h +++ b/Applications/FontEditor/GlyphMapWidget.h @@ -29,6 +29,7 @@ #include #include #include +#include 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 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 m_font; + RefPtr m_font; int m_glyph_count; int m_columns { 32 }; int m_horizontal_spacing { 2 }; diff --git a/Applications/FontEditor/main.cpp b/Applications/FontEditor/main.cpp index 9108445f1b..873248bd4e 100644 --- a/Applications/FontEditor/main.cpp +++ b/Applications/FontEditor/main.cpp @@ -36,7 +36,7 @@ #include #include #include -#include +#include #include #include #include @@ -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 edited_font; + RefPtr edited_font; if (path == nullptr) { path = "/tmp/saved.font"; - edited_font = Gfx::FontDatabase::default_font().clone(); + edited_font = static_ptr_cast(Gfx::FontDatabase::default_font().clone()); } else { - edited_font = Gfx::Font::load_from_file(path)->clone(); + edited_font = static_ptr_cast(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&& font, Gfx::IntPoint point) { + auto set_edited_font = [&](const String& path, RefPtr&& 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 new_font = Gfx::Font::load_from_file(open_path.value())->clone(); + RefPtr new_font = static_ptr_cast(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); diff --git a/Libraries/LibGfx/BitmapFont.cpp b/Libraries/LibGfx/BitmapFont.cpp new file mode 100644 index 0000000000..9933a7af4e --- /dev/null +++ b/Libraries/LibGfx/BitmapFont.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 BitmapFont::clone() const +{ + size_t bytes_per_glyph = sizeof(u32) * glyph_height(); + auto* new_rows = static_cast(malloc(bytes_per_glyph * m_glyph_count)); + memcpy(new_rows, m_rows, bytes_per_glyph * m_glyph_count); + auto* new_widths = static_cast(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::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(malloc(bytes_per_glyph * count)); + memset(new_rows, 0, bytes_per_glyph * count); + auto* new_widths = static_cast(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::load_from_memory(const u8* data) +{ + auto& header = *reinterpret_cast(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((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::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(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(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; +} + +} diff --git a/Libraries/LibGfx/BitmapFont.h b/Libraries/LibGfx/BitmapFont.h new file mode 100644 index 0000000000..ef62a63da6 --- /dev/null +++ b/Libraries/LibGfx/BitmapFont.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling + * 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 +#include +#include +#include +#include +#include +#include + +namespace Gfx { + +enum FontTypes { + Default = 0, + LatinExtendedA = 1 +}; + +class BitmapFont : public Font { +public: + NonnullRefPtr clone() const; + static NonnullRefPtr create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type); + + static RefPtr 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 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 m_bold_variant; +}; + +} diff --git a/Libraries/LibGfx/CMakeLists.txt b/Libraries/LibGfx/CMakeLists.txt index b919b3246a..2a6d9750db 100644 --- a/Libraries/LibGfx/CMakeLists.txt +++ b/Libraries/LibGfx/CMakeLists.txt @@ -1,6 +1,7 @@ set(SOURCES AffineTransform.cpp Bitmap.cpp + BitmapFont.cpp BMPLoader.cpp BMPWriter.cpp CharacterBitmap.cpp diff --git a/Libraries/LibGfx/Font.cpp b/Libraries/LibGfx/Font.cpp index 1706a90846..073279c4f8 100644 --- a/Libraries/LibGfx/Font.cpp +++ b/Libraries/LibGfx/Font.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2020, Stephan Unverwerth * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include 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::clone() const -{ - size_t bytes_per_glyph = sizeof(u32) * glyph_height(); - auto* new_rows = static_cast(malloc(bytes_per_glyph * m_glyph_count)); - memcpy(new_rows, m_rows, bytes_per_glyph * m_glyph_count); - auto* new_widths = static_cast(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::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(malloc(bytes_per_glyph * count)); - memset(new_rows, 0, bytes_per_glyph * count); - auto* new_widths = static_cast(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::load_from_memory(const u8* data) -{ - auto& header = *reinterpret_cast(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((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::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); + if (path.ends_with(".font")) { + return BitmapFont::load_from_file(path); } - - return width; + return {}; } -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; - } - - 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(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(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()); -} - -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; -} - -} +} \ No newline at end of file diff --git a/Libraries/LibGfx/Font.h b/Libraries/LibGfx/Font.h index a997a53d0f..2e74798725 100644 --- a/Libraries/LibGfx/Font.h +++ b/Libraries/LibGfx/Font.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2020, Stephan Unverwerth * 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 { public: - NonnullRefPtr clone() const; - static NonnullRefPtr create(u8 glyph_height, u8 glyph_width, bool fixed, FontTypes type); - static RefPtr load_from_file(const StringView& path); - bool write_to_file(const StringView& path); - ~Font(); + virtual NonnullRefPtr clone() const = 0; + virtual ~Font() {}; - u8 presentation_size() const { return m_presentation_size; } - void set_presentation_size(u8 size) { m_presentation_size = size; } + virtual u8 presentation_size() const = 0; - u16 weight() const { return m_weight; } - void set_weight(u16 weight) { m_weight = weight; } + virtual u16 weight() const = 0; + virtual GlyphBitmap glyph_bitmap(u32 code_point) const = 0; - GlyphBitmap glyph_bitmap(u32 code_point) const; + 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; - 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; } + virtual u8 min_glyph_width() const = 0; + virtual u8 max_glyph_width() const = 0; + virtual u8 glyph_fixed_width() const = 0; - 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; } + virtual u8 baseline() const = 0; + virtual u8 mean_line() const = 0; - u8 baseline() const { return m_baseline; } - void set_baseline(u8 baseline) - { - m_baseline = baseline; - update_x_height(); - } + virtual int width(const StringView&) const = 0; + virtual int width(const Utf8View&) const = 0; + virtual int width(const Utf32View&) const = 0; - u8 mean_line() const { return m_mean_line; } - void set_mean_line(u8 mean_line) - { - m_mean_line = mean_line; - update_x_height(); - } + virtual const String& name() const = 0; - int width(const StringView&) const; - int width(const Utf8View&) const; - int width(const Utf32View&) const; + virtual bool is_fixed_width() const = 0; - const String& name() const { return m_name; } - void set_name(String name) { m_name = move(name); } + virtual u8 glyph_spacing() const = 0; - bool is_fixed_width() const { return m_fixed_width; } - void set_fixed_width(bool b) { m_fixed_width = b; } + virtual int glyph_count() const = 0; - u8 glyph_spacing() const { return m_glyph_spacing; } - void set_glyph_spacing(u8 spacing) { m_glyph_spacing = spacing; } + virtual const String& family() const = 0; - void set_glyph_width(size_t ch, u8 width) - { - ASSERT(m_glyph_widths); - m_glyph_widths[ch] = width; - } + virtual String qualified_name() const = 0; - 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: - 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); - - static RefPtr 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 m_bold_variant; + virtual const Font& bold_variant() const = 0; }; } diff --git a/Userland/test-gfx-font.cpp b/Userland/test-gfx-font.cpp index ea0f3161d5..e1bf939bb5 100644 --- a/Userland/test-gfx-font.cpp +++ b/Userland/test-gfx-font.cpp @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include +#include #include #include #include @@ -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);