diff --git display/font.cc display/font.cc index 948ad0b3a..830d993b4 100644 --- display/font.cc +++ display/font.cc @@ -3,92 +3,113 @@ * (see LICENSE.txt) */ -#include -#include -#include +#include "font.h" + +#include "../macros.h" +#include "../simdebug.h" #include "../simsys.h" #include "../simtypes.h" -#include "../simmem.h" -#include "../simdebug.h" -#include "../macros.h" -#include "../dataobj/environment.h" -#include "font.h" #include "../utils/simstring.h" +#ifdef USE_FREETYPE +#include "../dataobj/environment.h" +#endif + +#include +#include +#include + -/* if defined, for the old .fnt files a .bdf core will be generated */ +// if defined, for the old .fnt files a .bdf core will be generated //#define DUMP_OLD_FONTS -static int nibble(sint8 c) +static uint8 nibble(sint8 c) { - return (c > '9') ? 10 + c - 'A' : c - '0'; + return (c > '9') ? (10 + c - 'A') : (c - '0'); } -/** - * Decodes a single line of a character - */ -static void dsp_decode_bdf_data_row(uint8 *target, int y, int xoff, int g_width, char *str) +font_t::glyph_t::glyph_t() : + yoff(0), + width(0), + advance(0xFF) { - uint16 data; - char buf[3]; + memset(data, 0, sizeof(data)); +} + +font_t::font_t() : + height(0), + descent(0) +{ + fname[0] = 0; +} + + +/// Decodes a single line of a glyph +static void dsp_decode_bdf_data_row(font_t::glyph_t *target, int y, int xoff, int g_width, const char *str) +{ + char buf[3]; buf[0] = str[0]; buf[1] = str[1]; buf[2] = '\0'; - data = (uint16)strtol(buf, NULL, 16)<<8; + + uint16 data = (uint16)strtol(buf, NULL, 16)<<8; + // read second byte but use only first nibble - if (g_width > 8) { + if( g_width > 8 ) { buf[0] = str[2]; buf[1] = str[3]; buf[2] = '\0'; data |= strtol(buf, NULL, 16); } + data >>= xoff; + // now store them, and the second nibble store interleaved - target[y] = data>>8; - if(g_width+xoff>8) { - target[y+CHARACTER_HEIGHT] = data; + target->data[y] = data>>8; + + if( g_width+xoff > 8 ) { + target->data[y+GLYPH_HEIGHT] = data; } } -/** - * Reads a single character. - * @returns index of character successfully read, or -1 on error - */ -static sint32 dsp_read_bdf_glyph(FILE *fin, uint8 *data, uint8 *screen_w, int char_limit, int f_height, int f_desc) +/// Reads a single glyph. +/// @returns index of glyph successfully read, or -1 on error +static sint32 dsp_read_bdf_glyph(FILE *fin, std::vector &data, int glyph_limit, int f_height, int f_desc) { - uint32 char_nr = 0; - int g_width, h, g_desc; + uint32 glyph_nr = 0; + int g_width = 0, h = 0, g_desc = 0; int d_width = -1; int xoff = 0; - while (!feof(fin)) { + while( !feof(fin) ) { char str[256]; fgets(str, sizeof(str), fin); // encoding (sint8 number) in decimal - if (strstart(str, "ENCODING")) { - char_nr = atoi(str + 8); - if (char_nr == 0 || (sint32)char_nr >= char_limit) { - fprintf(stderr, "Unexpected character (%i) for %i character font!\n", char_nr, char_limit); - char_nr = 0; + if( strstart(str, "ENCODING") ) { + glyph_nr = atoi(str + 8); + if( glyph_nr == 0 || (sint32)glyph_nr >= glyph_limit ) { + dbg->error("dsp_read_bdf_glyph", "Unexpected glyph (%i) for %i glyph font!\n", glyph_nr, glyph_limit); + glyph_nr = 0; } - memset( data+CHARACTER_LEN*char_nr, 0, CHARACTER_LEN ); + + data[glyph_nr] = font_t::glyph_t(); continue; } // information over size and coding - if (strstart(str, "BBX")) { + if( strstart(str, "BBX") ) { sscanf(str + 3, "%d %d %d %d", &g_width, &h, &xoff, &g_desc); continue; } // information over size and coding - if (strstart(str, "DWIDTH")) { + if( strstart(str, "DWIDTH") ) { d_width = atoi(str + 6); continue; } @@ -96,215 +117,200 @@ static sint32 dsp_read_bdf_glyph(FILE *fin, uint8 *data, uint8 *screen_w, int ch // start if bitmap data if (strstart(str, "BITMAP")) { const int top = f_height + f_desc - h - g_desc; - int y; - // maximum size CHARACTER_HEIGHT pixels - h += top; - if (h > CHARACTER_HEIGHT) { - h = CHARACTER_HEIGHT; - } + // maximum size GLYPH_HEIGHT pixels + h = std::min(h + top, GLYPH_HEIGHT); // read for height times - for (y = top; y < h; y++) { + for (int y = top; y < h; y++) { fgets(str, sizeof(str), fin); if( y>=0 ) { - dsp_decode_bdf_data_row(data + char_nr*CHARACTER_LEN, y, xoff, g_width, str); + dsp_decode_bdf_data_row(&data[glyph_nr], y, xoff, g_width, str); } } continue; } // finally add width information (width = 0: not there!) - if (strstart(str, "ENDCHAR")) { - uint8 start_h=0, i; + if( strstart(str, "ENDCHAR") ) { + uint8 start_h = 0; // find the start offset - for( i=0; i<23; i++ ) { - if(data[CHARACTER_LEN*char_nr + i]==0 && data[CHARACTER_LEN*char_nr+CHARACTER_HEIGHT+i]==0) { + for( uint8 i=0; iwarning( "dsp_read_bdf_glyph", "BDF warning: %i has no screen width assigned!\n", glyph_nr); d_width = g_width + 1; } - screen_w[char_nr] = d_width; + + data[glyph_nr].advance = d_width; + // finished - return char_nr; + return glyph_nr; } } return -1; } -/** - * Reads a bdf font character - */ -static bool dsp_read_bdf_font(FILE* fin, font_t* font) +bool font_t::load_from_bdf(FILE *fin) { - uint8* screen_widths = NULL; - uint8* data = NULL; - int f_height; - int f_desc; - int f_chars = 0; + dbg->message("font_t::load_from_bdf", "Loading BDF font '%s'", fname); + glyphs.clear(); + + int f_height = 0; + int f_desc = 0; + int f_numglyphs = 0; sint32 max_glyph = 0; - while (!feof(fin)) { + while( !feof(fin) ) { char str[256]; fgets(str, sizeof(str), fin); - if (strstart(str, "FONTBOUNDINGBOX")) { + if( strstart(str, "FONTBOUNDINGBOX") ) { sscanf(str + 15, "%*d %d %*d %d", &f_height, &f_desc); continue; } - if (strstart(str, "CHARS") && str[5] <= ' ') { + if( strstart(str, "CHARS") && str[5] <= ' ' ) { // the characters 0xFFFF and 0xFFFE are guaranteed to be non-unicode characters - f_chars = atoi(str + 5) <= 256 ? 256 : 65534; + f_numglyphs = atoi(str + 5) <= 0x100 ? 0x100 : 0xFFFE; - data = (uint8*)calloc(f_chars, CHARACTER_LEN); - if (data == NULL) { - fprintf(stderr, "No enough memory for font allocation!\n"); - return false; - } - - screen_widths = (uint8*)malloc(f_chars); - memset( screen_widths, 0xFF, f_chars ); - if (screen_widths == NULL) { - free(data); - fprintf(stderr, "No enough memory for font allocation!\n"); - return false; - } - - data[32 * CHARACTER_LEN] = 0; // screen width of space - screen_widths[32] = clamp(f_height / 2, 3, 23); + glyphs.resize(std::max(f_numglyphs, 0)); + glyphs[(uint32)' '].advance = clamp(f_height / 2, 3, 23); continue; } - if (strstart(str, "STARTCHAR") && f_chars > 0) { - sint32 chr = dsp_read_bdf_glyph(fin, data, screen_widths, f_chars, f_height, f_desc); - if( chr > max_glyph ) { - max_glyph = chr; - } + if( strstart(str, "STARTCHAR") && f_numglyphs > 0 ) { + const sint32 chr = dsp_read_bdf_glyph(fin, glyphs, f_numglyphs, f_height, f_desc); + max_glyph = std::max(max_glyph, chr); continue; } } // ok, was successful? - if (f_chars > 0) { - int h; - - // init default char for missing characters (just a box) - screen_widths[0] = 8; - data[0] = 0; - data[1] = 0x7E; - const int real_font_height = ( f_height>CHARACTER_HEIGHT ? CHARACTER_HEIGHT : f_height ); - for(h = 2; h < real_font_height - 2; h++) { - data[h] = 0x42; - } - data[h++] = 0x7E; - for (; h < CHARACTER_LEN-2; h++) { - data[h] = 0; - } - data[CHARACTER_LEN-2] = 1; // y-offset - data[CHARACTER_LEN-1] = 7; // real width + if( f_numglyphs <= 0 ) { + return false; + } - font->screen_width = screen_widths; - font->char_data = data; - font->height = f_height; - font->descent = f_desc; - font->num_chars = max_glyph+1; + // init default glyph for missing glyphs (just a box) + glyphs[0].advance = 8; + glyphs[0].data[0] = 0; + glyphs[0].data[1] = 0x7E; - // Use only needed amount - font->char_data = (uint8 *)realloc( font->char_data, font->num_chars*CHARACTER_LEN ); + const int real_font_height = std::min(f_height, GLYPH_HEIGHT); + int h = 2; - return true; + for( ; h < real_font_height-2; h++ ) { + glyphs[0].data[h] = 0x42; } - return false; + glyphs[0].data[h++] = 0x7E; + for( ; h < (int)sizeof(glyphs[0].data); h++ ) { + glyphs[0].data[h] = 0; + } + + glyphs[0].yoff = 1; // y-offset + glyphs[0].width = 7; // real width + + height = f_height; + descent = f_desc; + + // Use only needed amount + glyphs.resize( (uint32)max_glyph+1 ); + + return true; } #ifdef USE_FREETYPE + #include #include FT_FREETYPE_H #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H -bool load_FT_font( font_t* fnt, const char* fname, int pixel_height ) +bool font_t::load_from_freetype(const char *fname, int pixel_height) { FT_Library ft_library = NULL; - int error; - if( !ft_library && FT_Init_FreeType(&ft_library) != FT_Err_Ok ) { - ft_library = NULL; + if( FT_Init_FreeType(&ft_library) != FT_Err_Ok ) { + dbg->error( "font_t::load_from_freetype", "Freetype initialization failed" ); return false; } - // Ok, we guessed something about the finename, now actually load it + // Ok, we guessed something about the filename, now actually load it FT_Face face; - error = FT_New_Face( ft_library, fname, 0, &face );/* create face object */ - if( error ) { - dbg->error( "load_FT_font", "Cannot load %s", fname ); + + if( FT_New_Face( ft_library, fname, 0, &face ) != FT_Err_Ok ) { + dbg->error( "font_t::load_from_freetype", "Cannot load %s", fname ); FT_Done_FreeType( ft_library ); - ft_library = NULL; return false; } - FT_Set_Pixel_Sizes( face, 0, pixel_height ); - fnt->char_data = (uint8 *)calloc( 65536, CHARACTER_LEN ); - fnt->screen_width = (uint8 *)malloc( 65536 ); + if( FT_Set_Pixel_Sizes( face, 0, pixel_height ) != FT_Err_Ok ) { + dbg->error( "font_t::load_from_freetype", "Cannot load %s", fname); + FT_Done_Face(face); + FT_Done_FreeType(ft_library); + return false; + } + + glyphs.resize(0x10000); + + const sint16 ascent = face->size->metrics.ascender/64; + height = std::min( face->size->metrics.height/64, (FT_Pos)23 ); + descent = face->size->metrics.descender/64; + + tstrncpy( this->fname, fname, lengthof(this->fname) ); - sint16 ascent = face->size->metrics.ascender/64; - fnt->height = min( face->size->metrics.height/64, 23 ); - fnt->descent = face->size->metrics.descender/64; - fnt->num_chars = 0; - tstrncpy( fnt->fname, fname, lengthof(fnt->fname) ); + uint32 num_glyphs = 0; - for( uint32 char_nr=0; char_nr<65535; char_nr++ ) { + for( uint32 glyph_nr=0; glyph_nr<0xFFFF; glyph_nr++ ) { - uint32 idx = FT_Get_Char_Index( face, char_nr ); - if( !idx && char_nr ) { - // character not there ... (but we need to render chqaracter zero) - fnt->screen_width[char_nr] = 255; + uint32 idx = FT_Get_Char_Index( face, glyph_nr ); + if( idx==0 && glyph_nr!=0 ) { + // glyph not there, we need to render glyph 0 instead + glyphs[glyph_nr].advance = 0xFF; continue; } /* load glyph image into the slot (erase previous one) */ - error = FT_Load_Glyph( face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME ); - if( error ) { - // character not there ... - fnt->screen_width[char_nr] = 255; + if( FT_Load_Glyph( face, idx, FT_LOAD_RENDER | FT_LOAD_MONOCHROME ) != FT_Err_Ok ) { + // glyph not there ... + glyphs[glyph_nr].advance = 0xFF; continue; } - error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_MONO ); - if( error || face->glyph->bitmap.pitch == 0 ) { - // character not there ... - fnt->screen_width[char_nr] = 255; + const FT_Error error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_MONO ); + if( error != FT_Err_Ok || face->glyph->bitmap.pitch == 0 ) { + // glyph not there ... + glyphs[glyph_nr].advance = 0xFF; continue; } // use only needed amount - fnt->num_chars = char_nr+1; + num_glyphs = glyph_nr+1; /* now render into cache * the bitmap is at slot->bitmap - * the character base is at slot->bitmap_left, CELL_HEIGHT - slot->bitmap_top + * the glyph base is at slot->bitmap_left, CELL_HEIGHT - slot->bitmap_top */ - // if the character is too high + // if the glyph is too high int y_off = ascent - face->glyph->bitmap_top; int by_off = 0; if( y_off < 0 ) { @@ -313,224 +319,308 @@ bool load_FT_font( font_t* fnt, const char* fname, int pixel_height ) } // asked for monocrome so slot->pixel_mode == FT_PIXEL_MODE_MONO - for( int y = y_off, by = by_off; y < CHARACTER_HEIGHT && (uint)by < face->glyph->bitmap.rows; y++, by++ ) { - fnt->char_data[(char_nr*CHARACTER_LEN)+y] = face->glyph->bitmap.buffer[by * face->glyph->bitmap.pitch]; + for( int y = y_off, by = by_off; y < GLYPH_HEIGHT && (uint32)by < face->glyph->bitmap.rows; y++, by++ ) { + glyphs[glyph_nr].data[y] = face->glyph->bitmap.buffer[by * face->glyph->bitmap.pitch]; } if( face->glyph->bitmap.width > 8 ) { // render second row - for( int y = y_off, by = by_off; y < CHARACTER_HEIGHT && (uint)by < face->glyph->bitmap.rows; y++, by++ ) { - fnt->char_data[(char_nr*CHARACTER_LEN)+CHARACTER_HEIGHT+y] = face->glyph->bitmap.buffer[by * face->glyph->bitmap.pitch+1]; + for( int y = y_off, by = by_off; y < GLYPH_HEIGHT && (uint)by < face->glyph->bitmap.rows; y++, by++ ) { + glyphs[glyph_nr].data[y+GLYPH_HEIGHT] = face->glyph->bitmap.buffer[by * face->glyph->bitmap.pitch+1]; } } - fnt->screen_width[char_nr] = face->glyph->bitmap.width+1; - fnt->char_data[CHARACTER_LEN * char_nr + CHARACTER_LEN-2] = y_off; // h_offset - fnt->char_data[CHARACTER_LEN * char_nr + CHARACTER_LEN-1] = max(16,face->glyph->bitmap.width); + glyphs[glyph_nr].advance = face->glyph->bitmap.width+1; + glyphs[glyph_nr].yoff = y_off; // h_offset + glyphs[glyph_nr].width = std::max(16u, face->glyph->bitmap.width); } - if( fnt->num_chars<128 ) { + if( num_glyphs<0x80 ) { FT_Done_Face( face ); FT_Done_FreeType( ft_library ); - ft_library = NULL; - free( fnt->char_data ); - free( fnt->screen_width ); return false; } // Use only needed amount - fnt->char_data = (uint8 *)realloc( fnt->char_data, fnt->num_chars*CHARACTER_LEN ); - fnt->screen_width[' '] = fnt->screen_width['n']; + glyphs.resize(num_glyphs); + glyphs[(uint32)' '].advance = glyphs[(uint32)'n'].advance; FT_Done_Face( face ); FT_Done_FreeType( ft_library ); - ft_library = NULL; return true; } + #endif -void debug_font( font_t* fnt) +void font_t::print_debug() const { - dbg->debug("debug_font", "Loaded font %s with %i characters\n", fnt->fname, fnt->num_chars); - dbg->debug("debug_font", "height: %i, descent: %i", fnt->height, fnt->descent ); - for( int char_nr = 32; char_nr<128; char_nr ++ ) { - char msg[1024], *c; - c = msg + sprintf( msg,"char %c: width %i, top %i\n", char_nr, fnt->char_data[(char_nr*CHARACTER_LEN)+CHARACTER_LEN-1], fnt->char_data[(char_nr*CHARACTER_LEN)+CHARACTER_LEN-2] ); - for( int y = 0; y < CHARACTER_HEIGHT; y++ ) { - for( int x = 0; x < 8 && x < fnt->char_data[(char_nr*CHARACTER_LEN)+CHARACTER_LEN-1]; x++ ) { - *c++ = (fnt->char_data[(char_nr*CHARACTER_LEN)+y] >> (7-x)) & 1 ? '*' : ' '; + dbg->debug("font_t::print_debug", "Loaded font %s with %i glyphs\n", get_fname(), get_num_glyphs()); + dbg->debug("font_t::print_debug", "height: %i, descent: %i", height, descent ); + + for( uint8 glyph_nr = ' '; glyph_nr<128; glyph_nr ++ ) { + char msg[1024]; + char *c = msg + sprintf(msg, "glyph %c: width %i, top %i\n", glyph_nr, glyphs[glyph_nr].width, glyphs[glyph_nr].yoff ); + + for( int y = 0; y < GLYPH_HEIGHT; y++ ) { + for( int x = 0; x < 8 && x < glyphs[glyph_nr].width; x++ ) { + *c++ = (glyphs[glyph_nr].data[y] >> (7-x)) & 1 ? '*' : ' '; } *c++ = '\n'; } *c++ = 0; - dbg->debug("character data", msg ); + dbg->debug("font_t::print_debug", "glyph data: %s", msg ); } } - -bool load_font(font_t* fnt, const char* fname) +bool font_t::load_from_file(const char *srcfilename) { + tstrncpy( fname, srcfilename, lengthof(fname) ); + FILE* f = dr_fopen(fname, "rb"); - int c; - if (f == NULL) { - fprintf(stderr, "Error: Cannot open '%s'\n", fname); + if( f == NULL ) { + dbg->error("font_t::load_from_file", "Cannot open '%s'", fname); return false; } - c = getc(f); - if(c<0) { - fprintf(stderr, "Error: Cannot parse font '%s'\n", fname); + const int res = fgetc(f); + if( res==EOF ) { + dbg->error("font_t::load_from_file", "Cannot parse font '%s'", fname); fclose(f); return false; } - // binary => the assume dumpe prop file - if( c < 32 && strstr(fname,".fnt") ) { - // read classical prop font - uint8 npr_fonttab[3072]; - int i; + const uint8 c = res & 0xFF; + // binary => the assume dumped prop file + if( c < ' ' && strstr(fname, ".fnt") ) { rewind(f); - fprintf(stdout, "Loading font '%s'\n", fname); + return load_from_fnt(f); + } - if (fread(npr_fonttab, sizeof(npr_fonttab), 1, f) != 1) { - fprintf(stderr, "Error: %s wrong size for old format prop font!\n", fname); - fclose(f); - return false; - } + // load old hex font format + if( c == '0' ) { + rewind(f); + return load_from_hex(f); + } + + bool ok = load_from_bdf(f); + fclose(f); + +#ifdef USE_FREETYPE + if( !ok ) { + ok = load_from_freetype( fname, env_t::fontsize ); + } +#endif + + if( ok ) { + print_debug(); + } + + return ok; +} + + +bool font_t::load_from_fnt(FILE *f) +{ + assert(ftell(f) == 0); + + // read classical prop font + dbg->message("font_t::load_from_fnt", "Loading font '%s'", fname); + + uint8 npr_fonttab[3072]; + + if( fread(npr_fonttab, sizeof(npr_fonttab), 1, f) != 1 ) { + dbg->error( "font_t::load_from_fnt" "%s wrong size for old format prop font!", fname ); fclose(f); + return false; + } + + fclose(f); + #ifdef DUMP_OLD_FONTS - f = fopen("C:\\prop.bdf","w"); + f = fopen("C:\\prop.bdf","w"); #endif - // convert to new standard font - fnt->screen_width = MALLOCN(uint8, 256); - fnt->char_data = MALLOCN(uint8, CHARACTER_LEN * 256); - fnt->num_chars = 256; - fnt->height = 11; - fnt->descent = -2; - - for (i = 0; i < 256; i++) { - int j; - uint8 start_h; - - fnt->screen_width[i] = npr_fonttab[256 + i]; - if( fnt->screen_width[i] == 0 ) { - // undefined char - fnt->screen_width[i] = -1; - } - for (j = 0; j < 10; j++) { - fnt->char_data[i * CHARACTER_LEN + j] = npr_fonttab[512 + i * 10 + j]; - } - for (; j < CHARACTER_LEN-2; j++) { - fnt->char_data[i * CHARACTER_LEN + j] = 0; - } - // find the start offset - for( start_h=0; fnt->char_data[CHARACTER_LEN*i + start_h]==0 && start_h<10; start_h++ ) - ; - fnt->char_data[CHARACTER_LEN * i + CHARACTER_LEN-2] = start_h; + // convert to new standard font + glyphs.resize(0x100); + + height = 11; + descent = -2; + + for( std::size_t i = 0; i < 0x100; i++ ) { + uint8 start_h; + + glyphs[i].advance = npr_fonttab[0x100 + i]; + if( glyphs[i].advance == 0 ) { + glyphs[i].advance = 0xFF; // undefined glyph + } + + int j = 0; + for (; j < 10; j++) { + glyphs[i].data[j] = npr_fonttab[512 + i * 10 + j]; + } - fnt->char_data[CHARACTER_LEN * i + CHARACTER_LEN-1] = npr_fonttab[i]; + for (; j < (int)sizeof(glyphs[i].data); j++) { + glyphs[i].data[j] = 0; + } + + // find the start offset + for( start_h=0; glyphs[i].data[start_h]==0 && start_h<10; start_h++ ) { + ; + } + + glyphs[i].yoff = start_h; + glyphs[i].width = npr_fonttab[i]; #ifdef DUMP_OLD_FONTS - //try to make bdf - if(start_h<10) { - // find bounding box - int h=10; - while(h>start_h && fnt->char_data[CHARACTER_LEN*i + h-1]==0) { - h--; - } - // emulate character - fprintf( f, "STARTCHAR char%0i\n", i ); - fprintf( f, "ENCODING %i\n", i ); - fprintf( f, "SWIDTH %i 0\n", (int)(0.5+77.875*(double)fnt->screen_width[i]) ); - fprintf( f, "DWIDTH %i\n", fnt->screen_width[i] ); - fprintf( f, "BBX %i %i %i %i\n", fnt->screen_width[i], h-start_h, 0, 8-h ); - fprintf( f, "BITMAP\n" ); - for( j=start_h; jchar_data[CHARACTER_LEN*i + j] ); - } - fprintf( f, "ENDCHAR\n" ); + //try to make bdf + if( start_h<10 ) { + // find bounding box + int h=10; + while(h>start_h && glyphs[i].data[h-1]==0) { + h--; } -#endif + + // emulate character + fprintf( f, "STARTCHAR char%0i\n", i ); + fprintf( f, "ENCODING %i\n", i ); + fprintf( f, "SWIDTH %i 0\n", (int)(0.5+77.875*(double)glyphs[i].advance) ); + fprintf( f, "DWIDTH %i\n", glyphs[i].advance ); + fprintf( f, "BBX %i %i %i %i\n", glyphs[i].advance, h-start_h, 0, 8-h ); + fprintf( f, "BITMAP\n" ); + + for( j=start_h; jscreen_width[32] = 4; - fnt->char_data[CHARACTER_LEN*32 + CHARACTER_LEN-1] = 0; // space width - fprintf(stderr, "%s successfully loaded as old format prop font!\n", fname); - return true; - } + glyphs[(uint32)' '].advance = 4; + glyphs[(uint32)' '].width = 0; - // load old hex font format - if (c == '0') { - uint8 dr_4x7font[7 * 256]; - char buf[80]; - int i; + dbg->message( "font_t::load_from_fnt", "%s successfully loaded as old format prop font!", fname); + return true; +} - fprintf(stderr, "Reading hex-font %s.\n", fname ); - rewind(f); - while (fgets(buf, sizeof(buf), f) != NULL) { - const char* p; - int line; - int n; +bool font_t::load_from_hex(FILE *f) +{ + assert(ftell(f) == 0); - sscanf(buf, "%4x", &n); + uint8 dr_4x7font[7 * 256]; + char buf[80]; - p = buf + 5; - for (line = 0; line < 7; line++) { - int val = nibble(p[0]) * 16 + nibble(p[1]); + dbg->message( "font_t::load_from_hex", "Reading hex-font %s.", fname ); - dr_4x7font[n * 7 + line] = val; - p += 2; - } + while( fgets(buf, sizeof(buf), f) != NULL ) { + int n; + sscanf(buf, "%4x", &n); + + const char *p = buf + 5; + for( int line = 0; line < 7; line++ ) { + const uint8 val = nibble(p[0]) * 16 + nibble(p[1]); + + dr_4x7font[n * 7 + line] = val; + p += 2; } - fclose(f); - // convert to new standard font - fnt->screen_width = MALLOCN(uint8, 256); - fnt->char_data = MALLOCN(uint8, CHARACTER_LEN * 256); - fnt->num_chars = 256; - fnt->height = 7; - fnt->descent = -1; - - for (i = 0; i < 256; i++) { - int j; - - fnt->screen_width[i] = 4; - for (j = 0; j < 7; j++) { - fnt->char_data[i * CHARACTER_LEN + j] = dr_4x7font[i * 7 + j]; - } - for (; j < 15; j++) { - fnt->char_data[i * CHARACTER_LEN + j] = 0; - } - fnt->char_data[i * CHARACTER_LEN + CHARACTER_LEN-2] = 0; - fnt->char_data[i * CHARACTER_LEN + CHARACTER_LEN-1] = 3; + } + + fclose(f); + + // convert to new standard font + glyphs.resize(0x100); + + height = 7; + descent = -1; + + for( std::size_t i = 0; i < 0x100; i++ ) { + std::size_t j; + + for (j = 0; j < 7; j++) { + glyphs[i].data[j] = dr_4x7font[i * 7 + j]; + } + for (; j < 15; j++) { + glyphs[i].data[j] = 0; } - fprintf(stderr, "%s successfully loaded as old format hex font!\n", fname); - return true; + + glyphs[i].yoff = 0; + glyphs[i].width = 3; + glyphs[i].advance = 4; } - fprintf(stderr, "Loading BDF font '%s'\n", fname); - if (dsp_read_bdf_font(f, fnt)) { - debug_font( fnt ); - fclose(f); - return true; + dbg->message("font_t::load_from_hex", "%s successfully loaded as old format hex font!", fname); + return true; +} + + +bool font_t::is_valid_glyph(utf32 c) const +{ + return is_loaded() && c < get_num_glyphs() && glyphs[c].advance != 0xFF; +} + + +uint8 font_t::get_glyph_advance(utf32 c) const +{ + if( !is_loaded() ) { + return 0; + } + else if( c >= get_num_glyphs() || glyphs[c].advance == 0xFF ) { + return glyphs[0].advance; } - fclose(f); -#ifdef USE_FREETYPE - // because it occasionally choke on BDF files ... - if( load_FT_font( fnt, fname, env_t::fontsize) ) { - debug_font( fnt ); - return true; + return glyphs[c].advance; +} + + +uint8 font_t::get_glyph_width(utf32 c) const +{ + if( !is_loaded() ) { + return 0; + } + else if( c >= get_num_glyphs() ) { + c = 0; + } + + return glyphs[c].width; +} + + +uint8 font_t::get_glyph_height(utf32) const +{ + return GLYPH_HEIGHT; +} + + +uint8 font_t::get_glyph_yoffset(uint32 c) const +{ + if( !is_loaded() ) { + return 0; + } + else if( c >= get_num_glyphs() ) { + c = 0; + } + + return glyphs[c].yoff; +} + + +const uint8 *font_t::get_glyph_data(utf32 c) const +{ + if( !is_loaded() ) { + return NULL; + } + else if( c >= get_num_glyphs() ) { + c = 0; } -#endif - return false; + return glyphs[c].data; } diff --git display/font.h display/font.h index af736f9f1..44349b2c6 100644 --- display/font.h +++ display/font.h @@ -8,39 +8,93 @@ #include "../simtypes.h" +#include "../unicode.h" +#include + + +#define GLYPH_HEIGHT (23) + + +/** + * Terminology: + * - glyph: display data of a single character + * - font: a collection of glyphs (usually called a font face) + * - width,height: size of the glyph + * - advance: number of pixels between the start of a glyph and the next glyph + * (not necessarily equal to width) + */ class font_t { public: - char fname[PATH_MAX]; - sint16 height; - sint16 descent; - uint16 num_chars; - uint8 *screen_width; - uint8 *char_data; + /// glyph data is stored dense in an array + /// 23 rows of bytes, second column for widths between 8 and 16 + struct glyph_t + { + glyph_t(); - enum fonttype { BINARY=1, BDF=2, FREETYPE=3 }; + uint8 data[2*GLYPH_HEIGHT]; + uint8 yoff; // = ascent - bearingY + uint8 width; + uint8 advance; + }; - font_t() - : height(0), descent(0), num_chars(0), screen_width(NULL), char_data(NULL) - { - fname[0]= 0; - } -}; +public: + font_t(); -/* - * characters are stored dense in a array - * 23 rows of bytes, second row for widths between 8 and 16 - * then the start offset for drawing - * then a byte with the real width - */ -#define CHARACTER_LEN (48) -#define CHARACTER_HEIGHT (23) +public: + /// @returns true on success + bool load_from_file(const char *fname); + bool is_loaded() const { return !glyphs.empty(); } + const char *get_fname() const { return fname; } + sint16 get_height() const { return height; } + sint16 get_descent() const { return descent; } -/** - * Loads a font - */ -bool load_font(font_t* font, const char* fname); + /// @returns true if this is a valid (defined) glyph + bool is_valid_glyph(utf32 c) const; + + /// @returns size in pixels between the start of this glyph and the next glyph + uint8 get_glyph_advance(utf32 c) const; + + /// @returns width in pixels of the glyph of a character + uint8 get_glyph_width(utf32 c) const; + + /// @returns height in pixels of the glyph of a character + uint8 get_glyph_height(utf32 c) const; + + /// @returns yoffset in pixels of the glyph of a character + uint8 get_glyph_yoffset(uint32 c) const; + + /// @returns glyph data of a character + /// @sa font_t::glyph_t::data + const uint8 *get_glyph_data(utf32 c) const; + +private: + /// Load a BDF font + bool load_from_bdf(FILE *fin); + + /// Load an old hex font + bool load_from_hex(FILE *fin); + + /// Load a .fnt file + bool load_from_fnt(FILE *fin); + +#ifdef USE_FREETYPE + /// Load a freetype font + bool load_from_freetype(const char *fname, int pixel_height); +#endif + + void print_debug() const; + + uint32 get_num_glyphs() const { return glyphs.size(); } + +private: + char fname[PATH_MAX]; + sint16 height; + sint16 descent; + + std::vector glyphs; +}; #endif diff --git display/simgraph.h display/simgraph.h index bf41f008b..38e5956b8 100644 --- display/simgraph.h +++ display/simgraph.h @@ -133,7 +133,7 @@ void simgraph_resize(KOORD_VAL w, KOORD_VAL h); * Loads the font, returns the number of characters in it * @param reload if true forces reload */ -uint16 display_load_font(const char* fname, bool reload = false); +bool display_load_font(const char *fname, bool reload = false); image_id get_image_count(); void register_image(class image_t *); diff --git display/simgraph16.cc display/simgraph16.cc index 3ba42193e..e4262fd44 100644 --- display/simgraph16.cc +++ display/simgraph16.cc @@ -4102,33 +4102,31 @@ void display_array_wh(KOORD_VAL xp, KOORD_VAL yp, KOORD_VAL w, KOORD_VAL h, cons // --------------------------------- text rendering stuff ------------------------------ -uint16 display_load_font(const char* fname, bool reload) +bool display_load_font(const char *fname, bool reload) { - font_t fnt; + font_t loaded_fnt; if( fname == NULL ) { dbg->error( "display_load_font", "NULL filename" ); - return 0; + return false; } + // skip reloading if already in memory, if bdf font - if( !reload && large_font.num_chars>0 && strcmp( large_font.fname, fname ) == 0 ) { - return large_font.num_chars; + if( !reload && large_font.is_loaded() && strcmp( large_font.get_fname(), fname ) == 0 ) { + return true; } - tstrncpy( large_font.fname, fname, lengthof(large_font.fname) ); - if( load_font(&fnt, fname) ) { - - free(large_font.screen_width); - free(large_font.char_data); - large_font = fnt; - large_font_ascent = large_font.height + large_font.descent; - large_font_total_height = large_font.height; // this is the actual LINESPACE + if( loaded_fnt.load_from_file(fname) ) { + large_font = loaded_fnt; + large_font_ascent = large_font.get_height() + large_font.get_descent(); + large_font_total_height = large_font.get_height(); // this is the actual LINESPACE env_t::fontname = fname; - return large_font.num_chars; + return large_font.is_loaded(); } - return 0; + + return false; } @@ -4150,12 +4148,7 @@ sint32 get_prev_char(const char* text, sint32 pos) KOORD_VAL display_get_char_width(utf32 c) { - KOORD_VAL pixel_width; - if( c >= large_font.num_chars || (pixel_width = large_font.screen_width[c]) == 0xFF ) { - // default width for missing characters - return large_font.screen_width[0]; - } - return pixel_width; + return large_font.get_glyph_advance(c); } @@ -4192,13 +4185,7 @@ utf32 get_next_char_with_metrics(const char* &text, unsigned char &byte_length, else { text += len; byte_length = len; - if( char_code >= large_font.num_chars || (pixel_width=large_font.screen_width[char_code]) == 0xFF ) { - // default width for missing characters - pixel_width = large_font.screen_width[0]; - } - else { - pixel_width = large_font.screen_width[char_code]; - } + pixel_width = large_font.get_glyph_advance(char_code); } return char_code; } @@ -4207,11 +4194,7 @@ utf32 get_next_char_with_metrics(const char* &text, unsigned char &byte_length, /* returns true, if this is a valid character */ bool has_character( utf16 char_code ) { - if( char_code >= large_font.num_chars || large_font.screen_width[char_code] == 0xFF ) { - // missing characters - return false; - } - return true; + return large_font.is_valid_glyph(char_code); } @@ -4277,11 +4260,8 @@ utf32 get_prev_char_with_metrics(const char* &text, const char *const text_start size_t len = 0; char_code = utf8_decoder_t::decode((utf8 const *)text, len); byte_length = len; + pixel_width = large_font.get_glyph_advance(char_code); - if( char_code >= large_font.num_chars || (pixel_width=large_font.screen_width[char_code]) == 0xFF ) { - // default width for missing characters - pixel_width = large_font.screen_width[0]; - } return char_code; } @@ -4293,20 +4273,15 @@ int display_calc_proportional_string_len_width(const char *text, size_t len) { const font_t* const fnt = &large_font; unsigned int width = 0; - int w; // decode char const char *const end = text + len; while( text < end ) { - utf32 iUnicode = utf8_decoder_t::decode((utf8 const *&)text); + const utf32 iUnicode = utf8_decoder_t::decode((utf8 const *&)text); if( iUnicode == UNICODE_NUL || iUnicode == '\n') { return width; } - else if( iUnicode >= fnt->num_chars || (w = fnt->screen_width[iUnicode]) == 0xFF ) { - // default width for missing characters - w = fnt->screen_width[0]; - } - width += w; + width += fnt->get_glyph_advance(iUnicode); } return width; @@ -4321,7 +4296,6 @@ void display_calc_proportional_multiline_string_len_width(int &xw, int &yh, cons { const font_t* const fnt = &large_font; int width = 0; - int w; xw = yh = 0; @@ -4338,12 +4312,10 @@ void display_calc_proportional_multiline_string_len_width(int &xw, int &yh, cons if( iUnicode == UNICODE_NUL ) { return; } - else if( iUnicode >= fnt->num_chars || (w = fnt->screen_width[iUnicode]) == 0xFF ) { - // default width for missing characters - w = fnt->screen_width[0]; - } - width += w; + + width += fnt->get_glyph_advance(iUnicode); } + xw = max( xw, width ); yh += LINESPACE; } @@ -4377,25 +4349,16 @@ static unsigned char get_h_mask(const int xL, const int xR, const int cL, const return mask; } + /** * len parameter added - use -1 for previous behaviour. * completely renovated for unicode and 10 bit width and variable height */ int display_text_proportional_len_clip_rgb(KOORD_VAL x, KOORD_VAL y, const char* txt, control_alignment_t flags, const PIXVAL color, bool dirty, sint32 len CLIP_NUM_DEF) { - const font_t* const fnt = &large_font; KOORD_VAL cL, cR, cT, cB; - utf32 c; - size_t iTextPos = 0; // pointer on text position - int char_width_1, char_width_2; // 1 is char only, 2 includes room - int screen_pos; - const uint8 *char_data; - const uint8 *p; - KOORD_VAL yy = y + fnt->height; - KOORD_VAL x0; // store the initial x (for dirty marking) - KOORD_VAL y_offset, char_height; // real y for display with clipping - - // TAKE CARE: Clipping area may be larger than actual screen size ... + + // TAKE CARE: Clipping area may be larger than actual screen size if( (flags & DT_CLIP) ) { cL = CR.clip_rect.x; cR = CR.clip_rect.xx; @@ -4408,8 +4371,9 @@ int display_text_proportional_len_clip_rgb(KOORD_VAL x, KOORD_VAL y, const char* cT = 0; cB = disp_height; } - // don't know len yet ... + if (len < 0) { + // don't know len yet len = 0x7FFF; } @@ -4429,64 +4393,61 @@ int display_text_proportional_len_clip_rgb(KOORD_VAL x, KOORD_VAL y, const char* } // still something to display? - if (x >= cR || y >= cB || y + fnt->height <= cT) { + const font_t *const fnt = &large_font; + + if (x >= cR || y >= cB || y + fnt->get_height() <= cT) { // nothing to display return 0; } - // x0 contains the starting x - x0 = x; - y_offset = 0; - char_height = fnt->height; + // store the initial x (for dirty marking) + const KOORD_VAL x0 = x; + + KOORD_VAL y_offset = 0; // real y for display with clipping + KOORD_VAL glyph_height = fnt->get_height(); + const KOORD_VAL yy = y + fnt->get_height(); + // calculate vertical y clipping parameters if (y < cT) { y_offset = cT - y; } if (yy > cB) { - char_height -= yy - cB; + glyph_height -= yy - cB; } - // big loop, char by char + // big loop, draw char by char utf8_decoder_t decoder((utf8 const*)txt); - while (iTextPos < (size_t)len && decoder.has_next()) { - int h; - uint8 char_yoffset; + size_t iTextPos = 0; // pointer on text position + while (iTextPos < (size_t)len && decoder.has_next()) { // decode char - c = decoder.next(); + utf32 c = decoder.next(); iTextPos = decoder.get_position() - (utf8 const*)txt; - if( c == '\n') { + if( c == '\n' ) { // stop at linebreak break; } // print unknown character? - if( c >= fnt->num_chars || fnt->screen_width[c] == 0xFF ) { + else if( !fnt->is_valid_glyph(c) ) { c = 0; } // get the data from the font - char_data = fnt->char_data + CHARACTER_LEN * c; - char_width_1 = char_data[CHARACTER_LEN-1]; - char_yoffset = (sint8)char_data[CHARACTER_LEN-2]; - char_width_2 = fnt->screen_width[c]; - - // do the display - if( y_offset>char_yoffset ) { - char_yoffset = (uint8)y_offset; - } + int glyph_width = fnt->get_glyph_width(c); + const uint8 glyph_yoffset = std::min(fnt->get_glyph_yoffset(c), (uint8)y_offset); // currently max character width 16 bit supported by font.h/font.cc for( int i=0; i<2; i++ ) { + const uint8 bits = std::min(8, glyph_width); + uint8 mask = get_h_mask(x + i*8, x + i*8 + bits, cL, cR); + glyph_width -= bits; - uint8 bits = min(8, char_width_1); - unsigned char mask = get_h_mask(x + i*8, x + i*8 + bits, cL, cR); - char_width_1 -= bits; + const uint8 *p = fnt->get_glyph_data(c) + glyph_yoffset + i*fnt->get_glyph_height(c); + if( mask!=0 ) { + int screen_pos = (y+glyph_yoffset) * disp_width + x + i*8; - p = char_data + char_yoffset + i*CHARACTER_HEIGHT; - if( mask ) { - screen_pos = (y+char_yoffset) * disp_width + x + i*8; - for (h = char_yoffset; h < char_height; h++) { + for (int h = glyph_yoffset; h < glyph_height; h++) { unsigned int dat = *p++ & mask; PIXVAL* dst = textur + screen_pos; @@ -4501,14 +4462,15 @@ int display_text_proportional_len_clip_rgb(KOORD_VAL x, KOORD_VAL y, const char* } } } - // next char: screen width - x += char_width_2; + + x += fnt->get_glyph_advance(c); } if( dirty ) { // here, because only now we know the length also for ALIGN_LEFT text - mark_rect_dirty_clip( x0, y, x - 1, y + large_font_total_height - 1 CLIP_NUM_PAR); + mark_rect_dirty_clip( x0, y, x - 1, y + LINESPACE - 1 CLIP_NUM_PAR); } + // warning: actual len might be longer, due to clipping! return x - x0; } @@ -5107,9 +5069,6 @@ void simgraph_init(KOORD_VAL width, KOORD_VAL height, int full_screen) textur = dr_textur_init(); // init, load, and check fonts - large_font.screen_width = NULL; - large_font.char_data = NULL; - if( !display_load_font(env_t::fontname.c_str()) && !display_load_font(FONT_PATH_X "prop.fnt") ) { dr_fatal_notify( "No fonts found!" ); fprintf(stderr, "Error: No fonts found!"); @@ -5203,7 +5162,7 @@ void simgraph_init(KOORD_VAL width, KOORD_VAL height, int full_screen) */ int is_display_init() { - return textur != NULL && large_font.num_chars>0; + return textur != NULL && large_font.is_loaded(); }