mirror of
https://github.com/holub/mame
synced 2025-05-06 14:25:54 +03:00
866 lines
24 KiB
C++
866 lines
24 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/***************************************************************************
|
|
|
|
rendfont.c
|
|
|
|
Rendering system font management.
|
|
|
|
***************************************************************************/
|
|
|
|
#include "emu.h"
|
|
#include "rendfont.h"
|
|
#include "emuopts.h"
|
|
#include "coreutil.h"
|
|
|
|
#include "osdepend.h"
|
|
#include "uismall.fh"
|
|
|
|
#include "ui/cmdrender.h"
|
|
|
|
//**************************************************************************
|
|
// INLINE FUNCTIONS
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// next_line - return a pointer to the start of
|
|
// the next line
|
|
//-------------------------------------------------
|
|
|
|
inline const char *next_line(const char *ptr)
|
|
{
|
|
// scan forward until we hit the end or a carriage return
|
|
while (*ptr != 13 && *ptr != 10 && *ptr != 0) ptr++;
|
|
|
|
// if we hit the end, return nullptr
|
|
if (*ptr == 0)
|
|
return nullptr;
|
|
|
|
// eat the trailing linefeed if present
|
|
if (*++ptr == 10)
|
|
ptr++;
|
|
return ptr;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_char - return a pointer to a character
|
|
// in a font, expanding if necessary
|
|
//-------------------------------------------------
|
|
|
|
inline render_font::glyph &render_font::get_char(unicode_char chnum)
|
|
{
|
|
static glyph dummy_glyph;
|
|
|
|
// grab the table; if none, return the dummy character
|
|
if (!m_glyphs[chnum / 256] && m_format == FF_OSD)
|
|
m_glyphs[chnum / 256] = new glyph[256];
|
|
if (!m_glyphs[chnum / 256])
|
|
{
|
|
//mamep: make table for command glyph
|
|
if (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT)
|
|
m_glyphs[chnum / 256] = new glyph[256];
|
|
else
|
|
return dummy_glyph;
|
|
}
|
|
|
|
// if the character isn't generated yet, do it now
|
|
glyph &gl = m_glyphs[chnum / 256][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;
|
|
|
|
if (!glyph_ch.bitmap.valid())
|
|
char_expand(chnum, glyph_ch);
|
|
|
|
//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.bitmap.allocate(gl.bmwidth, gl.bmheight);
|
|
rectangle clip;
|
|
clip.min_x = clip.min_y = 0;
|
|
clip.max_x = glyph_ch.bitmap.width() - 1;
|
|
clip.max_y = glyph_ch.bitmap.height() - 1;
|
|
render_texture::hq_scale(gl.bitmap, glyph_ch.bitmap, clip, nullptr);
|
|
|
|
/* wrap a texture around the bitmap */
|
|
gl.texture = m_manager.texture_alloc(render_texture::hq_scale);
|
|
gl.texture->set_bitmap(gl.bitmap, gl.bitmap.cliprect(), TEXFORMAT_ARGB32);
|
|
}
|
|
else
|
|
char_expand(chnum, gl);
|
|
}
|
|
|
|
// return the resulting character
|
|
return gl;
|
|
}
|
|
|
|
|
|
|
|
//**************************************************************************
|
|
// RENDER FONT
|
|
//**************************************************************************
|
|
|
|
//-------------------------------------------------
|
|
// render_font - constructor
|
|
//-------------------------------------------------
|
|
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
m_osdfont = manager.machine().osd().font_alloc();
|
|
if (m_osdfont)
|
|
{
|
|
if (m_osdfont->open(manager.machine().options().font_path(), filename, m_height))
|
|
{
|
|
m_scale = 1.0f / (float)m_height;
|
|
m_format = FF_OSD;
|
|
|
|
//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)
|
|
filename = "ui.bdf";
|
|
|
|
// attempt to load the cached version of the font first
|
|
if (filename != nullptr && load_cached_bdf(filename))
|
|
{
|
|
//mamep: allocate command glyph font
|
|
render_font_command_glyph();
|
|
return;
|
|
}
|
|
|
|
// load the raw 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);
|
|
render_font_command_glyph();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// ~render_font - destructor
|
|
//-------------------------------------------------
|
|
|
|
render_font::~render_font()
|
|
{
|
|
// free all the subtables
|
|
for (auto & elem : m_glyphs)
|
|
if (elem)
|
|
{
|
|
for (unsigned int charnum = 0; charnum < 256; charnum++)
|
|
{
|
|
glyph &gl = elem[charnum];
|
|
m_manager.texture_free(gl.texture);
|
|
}
|
|
delete[] elem;
|
|
}
|
|
|
|
for (auto & elem : m_glyphs_cmd)
|
|
if (elem)
|
|
{
|
|
for (unsigned int charnum = 0; charnum < 256; charnum++)
|
|
{
|
|
glyph &gl = elem[charnum];
|
|
m_manager.texture_free(gl.texture);
|
|
}
|
|
delete[] elem;
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// char_expand - expand the raw data for a
|
|
// character into a bitmap
|
|
//-------------------------------------------------
|
|
|
|
void render_font::char_expand(unicode_char chnum, glyph &gl)
|
|
{
|
|
rgb_t color = rgb_t(0xff,0xff,0xff,0xff);
|
|
bool is_cmd = (chnum >= COMMAND_UNICODE && chnum < COMMAND_UNICODE + MAX_GLYPH_FONT);
|
|
|
|
if (gl.color)
|
|
color = gl.color;
|
|
|
|
if (is_cmd)
|
|
{
|
|
// punt if nothing there
|
|
if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr)
|
|
return;
|
|
|
|
// allocate a new bitmap of the size we need
|
|
gl.bitmap.allocate(gl.bmwidth, m_height_cmd);
|
|
gl.bitmap.fill(0);
|
|
|
|
// extract the data
|
|
const char *ptr = gl.rawdata;
|
|
UINT8 accum = 0, accumbit = 7;
|
|
for (int y = 0; y < gl.bmheight; y++)
|
|
{
|
|
int desty = y + m_height_cmd + m_yoffs_cmd - gl.yoffs - gl.bmheight;
|
|
UINT32 *dest = (desty >= 0 && desty < m_height_cmd) ? &gl.bitmap.pix32(desty, 0) : nullptr;
|
|
{
|
|
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);
|
|
accumbit = (accumbit - 1) & 7;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if we're an OSD font, query the info
|
|
else if (m_format == FF_OSD)
|
|
{
|
|
// we set bmwidth to -1 if we've previously queried and failed
|
|
if (gl.bmwidth == -1)
|
|
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))
|
|
{
|
|
gl.bitmap.reset();
|
|
gl.bmwidth = -1;
|
|
return;
|
|
}
|
|
|
|
// populate the bmwidth/bmheight fields
|
|
gl.bmwidth = gl.bitmap.width();
|
|
gl.bmheight = gl.bitmap.height();
|
|
}
|
|
// other formats need to parse their data
|
|
else
|
|
{
|
|
// punt if nothing there
|
|
if (gl.bmwidth == 0 || gl.bmheight == 0 || gl.rawdata == nullptr)
|
|
return;
|
|
|
|
// allocate a new bitmap of the size we need
|
|
gl.bitmap.allocate(gl.bmwidth, m_height);
|
|
gl.bitmap.fill(0);
|
|
|
|
// extract the data
|
|
const char *ptr = gl.rawdata;
|
|
UINT8 accum = 0, accumbit = 7;
|
|
for (int y = 0; y < gl.bmheight; y++)
|
|
{
|
|
int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight;
|
|
UINT32 *dest = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix32(desty) : nullptr;
|
|
|
|
// text format
|
|
if (m_format == FF_TEXT)
|
|
{
|
|
// loop over bytes
|
|
for (int x = 0; x < gl.bmwidth; x += 4)
|
|
{
|
|
// scan for the next hex digit
|
|
int bits = -1;
|
|
while (*ptr != 13 && bits == -1)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
// advance to the next line
|
|
ptr = next_line(ptr);
|
|
}
|
|
|
|
// cached format
|
|
else if (m_format == FF_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);
|
|
accumbit = (accumbit - 1) & 7;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// wrap a texture around the bitmap
|
|
gl.texture = m_manager.texture_alloc(render_texture::hq_scale);
|
|
gl.texture->set_bitmap(gl.bitmap, gl.bitmap.cliprect(), TEXFORMAT_ARGB32);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_char_texture_and_bounds - return the
|
|
// texture for a character and compute the
|
|
// bounds of the final bitmap
|
|
//-------------------------------------------------
|
|
|
|
render_texture *render_font::get_char_texture_and_bounds(float height, float aspect, unicode_char chnum, render_bounds &bounds)
|
|
{
|
|
glyph &gl = get_char(chnum);
|
|
|
|
// on entry, assume x0,y0 are the top,left coordinate of the cell and add
|
|
// the character bounding box to that position
|
|
float scale = m_scale * height;
|
|
bounds.x0 += float(gl.xoffs) * scale * aspect;
|
|
|
|
// compute x1,y1 from there based on the bitmap size
|
|
bounds.x1 = bounds.x0 + float(gl.bmwidth) * scale * aspect;
|
|
bounds.y1 = bounds.y0 + float(m_height) * scale;
|
|
|
|
// return the texture
|
|
return gl.texture;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// get_scaled_bitmap_and_bounds - return a
|
|
// scaled bitmap and bounding rect for a char
|
|
//-------------------------------------------------
|
|
|
|
void render_font::get_scaled_bitmap_and_bounds(bitmap_argb32 &dest, float height, float aspect, unicode_char chnum, rectangle &bounds)
|
|
{
|
|
glyph &gl = get_char(chnum);
|
|
|
|
// on entry, assume x0,y0 are the top,left coordinate of the cell and add
|
|
// the character bounding box to that position
|
|
float scale = m_scale * height;
|
|
bounds.min_x = float(gl.xoffs) * scale * aspect;
|
|
bounds.min_y = 0;
|
|
|
|
// compute x1,y1 from there based on the bitmap size
|
|
bounds.set_width(float(gl.bmwidth) * scale * aspect);
|
|
bounds.set_height(float(m_height) * scale);
|
|
|
|
// if the bitmap isn't big enough, bail
|
|
if (dest.width() < bounds.width() || dest.height() < bounds.height())
|
|
return;
|
|
|
|
// if no texture, fill the target
|
|
if (gl.texture == nullptr)
|
|
{
|
|
dest.fill(0);
|
|
return;
|
|
}
|
|
|
|
// scale the font
|
|
bitmap_argb32 tempbitmap(&dest.pix(0), bounds.width(), bounds.height(), dest.rowpixels());
|
|
render_texture::hq_scale(tempbitmap, gl.bitmap, gl.bitmap.cliprect(), nullptr);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// char_width - return the width of a character
|
|
// at the given height
|
|
//-------------------------------------------------
|
|
|
|
float render_font::char_width(float height, float aspect, unicode_char ch)
|
|
{
|
|
return float(get_char(ch).width) * m_scale * height * aspect;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// string_width - return the width of a string
|
|
// at the given height
|
|
//-------------------------------------------------
|
|
|
|
float render_font::string_width(float height, float aspect, const char *string)
|
|
{
|
|
// loop over the string and accumulate widths
|
|
int totwidth = 0;
|
|
|
|
const char *ends = string + strlen(string);
|
|
const char *s = string;
|
|
unicode_char schar;
|
|
|
|
// loop over characters
|
|
while (*s != 0)
|
|
{
|
|
int scharcount = uchar_from_utf8(&schar, s, ends - s);
|
|
totwidth += get_char(schar).width;
|
|
s += scharcount;
|
|
}
|
|
|
|
|
|
// scale the final result based on height
|
|
return float(totwidth) * m_scale * height * aspect;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// utf8string_width - return the width of a
|
|
// UTF8-encoded string at the given height
|
|
//-------------------------------------------------
|
|
|
|
float render_font::utf8string_width(float height, float aspect, const char *utf8string)
|
|
{
|
|
int length = strlen(utf8string);
|
|
|
|
// loop over the string and accumulate widths
|
|
int count;
|
|
int totwidth = 0;
|
|
for (int offset = 0; offset < length; offset += count)
|
|
{
|
|
unicode_char uchar;
|
|
count = uchar_from_utf8(&uchar, utf8string + offset, length - offset);
|
|
if (count == -1)
|
|
break;
|
|
if (uchar < 0x10000)
|
|
totwidth += get_char(uchar).width;
|
|
}
|
|
|
|
// scale the final result based on height
|
|
return float(totwidth) * m_scale * height * aspect;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// load_cached_bdf - attempt to load a cached
|
|
// version of the BDF font 'filename'; if that
|
|
// fails, fall back on the regular BDF loader
|
|
// and create a new cached version
|
|
//-------------------------------------------------
|
|
|
|
bool render_font::load_cached_bdf(const char *filename)
|
|
{
|
|
// 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);
|
|
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
|
|
UINT32 bytes = file.read(&m_rawdata[0], MIN(CACHED_BDF_HASH_SIZE, m_rawsize));
|
|
if (bytes != MIN(CACHED_BDF_HASH_SIZE, m_rawsize))
|
|
return false;
|
|
|
|
// has the chunk
|
|
UINT32 hash = core_crc32(0, (const UINT8 *)&m_rawdata[0], bytes) ^ (UINT32)m_rawsize;
|
|
|
|
// 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");
|
|
|
|
// attempt to open the cached version of the font
|
|
{
|
|
emu_file cachefile(m_manager.machine().options().font_path(), OPEN_FLAG_READ);
|
|
filerr = cachefile.open(cachedname.c_str());
|
|
if (filerr == osd_file::error::NONE)
|
|
{
|
|
// if we have a cached version, load it
|
|
bool result = load_cached(cachefile, 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)
|
|
{
|
|
UINT32 read = file.read(&m_rawdata[bytes], m_rawsize - bytes);
|
|
if (read != m_rawsize - bytes)
|
|
{
|
|
m_rawdata.clear();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// NULL-terminate the data and attach it to the font
|
|
m_rawdata[m_rawsize] = 0;
|
|
|
|
// load the BDF
|
|
bool result = load_bdf();
|
|
|
|
// if we loaded okay, create a cached one
|
|
if (result)
|
|
save_cached(cachedname.c_str(), hash);
|
|
|
|
// close the file
|
|
return result;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// load_bdf - parse and load a BDF font
|
|
//-------------------------------------------------
|
|
|
|
bool render_font::load_bdf()
|
|
{
|
|
// set the format to text
|
|
m_format = FF_TEXT;
|
|
|
|
// first find the FONTBOUNDINGBOX tag
|
|
const char *ptr;
|
|
for (ptr = &m_rawdata[0]; ptr != nullptr; ptr = next_line(ptr))
|
|
{
|
|
// we only care about a tiny few fields
|
|
if (strncmp(ptr, "FONTBOUNDINGBOX ", 16) == 0)
|
|
{
|
|
int dummy1, dummy2;
|
|
if (sscanf(ptr + 16, "%d %d %d %d", &dummy1, &m_height, &dummy2, &m_yoffs) != 4)
|
|
return false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// compute the scale factor
|
|
m_scale = 1.0f / (float)m_height;
|
|
|
|
// now scan for characters
|
|
int charcount = 0;
|
|
for ( ; ptr != nullptr; ptr = next_line(ptr))
|
|
{
|
|
// 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)
|
|
{
|
|
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))
|
|
{
|
|
// ENCODING tells us which character
|
|
if (strncmp(ptr, "ENCODING ", 9) == 0)
|
|
{
|
|
if (sscanf(ptr + 9, "%d", &charnum) != 1)
|
|
return 1;
|
|
}
|
|
|
|
// DWIDTH tells us the width to the next character
|
|
else if (strncmp(ptr, "DWIDTH ", 7) == 0)
|
|
{
|
|
int dummy1;
|
|
if (sscanf(ptr + 7, "%d %d", &width, &dummy1) != 2)
|
|
return 1;
|
|
}
|
|
|
|
// BBX tells us the height/width of the bitmap and the offsets
|
|
else if (strncmp(ptr, "BBX ", 4) == 0)
|
|
{
|
|
if (sscanf(ptr + 4, "%d %d %d %d", &bmwidth, &bmheight, &xoffs, &yoffs) != 4)
|
|
return 1;
|
|
}
|
|
|
|
// BITMAP is the start of the data
|
|
else if (strncmp(ptr, "BITMAP", 6) == 0)
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// if we have everything, allocate a new character
|
|
if (charnum >= 0 && charnum < 65536 && rawdata != nullptr && bmwidth >= 0 && bmheight >= 0)
|
|
{
|
|
// if we don't have a subtable yet, make one
|
|
if (!m_glyphs[charnum / 256])
|
|
m_glyphs[charnum / 256] = new glyph[256];
|
|
|
|
// 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;
|
|
}
|
|
|
|
// some progress for big fonts
|
|
if (++charcount % 256 == 0)
|
|
osd_printf_warning("Loading BDF font... (%d characters loaded)\n", charcount);
|
|
}
|
|
}
|
|
|
|
// make sure all the numbers are the same width
|
|
if (m_glyphs[0])
|
|
{
|
|
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;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// load_cached - load a font in cached format
|
|
//-------------------------------------------------
|
|
|
|
bool render_font::load_cached(emu_file &file, UINT32 hash)
|
|
{
|
|
// get the file size
|
|
UINT64 filesize = file.size();
|
|
|
|
// first read the header
|
|
UINT8 header[CACHED_HEADER_SIZE];
|
|
UINT32 bytes_read = file.read(header, CACHED_HEADER_SIZE);
|
|
if (bytes_read != CACHED_HEADER_SIZE)
|
|
return false;
|
|
|
|
// validate the header
|
|
if (header[0] != 'f' || header[1] != 'o' || header[2] != 'n' || header[3] != 't')
|
|
return false;
|
|
if (hash && (header[4] != (UINT8)(hash >> 24) || header[5] != (UINT8)(hash >> 16) || header[6] != (UINT8)(hash >> 8) || header[7] != (UINT8)hash))
|
|
return false;
|
|
m_height = (header[8] << 8) | header[9];
|
|
m_scale = 1.0f / (float)m_height;
|
|
m_yoffs = (INT16)((header[10] << 8) | header[11]);
|
|
UINT32 numchars = (header[12] << 24) | (header[13] << 16) | (header[14] << 8) | header[15];
|
|
if (filesize - CACHED_HEADER_SIZE < numchars * CACHED_CHAR_SIZE)
|
|
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)
|
|
{
|
|
m_rawdata.clear();
|
|
return false;
|
|
}
|
|
|
|
// extract the data from the data
|
|
UINT64 offset = numchars * CACHED_CHAR_SIZE;
|
|
for (int chindex = 0; chindex < numchars; chindex++)
|
|
{
|
|
const UINT8 *info = reinterpret_cast<UINT8 *>(&m_rawdata[chindex * CACHED_CHAR_SIZE]);
|
|
int chnum = (info[0] << 8) | info[1];
|
|
|
|
// if we don't have a subtable yet, make one
|
|
if (!m_glyphs[chnum / 256])
|
|
m_glyphs[chnum / 256] = new glyph[256];
|
|
|
|
// fill in the entry
|
|
glyph &gl = m_glyphs[chnum / 256][chnum % 256];
|
|
gl.width = (info[2] << 8) | info[3];
|
|
gl.xoffs = (INT16)((info[4] << 8) | info[5]);
|
|
gl.yoffs = (INT16)((info[6] << 8) | info[7]);
|
|
gl.bmwidth = (info[8] << 8) | info[9];
|
|
gl.bmheight = (info[10] << 8) | info[11];
|
|
gl.rawdata = &m_rawdata[offset];
|
|
|
|
// advance the offset past the character
|
|
offset += (gl.bmwidth * gl.bmheight + 7) / 8;
|
|
if (offset > filesize - CACHED_HEADER_SIZE)
|
|
{
|
|
m_rawdata.clear();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// reuse the chartable as a temporary buffer
|
|
m_format = FF_CACHED;
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------
|
|
// save_cached - save a font in cached format
|
|
//-------------------------------------------------
|
|
|
|
bool render_font::save_cached(const char *filename, UINT32 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)
|
|
return false;
|
|
|
|
// determine the number of characters
|
|
int numchars = 0;
|
|
for (int chnum = 0; chnum < 65536; chnum++)
|
|
{
|
|
if (m_glyphs[chnum / 256])
|
|
{
|
|
glyph &gl = m_glyphs[chnum / 256][chnum % 256];
|
|
if (gl.width > 0)
|
|
numchars++;
|
|
}
|
|
}
|
|
|
|
try
|
|
{
|
|
// allocate an array to hold the character data
|
|
dynamic_buffer chartable(numchars * CACHED_CHAR_SIZE, 0);
|
|
|
|
// allocate a temp buffer to compress into
|
|
dynamic_buffer tempbuffer(65536);
|
|
|
|
// write the header
|
|
UINT8 *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]);
|
|
UINT32 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)
|
|
throw emu_fatalerror("Error writing cached file");
|
|
|
|
// loop over all characters
|
|
int tableindex = 0;
|
|
for (int chnum = 0; chnum < 65536; chnum++)
|
|
{
|
|
glyph &gl = get_char(chnum);
|
|
if (gl.width > 0)
|
|
{
|
|
// write out a bit-compressed bitmap if we have one
|
|
if (gl.bitmap.valid())
|
|
{
|
|
// write the data to the tempbuffer
|
|
dest = &tempbuffer[0];
|
|
UINT8 accum = 0;
|
|
UINT8 accbit = 7;
|
|
|
|
// bit-encode the character data
|
|
for (int y = 0; y < gl.bmheight; y++)
|
|
{
|
|
int desty = y + m_height + m_yoffs - gl.yoffs - gl.bmheight;
|
|
const UINT32 *src = (desty >= 0 && desty < m_height) ? &gl.bitmap.pix32(desty) : nullptr;
|
|
for (int x = 0; x < gl.bmwidth; x++)
|
|
{
|
|
if (src != nullptr && rgb_t(src[x]).a() != 0)
|
|
accum |= 1 << accbit;
|
|
if (accbit-- == 0)
|
|
{
|
|
*dest++ = accum;
|
|
accum = 0;
|
|
accbit = 7;
|
|
}
|
|
}
|
|
}
|
|
|
|
// flush any extra
|
|
if (accbit != 7)
|
|
*dest++ = accum;
|
|
|
|
// write the data
|
|
bytes_written = file.write(&tempbuffer[0], dest - &tempbuffer[0]);
|
|
if (bytes_written != dest - &tempbuffer[0])
|
|
throw emu_fatalerror("Error writing cached file");
|
|
|
|
// free the bitmap and texture
|
|
m_manager.texture_free(gl.texture);
|
|
gl.bitmap.reset();
|
|
gl.texture = nullptr;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// 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");
|
|
return true;
|
|
}
|
|
catch (...)
|
|
{
|
|
file.remove_on_close();
|
|
return false;
|
|
}
|
|
}
|