summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--scripts/build/png2bdc.py109
-rw-r--r--scripts/font/NotoSans-Bold.bdcbin52993 -> 61317 bytes
-rw-r--r--scripts/src/emu.lua1
-rw-r--r--src/emu/rendfont.cpp1381
-rw-r--r--src/emu/rendfont.h41
-rw-r--r--src/emu/ui/cmddata.h220
-rw-r--r--src/emu/ui/cmdrender.h150
-rw-r--r--src/frontend/mame/ui/selmenu.cpp47
-rw-r--r--src/frontend/mame/ui/selmenu.h19
-rw-r--r--src/lib/util/coretmpl.h388
-rw-r--r--uismall.bdf7
11 files changed, 1716 insertions, 647 deletions
diff --git a/scripts/build/png2bdc.py b/scripts/build/png2bdc.py
index 4ffeb4c01ab..a1be5c552d0 100644
--- a/scripts/build/png2bdc.py
+++ b/scripts/build/png2bdc.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python
##
## license:BSD-3-Clause
## copyright-holders:Aaron Giles, Andrew Gardner
@@ -50,7 +50,7 @@
##
## Python note:
## This is a near-literal translation of the original C++ code. As such there
-## are some very non-pythonic things done throughout. The conversion was done
+## are some very non-pythonic things done throughout. The conversion was done
## this way so as to insure compatibility as much as possible given the small
## number of test cases.
##
@@ -74,7 +74,7 @@ class RenderFontChar:
"""
Contains information about a single character in a font.
"""
-
+
def __init__(self):
"""
"""
@@ -90,10 +90,11 @@ class RenderFont:
"""
Contains information about a font
"""
-
+
def __init__(self):
self.height = 0 # height of the font, from ascent to descent
self.yOffs = 0 # y offset from baseline to descent
+ self.defChar = -1 # default character for glyphs not present
self.chars = list() # array of characters
for i in range(0, 65536):
self.chars.append(RenderFontChar())
@@ -107,7 +108,7 @@ def pixelIsSet(value):
return (value & 0xffffff) == 0
-def renderFontSaveCached(font, filename, hash32):
+def renderFontSaveCached(font, filename, length64, hash32):
"""
"""
fp = open(filename, "wb")
@@ -120,45 +121,61 @@ def renderFontSaveCached(font, filename, hash32):
if c.width > 0:
numChars += 1
- CACHED_CHAR_SIZE = 12
- CACHED_HEADER_SIZE = 16
-
+ CACHED_CHAR_SIZE = 16
+ CACHED_HEADER_SIZE = 32
+
try:
+ fp.write(b'b')
+ fp.write(b'd')
+ fp.write(b'c')
fp.write(b'f')
- fp.write(b'o')
fp.write(b'n')
fp.write(b't')
+ fp.write(b2p(1))
+ fp.write(b2p(0))
+ fp.write(b2p(length64 >> 56 & 0xff))
+ fp.write(b2p(length64 >> 48 & 0xff))
+ fp.write(b2p(length64 >> 40 & 0xff))
+ fp.write(b2p(length64 >> 32 & 0xff))
+ fp.write(b2p(length64 >> 24 & 0xff))
+ fp.write(b2p(length64 >> 16 & 0xff))
+ fp.write(b2p(length64 >> 8 & 0xff))
+ fp.write(b2p(length64 >> 0 & 0xff))
fp.write(b2p(hash32 >> 24 & 0xff))
fp.write(b2p(hash32 >> 16 & 0xff))
fp.write(b2p(hash32 >> 8 & 0xff))
fp.write(b2p(hash32 >> 0 & 0xff))
- fp.write(b2p(font.height >> 8 & 0xff))
- fp.write(b2p(font.height >> 0 & 0xff))
- fp.write(b2p(font.yOffs >> 8 & 0xff))
- fp.write(b2p(font.yOffs >> 0 & 0xff))
fp.write(b2p(numChars >> 24 & 0xff))
fp.write(b2p(numChars >> 16 & 0xff))
fp.write(b2p(numChars >> 8 & 0xff))
fp.write(b2p(numChars >> 0 & 0xff))
-
+ fp.write(b2p(font.height >> 8 & 0xff))
+ fp.write(b2p(font.height >> 0 & 0xff))
+ fp.write(b2p(font.yOffs >> 8 & 0xff))
+ fp.write(b2p(font.yOffs >> 0 & 0xff))
+ fp.write(b2p(font.defChar >> 24 & 0xff))
+ fp.write(b2p(font.defChar >> 16 & 0xff))
+ fp.write(b2p(font.defChar >> 8 & 0xff))
+ fp.write(b2p(font.defChar >> 0 & 0xff))
+
# Write a blank table at first (?)
charTable = [0]*(numChars * CACHED_CHAR_SIZE)
for i in range(numChars * CACHED_CHAR_SIZE):
fp.write(b2p(charTable[i]))
-
+
# Loop over all characters
tableIndex = 0
-
+
for i in range(len(font.chars)):
c = font.chars[i]
if c.width == 0:
continue
-
+
if c.bitmap:
dBuffer = list()
accum = 0
accbit = 7
-
+
# Bit-encode the character data
for y in range(0, c.bmHeight):
src = None
@@ -173,42 +190,44 @@ def renderFontSaveCached(font, filename, hash32):
dBuffer.append(accum)
accum = 0
accbit = 7
-
+
# Flush any extra
if accbit != 7:
dBuffer.append(accum)
-
+
# Write the data
for j in range(len(dBuffer)):
fp.write(b2p(dBuffer[j]))
-
+
destIndex = tableIndex * CACHED_CHAR_SIZE
- charTable[destIndex + 0] = i >> 8 & 0xff
- charTable[destIndex + 1] = i >> 0 & 0xff
- charTable[destIndex + 2] = c.width >> 8 & 0xff
- charTable[destIndex + 3] = c.width >> 0 & 0xff
- charTable[destIndex + 4] = c.xOffs >> 8 & 0xff
- charTable[destIndex + 5] = c.xOffs >> 0 & 0xff
- charTable[destIndex + 6] = c.yOffs >> 8 & 0xff
- charTable[destIndex + 7] = c.yOffs >> 0 & 0xff
- charTable[destIndex + 8] = c.bmWidth >> 8 & 0xff
- charTable[destIndex + 9] = c.bmWidth >> 0 & 0xff
- charTable[destIndex + 10] = c.bmHeight >> 8 & 0xff
- charTable[destIndex + 11] = c.bmHeight >> 0 & 0xff
+ charTable[destIndex + 0] = i >> 24 & 0xff
+ charTable[destIndex + 1] = i >> 16 & 0xff
+ charTable[destIndex + 2] = i >> 8 & 0xff
+ charTable[destIndex + 3] = i >> 0 & 0xff
+ charTable[destIndex + 4] = c.width >> 8 & 0xff
+ charTable[destIndex + 5] = c.width >> 0 & 0xff
+ charTable[destIndex + 8] = c.xOffs >> 8 & 0xff
+ charTable[destIndex + 9] = c.xOffs >> 0 & 0xff
+ charTable[destIndex + 10] = c.yOffs >> 8 & 0xff
+ charTable[destIndex + 11] = c.yOffs >> 0 & 0xff
+ charTable[destIndex + 12] = c.bmWidth >> 8 & 0xff
+ charTable[destIndex + 13] = c.bmWidth >> 0 & 0xff
+ charTable[destIndex + 14] = c.bmHeight >> 8 & 0xff
+ charTable[destIndex + 15] = c.bmHeight >> 0 & 0xff
tableIndex += 1
-
+
# Seek back to the beginning and rewrite the table
fp.seek(CACHED_HEADER_SIZE, 0)
for i in range(numChars * CACHED_CHAR_SIZE):
fp.write(b2p(charTable[i]))
-
+
fp.close()
return 0
except:
print(sys.exc_info[1])
return 1
-
+
def bitmapToChars(pngObject, font):
"""
@@ -225,7 +244,7 @@ def bitmapToChars(pngObject, font):
for r,g,b,a in zip(irpd, irpd, irpd, irpd):
cRow.append(a << 24 | r << 16 | g << 8 | b)
bitmap.append(cRow)
-
+
rowStart = 0
while rowStart < height:
# Find the top of the row
@@ -317,7 +336,7 @@ def bitmapToChars(pngObject, font):
ch.yOffs = font.yOffs
ch.bmWidth = len(ch.bitmap[0])
ch.bmHeight = len(ch.bitmap)
-
+
# Insert the character into the list
font.chars[chStart] = ch
@@ -327,7 +346,7 @@ def bitmapToChars(pngObject, font):
# Next row
rowStart = rowEnd + 1
-
+
# Return non-zero if we errored
return rowStart < height
@@ -341,7 +360,7 @@ def main():
sys.stderr.write("Usage:\n%s <input.png> [<input2.png> [...]] <output.bdc>\n" % sys.argv[0])
return 1
bdcName = sys.argv[-1]
-
+
font = RenderFont()
for i in range(1, len(sys.argv)-1):
filename = sys.argv[i]
@@ -359,12 +378,12 @@ def main():
error = bitmapToChars(pngObject, font)
if error:
return 1
-
- error = renderFontSaveCached(font, bdcName, 0)
+
+ error = renderFontSaveCached(font, bdcName, 0, 0)
return error
-
-
-
+
+
+
########################################
## Program entry point
########################################
diff --git a/scripts/font/NotoSans-Bold.bdc b/scripts/font/NotoSans-Bold.bdc
index 8c13907b86a..2ac5c7e0e59 100644
--- a/scripts/font/NotoSans-Bold.bdc
+++ b/scripts/font/NotoSans-Bold.bdc
Binary files differ
diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua
index cd94d54f925..b67226e1576 100644
--- a/scripts/src/emu.lua
+++ b/scripts/src/emu.lua
@@ -183,7 +183,6 @@ files {
MAME_DIR .. "src/emu/video.h",
MAME_DIR .. "src/emu/rendersw.hxx",
MAME_DIR .. "src/emu/ui/uimain.h",
- MAME_DIR .. "src/emu/ui/cmdrender.h", -- TODO: remove
MAME_DIR .. "src/emu/ui/cmddata.h", -- TODO: remove
MAME_DIR .. "src/emu/debug/debugcmd.cpp",
MAME_DIR .. "src/emu/debug/debugcmd.h",
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;
+ }
+ }
+ }
+}
diff --git a/src/emu/rendfont.h b/src/emu/rendfont.h
index 558b5c55aa5..589388855a6 100644
--- a/src/emu/rendfont.h
+++ b/src/emu/rendfont.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
rendfont.h
@@ -8,8 +8,8 @@
***************************************************************************/
-#ifndef __RENDFONT_H__
-#define __RENDFONT_H__
+#ifndef MAME_EMU_RENDFONT_H
+#define MAME_EMU_RENDFONT_H
#include "render.h"
@@ -53,11 +53,15 @@ private:
{
public:
glyph()
- : width(0),
- xoffs(0), yoffs(0),
- bmwidth(0), bmheight(0),
- rawdata(nullptr),
- texture(nullptr) { }
+ : width(-1)
+ , xoffs(-1), yoffs(-1)
+ , bmwidth(0), bmheight(0)
+ , rawdata(nullptr)
+ , texture(nullptr)
+ , bitmap()
+ , color()
+ {
+ }
s32 width; // width from this character to the next
s32 xoffs, yoffs; // X and Y offset from baseline to top,left of bitmap
@@ -67,16 +71,15 @@ private:
bitmap_argb32 bitmap; // pointer to the bitmap containing the raw data
rgb_t color;
-
};
// internal format
- enum format
+ enum class format
{
- FF_UNKNOWN,
- FF_TEXT,
- FF_CACHED,
- FF_OSD
+ UNKNOWN,
+ TEXT,
+ CACHED,
+ OSD
};
// helpers
@@ -84,9 +87,8 @@ private:
void char_expand(char32_t chnum, glyph &ch);
bool load_cached_bdf(const char *filename);
bool load_bdf();
- bool load_cached(emu_file &file, u32 hash);
- bool load_cached_cmd(emu_file &file, u32 hash);
- bool save_cached(const char *filename, u32 hash);
+ bool load_cached(emu_file &file, u64 length, u32 hash);
+ bool save_cached(const char *filename, u64 length, u32 hash);
void render_font_command_glyph();
@@ -95,6 +97,7 @@ private:
format m_format; // format of font data
int m_height; // height of the font, from ascent to descent
int m_yoffs; // y offset from baseline to descent
+ int m_defchar; // default substitute character
float m_scale; // 1 / height precomputed
glyph *m_glyphs[17*256]; // array of glyph subtables
std::vector<char> m_rawdata; // pointer to the raw data for the font
@@ -107,11 +110,9 @@ private:
std::vector<char> m_rawdata_cmd; // pointer to the raw data for the font
// constants
- static const int CACHED_CHAR_SIZE = 12;
- static const int CACHED_HEADER_SIZE = 16;
static const u64 CACHED_BDF_HASH_SIZE = 1024;
};
void convert_command_glyph(std::string &s);
-#endif /* __RENDFONT_H__ */
+#endif /* MAME_EMU_RENDFONT_H */
diff --git a/src/emu/ui/cmddata.h b/src/emu/ui/cmddata.h
index 14c09c73883..675195f7c76 100644
--- a/src/emu/ui/cmddata.h
+++ b/src/emu/ui/cmddata.h
@@ -50,7 +50,7 @@ enum
#define COMMAND_CONVERT_TEXT '@'
// Defined Game Command Font Color Array
-static rgb_t color_table[] =
+static rgb_t const color_table[] =
{
0, // dummy
BUTTON_COLOR_RED, // BTN_A
@@ -147,19 +147,19 @@ static rgb_t color_table[] =
struct fix_command_t
{
- unsigned char glyph_char;
- const int glyph_code;
+ char glyph_char;
+ unsigned glyph_code;
};
struct fix_strings_t
{
- const char *glyph_str;
- const int glyph_code;
- int glyph_str_len;
+ char const *glyph_str;
+ int const glyph_code;
+ unsigned glyph_str_len;
};
-static fix_command_t default_text[] =
+static fix_command_t const default_text[] =
{
// Alphabetic Buttons (NeoGeo): A~D,H,Z
{ 'A', 1 }, // BTN_A
@@ -248,7 +248,7 @@ static fix_command_t default_text[] =
{ 0, 0 } // end of array
};
-static fix_command_t expand_text[] =
+static fix_command_t const expand_text[] =
{
// Alphabetic Buttons (NeoGeo): S (Slash Button)
{ 's', 19 }, // BTN_S
@@ -289,114 +289,114 @@ static fix_command_t expand_text[] =
static fix_strings_t convert_text[] =
{
// Alphabetic Buttons: A~Z
- { "A-button", 1 }, // BTN_A
- { "B-button", 2 }, // BTN_B
- { "C-button", 3 }, // BTN_C
- { "D-button", 4 }, // BTN_D
- { "E-button", 5 }, // BTN_E
- { "F-button", 6 }, // BTN_F
- { "G-button", 7 }, // BTN_G
- { "H-button", 8 }, // BTN_H
- { "I-button", 9 }, // BTN_I
- { "J-button", 10 }, // BTN_J
- { "K-button", 11 }, // BTN_K
- { "L-button", 12 }, // BTN_L
- { "M-button", 13 }, // BTN_M
- { "N-button", 14 }, // BTN_N
- { "O-button", 15 }, // BTN_O
- { "P-button", 16 }, // BTN_P
- { "Q-button", 17 }, // BTN_Q
- { "R-button", 18 }, // BTN_R
- { "S-button", 19 }, // BTN_S
- { "T-button", 20 }, // BTN_T
- { "U-button", 21 }, // BTN_U
- { "V-button", 22 }, // BTN_V
- { "W-button", 23 }, // BTN_W
- { "X-button", 24 }, // BTN_X
- { "Y-button", 25 }, // BTN_Y
- { "Z-button", 26 }, // BTN_Z
+ { "A-button", 1, 0 }, // BTN_A
+ { "B-button", 2, 0 }, // BTN_B
+ { "C-button", 3, 0 }, // BTN_C
+ { "D-button", 4, 0 }, // BTN_D
+ { "E-button", 5, 0 }, // BTN_E
+ { "F-button", 6, 0 }, // BTN_F
+ { "G-button", 7, 0 }, // BTN_G
+ { "H-button", 8, 0 }, // BTN_H
+ { "I-button", 9, 0 }, // BTN_I
+ { "J-button", 10, 0 }, // BTN_J
+ { "K-button", 11, 0 }, // BTN_K
+ { "L-button", 12, 0 }, // BTN_L
+ { "M-button", 13, 0 }, // BTN_M
+ { "N-button", 14, 0 }, // BTN_N
+ { "O-button", 15, 0 }, // BTN_O
+ { "P-button", 16, 0 }, // BTN_P
+ { "Q-button", 17, 0 }, // BTN_Q
+ { "R-button", 18, 0 }, // BTN_R
+ { "S-button", 19, 0 }, // BTN_S
+ { "T-button", 20, 0 }, // BTN_T
+ { "U-button", 21, 0 }, // BTN_U
+ { "V-button", 22, 0 }, // BTN_V
+ { "W-button", 23, 0 }, // BTN_W
+ { "X-button", 24, 0 }, // BTN_X
+ { "Y-button", 25, 0 }, // BTN_Y
+ { "Z-button", 26, 0 }, // BTN_Z
// Special Moves and Buttons
- { "decrease", 37 }, // BTN_DEC
- { "increase", 38 }, // BTN_INC
- { "BALL", 45 }, // Joystick Ball
- { "start", 51 }, // BTN_START
- { "select", 52 }, // BTN_SELECT
- { "punch", 53 }, // BTN_PUNCH
- { "kick", 54 }, // BTN_KICK
- { "guard", 55 }, // BTN_GUARD
- { "L-punch", 57 }, // Light Punch
- { "M-punch", 58 }, // Middle Punch
- { "S-punch", 59 }, // Strong Punch
- { "L-kick", 60 }, // Light Kick
- { "M-kick", 61 }, // Middle Kick
- { "S-kick", 62 }, // Strong Kick
- { "3-kick", 63 }, // 3 Kick
- { "3-punch", 64 }, // 3 Punch
- { "2-kick", 65 }, // 2 Kick
- { "2-punch", 66 }, // 2 Pick
+ { "decrease", 37, 0 }, // BTN_DEC
+ { "increase", 38, 0 }, // BTN_INC
+ { "BALL", 45, 0 }, // Joystick Ball
+ { "start", 51, 0 }, // BTN_START
+ { "select", 52, 0 }, // BTN_SELECT
+ { "punch", 53, 0 }, // BTN_PUNCH
+ { "kick", 54, 0 }, // BTN_KICK
+ { "guard", 55, 0 }, // BTN_GUARD
+ { "L-punch", 57, 0 }, // Light Punch
+ { "M-punch", 58, 0 }, // Middle Punch
+ { "S-punch", 59, 0 }, // Strong Punch
+ { "L-kick", 60, 0 }, // Light Kick
+ { "M-kick", 61, 0 }, // Middle Kick
+ { "S-kick", 62, 0 }, // Strong Kick
+ { "3-kick", 63, 0 }, // 3 Kick
+ { "3-punch", 64, 0 }, // 3 Punch
+ { "2-kick", 65, 0 }, // 2 Kick
+ { "2-punch", 66, 0 }, // 2 Pick
// Custom Buttons and Cursor Buttons
- { "custom1", 67 }, // CUSTOM_1
- { "custom2", 68 }, // CUSTOM_2
- { "custom3", 69 }, // CUSTOM_3
- { "custom4", 70 }, // CUSTOM_4
- { "custom5", 71 }, // CUSTOM_5
- { "custom6", 72 }, // CUSTOM_6
- { "custom7", 73 }, // CUSTOM_7
- { "custom8", 74 }, // CUSTOM_8
- { "up", 75 }, // (Cursor Up)
- { "down", 76 }, // (Cursor Down)
- { "left", 77 }, // (Cursor Left)
- { "right", 78 }, // (Cursor Right)
+ { "custom1", 67, 0 }, // CUSTOM_1
+ { "custom2", 68, 0 }, // CUSTOM_2
+ { "custom3", 69, 0 }, // CUSTOM_3
+ { "custom4", 70, 0 }, // CUSTOM_4
+ { "custom5", 71, 0 }, // CUSTOM_5
+ { "custom6", 72, 0 }, // CUSTOM_6
+ { "custom7", 73, 0 }, // CUSTOM_7
+ { "custom8", 74, 0 }, // CUSTOM_8
+ { "up", 75, 0 }, // (Cursor Up)
+ { "down", 76, 0 }, // (Cursor Down)
+ { "left", 77, 0 }, // (Cursor Left)
+ { "right", 78, 0 }, // (Cursor Right)
// Player Lever
- { "lever", 79 }, // Non Player Lever
- { "nplayer", 80 }, // Gray Color Lever
- { "1player", 81 }, // 1 Player Lever
- { "2player", 82 }, // 2 Player Lever
- { "3player", 83 }, // 3 Player Lever
- { "4player", 84 }, // 4 Player Lever
- { "5player", 85 }, // 5 Player Lever
- { "6player", 86 }, // 6 Player Lever
- { "7player", 87 }, // 7 Player Lever
- { "8player", 88 }, // 8 Player Lever
+ { "lever", 79, 0 }, // Non Player Lever
+ { "nplayer", 80, 0 }, // Gray Color Lever
+ { "1player", 81, 0 }, // 1 Player Lever
+ { "2player", 82, 0 }, // 2 Player Lever
+ { "3player", 83, 0 }, // 3 Player Lever
+ { "4player", 84, 0 }, // 4 Player Lever
+ { "5player", 85, 0 }, // 5 Player Lever
+ { "6player", 86, 0 }, // 6 Player Lever
+ { "7player", 87, 0 }, // 7 Player Lever
+ { "8player", 88, 0 }, // 8 Player Lever
// Composition of Arrow Directions
- { "-->", 90 }, // Arrow
- { "==>", 91 }, // Continue Arrow
- { "hcb", 100 }, // Half Circle Back
- { "huf", 101 }, // Half Circle Front Up
- { "hcf", 102 }, // Half Circle Front
- { "hub", 103 }, // Half Circle Back Up
- { "qfd", 104 }, // 1/4 Cir For 2 Down
- { "qdb", 105 }, // 1/4 Cir Down 2 Back
- { "qbu", 106 }, // 1/4 Cir Back 2 Up
- { "quf", 107 }, // 1/4 Cir Up 2 For
- { "qbd", 108 }, // 1/4 Cir Back 2 Down
- { "qdf", 109 }, // 1/4 Cir Down 2 For
- { "qfu", 110 }, // 1/4 Cir For 2 Up
- { "qub", 111 }, // 1/4 Cir Up 2 Back
- { "fdf", 112 }, // Full Clock Forward
- { "fub", 113 }, // Full Clock Back
- { "fuf", 114 }, // Full Count Forward
- { "fdb", 115 }, // Full Count Back
- { "xff", 116 }, // 2x Forward
- { "xbb", 117 }, // 2x Back
- { "dsf", 118 }, // Dragon Screw Forward
- { "dsb", 119 }, // Dragon Screw Back
+ { "-->", 90, 0 }, // Arrow
+ { "==>", 91, 0 }, // Continue Arrow
+ { "hcb", 100, 0 }, // Half Circle Back
+ { "huf", 101, 0 }, // Half Circle Front Up
+ { "hcf", 102, 0 }, // Half Circle Front
+ { "hub", 103, 0 }, // Half Circle Back Up
+ { "qfd", 104, 0 }, // 1/4 Cir For 2 Down
+ { "qdb", 105, 0 }, // 1/4 Cir Down 2 Back
+ { "qbu", 106, 0 }, // 1/4 Cir Back 2 Up
+ { "quf", 107, 0 }, // 1/4 Cir Up 2 For
+ { "qbd", 108, 0 }, // 1/4 Cir Back 2 Down
+ { "qdf", 109, 0 }, // 1/4 Cir Down 2 For
+ { "qfu", 110, 0 }, // 1/4 Cir For 2 Up
+ { "qub", 111, 0 }, // 1/4 Cir Up 2 Back
+ { "fdf", 112, 0 }, // Full Clock Forward
+ { "fub", 113, 0 }, // Full Clock Back
+ { "fuf", 114, 0 }, // Full Count Forward
+ { "fdb", 115, 0 }, // Full Count Back
+ { "xff", 116, 0 }, // 2x Forward
+ { "xbb", 117, 0 }, // 2x Back
+ { "dsf", 118, 0 }, // Dragon Screw Forward
+ { "dsb", 119, 0 }, // Dragon Screw Back
// Big letter Text
- { "AIR", 121 }, // AIR
- { "DIR", 122 }, // DIR
- { "MAX", 123 }, // MAX
- { "TAP", 124 }, // TAP
+ { "AIR", 121, 0 }, // AIR
+ { "DIR", 122, 0 }, // DIR
+ { "MAX", 123, 0 }, // MAX
+ { "TAP", 124, 0 }, // TAP
// Condition of Positions
- { "jump", 125 }, // Jump
- { "hold", 126 }, // Hold
- { "air", 127 }, // Air
- { "sit", 128 }, // Squatting
- { "close", 129 }, // Close
- { "away", 130 }, // Away
- { "charge", 131 }, // Charge
- { "tap", 132 }, // Serious Tap
- { "button", 133 }, // Any Button
- { nullptr, 0 } // end of array
+ { "jump", 125, 0 }, // Jump
+ { "hold", 126, 0 }, // Hold
+ { "air", 127, 0 }, // Air
+ { "sit", 128, 0 }, // Squatting
+ { "close", 129, 0 }, // Close
+ { "away", 130, 0 }, // Away
+ { "charge", 131, 0 }, // Charge
+ { "tap", 132, 0 }, // Serious Tap
+ { "button", 133, 0 }, // Any Button
+ { nullptr, 0, 0 } // end of array
};
#endif /* __UI_CMDDATA_H__ */
diff --git a/src/emu/ui/cmdrender.h b/src/emu/ui/cmdrender.h
deleted file mode 100644
index 905b681f746..00000000000
--- a/src/emu/ui/cmdrender.h
+++ /dev/null
@@ -1,150 +0,0 @@
-// license:BSD-3-Clause
-// copyright-holders:Maurizio Petrarota
-/***************************************************************************
-
- ui/cmdrender.h
-
- UI command render fonts.
-
-***************************************************************************/
-
-#include "ui/uicmd14.fh"
-#include "ui/cmddata.h"
-
-void convert_command_glyph(std::string &str)
-{
- int j;
- int len = str.length();
- int buflen = (len + 2) * 2;
- char *d = global_alloc_array(char, buflen);
-
- for (int i = j = 0; i < len;)
- {
- fix_command_t *fixcmd = nullptr;
- char32_t uchar;
- int ucharcount = uchar_from_utf8(&uchar, str.substr(i).c_str(), len - i);
- if (ucharcount == -1)
- break;
- else if (ucharcount != 1)
- goto process_next;
- else if (str[i] == '\n')
- uchar = '\n';
- else if (str[i] == COMMAND_CONVERT_TEXT)
- {
- if (str[i] == str[i + 1])
- ++i;
- else
- {
- fix_strings_t *fixtext = convert_text;
- for (; fixtext->glyph_code; ++fixtext)
- {
- if (!fixtext->glyph_str_len)
- fixtext->glyph_str_len = strlen(fixtext->glyph_str);
-
- if (strncmp(fixtext->glyph_str, str.substr(i + 1).c_str(), fixtext->glyph_str_len) == 0)
- {
- uchar = fixtext->glyph_code + COMMAND_UNICODE;
- i += strlen(fixtext->glyph_str);
- break;
- }
- }
- }
- }
- else if (str[i] == COMMAND_DEFAULT_TEXT)
- fixcmd = default_text;
- else if (str[i] == COMMAND_EXPAND_TEXT)
- fixcmd = expand_text;
-
- if (fixcmd)
- {
- if (str[i] == str[i + 1])
- i++;
- else
- {
- for (; fixcmd->glyph_code; ++fixcmd)
- if (str[i + 1] == fixcmd->glyph_char)
- {
- uchar = fixcmd->glyph_code + COMMAND_UNICODE;
- ++i;
- break;
- }
- }
- }
-process_next:
- i += ucharcount;
- ucharcount = utf8_from_uchar(d + j, buflen - j - 1, uchar);
- if (ucharcount == -1)
- break;
- j += ucharcount;
- }
- d[j] = '\0';
- str = d;
- global_free_array(d);
-}
-
-void render_font::render_font_command_glyph()
-{
- emu_file ramfile(OPEN_FLAG_READ);
-
- if (ramfile.open_ram(font_uicmd14, sizeof(font_uicmd14)) == osd_file::error::NONE)
- load_cached_cmd(ramfile, 0);
-}
-
-bool render_font::load_cached_cmd(emu_file &file, u32 hash)
-{
- u64 filesize = file.size();
- u8 header[CACHED_HEADER_SIZE];
- u32 bytes_read = file.read(header, CACHED_HEADER_SIZE);
-
- if (bytes_read != CACHED_HEADER_SIZE)
- return false;
-
- if (header[0] != 'f' || header[1] != 'o' || header[2] != 'n' || header[3] != 't')
- return false;
- if (header[4] != u8(hash >> 24) || header[5] != u8(hash >> 16) || header[6] != u8(hash >> 8) || header[7] != u8(hash))
- return false;
- m_height_cmd = (header[8] << 8) | header[9];
- m_yoffs_cmd = 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)
- return false;
-
- m_rawdata_cmd.resize(filesize - CACHED_HEADER_SIZE);
- bytes_read = file.read(&m_rawdata_cmd[0], filesize - CACHED_HEADER_SIZE);
- if (bytes_read != filesize - CACHED_HEADER_SIZE)
- {
- m_rawdata_cmd.clear();
- return false;
- }
-
- u64 offset = numchars * CACHED_CHAR_SIZE;
- for (int chindex = 0; chindex < numchars; chindex++)
- {
- const u8 *info = reinterpret_cast<u8 *>(&m_rawdata_cmd[chindex * CACHED_CHAR_SIZE]);
- int chnum = (info[0] << 8) | info[1];
-
- if (!m_glyphs_cmd[chnum / 256])
- m_glyphs_cmd[chnum / 256] = new glyph[256];
-
- glyph &gl = m_glyphs_cmd[chnum / 256][chnum % 256];
-
- if (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + COLOR_BUTTONS)
- gl.color = color_table[chnum - COMMAND_UNICODE];
-
- 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.rawdata = &m_rawdata_cmd[offset];
-
- offset += (gl.bmwidth * gl.bmheight + 7) / 8;
- if (offset > filesize - CACHED_HEADER_SIZE)
- {
- m_rawdata_cmd.clear();
- return false;
- }
- }
-
- return true;
-}
diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp
index 84c0300d628..c8e598df443 100644
--- a/src/frontend/mame/ui/selmenu.cpp
+++ b/src/frontend/mame/ui/selmenu.cpp
@@ -139,12 +139,6 @@ menu_select_launch::cache::~cache()
menu_select_launch::~menu_select_launch()
{
- // need to manually clean up icon textures for now
- for (auto &texture : m_icons_texture)
- {
- if (texture)
- machine().render().texture_free(texture);
- }
}
@@ -164,6 +158,7 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
, m_pressed(false)
, m_repeat(0)
, m_right_visible_lines(0)
+ , m_icons(MAX_ICONS_RENDER)
{
// set up persistent cache for machine run
{
@@ -181,18 +176,6 @@ menu_select_launch::menu_select_launch(mame_ui_manager &mui, render_container &c
add_cleanup_callback(&menu_select_launch::exit);
}
}
-
- // initialise icon cache
- if (is_swlist)
- {
- std::fill(std::begin(m_icons_texture), std::end(m_icons_texture), nullptr);
- }
- else
- {
- std::generate(std::begin(m_icons_texture), std::end(m_icons_texture), [&render = machine().render()]() { return render.texture_alloc(); });
- std::generate(std::begin(m_icons_bitmap), std::end(m_icons_bitmap), []() { return std::make_unique<bitmap_argb32>(); });
- }
- std::fill(std::begin(m_old_icons), std::end(m_old_icons), nullptr);
}
@@ -624,9 +607,15 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl
auto x1 = x0 + ud_arrow_width;
auto y1 = y0 + ui().get_line_height();
- if (m_old_icons[linenum] != driver || ui_globals::redraw_icon)
+ icon_cache::iterator icon(m_icons.find(driver));
+ if ((m_icons.end() == icon) || ui_globals::redraw_icon)
{
- m_old_icons[linenum] = driver;
+ if (m_icons.end() == icon)
+ {
+ texture_ptr texture(machine().render().texture_alloc(), [&render = machine().render()] (render_texture *texture) { render.texture_free(texture); });
+ bitmap_ptr bitmap(std::make_unique<bitmap_argb32>());
+ icon = m_icons.emplace(std::piecewise_construct, std::forward_as_tuple(driver), std::forward_as_tuple(std::move(texture), std::move(bitmap))).first;
+ }
// set clone status
bool cloneof = strcmp(driver->parent, "0");
@@ -657,6 +646,7 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl
render_load_ico(*tmp, snapfile, nullptr, fullname.c_str());
}
+ bitmap_argb32 &bitmap(*icon->second.second);
if (tmp->valid())
{
float panel_width = x1 - x0;
@@ -697,24 +687,25 @@ float menu_select_launch::draw_icon(int linenum, void *selectedref, float x0, fl
else
dest_bitmap = tmp;
- m_icons_bitmap[linenum]->allocate(panel_width_pixel, panel_height_pixel);
-
+ bitmap.allocate(panel_width_pixel, panel_height_pixel);
for (int x = 0; x < dest_xPixel; x++)
for (int y = 0; y < dest_yPixel; y++)
- m_icons_bitmap[linenum]->pix32(y, x) = dest_bitmap->pix32(y, x);
+ bitmap.pix32(y, x) = dest_bitmap->pix32(y, x);
auto_free(machine(), dest_bitmap);
- m_icons_texture[linenum]->set_bitmap(*m_icons_bitmap[linenum], m_icons_bitmap[linenum]->cliprect(), TEXFORMAT_ARGB32);
+ icon->second.first->set_bitmap(bitmap, bitmap.cliprect(), TEXFORMAT_ARGB32);
+ }
+ else
+ {
+ bitmap.reset();
}
- else if (m_icons_bitmap[linenum] != nullptr)
- m_icons_bitmap[linenum]->reset();
auto_free(machine(), tmp);
}
- if (m_icons_bitmap[linenum] != nullptr && m_icons_bitmap[linenum]->valid())
- container().add_quad(x0, y0, x1, y1, rgb_t::white(), m_icons_texture[linenum], PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
+ if (icon->second.second->valid())
+ container().add_quad(x0, y0, x1, y1, rgb_t::white(), icon->second.first.get(), PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA));
return ud_arrow_width * 1.5f;
}
diff --git a/src/frontend/mame/ui/selmenu.h b/src/frontend/mame/ui/selmenu.h
index f5d4deac84c..95e8dbfe607 100644
--- a/src/frontend/mame/ui/selmenu.h
+++ b/src/frontend/mame/ui/selmenu.h
@@ -104,7 +104,9 @@ private:
using cache_ptr = std::shared_ptr<cache>;
using cache_ptr_map = std::map<running_machine *, cache_ptr>;
- static constexpr std::size_t MAX_ICONS_RENDER = 40;
+ using icon_cache = util::lru_cache_map<game_driver const *, std::pair<texture_ptr, bitmap_ptr> >;
+
+ static constexpr std::size_t MAX_ICONS_RENDER = 128;
void reset_pressed() { m_pressed = false; m_repeat = 0; }
bool mouse_pressed() const { return (osd_ticks() >= m_repeat); }
@@ -115,11 +117,11 @@ private:
// draw left panel
virtual float draw_left_panel(float x1, float y1, float x2, float y2) = 0;
- game_driver const *m_info_driver;
- ui_software_info const *m_info_software;
- int m_info_view;
- std::vector<std::string> m_items_list;
- std::string m_info_buffer;
+ game_driver const *m_info_driver;
+ ui_software_info const *m_info_software;
+ int m_info_view;
+ std::vector<std::string> m_items_list;
+ std::string m_info_buffer;
// draw infos
void infos_render(float x1, float y1, float x2, float y2);
@@ -187,10 +189,7 @@ private:
int m_right_visible_lines; // right box lines
-
- render_texture *m_icons_texture[MAX_ICONS_RENDER];
- bitmap_ptr m_icons_bitmap[MAX_ICONS_RENDER];
- game_driver const *m_old_icons[MAX_ICONS_RENDER];
+ icon_cache m_icons;
static std::mutex s_cache_guard;
static cache_ptr_map s_caches;
diff --git a/src/lib/util/coretmpl.h b/src/lib/util/coretmpl.h
index 758e4821aa3..4d17f3c0a4b 100644
--- a/src/lib/util/coretmpl.h
+++ b/src/lib/util/coretmpl.h
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Aaron Giles
+// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
coretmpl.h
@@ -17,9 +17,18 @@
#include "corealloc.h"
#include <array>
+#include <cassert>
#include <cstddef>
+#include <functional>
+#include <initializer_list>
#include <iterator>
+#include <list>
+#include <map>
+#include <memory>
+#include <set>
#include <stdexcept>
+#include <tuple>
+#include <type_traits>
#include <utility>
#include <vector>
@@ -433,6 +442,383 @@ private:
};
+// LRU cache that behaves like std::map with differences:
+// * drops least-recently used items if necessary on insert to prevent size from exceeding max_size
+// * iterates from least- to most-recently used rather than in order by key
+// * iterators to dropped items are invalidated
+// * not all map interfaces implemented
+// * copyable and swappable but not movable
+// * swap may invalidate past-the-end iterator, other iterators refer to new container
+template <typename Key, typename T, typename Compare = std::less<Key>, class Allocator = std::allocator<std::pair<Key const, T> > >
+class lru_cache_map
+{
+private:
+ class iterator_compare;
+ typedef std::list<std::pair<Key const, T>, Allocator> value_list;
+ typedef typename std::allocator_traits<Allocator>::template rebind_alloc<typename value_list::iterator> iterator_allocator_type;
+ typedef std::set<typename value_list::iterator, iterator_compare, iterator_allocator_type> iterator_set;
+
+ class iterator_compare
+ {
+ public:
+ typedef std::true_type is_transparent;
+ iterator_compare(Compare const &comp) : m_comp(comp) { }
+ iterator_compare(iterator_compare const &that) = default;
+ iterator_compare(iterator_compare &&that) = default;
+ Compare key_comp() const { return m_comp; }
+ iterator_compare &operator=(iterator_compare const &that) = default;
+ iterator_compare &operator=(iterator_compare &&that) = default;
+ bool operator()(typename value_list::iterator const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs->first, rhs->first); }
+ template <typename K> bool operator()(typename value_list::iterator const &lhs, K const &rhs) const { return m_comp(lhs->first, rhs); }
+ template <typename K> bool operator()(K const &lhs, typename value_list::iterator const &rhs) const { return m_comp(lhs, rhs->first); }
+ private:
+ Compare m_comp;
+ };
+
+public:
+ typedef Key key_type;
+ typedef T mapped_type;
+ typedef std::pair<Key const, T> value_type;
+ typedef typename value_list::size_type size_type;
+ typedef typename value_list::difference_type difference_type;
+ typedef Compare key_compare;
+ typedef Allocator allocator_type;
+ typedef value_type &reference;
+ typedef value_type const &const_reference;
+ typedef typename std::allocator_traits<Allocator>::pointer pointer;
+ typedef typename std::allocator_traits<Allocator>::const_pointer const_pointer;
+ typedef typename value_list::iterator iterator;
+ typedef typename value_list::const_iterator const_iterator;
+ typedef typename value_list::reverse_iterator reverse_iterator;
+ typedef typename value_list::const_reverse_iterator const_reverse_iterator;
+
+ explicit lru_cache_map(size_type max_size)
+ : lru_cache_map(max_size, key_compare())
+ {
+ }
+ lru_cache_map(size_type max_size, key_compare const &comp, allocator_type const &alloc = allocator_type())
+ : m_max_size(max_size)
+ , m_size(0U)
+ , m_elements(alloc)
+ , m_mapping(iterator_compare(comp), iterator_allocator_type(alloc))
+ {
+ assert(0U < m_max_size);
+ }
+ lru_cache_map(lru_cache_map const &that)
+ : m_max_size(that.m_max_size)
+ , m_size(that.m_size)
+ , m_elements(that.m_elements)
+ , m_mapping(that.m_mapping.key_comp(), that.m_mapping.get_allocator())
+ {
+ for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
+ m_mapping.insert(it);
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ }
+
+ allocator_type get_allocator() const { return m_elements.get_allocator(); }
+
+ iterator begin() { return m_elements.begin(); }
+ const_iterator begin() const { return m_elements.cbegin(); }
+ const_iterator cbegin() const { return m_elements.cbegin(); }
+ iterator end() { return m_elements.end(); }
+ const_iterator end() const { return m_elements.cend(); }
+ const_iterator cend() const { return m_elements.cend(); }
+ reverse_iterator rbegin() { return m_elements.rbegin(); }
+ const_reverse_iterator rbegin() const { return m_elements.crbegin(); }
+ const_reverse_iterator crbegin() const { return m_elements.crbegin(); }
+ reverse_iterator rend() { return m_elements.end(); }
+ const_reverse_iterator rend() const { return m_elements.crend(); }
+ const_reverse_iterator crend() const { return m_elements.crend(); }
+
+ bool empty() const { return !m_size; }
+ size_type size() const { return m_size; }
+ size_type max_size() const { return m_max_size; }
+
+ mapped_type &operator[](key_type const &key)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(key));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(key), std::tuple<>()));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return inserted->second;
+ }
+ mapped_type &operator[](key_type &&key)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(key));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(key, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple<>()));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return inserted->second;
+ }
+ mapped_type &at(key_type const &key)
+ {
+ typename iterator_set::iterator existing(m_mapping.find(key));
+ if (m_mapping.end() != existing)
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ else
+ {
+ throw std::out_of_range("lru_cache_map::at");
+ }
+ }
+ mapped_type const &at(key_type const &key) const
+ {
+ typename iterator_set::iterator existing(m_mapping.find(key));
+ if (m_mapping.end() != existing)
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return (*existing)->second;
+ }
+ else
+ {
+ throw std::out_of_range("lru_cache_map::at");
+ }
+ }
+
+ void clear()
+ {
+ m_size = 0U;
+ m_elements.clear();
+ m_mapping.clear();
+ }
+ std::pair<iterator, bool> insert(value_type const &value)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return std::pair<iterator, bool>(*existing, false);
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), value));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return std::pair<iterator, bool>(inserted, true);
+ }
+ std::pair<iterator, bool> insert(value_type &&value)
+ {
+ typename iterator_set::iterator existing(m_mapping.lower_bound(value.first));
+ if ((m_mapping.end() != existing) && !m_mapping.key_comp()(value.first, *existing))
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *existing);
+ return std::pair<iterator, bool>(*existing, false);
+ }
+ make_space(existing);
+ iterator const inserted(m_elements.emplace(m_elements.end(), std::move(value)));
+ m_mapping.insert(existing, inserted);
+ ++m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return std::pair<iterator, bool>(inserted, true);
+ }
+ template <typename P>
+ std::pair<iterator, bool> insert(P &&value)
+ {
+ // FIXME: should only participate in overload resolution if std::is_constructible<value_type, P&&>::value
+ return emplace(std::forward<P>(value));
+ }
+ template <typename InputIt>
+ void insert(InputIt first, InputIt last)
+ {
+ while (first != last)
+ {
+ insert(*first);
+ ++first;
+ }
+ }
+ void insert(std::initializer_list<value_type> ilist)
+ {
+ for (value_type const &value : ilist)
+ insert(value);
+ }
+ template <typename... Params>
+ std::pair<iterator, bool> emplace(Params &&... args)
+ {
+ // TODO: is there a more efficient way than depending on value_type being efficiently movable?
+ return insert(value_type(std::forward<Params>(args)...));
+ }
+ iterator erase(const_iterator pos)
+ {
+ m_mapping.erase(m_elements.erase(pos, pos));
+ iterator const result(m_elements.erase(pos));
+ --m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return result;
+ }
+ iterator erase(const_iterator first, const_iterator last)
+ {
+ iterator pos(m_elements.erase(first, first));
+ while (pos != last)
+ {
+ m_mapping.erase(pos);
+ pos = m_elements.erase(pos);
+ --m_size;
+ }
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return pos;
+ }
+ size_type erase(key_type const &key)
+ {
+ typename iterator_set::iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return 0U;
+ }
+ else
+ {
+ m_elements.erase(*found);
+ m_mapping.erase(found);
+ --m_size;
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ return 1U;
+ }
+ }
+ void swap(lru_cache_map &that)
+ {
+ using std::swap;
+ swap(m_max_size, that.m_max_size);
+ swap(m_size, that.m_size);
+ swap(m_elements, that.m_elements);
+ swap(m_mapping, that.m_mapping);
+ }
+
+ size_type count(key_type const &key) const
+ {
+ // TODO: perhaps this should freshen an element
+ return m_mapping.count(key);
+ }
+ template <typename K>
+ size_type count(K const &x) const
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ // TODO: perhaps this should freshen an element
+ return m_mapping.count(x);
+ }
+ iterator find(key_type const &key)
+ {
+ typename iterator_set::const_iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ iterator find(key_type const &key) const
+ {
+ typename iterator_set::const_iterator const found(m_mapping.find(key));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ template <typename K>
+ iterator find(K const &x)
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ typename iterator_set::const_iterator const found(m_mapping.find(x));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+ template <typename K>
+ iterator find(K const &x) const
+ {
+ // FIXME: should only enable this overload if Compare::is_transparent
+ typename iterator_set::const_iterator const found(m_mapping.find(x));
+ if (m_mapping.end() == found)
+ {
+ return m_elements.end();
+ }
+ else
+ {
+ m_elements.splice(m_elements.cend(), m_elements, *found);
+ return *found;
+ }
+ }
+
+ key_compare key_comp() const
+ {
+ return m_mapping.key_comp().key_comp();
+ }
+
+ lru_cache_map &operator=(lru_cache_map const &that)
+ {
+ m_max_size = that.m_max_size;
+ m_size = that.m_size;
+ m_elements = that.m_elements;
+ m_mapping.clear();
+ for (iterator it = m_elements.begin(); it != m_elements.end(); ++it)
+ m_mapping.insert(it);
+ assert(m_elements.size() == m_size);
+ assert(m_mapping.size() == m_size);
+ }
+
+private:
+ void make_space(typename iterator_set::iterator &existing)
+ {
+ while (m_max_size <= m_size)
+ {
+ if ((m_mapping.end() != existing) && (m_elements.begin() == *existing))
+ existing = m_mapping.erase(existing);
+ else
+ m_mapping.erase(m_elements.begin());
+ m_elements.erase(m_elements.begin());
+ --m_size;
+ }
+ }
+
+ size_type m_max_size;
+ size_type m_size;
+ mutable value_list m_elements;
+ iterator_set m_mapping;
+};
+
+template <typename Key, typename T, typename Compare, class Allocator>
+void swap(lru_cache_map<Key, T, Compare, Allocator> &lhs, lru_cache_map<Key, T, Compare, Allocator> &rhs)
+{
+ lhs.swap(rhs);
+}
+
+
template <typename T, std::size_t N, bool WriteWrap = false, bool ReadWrap = WriteWrap>
class fifo : protected std::array<T, N>
{
diff --git a/uismall.bdf b/uismall.bdf
index f98f8063bff..526d60ae398 100644
--- a/uismall.bdf
+++ b/uismall.bdf
@@ -3,7 +3,7 @@ COMMENT Copyright (C) 1997-2016 MAMEDev and contributors
FONT -mamedev-uismall-Medium-R-Normal--16-120-96-96-P-100-ISO10646-1
SIZE 12 96 96
FONTBOUNDINGBOX 8 11 0 -2
-STARTPROPERTIES 17
+STARTPROPERTIES 18
POINT_SIZE 120
PIXEL_SIZE 16
RESOLUTION_X 96
@@ -21,6 +21,7 @@ SETWIDTH_NAME "Normal"
ADD_STYLE_NAME ""
CHARSET_REGISTRY "ISO10646"
CHARSET_ENCODING "1"
+DEFAULT_CHAR 173
ENDPROPERTIES
CHARS 1024
STARTCHAR space
@@ -1563,8 +1564,8 @@ E8
ENDCHAR
STARTCHAR paragraph
ENCODING 182
-SWIDTH 375 0
-DWIDTH 6 0
+SWIDTH 437 0
+DWIDTH 7 0
BBX 6 9 0 -2
BITMAP
7C