summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/rendfont.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/rendfont.c')
-rw-r--r--trunk/src/emu/rendfont.c816
1 files changed, 816 insertions, 0 deletions
diff --git a/trunk/src/emu/rendfont.c b/trunk/src/emu/rendfont.c
new file mode 100644
index 00000000000..c96b155d8a5
--- /dev/null
+++ b/trunk/src/emu/rendfont.c
@@ -0,0 +1,816 @@
+/***************************************************************************
+
+ rendfont.c
+
+ Rendering system font management.
+
+****************************************************************************
+
+ Copyright Aaron Giles
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name 'MAME' nor the names of its contributors may be
+ used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "rendfont.h"
+#include "rendutil.h"
+#include "emuopts.h"
+#include "zlib.h"
+
+#include "uismall.fh"
+
+
+//**************************************************************************
+// 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 NULL
+ if (*ptr == 0)
+ return NULL;
+
+ // 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
+ glyph *glyphtable = m_glyphs[chnum / 256];
+ if (glyphtable == NULL && m_format == FF_OSD)
+ glyphtable = m_glyphs[chnum / 256] = auto_alloc_array_clear(m_manager.machine(), glyph, 256);
+ if (glyphtable == NULL)
+ return dummy_glyph;
+
+ // if the character isn't generated yet, do it now
+ glyph &gl = glyphtable[chnum % 256];
+ if (gl.bitmap == NULL)
+ 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_rawdata(NULL),
+ m_rawsize(0),
+ m_osdfont(NULL)
+{
+ memset(m_glyphs, 0, sizeof(m_glyphs));
+
+ // if this is an OSD font, we're done
+ if (filename != NULL)
+ {
+ m_osdfont = manager.machine().osd().font_open(filename, m_height);
+ if (m_osdfont != NULL)
+ {
+ m_scale = 1.0f / (float)m_height;
+ m_format = FF_OSD;
+ return;
+ }
+ }
+
+ // if the filename is 'default' default to 'ui.bdf' for backwards compatibility
+ if (filename != NULL && mame_stricmp(filename, "default") == 0)
+ filename = "ui.bdf";
+
+ // attempt to load the cached version of the font first
+ if (filename != NULL && load_cached_bdf(filename))
+ return;
+
+ // load the raw data instead
+ emu_file ramfile(OPEN_FLAG_READ);
+ file_error filerr = ramfile.open_ram(font_uismall, sizeof(font_uismall));
+ if (filerr == FILERR_NONE)
+ load_cached(ramfile, 0);
+}
+
+
+//-------------------------------------------------
+// ~render_font - destructor
+//-------------------------------------------------
+
+render_font::~render_font()
+{
+ // free all the subtables
+ for (int tablenum = 0; tablenum < 256; tablenum++)
+ if (m_glyphs[tablenum] != NULL)
+ {
+ // loop over characters
+ for (int charnum = 0; charnum < 256; charnum++)
+ {
+ glyph &gl = m_glyphs[tablenum][charnum];
+ m_manager.texture_free(gl.texture);
+ auto_free(m_manager.machine(), gl.bitmap);
+ }
+
+ // free the subtable itself
+ auto_free(m_manager.machine(), m_glyphs[tablenum]);
+ }
+
+ // free the raw data and the size itself
+ auto_free(m_manager.machine(), m_rawdata);
+
+ // release the OSD font
+ if (m_osdfont != NULL)
+ m_manager.machine().osd().font_close(m_osdfont);
+}
+
+
+//-------------------------------------------------
+// char_expand - expand the raw data for a
+// character into a bitmap
+//-------------------------------------------------
+
+void render_font::char_expand(unicode_char chnum, glyph &gl)
+{
+ // if we're an OSD font, query the info
+ 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
+ gl.bitmap = m_manager.machine().osd().font_get_bitmap(m_osdfont, chnum, gl.width, gl.xoffs, gl.yoffs);
+ if (gl.bitmap == NULL)
+ {
+ 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 == NULL)
+ return;
+
+ // allocate a new bitmap of the size we need
+ gl.bitmap = auto_alloc(m_manager.machine(), bitmap_t(gl.bmwidth, m_height, BITMAP_FORMAT_ARGB32));
+ bitmap_fill(gl.bitmap, NULL, 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) ? BITMAP_ADDR32(gl.bitmap, desty, 0) : NULL;
+
+ // 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 != NULL)
+ {
+ *dest++ = (bits & 8) ? MAKE_ARGB(0xff,0xff,0xff,0xff) : MAKE_ARGB(0x00,0xff,0xff,0xff);
+ *dest++ = (bits & 4) ? MAKE_ARGB(0xff,0xff,0xff,0xff) : MAKE_ARGB(0x00,0xff,0xff,0xff);
+ *dest++ = (bits & 2) ? MAKE_ARGB(0xff,0xff,0xff,0xff) : MAKE_ARGB(0x00,0xff,0xff,0xff);
+ *dest++ = (bits & 1) ? MAKE_ARGB(0xff,0xff,0xff,0xff) : MAKE_ARGB(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 != NULL)
+ *dest++ = (accum & (1 << accumbit)) ? MAKE_ARGB(0xff,0xff,0xff,0xff) : MAKE_ARGB(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, NULL, 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_t &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.max_x = bounds.min_x + (float)gl.bmwidth * scale * aspect;
+ bounds.max_y = bounds.min_y + (float)m_height * scale;
+
+ // if the bitmap isn't big enough, bail
+ if (dest.width < bounds.max_x - bounds.min_x || dest.height < bounds.max_y - bounds.min_y)
+ return;
+
+ // if no texture, fill the target
+ if (gl.texture == NULL)
+ {
+ bitmap_fill(&dest, NULL, 0);
+ return;
+ }
+
+ // scale the font
+ INT32 origwidth = dest.width;
+ INT32 origheight = dest.height;
+ dest.width = bounds.max_x - bounds.min_x;
+ dest.height = bounds.max_y - bounds.min_y;
+ rectangle clip;
+ clip.min_x = clip.min_y = 0;
+ clip.max_x = gl.bitmap->width - 1;
+ clip.max_y = gl.bitmap->height - 1;
+ render_texture::hq_scale(dest, *gl.bitmap, clip, NULL);
+ dest.width = origwidth;
+ dest.height = origheight;
+}
+
+
+//-------------------------------------------------
+// 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;
+ for (const unsigned char *ptr = (const unsigned char *)string; *ptr != 0; ptr++)
+ totwidth += get_char(*ptr).width;
+
+ // 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(manager().machine().options().font_path(), OPEN_FLAG_READ);
+ file_error filerr = file.open(filename);
+ if (filerr != FILERR_NONE)
+ return false;
+
+ // determine the file size and allocate memory
+ m_rawsize = file.size();
+ char *data = auto_alloc_array_clear(m_manager.machine(), char, m_rawsize + 1);
+
+ // read the first chunk
+ UINT32 bytes = file.read(data, MIN(CACHED_BDF_HASH_SIZE, m_rawsize));
+ if (bytes != MIN(CACHED_BDF_HASH_SIZE, m_rawsize))
+ return false;
+
+ // has the chunk
+ UINT32 hash = crc32(0, (const UINT8 *)data, bytes) ^ (UINT32)m_rawsize;
+
+ // create the cached filename, changing the 'F' to a 'C' on the extension
+ astring cachedname(filename);
+ cachedname.del(cachedname.len() - 3, 3).cat("bdc");
+
+ // attempt to open the cached version of the font
+ {
+ emu_file cachefile(manager().machine().options().font_path(), OPEN_FLAG_READ);
+ filerr = cachefile.open(cachedname);
+ if (filerr == FILERR_NONE)
+ {
+ // if we have a cached version, load it
+ bool result = load_cached(cachefile, hash);
+
+ // if that worked, we're done
+ if (result)
+ {
+ auto_free(m_manager.machine(), data);
+ return true;
+ }
+ }
+ }
+
+ // read in the rest of the font
+ if (bytes < m_rawsize)
+ {
+ UINT32 read = file.read(data + bytes, m_rawsize - bytes);
+ if (read != m_rawsize - bytes)
+ {
+ auto_free(m_manager.machine(), data);
+ return false;
+ }
+ }
+
+ // NULL-terminate the data and attach it to the font
+ data[m_rawsize] = 0;
+ m_rawdata = data;
+
+ // load the BDF
+ bool result = load_bdf();
+
+ // if we loaded okay, create a cached one
+ if (result)
+ save_cached(cachedname, 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; ptr != NULL; 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 != NULL; 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 = NULL;
+ int charnum = -1;
+ int width = -1;
+
+ // scan for interesting per-character tags
+ for ( ; ptr != NULL; 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 != NULL && strncmp(ptr, "ENDCHAR", 7) != 0; ptr = next_line(ptr)) ;
+ break;
+ }
+ }
+
+ // if we have everything, allocate a new character
+ if (charnum >= 0 && charnum < 65536 && rawdata != NULL && bmwidth >= 0 && bmheight >= 0)
+ {
+ // if we don't have a subtable yet, make one
+ if (m_glyphs[charnum / 256] == NULL)
+ m_glyphs[charnum / 256] = auto_alloc_array_clear(m_manager.machine(), 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)
+ mame_printf_warning("Loading BDF font... (%d characters loaded)\n", charcount);
+ }
+ }
+
+ // make sure all the numbers are the same width
+ if (m_glyphs[0] != NULL)
+ {
+ 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 (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]);
+ int 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
+ UINT8 *data = auto_alloc_array(m_manager.machine(), UINT8, filesize - CACHED_HEADER_SIZE);
+ bytes_read = file.read(data, filesize - CACHED_HEADER_SIZE);
+ if (bytes_read != filesize - CACHED_HEADER_SIZE)
+ {
+ auto_free(m_manager.machine(), data);
+ return false;
+ }
+
+ // extract the data from the data
+ UINT64 offset = numchars * CACHED_CHAR_SIZE;
+ for (int chindex = 0; chindex < numchars; chindex++)
+ {
+ const UINT8 *info = &data[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] == NULL)
+ m_glyphs[chnum / 256] = auto_alloc_array_clear(m_manager.machine(), 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 = (char *)data + offset;
+
+ // advance the offset past the character
+ offset += (gl.bmwidth * gl.bmheight + 7) / 8;
+ if (offset > filesize - CACHED_HEADER_SIZE)
+ {
+ auto_free(m_manager.machine(), data);
+ return false;
+ }
+ }
+
+ // reuse the chartable as a temporary buffer
+ m_format = FF_CACHED;
+ m_rawdata = (char *)data;
+ return true;
+}
+
+
+//-------------------------------------------------
+// save_cached - save a font in cached format
+//-------------------------------------------------
+
+bool render_font::save_cached(const char *filename, UINT32 hash)
+{
+ mame_printf_warning("Generating cached BDF font...\n");
+
+ // attempt to open the file
+ emu_file file(manager().machine().options().font_path(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE);
+ file_error filerr = file.open(filename);
+ if (filerr != FILERR_NONE)
+ return false;
+
+ // determine the number of characters
+ int numchars = 0;
+ for (int chnum = 0; chnum < 65536; chnum++)
+ {
+ glyph *chtable = m_glyphs[chnum / 256];
+ if (chtable != NULL)
+ {
+ glyph &gl = chtable[chnum % 256];
+ if (gl.width > 0)
+ numchars++;
+ }
+ }
+
+ UINT8 *chartable = NULL;
+ UINT8 *tempbuffer = NULL;
+ try
+ {
+ // allocate an array to hold the character data
+ chartable = auto_alloc_array_clear(m_manager.machine(), UINT8, numchars * CACHED_CHAR_SIZE);
+
+ // allocate a temp buffer to compress into
+ tempbuffer = auto_alloc_array(m_manager.machine(), UINT8, 65536);
+
+ // write the header
+ UINT8 *dest = tempbuffer;
+ *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, dest - tempbuffer);
+ if (bytes_written != dest - tempbuffer)
+ throw emu_fatalerror("Error writing cached file");
+
+ // write the empty table to the beginning of the file
+ bytes_written = file.write(chartable, 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 != NULL)
+ {
+ // write the data to the tempbuffer
+ dest = tempbuffer;
+ 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) ? BITMAP_ADDR32(gl.bitmap, desty, 0) : NULL;
+ for (int x = 0; x < gl.bmwidth; x++)
+ {
+ if (src != NULL && RGB_ALPHA(src[x]) != 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, dest - tempbuffer);
+ if (bytes_written != dest - tempbuffer)
+ throw emu_fatalerror("Error writing cached file");
+
+ // free the bitmap and texture
+ m_manager.texture_free(gl.texture);
+ auto_free(m_manager.machine(), gl.bitmap);
+ gl.texture = NULL;
+ gl.bitmap = NULL;
+ }
+
+ // 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, numchars * CACHED_CHAR_SIZE);
+ if (bytes_written != numchars * CACHED_CHAR_SIZE)
+ throw emu_fatalerror("Error writing cached file");
+
+ // all done
+ auto_free(m_manager.machine(), tempbuffer);
+ auto_free(m_manager.machine(), chartable);
+ return true;
+ }
+ catch (...)
+ {
+ file.remove_on_close();
+ auto_free(m_manager.machine(), tempbuffer);
+ auto_free(m_manager.machine(), chartable);
+ return false;
+ }
+}