mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 14:48:14 +00:00
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/.
This commit is contained in:
parent
655753c557
commit
7f91aec25c
7 changed files with 170 additions and 10 deletions
|
@ -2,6 +2,15 @@
|
|||
#include "Peanut8x10.h"
|
||||
#include "Liza8x10.h"
|
||||
#include <AK/kmalloc.h>
|
||||
#include <AK/BufferStream.h>
|
||||
#include <AK/StdLibExtras.h>
|
||||
|
||||
#ifdef USERLAND
|
||||
#include <LibC/unistd.h>
|
||||
#include <LibC/stdio.h>
|
||||
#include <LibC/fcntl.h>
|
||||
#include <LibC/errno.h>
|
||||
#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> 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> 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<char**>(kmalloc(sizeof(char*) * 256));
|
||||
for (unsigned glyph_index = 0; glyph_index < 256; ++glyph_index) {
|
||||
new_glyphs[glyph_index] = static_cast<char*>(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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue