summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/rendfont.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/rendfont.cpp')
-rw-r--r--src/emu/rendfont.cpp1381
1 files changed, 1102 insertions, 279 deletions
diff --git a/src/emu/rendfont.cpp b/src/emu/rendfont.cpp
index 9adbbe2dc33..5e7f0329fd2 100644
--- a/src/emu/rendfont.cpp
+++ b/src/emu/rendfont.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
rendfont.c
@@ -16,10 +16,432 @@
#include "osdepend.h"
#include "uismall.fh"
-#include "ui/cmdrender.h"
+#include "ui/uicmd14.fh"
+#include "ui/cmddata.h"
+#include <algorithm>
#include <cstddef>
#include <cstring>
+#include <iterator>
+#include <limits>
+
+
+#define VERBOSE 0
+
+#define LOG(...) do { if (VERBOSE) osd_printf_verbose(__VA_ARGS__); } while (0)
+
+
+namespace {
+
+template <typename Iterator>
+class bdf_helper
+{
+public:
+ bdf_helper(Iterator const &begin, Iterator const &end)
+ : m_keyword_begin(begin)
+ , m_keyword_end(begin)
+ , m_value_begin(begin)
+ , m_value_end(begin)
+ , m_line_end(begin)
+ , m_end(end)
+ {
+ next_line();
+ }
+
+ bool at_end() const { return m_end == m_keyword_begin; }
+
+ void next_line()
+ {
+ m_keyword_begin = m_line_end;
+ while ((m_end != m_keyword_begin) && (('\r' == *m_keyword_begin) || ('\n' == *m_keyword_begin)))
+ ++m_keyword_begin;
+
+ m_keyword_end = m_keyword_begin;
+ while ((m_end != m_keyword_end) && (' ' != *m_keyword_end) && ('\t' != *m_keyword_end) && ('\r' != *m_keyword_end) && ('\n' != *m_keyword_end))
+ ++m_keyword_end;
+
+ m_value_begin = m_keyword_end;
+ while ((m_end != m_value_begin) && ((' ' == *m_value_begin) || ('\t' == *m_value_begin)) && ('\r' != *m_value_begin) && ('\n' != *m_value_begin))
+ ++m_value_begin;
+
+ m_value_end = m_line_end = m_value_begin;
+ while ((m_end != m_line_end) && ('\r' != *m_line_end) && ('\n' != *m_line_end))
+ {
+ if ((' ' != *m_line_end) && ('\t' != *m_line_end))
+ m_value_end = ++m_line_end;
+ else
+ ++m_line_end;
+ }
+ }
+
+ bool is_keyword(char const *keyword) const
+ {
+ Iterator pos(m_keyword_begin);
+ while (true)
+ {
+ if (m_keyword_end == pos)
+ {
+ return '\0' == *keyword;
+ }
+ else if (('\0' == *keyword) || (*pos != *keyword))
+ {
+ return false;
+ }
+ else
+ {
+ ++pos;
+ ++keyword;
+ }
+ }
+ }
+
+ Iterator const &keyword_begin() const { return m_keyword_begin; }
+ Iterator const &keyword_end() const { return m_keyword_end; }
+ auto keyword_length() const { return std::distance(m_keyword_begin, m_keyword_end); }
+
+ Iterator const &value_begin() const { return m_value_begin; }
+ Iterator const &value_end() const { return m_value_end; }
+ auto value_length() const { return std::distance(m_value_begin, m_value_end); }
+
+private:
+ Iterator m_keyword_begin;
+ Iterator m_keyword_end;
+ Iterator m_value_begin;
+ Iterator m_value_end;
+ Iterator m_line_end;
+ Iterator const m_end;
+};
+
+
+class bdc_header
+{
+public:
+ static constexpr unsigned MAJVERSION = 1;
+ static constexpr unsigned MINVERSION = 0;
+
+ bool read(emu_file &f)
+ {
+ return f.read(m_data, sizeof(m_data)) == sizeof(m_data);
+ }
+ bool write(emu_file &f)
+ {
+ return f.write(m_data, sizeof(m_data)) == sizeof(m_data);
+ }
+
+ bool check_magic() const
+ {
+ return !std::memcmp(MAGIC, m_data + OFFS_MAGIC, OFFS_MAJVERSION - OFFS_MAGIC);
+ }
+ unsigned get_major_version() const
+ {
+ return m_data[OFFS_MAJVERSION];
+ }
+ unsigned get_minor_version() const
+ {
+ return m_data[OFFS_MINVERSION];
+ }
+ u64 get_original_length() const
+ {
+ return
+ (u64(m_data[OFFS_ORIGLENGTH + 0]) << (7 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 1]) << (6 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 2]) << (5 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 3]) << (4 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 4]) << (3 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 5]) << (2 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 6]) << (1 * 8)) |
+ (u64(m_data[OFFS_ORIGLENGTH + 7]) << (0 * 8));
+ }
+ u32 get_original_hash() const
+ {
+ return
+ (u32(m_data[OFFS_ORIGHASH + 0]) << (3 * 8)) |
+ (u32(m_data[OFFS_ORIGHASH + 1]) << (2 * 8)) |
+ (u32(m_data[OFFS_ORIGHASH + 2]) << (1 * 8)) |
+ (u32(m_data[OFFS_ORIGHASH + 3]) << (0 * 8));
+ }
+ u32 get_glyph_count() const
+ {
+ return
+ (u32(m_data[OFFS_GLYPHCOUNT + 0]) << (3 * 8)) |
+ (u32(m_data[OFFS_GLYPHCOUNT + 1]) << (2 * 8)) |
+ (u32(m_data[OFFS_GLYPHCOUNT + 2]) << (1 * 8)) |
+ (u32(m_data[OFFS_GLYPHCOUNT + 3]) << (0 * 8));
+ }
+ u16 get_height() const
+ {
+ return
+ (u16(m_data[OFFS_HEIGHT + 0]) << (1 * 8)) |
+ (u16(m_data[OFFS_HEIGHT + 1]) << (0 * 8));
+ }
+ s16 get_y_offset() const
+ {
+ return
+ (u16(m_data[OFFS_YOFFSET + 0]) << (1 * 8)) |
+ (u16(m_data[OFFS_YOFFSET + 1]) << (0 * 8));
+ }
+ s32 get_default_character() const
+ {
+ return
+ (u32(m_data[OFFS_DEFCHAR + 0]) << (3 * 8)) |
+ (u32(m_data[OFFS_DEFCHAR + 1]) << (2 * 8)) |
+ (u32(m_data[OFFS_DEFCHAR + 2]) << (1 * 8)) |
+ (u32(m_data[OFFS_DEFCHAR + 3]) << (0 * 8));
+ }
+
+ void set_magic()
+ {
+ std::memcpy(m_data + OFFS_MAGIC, MAGIC, OFFS_MAJVERSION - OFFS_MAGIC);
+ }
+ void set_version()
+ {
+ m_data[OFFS_MAJVERSION] = MAJVERSION;
+ m_data[OFFS_MINVERSION] = MINVERSION;
+ }
+ void set_original_length(u64 value)
+ {
+ m_data[OFFS_ORIGLENGTH + 0] = u8((value >> (7 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 1] = u8((value >> (6 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 2] = u8((value >> (5 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 3] = u8((value >> (4 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 4] = u8((value >> (3 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 5] = u8((value >> (2 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 6] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGLENGTH + 7] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_original_hash(u32 value)
+ {
+ m_data[OFFS_ORIGHASH + 0] = u8((value >> (3 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGHASH + 1] = u8((value >> (2 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGHASH + 2] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_ORIGHASH + 3] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_glyph_count(u32 value)
+ {
+ m_data[OFFS_GLYPHCOUNT + 0] = u8((value >> (3 * 8)) & 0x00ff);
+ m_data[OFFS_GLYPHCOUNT + 1] = u8((value >> (2 * 8)) & 0x00ff);
+ m_data[OFFS_GLYPHCOUNT + 2] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_GLYPHCOUNT + 3] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_height(u16 value)
+ {
+ m_data[OFFS_HEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_HEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_y_offset(s16 value)
+ {
+ m_data[OFFS_YOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_YOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_default_character(s32 value)
+ {
+ m_data[OFFS_DEFCHAR + 0] = u8((value >> (3 * 8)) & 0x00ff);
+ m_data[OFFS_DEFCHAR + 1] = u8((value >> (2 * 8)) & 0x00ff);
+ m_data[OFFS_DEFCHAR + 2] = u8((value >> (1 * 8)) & 0x00ff);
+ m_data[OFFS_DEFCHAR + 3] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+
+private:
+ static constexpr std::size_t OFFS_MAGIC = 0x00; // 0x06 bytes
+ static constexpr std::size_t OFFS_MAJVERSION = 0x06; // 0x01 bytes (binary integer)
+ static constexpr std::size_t OFFS_MINVERSION = 0x07; // 0x01 bytes (binary integer)
+ static constexpr std::size_t OFFS_ORIGLENGTH = 0x08; // 0x08 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_ORIGHASH = 0x10; // 0x04 bytes
+ static constexpr std::size_t OFFS_GLYPHCOUNT = 0x14; // 0x04 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_HEIGHT = 0x18; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_YOFFSET = 0x1a; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_DEFCHAR = 0x1c; // 0x04 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_END = 0x20;
+
+ static u8 const MAGIC[OFFS_MAJVERSION - OFFS_MAGIC];
+
+ u8 m_data[OFFS_END];
+};
+
+u8 const bdc_header::MAGIC[OFFS_MAJVERSION - OFFS_MAGIC] = { 'b', 'd', 'c', 'f', 'n', 't' };
+
+
+class bdc_table_entry
+{
+public:
+ bdc_table_entry(void *bytes)
+ : m_ptr(reinterpret_cast<u8 *>(bytes))
+ {
+ }
+ bdc_table_entry(bdc_table_entry const &that) = default;
+ bdc_table_entry(bdc_table_entry &&that) = default;
+
+ bdc_table_entry get_next() const
+ {
+ return bdc_table_entry(m_ptr + OFFS_END);
+ }
+
+ u32 get_encoding() const
+ {
+ return
+ (u32(m_ptr[OFFS_ENCODING + 0]) << (3 * 8)) |
+ (u32(m_ptr[OFFS_ENCODING + 1]) << (2 * 8)) |
+ (u32(m_ptr[OFFS_ENCODING + 2]) << (1 * 8)) |
+ (u32(m_ptr[OFFS_ENCODING + 3]) << (0 * 8));
+ }
+ u16 get_x_advance() const
+ {
+ return
+ (u16(m_ptr[OFFS_XADVANCE + 0]) << (1 * 8)) |
+ (u16(m_ptr[OFFS_XADVANCE + 1]) << (0 * 8));
+ }
+ s16 get_bb_x_offset() const
+ {
+ return
+ (u16(m_ptr[OFFS_BBXOFFSET + 0]) << (1 * 8)) |
+ (u16(m_ptr[OFFS_BBXOFFSET + 1]) << (0 * 8));
+ }
+ s16 get_bb_y_offset() const
+ {
+ return
+ (u16(m_ptr[OFFS_BBYOFFSET + 0]) << (1 * 8)) |
+ (u16(m_ptr[OFFS_BBYOFFSET + 1]) << (0 * 8));
+ }
+ u16 get_bb_width() const
+ {
+ return
+ (u16(m_ptr[OFFS_BBWIDTH + 0]) << (1 * 8)) |
+ (u16(m_ptr[OFFS_BBWIDTH + 1]) << (0 * 8));
+ }
+ u16 get_bb_height() const
+ {
+ return
+ (u16(m_ptr[OFFS_BBHEIGHT + 0]) << (1 * 8)) |
+ (u16(m_ptr[OFFS_BBHEIGHT + 1]) << (0 * 8));
+ }
+
+ void set_encoding(u32 value)
+ {
+ m_ptr[OFFS_ENCODING + 0] = u8((value >> (3 * 8)) & 0x00ff);
+ m_ptr[OFFS_ENCODING + 1] = u8((value >> (2 * 8)) & 0x00ff);
+ m_ptr[OFFS_ENCODING + 2] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_ENCODING + 3] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_x_advance(u16 value)
+ {
+ m_ptr[OFFS_XADVANCE + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_XADVANCE + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_bb_x_offset(s16 value)
+ {
+ m_ptr[OFFS_BBXOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_BBXOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_bb_y_offset(s16 value)
+ {
+ m_ptr[OFFS_BBYOFFSET + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_BBYOFFSET + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_bb_width(u16 value)
+ {
+ m_ptr[OFFS_BBWIDTH + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_BBWIDTH + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+ void set_bb_height(u16 value)
+ {
+ m_ptr[OFFS_BBHEIGHT + 0] = u8((value >> (1 * 8)) & 0x00ff);
+ m_ptr[OFFS_BBHEIGHT + 1] = u8((value >> (0 * 8)) & 0x00ff);
+ }
+
+ bdc_table_entry &operator=(bdc_table_entry const &that) = default;
+ bdc_table_entry &operator=(bdc_table_entry &&that) = default;
+
+ static std::size_t size()
+ {
+ return OFFS_END;
+ }
+
+private:
+ static constexpr std::size_t OFFS_ENCODING = 0x00; // 0x04 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_XADVANCE = 0x04; // 0x02 bytes (big-endian binary integer)
+ // two bytes reserved
+ static constexpr std::size_t OFFS_BBXOFFSET = 0x08; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_BBYOFFSET = 0x0a; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_BBWIDTH = 0x0c; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_BBHEIGHT = 0x0e; // 0x02 bytes (big-endian binary integer)
+ static constexpr std::size_t OFFS_END = 0x10;
+
+ u8 *m_ptr;
+};
+
+} // anonymous namespace
+
+
+void convert_command_glyph(std::string &str)
+{
+ str.c_str(); // force NUL-termination - we depend on it later
+ std::size_t const len(str.length());
+ std::vector<char> buf(2 * (len + 1));
+ std::size_t j(0);
+ for (std::size_t i = 0; len > i; )
+ {
+ // decode UTF-8
+ char32_t uchar;
+ int const codelen(uchar_from_utf8(&uchar, &str[i], len - i));
+ if (0 >= codelen)
+ break;
+ i += codelen;
+
+ // check for three metacharacters
+ fix_command_t const *fixcmd(nullptr);
+ switch (uchar)
+ {
+ case COMMAND_CONVERT_TEXT:
+ for (fix_strings_t *fixtext = convert_text; fixtext->glyph_code; ++fixtext)
+ {
+ if (!fixtext->glyph_str_len)
+ fixtext->glyph_str_len = std::strlen(fixtext->glyph_str);
+
+ if (!std::strncmp(fixtext->glyph_str, &str[i], fixtext->glyph_str_len))
+ {
+ uchar = fixtext->glyph_code + COMMAND_UNICODE;
+ i += strlen(fixtext->glyph_str);
+ break;
+ }
+ }
+ break;
+
+ case COMMAND_DEFAULT_TEXT:
+ fixcmd = default_text;
+ break;
+
+ case COMMAND_EXPAND_TEXT:
+ fixcmd = expand_text;
+ break;
+ }
+
+ // this substitutes a single character
+ if (fixcmd)
+ {
+ if (str[i] == uchar)
+ {
+ ++i;
+ }
+ else
+ {
+ while (fixcmd->glyph_code && (fixcmd->glyph_char != str[i]))
+ ++fixcmd;
+ if (fixcmd->glyph_code)
+ {
+ uchar = COMMAND_UNICODE + fixcmd->glyph_code;
+ ++i;
+ }
+ }
+ }
+
+ // copy character to output
+ int const outlen(utf8_from_uchar(&buf[j], buf.size() - j, uchar));
+ if (0 >= outlen)
+ break;
+ j += outlen;
+ }
+ str.assign(&buf[0], j);
+}
const u64 render_font::CACHED_BDF_HASH_SIZE;
@@ -58,30 +480,36 @@ inline render_font::glyph &render_font::get_char(char32_t chnum)
{
static glyph dummy_glyph;
- // grab the table; if none, return the dummy character
- if ((chnum / 256) >= ARRAY_LENGTH(m_glyphs))
- return dummy_glyph;
- if (!m_glyphs[chnum / 256] && m_format == FF_OSD)
- m_glyphs[chnum / 256] = new glyph[256];
- if (!m_glyphs[chnum / 256])
+ unsigned const page(chnum / 256);
+ if (page >= ARRAY_LENGTH(m_glyphs))
+ {
+ if ((0 <= m_defchar) && (chnum != m_defchar))
+ return get_char(m_defchar);
+ else
+ return dummy_glyph;
+ }
+ else if (!m_glyphs[page])
{
//mamep: make table for command glyph
- if (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)
- m_glyphs[chnum / 256] = new glyph[256];
+ if ((m_format == format::OSD) || ((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)))
+ m_glyphs[page] = new glyph[256];
+ else if ((0 <= m_defchar) && (chnum != m_defchar))
+ return get_char(m_defchar);
else
return dummy_glyph;
}
// if the character isn't generated yet, do it now
- glyph &gl = m_glyphs[chnum / 256][chnum % 256];
+ glyph &gl = m_glyphs[page][chnum % 256];
if (!gl.bitmap.valid())
{
//mamep: command glyph support
if (m_height_cmd && chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)
{
- glyph &glyph_ch = m_glyphs_cmd[chnum / 256][chnum % 256];
- float scale = (float)m_height / (float)m_height_cmd;
- if (m_format == FF_OSD) scale *= 0.90f;
+ glyph &glyph_ch = m_glyphs_cmd[page][chnum % 256];
+ float scale = float(m_height) / float(m_height_cmd);
+ if (m_format == format::OSD)
+ scale *= 0.90f;
if (!glyph_ch.bitmap.valid())
char_expand(chnum, glyph_ch);
@@ -89,11 +517,11 @@ inline render_font::glyph &render_font::get_char(char32_t chnum)
//mamep: for color glyph
gl.color = glyph_ch.color;
- gl.width = (int)(glyph_ch.width * scale + 0.5f);
- gl.xoffs = (int)(glyph_ch.xoffs * scale + 0.5f);
- gl.yoffs = (int)(glyph_ch.yoffs * scale + 0.5f);
- gl.bmwidth = (int)(glyph_ch.bmwidth * scale + 0.5f);
- gl.bmheight = (int)(glyph_ch.bmheight * scale + 0.5f);
+ gl.width = int(glyph_ch.width * scale + 0.5f);
+ gl.xoffs = int(glyph_ch.xoffs * scale + 0.5f);
+ gl.yoffs = int(glyph_ch.yoffs * scale + 0.5f);
+ gl.bmwidth = int(glyph_ch.bmwidth * scale + 0.5f);
+ gl.bmheight = int(glyph_ch.bmheight * scale + 0.5f);
gl.bitmap.allocate(gl.bmwidth, gl.bmheight);
rectangle clip;
@@ -107,10 +535,11 @@ inline render_font::glyph &render_font::get_char(char32_t chnum)
gl.texture->set_bitmap(gl.bitmap, gl.bitmap.cliprect(), TEXFORMAT_ARGB32);
}
else
+ {
char_expand(chnum, gl);
+ }
}
- // return the resulting character
return gl;
}
@@ -125,55 +554,53 @@ inline render_font::glyph &render_font::get_char(char32_t chnum)
//-------------------------------------------------
render_font::render_font(render_manager &manager, const char *filename)
- : m_manager(manager),
- m_format(FF_UNKNOWN),
- m_height(0),
- m_yoffs(0),
- m_scale(1.0f),
- m_rawsize(0),
- m_osdfont(),
- m_height_cmd(0),
- m_yoffs_cmd(0)
+ : m_manager(manager)
+ , m_format(format::UNKNOWN)
+ , m_height(0)
+ , m_yoffs(0)
+ , m_defchar(-1)
+ , m_scale(1.0f)
+ , m_rawsize(0)
+ , m_osdfont()
+ , m_height_cmd(0)
+ , m_yoffs_cmd(0)
{
memset(m_glyphs, 0, sizeof(m_glyphs));
memset(m_glyphs_cmd, 0, sizeof(m_glyphs_cmd));
// if this is an OSD font, we're done
- if (filename != nullptr)
+ if (filename)
{
m_osdfont = manager.machine().osd().font_alloc();
- if (m_osdfont)
+ if (m_osdfont && m_osdfont->open(manager.machine().options().font_path(), filename, m_height))
{
- if (m_osdfont->open(manager.machine().options().font_path(), filename, m_height))
- {
- m_scale = 1.0f / (float)m_height;
- m_format = FF_OSD;
+ m_scale = 1.0f / float(m_height);
+ m_format = format::OSD;
- //mamep: allocate command glyph font
- render_font_command_glyph();
- return;
- }
- m_osdfont.reset();
+ //mamep: allocate command glyph font
+ render_font_command_glyph();
+ return;
}
+ m_osdfont.reset();
}
// if the filename is 'default' default to 'ui.bdf' for backwards compatibility
- if (filename != nullptr && core_stricmp(filename, "default") == 0)
+ if (filename && !core_stricmp(filename, "default"))
filename = "ui.bdf";
- // attempt to load the cached version of the font first
- if (filename != nullptr && load_cached_bdf(filename))
+ // attempt to load an external BDF font first
+ if (filename && load_cached_bdf(filename))
{
//mamep: allocate command glyph font
render_font_command_glyph();
return;
}
- // load the raw data instead
+ // load the compiled in data instead
emu_file ramfile(OPEN_FLAG_READ);
- osd_file::error filerr = ramfile.open_ram(font_uismall, sizeof(font_uismall));
- if (filerr == osd_file::error::NONE)
- load_cached(ramfile, 0);
+ osd_file::error const filerr(ramfile.open_ram(font_uismall, sizeof(font_uismall)));
+ if (osd_file::error::NONE == filerr)
+ load_cached(ramfile, 0, 0);
render_font_command_glyph();
}
@@ -216,15 +643,15 @@ render_font::~render_font()
void render_font::char_expand(char32_t chnum, glyph &gl)
{
- rgb_t color = rgb_t(0xff,0xff,0xff,0xff);
- bool is_cmd = (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT);
+ LOG("render_font::char_expand: expanding character %u\n", unsigned(chnum));
- if (gl.color)
- color = gl.color;
+ rgb_t const fgcol(gl.color ? gl.color : rgb_t(0xff, 0xff, 0xff, 0xff));
+ rgb_t const bgcol(0x00, 0xff, 0xff, 0xff);
+ bool const is_cmd((chnum >= COMMAND_UNICODE) && (chnum < COMMAND_UNICODE + MAX_GLYPH_FONT));
if (is_cmd)
{
- // punt if nothing there
+ // abort if nothing there
if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr)
return;
@@ -245,37 +672,47 @@ void render_font::char_expand(char32_t chnum, glyph &gl)
if (accumbit == 7)
accum = *ptr++;
if (dest != nullptr)
- *dest++ = (accum & (1 << accumbit)) ? color : rgb_t(0x00,0xff,0xff,0xff);
+ *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol;
accumbit = (accumbit - 1) & 7;
}
}
}
}
- // if we're an OSD font, query the info
- else if (m_format == FF_OSD)
+ else if (m_format == format::OSD)
{
- // we set bmwidth to -1 if we've previously queried and failed
- if (gl.bmwidth == -1)
+ // if we're an OSD font, query the info
+ if (0 > gl.bmwidth)
+ {
+ // we set bmwidth to -1 if we've previously queried and failed
+ LOG("render_font::char_expand: previously failed to get bitmap from OSD font\n");
return;
-
- // attempt to get the font bitmap; if we fail, set bmwidth to -1
+ }
if (!m_osdfont->get_bitmap(chnum, gl.bitmap, gl.width, gl.xoffs, gl.yoffs))
{
+ // attempt to get the font bitmap failed - set bmwidth to -1
+ LOG("render_font::char_expand: get bitmap from OSD font failed\n");
gl.bitmap.reset();
gl.bmwidth = -1;
return;
}
-
- // populate the bmwidth/bmheight fields
- gl.bmwidth = gl.bitmap.width();
- gl.bmheight = gl.bitmap.height();
+ else
+ {
+ // populate the bmwidth/bmheight fields
+ LOG("render_font::char_expand: got %dx%d bitmap from OSD font\n", gl.bitmap.width(), gl.bitmap.height());
+ gl.bmwidth = gl.bitmap.width();
+ gl.bmheight = gl.bitmap.height();
+ }
+ }
+ else if (!gl.bmwidth || !gl.bmheight || !gl.rawdata)
+ {
+ // abort if nothing there
+ LOG("render_font::char_expand: empty bitmap bounds or no raw data\n");
+ return;
}
- // other formats need to parse their data
else
{
- // punt if nothing there
- if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr)
- return;
+ // other formats need to parse their data
+ LOG("render_font::char_expand: building bitmap from raw data\n");
// allocate a new bitmap of the size we need
gl.bitmap.allocate(gl.bmwidth, m_height);
@@ -283,55 +720,55 @@ void render_font::char_expand(char32_t chnum, glyph &gl)
// extract the data
const char *ptr = gl.rawdata;
- u8 accum = 0, accumbit = 7;
- for (int y = 0; y < gl.bmheight; y++)
+ u8 accum(0), accumbit(7);
+ for (int y = 0; y < gl.bmheight; ++y)
{
- int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight;
- u32 *dest = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix32(desty) : nullptr;
+ int const desty(y + m_height + m_yoffs - gl.yoffs - gl.bmheight);
+ u32 *dest(((0 <= desty) && (m_height > desty)) ? &gl.bitmap.pix32(desty) : nullptr);
- // text format
- if (m_format == FF_TEXT)
+ if (m_format == format::TEXT)
{
- // loop over bytes
- for (int x = 0; x < gl.bmwidth; x += 4)
+ if (dest)
{
- // scan for the next hex digit
- int bits = -1;
- while (*ptr != 13 && bits == -1)
+ for (int x = 0; gl.bmwidth > x; )
{
- if (*ptr >= '0' && *ptr <= '9')
- bits = *ptr++ - '0';
- else if (*ptr >= 'A' && *ptr <= 'F')
- bits = *ptr++ - 'A' + 10;
- else if (*ptr >= 'a' && *ptr <= 'f')
- bits = *ptr++ - 'a' + 10;
- else
- ptr++;
- }
+ // scan for the next hex digit
+ int bits = -1;
+ while (('\r' != *ptr) && ('\n' != *ptr) && (0 > bits))
+ {
+ if (*ptr >= '0' && *ptr <= '9')
+ bits = *ptr++ - '0';
+ else if (*ptr >= 'A' && *ptr <= 'F')
+ bits = *ptr++ - 'A' + 10;
+ else if (*ptr >= 'a' && *ptr <= 'f')
+ bits = *ptr++ - 'a' + 10;
+ else
+ ptr++;
+ }
- // expand the four bits
- if (dest != nullptr)
- {
- *dest++ = (bits & 8) ? color : rgb_t(0x00,0xff,0xff,0xff);
- *dest++ = (bits & 4) ? color : rgb_t(0x00,0xff,0xff,0xff);
- *dest++ = (bits & 2) ? color : rgb_t(0x00,0xff,0xff,0xff);
- *dest++ = (bits & 1) ? color : rgb_t(0x00,0xff,0xff,0xff);
+ // expand the four bits
+ *dest++ = (bits & 8) ? fgcol : bgcol;
+ if (gl.bmwidth > ++x)
+ *dest++ = (bits & 4) ? fgcol : bgcol;
+ if (gl.bmwidth > ++x)
+ *dest++ = (bits & 2) ? fgcol : bgcol;
+ if (gl.bmwidth > ++x)
+ *dest++ = (bits & 1) ? fgcol : bgcol;
+ ++x;
}
}
// advance to the next line
ptr = next_line(ptr);
}
-
- // cached format
- else if (m_format == FF_CACHED)
+ else if (m_format == format::CACHED)
{
for (int x = 0; x < gl.bmwidth; x++)
{
if (accumbit == 7)
accum = *ptr++;
if (dest != nullptr)
- *dest++ = (accum & (1 << accumbit)) ? color : rgb_t(0x00,0xff,0xff,0xff);
+ *dest++ = (accum & (1 << accumbit)) ? fgcol : bgcol;
accumbit = (accumbit - 1) & 7;
}
}
@@ -479,27 +916,45 @@ float render_font::utf8string_width(float height, float aspect, const char *utf8
bool render_font::load_cached_bdf(const char *filename)
{
+ osd_file::error filerr;
+ u32 chunk;
+ u64 bytes;
+
// first try to open the BDF itself
emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_READ);
- osd_file::error filerr = file.open(filename);
+ filerr = file.open(filename);
if (filerr != osd_file::error::NONE)
return false;
// determine the file size and allocate memory
- m_rawsize = file.size();
- m_rawdata.resize(m_rawsize + 1);
-
- // read the first chunk
- u32 bytes = file.read(&m_rawdata[0], std::min(CACHED_BDF_HASH_SIZE, m_rawsize));
- if (bytes != std::min(CACHED_BDF_HASH_SIZE, m_rawsize))
+ try
+ {
+ m_rawsize = file.size();
+ std::vector<char>::size_type const sz(m_rawsize + 1);
+ if (u64(sz) != (m_rawsize + 1))
+ return false;
+ m_rawdata.resize(sz);
+ }
+ catch (...)
+ {
return false;
+ }
- // has the chunk
- u32 hash = core_crc32(0, (const u8 *)&m_rawdata[0], bytes) ^ u32(m_rawsize);
+ // read the first chunk and hash it
+ chunk = u32((std::min<u64>)(CACHED_BDF_HASH_SIZE, m_rawsize));
+ bytes = file.read(&m_rawdata[0], chunk);
+ if (bytes != chunk)
+ {
+ m_rawdata.clear();
+ return false;
+ }
+ u32 const hash(core_crc32(0, reinterpret_cast<u8 const *>(&m_rawdata[0]), bytes));
// create the cached filename, changing the 'F' to a 'C' on the extension
std::string cachedname(filename);
- cachedname.erase(cachedname.length() - 3, 3).append("bdc");
+ if ((4U < cachedname.length()) && !core_stricmp(&cachedname[cachedname.length() - 4], ".bdf"))
+ cachedname.erase(cachedname.length() - 4);
+ cachedname.append(".bdc");
// attempt to open the cached version of the font
{
@@ -508,38 +963,36 @@ bool render_font::load_cached_bdf(const char *filename)
if (filerr == osd_file::error::NONE)
{
// if we have a cached version, load it
- bool result = load_cached(cachefile, hash);
+ bool const result = load_cached(cachefile, m_rawsize, hash);
// if that worked, we're done
if (result)
- {
- // don't do that - glyphs data point into this array ...
- // m_rawdata.reset();
return true;
- }
}
}
- // read in the rest of the font
- if (bytes < m_rawsize)
+ // read in the rest of the font and NUL-terminate it
+ while (bytes < m_rawsize)
{
- u32 read = file.read(&m_rawdata[bytes], m_rawsize - bytes);
- if (read != m_rawsize - bytes)
+ chunk = u32((std::min<u64>)(std::numeric_limits<u32>::max(), m_rawsize - bytes));
+ u32 const read(file.read(&m_rawdata[bytes], chunk));
+ bytes += read;
+ if (read != chunk)
{
m_rawdata.clear();
return false;
}
}
-
- // NULL-terminate the data and attach it to the font
- m_rawdata[m_rawsize] = 0;
+ m_rawdata[m_rawsize] = '\0';
// load the BDF
- bool result = load_bdf();
+ bool const result = load_bdf();
// if we loaded okay, create a cached one
if (result)
- save_cached(cachedname.c_str(), hash);
+ save_cached(cachedname.c_str(), m_rawsize, hash);
+ else
+ m_rawdata.clear();
// close the file
return result;
@@ -553,109 +1006,344 @@ bool render_font::load_cached_bdf(const char *filename)
bool render_font::load_bdf()
{
// set the format to text
- m_format = FF_TEXT;
+ m_format = format::TEXT;
- // first find the FONTBOUNDINGBOX tag
- const char *ptr;
- for (ptr = &m_rawdata[0]; ptr != nullptr; ptr = next_line(ptr))
+ bdf_helper<std::vector<char>::const_iterator> helper(std::cbegin(m_rawdata), std::cend(m_rawdata));
+
+ // the first thing we want to see is the STARTFONT declaration, failing that we can't do much
+ for ( ; !helper.is_keyword("STARTFONT"); helper.next_line())
+ {
+ if (helper.at_end())
+ {
+ osd_printf_error("render_font::load_bdf: expected STARTFONT\n");
+ return false;
+ }
+ }
+
+ // parse out the global information we need
+ bool have_bbox(false);
+ bool have_properties(false);
+ bool have_defchar(false);
+ for (helper.next_line(); !helper.is_keyword("CHARS"); helper.next_line())
{
- // we only care about a tiny few fields
- if (strncmp(ptr, "FONTBOUNDINGBOX ", 16) == 0)
+ if (helper.at_end())
{
- int dummy1, dummy2;
- if (sscanf(ptr + 16, "%d %d %d %d", &dummy1, &m_height, &dummy2, &m_yoffs) != 4)
+ // font with no characters is useless
+ osd_printf_error("render_font::load_bdf: no glyph section found\n");
+ return false;
+ }
+ else if (helper.is_keyword("FONTBOUNDINGBOX"))
+ {
+ // check for duplicate bounding box
+ if (have_bbox)
+ {
+ osd_printf_error("render_font::load_bdf: found additional bounding box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
return false;
- break;
+ }
+ have_bbox = true;
+
+ // parse bounding box and check that it's at least half sane
+ int width, xoffs;
+ if (4 == sscanf(&*helper.value_begin(), "%d %d %d %d", &width, &m_height, &xoffs, &m_yoffs))
+ {
+ LOG("render_font::load_bdf: got bounding box %dx%d %d,%d\n", width, m_height, xoffs, m_yoffs);
+ if ((0 >= m_height) || (0 >= width))
+ {
+ osd_printf_error("render_font::load_bdf: bounding box is invalid\n");
+ return false;
+ }
+ }
+ else
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse bounding box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ }
+ else if (helper.is_keyword("STARTPROPERTIES"))
+ {
+ // check for duplicated properties section
+ if (have_properties)
+ {
+ osd_printf_error("render_font::load_bdf: found additional properties\n");
+ return false;
+ }
+ have_properties = true;
+
+ // get property count for sanity check
+ int propcount;
+ if (1 != sscanf(&*helper.value_begin(), "%d", &propcount))
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse property count \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+
+ int actual(0);
+ for (helper.next_line(); !helper.is_keyword("ENDPROPERTIES"); helper.next_line())
+ {
+ ++actual;
+ if (helper.at_end())
+ {
+ // unterminated properties section
+ osd_printf_error("render_font::load_bdf: end of properties not found\n");
+ return false;
+ }
+ else if (helper.is_keyword("DEFAULT_CHAR"))
+ {
+ // check for duplicate default character
+ if (have_defchar)
+ {
+ osd_printf_error("render_font::load_bdf: found additional default character \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ have_defchar = true;
+
+ // parse default character
+ if (1 == sscanf(&*helper.value_begin(), "%d", &m_defchar))
+ {
+ LOG("render_font::load_bdf: got default character 0x%x\n", m_defchar);
+ }
+ else
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse default character \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ }
+ }
+
+ // sanity check on number of properties
+ if (actual != propcount)
+ {
+ osd_printf_error("render_font::load_bdf: incorrect number of properties %d\n", actual);
+ return false;
+ }
}
}
// compute the scale factor
- m_scale = 1.0f / (float)m_height;
+ if (!have_bbox)
+ {
+ osd_printf_error("render_font::load_bdf: no bounding box found\n");
+ return false;
+ }
+ m_scale = 1.0f / float(m_height);
+
+ // get expected character count
+ int expected;
+ if (1 == sscanf(&*helper.value_begin(), "%d", &expected))
+ {
+ LOG("render_font::load_bdf: got character count %d\n", expected);
+ }
+ else
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse character count \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
// now scan for characters
+ auto const nothex([] (char ch) { return (('0' > ch) || ('9' < ch)) && (('A' > ch) || ('Z' < ch)) && (('a' > ch) || ('z' < ch)); });
int charcount = 0;
- for ( ; ptr != nullptr; ptr = next_line(ptr))
+ for (helper.next_line(); !helper.is_keyword("ENDFONT"); helper.next_line())
{
- // stop at ENDFONT
- if (strncmp(ptr, "ENDFONT", 7) == 0)
- break;
-
- // once we hit a STARTCHAR, parse until the end
- if (strncmp(ptr, "STARTCHAR ", 10) == 0)
+ if (helper.at_end())
{
- int bmwidth = -1, bmheight = -1, xoffs = -1, yoffs = -1;
- const char *rawdata = nullptr;
- int charnum = -1;
- int width = -1;
-
- // scan for interesting per-character tags
- for ( ; ptr != nullptr; ptr = next_line(ptr))
+ // unterminated font
+ osd_printf_error("render_font::load_bdf: end of font not found\n");
+ return false;
+ }
+ else if (helper.is_keyword("STARTCHAR"))
+ {
+ // required glyph properties
+ bool have_encoding(false);
+ bool have_advance(false);
+ bool have_bbounds(false);
+ int encoding(-1);
+ int xadvance(-1);
+ int bbw(-1), bbh(-1), bbxoff(-1), bbyoff(-1);
+
+ // stuff for the bitmap data
+ bool in_bitmap(false);
+ int bitmap_rows(0);
+ char const *bitmap_data(nullptr);
+
+ // parse a glyph
+ for (helper.next_line(); !helper.is_keyword("ENDCHAR"); helper.next_line())
{
- // ENCODING tells us which character
- if (strncmp(ptr, "ENCODING ", 9) == 0)
+ if (helper.at_end())
{
- if (sscanf(ptr + 9, "%d", &charnum) != 1)
- return 1;
+ // unterminated glyph
+ osd_printf_error("render_font::load_bdf: end of glyph not found\n");
+ return false;
}
-
- // DWIDTH tells us the width to the next character
- else if (strncmp(ptr, "DWIDTH ", 7) == 0)
+ else if (in_bitmap)
{
- int dummy1;
- if (sscanf(ptr + 7, "%d %d", &width, &dummy1) != 2)
- return 1;
+ // quick sanity check
+ if ((2 * ((bbw + 7) / 8)) != helper.keyword_length())
+ {
+ osd_printf_error("render_font::load_bdf: incorrect length for bitmap line \"%.*s\"\n", int(helper.keyword_length()), &*helper.keyword_begin());
+ return false;
+ }
+ else if (std::find_if(helper.keyword_begin(), helper.keyword_end(), nothex) != helper.keyword_end())
+ {
+ osd_printf_error("render_font::load_bdf: found invalid character in bitmap line \"%.*s\"\n", int(helper.keyword_length()), &*helper.keyword_begin());
+ return false;
+ }
+
+ // track number of rows
+ if (1 == ++bitmap_rows)
+ bitmap_data = &*helper.keyword_begin();
+
}
+ else if (helper.is_keyword("ENCODING"))
+ {
+ // check for duplicate glyph encoding
+ if (have_encoding)
+ {
+ osd_printf_error("render_font::load_bdf: found additional glyph encoding \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ have_encoding = true;
- // BBX tells us the height/width of the bitmap and the offsets
- else if (strncmp(ptr, "BBX ", 4) == 0)
+ // need to support Adobe Standard Encoding "123" and non-standard glyph index "-1 123"
+ std::string const value(helper.value_begin(), helper.value_end());
+ int aux;
+ int const cnt(sscanf(value.c_str(), "%d %d", &encoding, &aux));
+ if ((2 == cnt) && (-1 == encoding) && (0 <= aux))
+ {
+ encoding = aux;
+ }
+ else if ((1 != cnt) || (0 > encoding))
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse glyph encoding \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ LOG("render_font::load_bdf: got glyph encoding %d\n", encoding);
+ }
+ else if (helper.is_keyword("DWIDTH"))
{
- if (sscanf(ptr + 4, "%d %d %d %d", &bmwidth, &bmheight, &xoffs, &yoffs) != 4)
- return 1;
+ // check for duplicate advance
+ if (have_advance)
+ {
+ osd_printf_error("render_font::load_bdf: found additional pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ have_advance = true;
+
+ // completely ignore vertical advance
+ int yadvance;
+ if (2 == sscanf(&*helper.value_begin(), "%d %d", &xadvance, &yadvance))
+ {
+ LOG("render_font::load_bdf: got pixel width %d,%d\n", xadvance, yadvance);
+ }
+ else
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
}
+ else if (helper.is_keyword("BBX"))
+ {
+ // check for duplicate black pixel box
+ if (have_bbounds)
+ {
+ osd_printf_error("render_font::load_bdf: found additional pixel width \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ have_bbounds = true;
- // BITMAP is the start of the data
- else if (strncmp(ptr, "BITMAP", 6) == 0)
+ // extract position/size of black pixel area
+ if (4 == sscanf(&*helper.value_begin(), "%d %d %d %d", &bbw, &bbh, &bbxoff, &bbyoff))
+ {
+ LOG("render_font::load_bdf: got black pixel box %dx%d %d,%d\n", bbw, bbh, bbxoff, bbyoff);
+ if ((0 > bbw) || (0 > bbh))
+ {
+ osd_printf_error("render_font::load_bdf: black pixel box is invalid\n");
+ return false;
+ }
+ }
+ else
+ {
+ osd_printf_error("render_font::load_bdf: failed to parse black pixel box \"%.*s\"\n", int(helper.value_length()), &*helper.value_begin());
+ return false;
+ }
+ }
+ else if (helper.is_keyword("BITMAP"))
{
- // stash the raw pointer and scan for the end of the character
- for (rawdata = ptr = next_line(ptr); ptr != nullptr && strncmp(ptr, "ENDCHAR", 7) != 0; ptr = next_line(ptr)) { }
- break;
+ // this is the bitmap - we need to already have properties before we get here
+ if (!have_advance)
+ {
+ osd_printf_error("render_font::load_bdf: glyph has no pixel width\n");
+ return false;
+ }
+ else if (!have_bbounds)
+ {
+ osd_printf_error("render_font::load_bdf: glyph has no black pixel box\n");
+ return false;
+ }
+ in_bitmap = true;
}
}
- // if we have everything, allocate a new character
- if (charnum >= 0 && charnum < (256 * ARRAY_LENGTH(m_glyphs)) && rawdata != nullptr && bmwidth >= 0 && bmheight >= 0)
+ // now check that we have what we need
+ if (!in_bitmap)
+ {
+ osd_printf_error("render_font::load_bdf: glyph has no bitmap\n");
+ return false;
+ }
+ else if (bitmap_rows != bbh)
+ {
+ osd_printf_error("render_font::load_bdf: incorrect number of bitmap lines %d\n", bitmap_rows);
+ return false;
+ }
+
+ // some kinds of characters will screw us up
+ if (0 > xadvance)
+ {
+ LOG("render_font::load_bdf: ignoring character with negative x advance\n");
+ }
+ else if ((256 * ARRAY_LENGTH(m_glyphs)) <= encoding)
+ {
+ LOG("render_font::load_bdf: ignoring character with encoding outside range\n");
+ }
+ else
{
// if we don't have a subtable yet, make one
- if (!m_glyphs[charnum / 256])
- m_glyphs[charnum / 256] = new glyph[256];
+ if (!m_glyphs[encoding / 256])
+ {
+ try
+ {
+ m_glyphs[encoding / 256] = new glyph[256];
+ }
+ catch (...)
+ {
+ osd_printf_error("render_font::load_bdf: allocation failed\n");
+ return false;
+ }
+ }
// fill in the entry
- glyph &gl = m_glyphs[charnum / 256][charnum % 256];
- gl.width = width;
- gl.bmwidth = bmwidth;
- gl.bmheight = bmheight;
- gl.xoffs = xoffs;
- gl.yoffs = yoffs;
- gl.rawdata = rawdata;
+ glyph &gl = m_glyphs[encoding / 256][encoding % 256];
+ gl.width = xadvance;
+ gl.bmwidth = bbw;
+ gl.bmheight = bbh;
+ gl.xoffs = bbxoff;
+ gl.yoffs = bbyoff;
+ gl.rawdata = bitmap_data;
}
// some progress for big fonts
- if (++charcount % 256 == 0)
+ if (0 == (++charcount % 256))
osd_printf_warning("Loading BDF font... (%d characters loaded)\n", charcount);
}
}
- // make sure all the numbers are the same width
- if (m_glyphs[0])
+ // check number of characters
+ if (expected != charcount)
{
- int maxwidth = 0;
- for (int ch = '0'; ch <= '9'; ch++)
- if (m_glyphs[0][ch].bmwidth > maxwidth)
- maxwidth = m_glyphs[0][ch].width;
- for (int ch = '0'; ch <= '9'; ch++)
- m_glyphs[0][ch].width = maxwidth;
+ osd_printf_error("render_font::load_bdf: incorrect character count %d\n", charcount);
+ return false;
}
+ // should have bailed by now if something went wrong
return true;
}
@@ -664,69 +1352,104 @@ bool render_font::load_bdf()
// load_cached - load a font in cached format
//-------------------------------------------------
-bool render_font::load_cached(emu_file &file, u32 hash)
+bool render_font::load_cached(emu_file &file, u64 length, u32 hash)
{
- // get the file size
- u64 filesize = file.size();
-
- // first read the header
- u8 header[CACHED_HEADER_SIZE];
- u32 bytes_read = file.read(header, CACHED_HEADER_SIZE);
- if (bytes_read != CACHED_HEADER_SIZE)
+ // get the file size, read the header, and check that it looks good
+ u64 const filesize(file.size());
+ bdc_header header;
+ if (!header.read(file))
+ {
+ osd_printf_warning("render_font::load_cached: error reading BDC header\n");
return false;
-
- // validate the header
- if (header[0] != 'f' || header[1] != 'o' || header[2] != 'n' || header[3] != 't')
+ }
+ else if (!header.check_magic() || (bdc_header::MAJVERSION != header.get_major_version()) || (bdc_header::MINVERSION != header.get_minor_version()))
+ {
+ LOG("render_font::load_cached: incompatible BDC file\n");
return false;
- if (hash && (header[4] != u8(hash >> 24) || header[5] != u8(hash >> 16) || header[6] != u8(hash >> 8) || header[7] != u8(hash)))
+ }
+ else if (length && ((header.get_original_length() != length) || (header.get_original_hash() != hash)))
+ {
+ LOG("render_font::load_cached: BDC file does not match original BDF file\n");
return false;
- m_height = (header[8] << 8) | header[9];
- m_scale = 1.0f / (float)m_height;
- m_yoffs = s16((header[10] << 8) | header[11]);
- u32 numchars = (header[12] << 24) | (header[13] << 16) | (header[14] << 8) | header[15];
- if (filesize - CACHED_HEADER_SIZE < numchars * CACHED_CHAR_SIZE)
+ }
+
+ // get global properties from the header
+ m_height = header.get_height();
+ m_scale = 1.0f / float(m_height);
+ m_yoffs = header.get_y_offset();
+ m_defchar = header.get_default_character();
+ u32 const numchars(header.get_glyph_count());
+ if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize)
+ {
+ LOG("render_font::load_cached: BDC file is too small to hold glyph table\n");
return false;
+ }
// now read the rest of the data
- m_rawdata.resize(filesize - CACHED_HEADER_SIZE);
- bytes_read = file.read(&m_rawdata[0], filesize - CACHED_HEADER_SIZE);
- if (bytes_read != filesize - CACHED_HEADER_SIZE)
+ u64 const remaining(filesize - file.tell());
+ try
{
- m_rawdata.clear();
- return false;
+ m_rawdata.resize(std::size_t(remaining));
+ }
+ catch (...)
+ {
+ osd_printf_error("render_font::load_cached: allocation error\n");
+ }
+ for (u64 bytes_read = 0; remaining > bytes_read; )
+ {
+ u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining));
+ if (file.read(&m_rawdata[bytes_read], chunk) != chunk)
+ {
+ osd_printf_error("render_font::load_cached: error reading BDC data\n");
+ m_rawdata.clear();
+ return false;
+ }
+ bytes_read += chunk;
}
// extract the data from the data
- u64 offset = numchars * CACHED_CHAR_SIZE;
- for (int chindex = 0; chindex < numchars; chindex++)
+ std::size_t offset(std::size_t(numchars) * bdc_table_entry::size());
+ bdc_table_entry entry(m_rawdata.empty() ? nullptr : &m_rawdata[0]);
+ for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next())
{
- const u8 *info = reinterpret_cast<u8 *>(&m_rawdata[chindex * CACHED_CHAR_SIZE]);
- int chnum = (info[0] << 8) | info[1];
-
// if we don't have a subtable yet, make one
+ int const chnum(entry.get_encoding());
+ LOG("render_font::load_cached: loading character %d\n", chnum);
if (!m_glyphs[chnum / 256])
- m_glyphs[chnum / 256] = new glyph[256];
+ {
+ try
+ {
+ m_glyphs[chnum / 256] = new glyph[256];
+ }
+ catch (...)
+ {
+ osd_printf_error("render_font::load_cached: allocation error\n");
+ m_rawdata.clear();
+ return false;
+ }
+ }
// fill in the entry
glyph &gl = m_glyphs[chnum / 256][chnum % 256];
- gl.width = (info[2] << 8) | info[3];
- gl.xoffs = s16((info[4] << 8) | info[5]);
- gl.yoffs = s16((info[6] << 8) | info[7]);
- gl.bmwidth = (info[8] << 8) | info[9];
- gl.bmheight = (info[10] << 8) | info[11];
+ gl.width = entry.get_x_advance();
+ gl.xoffs = entry.get_bb_x_offset();
+ gl.yoffs = entry.get_bb_y_offset();
+ gl.bmwidth = entry.get_bb_width();
+ gl.bmheight = entry.get_bb_height();
gl.rawdata = &m_rawdata[offset];
// advance the offset past the character
offset += (gl.bmwidth * gl.bmheight + 7) / 8;
- if (offset > filesize - CACHED_HEADER_SIZE)
+ if (m_rawdata.size() < offset)
{
+ osd_printf_verbose("render_font::load_cached: BDC file too small to hold all glyphs\n");
m_rawdata.clear();
return false;
}
}
- // reuse the chartable as a temporary buffer
- m_format = FF_CACHED;
+ // got everything
+ m_format = format::CACHED;
return true;
}
@@ -735,76 +1458,73 @@ bool render_font::load_cached(emu_file &file, u32 hash)
// save_cached - save a font in cached format
//-------------------------------------------------
-bool render_font::save_cached(const char *filename, u32 hash)
+bool render_font::save_cached(const char *filename, u64 length, u32 hash)
{
osd_printf_warning("Generating cached BDF font...\n");
// attempt to open the file
emu_file file(m_manager.machine().options().font_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE);
- osd_file::error filerr = file.open(filename);
- if (filerr != osd_file::error::NONE)
+ osd_file::error const filerr = file.open(filename);
+ if (osd_file::error::NONE != filerr)
return false;
- // determine the number of characters
- int numchars = 0;
- for (int chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++)
+ // count glyphs
+ unsigned numchars = 0;
+ for (glyph const *const page : m_glyphs)
{
- if (m_glyphs[chnum / 256])
+ for (unsigned chnum = 0; page && (256 > chnum); ++chnum)
{
- glyph &gl = m_glyphs[chnum / 256][chnum % 256];
- if (gl.width > 0)
- numchars++;
+ if (0 < page[chnum].width)
+ ++numchars;
}
}
+ LOG("render_font::save_cached: %u glyphs with positive advance to save\n", numchars);
try
{
+ u32 bytes_written;
+
+ {
+ LOG("render_font::save_cached: writing header\n");
+ bdc_header hdr;
+ hdr.set_magic();
+ hdr.set_version();
+ hdr.set_original_length(length);
+ hdr.set_original_hash(hash);
+ hdr.set_glyph_count(numchars);
+ hdr.set_height(m_height);
+ hdr.set_y_offset(m_yoffs);
+ hdr.set_default_character(m_defchar);
+ if (!hdr.write(file))
+ throw emu_fatalerror("Error writing cached file");
+ }
+ u64 const table_offs(file.tell());
+
// allocate an array to hold the character data
- std::vector<u8> chartable(numchars * CACHED_CHAR_SIZE, 0);
+ std::vector<u8> chartable(std::size_t(numchars) * bdc_table_entry::size(), 0);
// allocate a temp buffer to compress into
std::vector<u8> tempbuffer(65536);
- // write the header
- u8 *dest = &tempbuffer[0];
- *dest++ = 'f';
- *dest++ = 'o';
- *dest++ = 'n';
- *dest++ = 't';
- *dest++ = hash >> 24;
- *dest++ = hash >> 16;
- *dest++ = hash >> 8;
- *dest++ = hash & 0xff;
- *dest++ = m_height >> 8;
- *dest++ = m_height & 0xff;
- *dest++ = m_yoffs >> 8;
- *dest++ = m_yoffs & 0xff;
- *dest++ = numchars >> 24;
- *dest++ = numchars >> 16;
- *dest++ = numchars >> 8;
- *dest++ = numchars & 0xff;
- assert(dest == &tempbuffer[CACHED_HEADER_SIZE]);
- u32 bytes_written = file.write(&tempbuffer[0], CACHED_HEADER_SIZE);
- if (bytes_written != dest - &tempbuffer[0])
- throw emu_fatalerror("Error writing cached file");
-
// write the empty table to the beginning of the file
- bytes_written = file.write(&chartable[0], numchars * CACHED_CHAR_SIZE);
- if (bytes_written != numchars * CACHED_CHAR_SIZE)
+ bytes_written = file.write(&chartable[0], chartable.size());
+ if (bytes_written != chartable.size())
throw emu_fatalerror("Error writing cached file");
// loop over all characters
- int tableindex = 0;
- for (int chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++)
+ bdc_table_entry table_entry(chartable.empty() ? nullptr : &chartable[0]);
+ for (unsigned chnum = 0; chnum < (256 * ARRAY_LENGTH(m_glyphs)); chnum++)
{
- glyph &gl = get_char(chnum);
- if (gl.width > 0)
+ if (m_glyphs[chnum / 256] && (0 < m_glyphs[chnum / 256][chnum % 256].width))
{
+ LOG("render_font::save_cached: writing glyph %u\n", chnum);
+ glyph &gl(get_char(chnum));
+
// write out a bit-compressed bitmap if we have one
if (gl.bitmap.valid())
{
// write the data to the tempbuffer
- dest = &tempbuffer[0];
+ u8 *dest = &tempbuffer[0];
u8 accum = 0;
u8 accbit = 7;
@@ -842,27 +1562,34 @@ bool render_font::save_cached(const char *filename, u32 hash)
}
// compute the table entry
- dest = &chartable[tableindex++ * CACHED_CHAR_SIZE];
- *dest++ = chnum >> 8;
- *dest++ = chnum & 0xff;
- *dest++ = gl.width >> 8;
- *dest++ = gl.width & 0xff;
- *dest++ = gl.xoffs >> 8;
- *dest++ = gl.xoffs & 0xff;
- *dest++ = gl.yoffs >> 8;
- *dest++ = gl.yoffs & 0xff;
- *dest++ = gl.bmwidth >> 8;
- *dest++ = gl.bmwidth & 0xff;
- *dest++ = gl.bmheight >> 8;
- *dest++ = gl.bmheight & 0xff;
+ table_entry.set_encoding(chnum);
+ table_entry.set_x_advance(gl.width);
+ table_entry.set_bb_x_offset(gl.xoffs);
+ table_entry.set_bb_y_offset(gl.yoffs);
+ table_entry.set_bb_width(gl.bmwidth);
+ table_entry.set_bb_height(gl.bmheight);
+ table_entry = table_entry.get_next();
}
}
// seek back to the beginning and rewrite the table
- file.seek(CACHED_HEADER_SIZE, SEEK_SET);
- bytes_written = file.write(&chartable[0], numchars * CACHED_CHAR_SIZE);
- if (bytes_written != numchars * CACHED_CHAR_SIZE)
- throw emu_fatalerror("Error writing cached file");
+ if (!chartable.empty())
+ {
+ LOG("render_font::save_cached: writing character table\n");
+ file.seek(table_offs, SEEK_SET);
+ u8 const *bytes(&chartable[0]);
+ for (u64 remaining = chartable.size(); remaining; )
+ {
+ u32 const chunk((std::min<u64>)(std::numeric_limits<u32>::max(), remaining));
+ bytes_written = file.write(bytes, chunk);
+ if (chunk != bytes_written)
+ throw emu_fatalerror("Error writing cached file");
+ bytes += chunk;
+ remaining -= chunk;
+ }
+ }
+
+ // no trouble?
return true;
}
catch (...)
@@ -871,3 +1598,99 @@ bool render_font::save_cached(const char *filename, u32 hash)
return false;
}
}
+
+
+void render_font::render_font_command_glyph()
+{
+ // FIXME: this is copy/pasta from the BDC loading, and it shouldn't be injected into every font
+ emu_file file(OPEN_FLAG_READ);
+ if (file.open_ram(font_uicmd14, sizeof(font_uicmd14)) == osd_file::error::NONE)
+ {
+ // get the file size, read the header, and check that it looks good
+ u64 const filesize(file.size());
+ bdc_header header;
+ if (!header.read(file))
+ {
+ osd_printf_warning("render_font::render_font_command_glyph: error reading BDC header\n");
+ return;
+ }
+ else if (!header.check_magic() || (bdc_header::MAJVERSION != header.get_major_version()) || (bdc_header::MINVERSION != header.get_minor_version()))
+ {
+ LOG("render_font::render_font_command_glyph: incompatible BDC file\n");
+ return;
+ }
+
+ // get global properties from the header
+ m_height_cmd = header.get_height();
+ m_yoffs_cmd = header.get_y_offset();
+ u32 const numchars(header.get_glyph_count());
+ if ((file.tell() + (u64(numchars) * bdc_table_entry::size())) > filesize)
+ {
+ LOG("render_font::render_font_command_glyph: BDC file is too small to hold glyph table\n");
+ return;
+ }
+
+ // now read the rest of the data
+ u64 const remaining(filesize - file.tell());
+ try
+ {
+ m_rawdata_cmd.resize(std::size_t(remaining));
+ }
+ catch (...)
+ {
+ osd_printf_error("render_font::render_font_command_glyph: allocation error\n");
+ }
+ for (u64 bytes_read = 0; remaining > bytes_read; )
+ {
+ u32 const chunk((std::min)(u64(std::numeric_limits<u32>::max()), remaining));
+ if (file.read(&m_rawdata_cmd[bytes_read], chunk) != chunk)
+ {
+ osd_printf_error("render_font::render_font_command_glyph: error reading BDC data\n");
+ m_rawdata_cmd.clear();
+ return;
+ }
+ bytes_read += chunk;
+ }
+
+ // extract the data from the data
+ std::size_t offset(std::size_t(numchars) * bdc_table_entry::size());
+ bdc_table_entry entry(m_rawdata_cmd.empty() ? nullptr : &m_rawdata_cmd[0]);
+ for (unsigned chindex = 0; chindex < numchars; chindex++, entry = entry.get_next())
+ {
+ // if we don't have a subtable yet, make one
+ int const chnum(entry.get_encoding());
+ LOG("render_font::render_font_command_glyph: loading character %d\n", chnum);
+ if (!m_glyphs_cmd[chnum / 256])
+ {
+ try
+ {
+ m_glyphs_cmd[chnum / 256] = new glyph[256];
+ }
+ catch (...)
+ {
+ osd_printf_error("render_font::render_font_command_glyph: allocation error\n");
+ m_rawdata_cmd.clear();
+ return;
+ }
+ }
+
+ // fill in the entry
+ glyph &gl = m_glyphs_cmd[chnum / 256][chnum % 256];
+ gl.width = entry.get_x_advance();
+ gl.xoffs = entry.get_bb_x_offset();
+ gl.yoffs = entry.get_bb_y_offset();
+ gl.bmwidth = entry.get_bb_width();
+ gl.bmheight = entry.get_bb_height();
+ gl.rawdata = &m_rawdata_cmd[offset];
+
+ // advance the offset past the character
+ offset += (gl.bmwidth * gl.bmheight + 7) / 8;
+ if (m_rawdata_cmd.size() < offset)
+ {
+ osd_printf_verbose("render_font::render_font_command_glyph: BDC file too small to hold all glyphs\n");
+ m_rawdata_cmd.clear();
+ return;
+ }
+ }
+ }
+}