Serenity Operating System
at hosted 258 lines 8.3 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "Font.h" 28#include "Bitmap.h" 29#include "Emoji.h" 30#include <AK/BufferStream.h> 31#include <AK/MappedFile.h> 32#include <AK/StdLibExtras.h> 33#include <AK/Utf8View.h> 34#include <AK/kmalloc.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <stdio.h> 38#include <stdlib.h> 39#include <string.h> 40#include <sys/mman.h> 41#include <unistd.h> 42 43namespace Gfx { 44 45struct [[gnu::packed]] FontFileHeader 46{ 47 char magic[4]; 48 u8 glyph_width; 49 u8 glyph_height; 50 u8 type; 51 u8 is_variable_width; 52 u8 glyph_spacing; 53 u8 unused[5]; 54 char name[64]; 55}; 56 57Font& Font::default_font() 58{ 59 static Font* s_default_font; 60 static const char* default_font_path = "/res/fonts/Katica10.font"; 61 if (!s_default_font) { 62 s_default_font = Font::load_from_file(default_font_path).leak_ref(); 63 ASSERT(s_default_font); 64 } 65 return *s_default_font; 66} 67 68Font& Font::default_fixed_width_font() 69{ 70 static Font* s_default_fixed_width_font; 71 static const char* default_fixed_width_font_path = "/res/fonts/CsillaThin7x10.font"; 72 if (!s_default_fixed_width_font) { 73 s_default_fixed_width_font = Font::load_from_file(default_fixed_width_font_path).leak_ref(); 74 ASSERT(s_default_fixed_width_font); 75 } 76 return *s_default_fixed_width_font; 77} 78 79Font& Font::default_bold_fixed_width_font() 80{ 81 static Font* font; 82 static const char* default_bold_fixed_width_font_path = "/res/fonts/CsillaBold7x10.font"; 83 if (!font) { 84 font = Font::load_from_file(default_bold_fixed_width_font_path).leak_ref(); 85 ASSERT(font); 86 } 87 return *font; 88} 89 90Font& Font::default_bold_font() 91{ 92 static Font* s_default_bold_font; 93 static const char* default_bold_font_path = "/res/fonts/KaticaBold10.font"; 94 if (!s_default_bold_font) { 95 s_default_bold_font = Font::load_from_file(default_bold_font_path).leak_ref(); 96 ASSERT(s_default_bold_font); 97 } 98 return *s_default_bold_font; 99} 100 101NonnullRefPtr<Font> Font::clone() const 102{ 103 size_t bytes_per_glyph = sizeof(u32) * glyph_height(); 104 // FIXME: This is leaked! 105 auto* new_rows = static_cast<unsigned*>(kmalloc(bytes_per_glyph * 256)); 106 memcpy(new_rows, m_rows, bytes_per_glyph * 256); 107 auto* new_widths = static_cast<u8*>(kmalloc(256)); 108 if (m_glyph_widths) 109 memcpy(new_widths, m_glyph_widths, 256); 110 else 111 memset(new_widths, m_glyph_width, 256); 112 return adopt(*new Font(m_name, new_rows, new_widths, m_fixed_width, m_glyph_width, m_glyph_height, m_glyph_spacing)); 113} 114 115NonnullRefPtr<Font> Font::create(u8 glyph_height, u8 glyph_width, bool fixed) 116{ 117 size_t bytes_per_glyph = sizeof(u32) * glyph_height; 118 // FIXME: This is leaked! 119 auto* new_rows = static_cast<unsigned*>(malloc(bytes_per_glyph * 256)); 120 memset(new_rows, 0, bytes_per_glyph * 256); 121 auto* new_widths = static_cast<u8*>(malloc(256)); 122 memset(new_widths, glyph_width, 256); 123 return adopt(*new Font("Untitled", new_rows, new_widths, fixed, glyph_width, glyph_height, 1)); 124} 125 126Font::Font(const StringView& name, unsigned* rows, u8* widths, bool is_fixed_width, u8 glyph_width, u8 glyph_height, u8 glyph_spacing) 127 : m_name(name) 128 , m_rows(rows) 129 , m_glyph_widths(widths) 130 , m_glyph_width(glyph_width) 131 , m_glyph_height(glyph_height) 132 , m_min_glyph_width(glyph_width) 133 , m_max_glyph_width(glyph_width) 134 , m_glyph_spacing(glyph_spacing) 135 , m_fixed_width(is_fixed_width) 136{ 137 if (!m_fixed_width) { 138 u8 maximum = 0; 139 u8 minimum = 255; 140 for (int i = 0; i < 256; ++i) { 141 minimum = min(minimum, m_glyph_widths[i]); 142 maximum = max(maximum, m_glyph_widths[i]); 143 } 144 m_min_glyph_width = minimum; 145 m_max_glyph_width = maximum; 146 } 147} 148 149Font::~Font() 150{ 151} 152 153RefPtr<Font> Font::load_from_memory(const u8* data) 154{ 155 auto& header = *reinterpret_cast<const FontFileHeader*>(data); 156 if (memcmp(header.magic, "!Fnt", 4)) { 157 dbgprintf("header.magic != '!Fnt', instead it's '%c%c%c%c'\n", header.magic[0], header.magic[1], header.magic[2], header.magic[3]); 158 return nullptr; 159 } 160 if (header.name[63] != '\0') { 161 dbgprintf("Font name not fully null-terminated\n"); 162 return nullptr; 163 } 164 165 size_t bytes_per_glyph = sizeof(unsigned) * header.glyph_height; 166 167 auto* rows = const_cast<unsigned*>((const unsigned*)(data + sizeof(FontFileHeader))); 168 u8* widths = nullptr; 169 if (header.is_variable_width) 170 widths = (u8*)(rows) + 256 * bytes_per_glyph; 171 return adopt(*new Font(String(header.name), rows, widths, !header.is_variable_width, header.glyph_width, header.glyph_height, header.glyph_spacing)); 172} 173 174RefPtr<Font> Font::load_from_file(const StringView& path) 175{ 176 MappedFile mapped_file(path); 177 if (!mapped_file.is_valid()) 178 return nullptr; 179 180 auto font = load_from_memory((const u8*)mapped_file.data()); 181 if (!font) 182 return nullptr; 183 184 font->m_mapped_file = move(mapped_file); 185 return font; 186} 187 188bool Font::write_to_file(const StringView& path) 189{ 190 int fd = creat_with_path_length(path.characters_without_null_termination(), path.length(), 0644); 191 if (fd < 0) { 192 perror("open"); 193 return false; 194 } 195 196 FontFileHeader header; 197 memset(&header, 0, sizeof(FontFileHeader)); 198 memcpy(header.magic, "!Fnt", 4); 199 header.glyph_width = m_glyph_width; 200 header.glyph_height = m_glyph_height; 201 header.type = 0; 202 header.is_variable_width = !m_fixed_width; 203 header.glyph_spacing = m_glyph_spacing; 204 memcpy(header.name, m_name.characters(), min(m_name.length(), (size_t)63)); 205 206 size_t bytes_per_glyph = sizeof(unsigned) * m_glyph_height; 207 208 auto buffer = ByteBuffer::create_uninitialized(sizeof(FontFileHeader) + (256 * bytes_per_glyph) + 256); 209 BufferStream stream(buffer); 210 211 stream << ByteBuffer::wrap(&header, sizeof(FontFileHeader)); 212 stream << ByteBuffer::wrap(m_rows, (256 * bytes_per_glyph)); 213 stream << ByteBuffer::wrap(m_glyph_widths, 256); 214 215 ASSERT(stream.at_end()); 216 ssize_t nwritten = write(fd, buffer.data(), buffer.size()); 217 ASSERT(nwritten == (ssize_t)buffer.size()); 218 int rc = close(fd); 219 ASSERT(rc == 0); 220 return true; 221} 222 223int Font::glyph_or_emoji_width(u32 codepoint) const 224{ 225 if (codepoint < 256) 226 return glyph_width((char)codepoint); 227 228 if (m_fixed_width) 229 return m_glyph_width; 230 231 auto* emoji = Emoji::emoji_for_codepoint(codepoint); 232 if (emoji == nullptr) 233 return glyph_width('?'); 234 return emoji->size().width(); 235} 236 237int Font::width(const StringView& string) const 238{ 239 Utf8View utf8 { string }; 240 return width(utf8); 241} 242 243int Font::width(const Utf8View& utf8) const 244{ 245 bool first = true; 246 int width = 0; 247 248 for (u32 codepoint : utf8) { 249 if (!first) 250 width += glyph_spacing(); 251 first = false; 252 width += glyph_or_emoji_width(codepoint); 253 } 254 255 return width; 256} 257 258}