diff --git display/font.cc display/font.cc index 948ad0b3a..6bbe63dec 100644 --- display/font.cc +++ display/font.cc @@ -3,308 +3,312 @@ * (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 +#include + -/* if defined, for the old .fnt files a .bdf core will be generated */ -//#define DUMP_OLD_FONTS +// if defined, for the old .fnt files a .bdf core will be generated +// #define DUMP_OLD_FONTS -static int nibble(sint8 c) +font_t::glyph_t::glyph_t() : + yoff(0), + width(0), + advance(0xFF) { - return (c > '9') ? 10 + c - 'A' : c - '0'; + memset(bitmap, 0, sizeof(bitmap)); } -/** - * 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::font_t() : + linespace (0), + descent(0) { - uint16 data; - char buf[3]; + 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->bitmap[y] = data>>8; + + if( g_width+xoff > 8 ) { + target->bitmap[y+GLYPH_BITMAP_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 *bdf_file, std::vector &data, int glyph_limit, int f_height, int f_desc) { - uint32 char_nr = 0; - int g_width, h, g_desc; - int d_width = -1; + sint32 glyph_nr = 0; + int g_width = 0, h = 0, g_desc = 0; + int glyph_advance = -1; int xoff = 0; - while (!feof(fin)) { + while( !feof(bdf_file) ) { char str[256]; - fgets(str, sizeof(str), fin); + if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { + return -1; + } // 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 || 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 ); 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")) { - d_width = atoi(str + 6); + if( strstart(str, "DWIDTH") ) { + glyph_advance = atoi(str + 6); continue; } // 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, (int)GLYPH_BITMAP_HEIGHT); // read for height times - for (y = top; y < h; y++) { - fgets(str, sizeof(str), fin); + for (int y = top; y < h; y++) { + if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { + return -1; + } + 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: Glyph %i has no advance (screen width) assigned!\n", glyph_nr); + glyph_advance = g_width + 1; } - screen_w[char_nr] = d_width; + + data[glyph_nr].advance = glyph_advance; + // 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 *bdf_file) { - 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(bdf_file) ) { char str[256]; - fgets(str, sizeof(str), fin); + if( fgets(str, sizeof(str), bdf_file)==NULL && !feof(bdf_file) ) { + return false; + } - 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, GLYPH_BITMAP_HEIGHT); 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 glyph = dsp_read_bdf_glyph(bdf_file, glyphs, f_numglyphs, f_height, f_desc); + max_glyph = std::max(max_glyph, glyph); 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) - // 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, (int)GLYPH_BITMAP_HEIGHT); + int h = 2; - return true; + glyphs[0].bitmap[1] = 0x7E; // 0111 1110 + for( ; h < real_font_height-2; h++ ) { + glyphs[0].bitmap[h] = 0x42; // 0100 0010 } - return false; + glyphs[0].bitmap[h++] = 0x7E; // 0111 1110 + + + glyphs[0].yoff = 1; // y-offset + glyphs[0].width = 7; // real width + glyphs[0].advance = 8; + + linespace = 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); - 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) ); + const sint16 ascent = face->size->metrics.ascender/64; + linespace = std::min( face->size->metrics.height/64, (FT_Pos)GLYPH_BITMAP_HEIGHT ); + descent = face->size->metrics.descender/64; - for( uint32 char_nr=0; char_nr<65535; char_nr++ ) { + tstrncpy( this->fname, fname, lengthof(this->fname) ); - 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 num_glyphs = 0; + + for( uint32 glyph_nr=0; glyph_nr<0xFFFF; glyph_nr++ ) { + + 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 +317,244 @@ 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( uint32 y = y_off, by = by_off; y < GLYPH_BITMAP_HEIGHT && (uint32)by < face->glyph->bitmap.rows; y++, by++ ) { + glyphs[glyph_nr].bitmap[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 < (int)GLYPH_BITMAP_HEIGHT && (uint)by < face->glyph->bitmap.rows; y++, by++ ) { + glyphs[glyph_nr].bitmap[y+GLYPH_BITMAP_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", linespace, descent ); + + for( uint8 glyph_nr = ' '; glyph_nr<128; glyph_nr ++ ) { + char msg[128 + GLYPH_BITMAP_HEIGHT * (GLYPH_BITMAP_WIDTH+1)]; // +1 for trailing newline + + char *c = msg + sprintf(msg, "glyph %c: width %i, top %i\n", glyph_nr, get_glyph_width(glyph_nr), get_glyph_yoffset(glyph_nr) ); + + for( uint32 y = 0; y < GLYPH_BITMAP_HEIGHT; y++ ) { + for( uint32 x = 0; x < std::min(GLYPH_BITMAP_WIDTH, (uint32)get_glyph_width(glyph_nr)); x++ ) { + const uint8 data = get_glyph_bitmap(glyph_nr)[y+(x/CHAR_BIT)*GLYPH_BITMAP_HEIGHT]; + const bool bit_set = (data & (0x80>>(x%CHAR_BIT))) != 0; + + *c++ = bit_set ? '*' : ' '; } *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) { - FILE* f = dr_fopen(fname, "rb"); - int c; + tstrncpy( fname, srcfilename, lengthof(fname) ); + + FILE *fontfile = dr_fopen(fname, "rb"); - if (f == NULL) { - fprintf(stderr, "Error: Cannot open '%s'\n", fname); + if( fontfile == 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); - fclose(f); + const int res = fgetc(fontfile); + if( res==EOF ) { + dbg->error("font_t::load_from_file", "Cannot parse font '%s'", fname); + fclose(fontfile); 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; - rewind(f); - fprintf(stdout, "Loading font '%s'\n", fname); + // binary => the assume dumped prop file + if( c < ' ' && strstr(fname, ".fnt") ) { + rewind(fontfile); + return load_from_fnt(fontfile); + } - 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; - } + bool ok = load_from_bdf(fontfile); + fclose(fontfile); + +#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); + + linespace = 11; + descent = -2; + + for( int 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].bitmap[j] = npr_fonttab[512 + i * 10 + j]; + } + + for (; j < (int)sizeof(glyphs[i].bitmap); j++) { + glyphs[i].bitmap[j] = 0; + } + + // find the start offset + for( start_h=0; glyphs[i].bitmap[start_h]==0 && start_h<10; start_h++ ) { + ; + } - fnt->char_data[CHARACTER_LEN * i + CHARACTER_LEN-1] = npr_fonttab[i]; + 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].bitmap[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; +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; + } - sscanf(buf, "%4x", &n); + return glyphs[c].advance; +} - p = buf + 5; - for (line = 0; line < 7; line++) { - int 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; - } - fprintf(stderr, "%s successfully loaded as old format hex font!\n", fname); - return true; +uint8 font_t::get_glyph_width(utf32 c) const +{ + if( !is_loaded() ) { + return 0; + } + else if( c >= get_num_glyphs() ) { + c = 0; } - fprintf(stderr, "Loading BDF font '%s'\n", fname); - if (dsp_read_bdf_font(f, fnt)) { - debug_font( fnt ); - fclose(f); - return true; + return glyphs[c].width; +} + + +uint8 font_t::get_glyph_yoffset(uint32 c) const +{ + if( !is_loaded() ) { + return 0; + } + else if( c >= get_num_glyphs() ) { + c = 0; } - 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].yoff; +} + + +const uint8 *font_t::get_glyph_bitmap(utf32 c) const +{ + if( !is_loaded() ) { + return NULL; + } + else if( c >= get_num_glyphs() ) { + c = 0; } -#endif - return false; + return glyphs[c].bitmap; } diff --git display/font.h display/font.h index af736f9f1..40a8efb87 100644 --- display/font.h +++ display/font.h @@ -8,39 +8,94 @@ #include "../simtypes.h" +#include "../unicode.h" +#include + + +#define GLYPH_BITMAP_HEIGHT (23u) +#define GLYPH_BITMAP_WIDTH (16u) +#define GLYPH_BITMAP_BPP (1u) // cannot be changed currently + +// Size in bytes of a single row +#define GLYPH_ROW_PITCH (((GLYPH_BITMAP_WIDTH*GLYPH_BITMAP_BPP)+CHAR_BIT-1)/CHAR_BIT) + + +/** + * 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 bitmap[GLYPH_ROW_PITCH * GLYPH_BITMAP_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_linespace() const { return linespace; } + sint16 get_descent() const { return descent; } ///< Note: Because this value is in grid coordinates, it is NEGATIVE. + sint16 get_ascent() const { return linespace + 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 + { return is_loaded() && c < get_num_glyphs() && glyphs[c].advance != 0xFF; } + + /// @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 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::bitmap + const uint8 *get_glyph_bitmap(utf32 c) const; + +private: + /// Load a BDF font + bool load_from_bdf(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 linespace; + sint16 descent; + + std::vector glyphs; +}; #endif diff --git display/simgraph.h display/simgraph.h index bf41f008b..74c3b3ddf 100644 --- display/simgraph.h +++ display/simgraph.h @@ -15,11 +15,18 @@ #include "scr_coord.h" -extern int large_font_ascent; -extern int large_font_total_height; +#if COLOUR_DEPTH != 0 + +extern int default_font_ascent; +extern int default_font_linespace; + +# define LINEASCENT (default_font_ascent) +# define LINESPACE (default_font_linespace) +#else +# define LINEASCENT 0 +# define LINESPACE 0 +#endif -#define LINEASCENT (large_font_ascent) -#define LINESPACE (large_font_total_height) /** * Alignment enum to align controls against each other @@ -133,7 +140,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/simgraph0.cc display/simgraph0.cc index bb42cd364..d1f896ba8 100644 --- display/simgraph0.cc +++ display/simgraph0.cc @@ -9,9 +9,6 @@ #include "simgraph.h" -int large_font_height = 10; -int large_font_total_height = 11; -int large_font_ascent = 9; KOORD_VAL tile_raster_width = 16; // zoomed KOORD_VAL base_tile_raster_width = 16; // original @@ -72,9 +69,9 @@ void display_mark_img_dirty(image_id, KOORD_VAL, KOORD_VAL) { } -uint16 display_load_font(const char*, bool) +bool display_load_font(const char*, bool) { - return 1; + return true; } sint16 display_get_width() diff --git display/simgraph16.cc display/simgraph16.cc index 3ba42193e..1fc21dbb9 100644 --- display/simgraph16.cc +++ display/simgraph16.cc @@ -237,11 +237,12 @@ clipping_info_t clips; #define CR clips CLIP_NUM_INDEX -static font_t large_font; +static font_t default_font; // needed for resizing gui -int large_font_ascent = 9; -int large_font_total_height = 11; +int default_font_ascent = 0; +int default_font_linespace = 0; + #define MAX_PLAYER_COUNT (16) @@ -4102,33 +4103,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 && default_font.is_loaded() && strcmp( default_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) ) { + default_font = loaded_fnt; + default_font_ascent = default_font.get_ascent(); + default_font_linespace = default_font.get_linespace(); env_t::fontname = fname; - return large_font.num_chars; + return default_font.is_loaded(); } - return 0; + + return false; } @@ -4150,12 +4149,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 default_font.get_glyph_advance(c); } @@ -4192,13 +4186,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 = default_font.get_glyph_advance(char_code); } return char_code; } @@ -4207,11 +4195,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 default_font.is_valid_glyph(char_code); } @@ -4277,11 +4261,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 = default_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; } @@ -4291,22 +4272,17 @@ utf32 get_prev_char_with_metrics(const char* &text, const char *const text_start */ int display_calc_proportional_string_len_width(const char *text, size_t len) { - const font_t* const fnt = &large_font; + const font_t* const fnt = &default_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; @@ -4319,9 +4295,8 @@ int display_calc_proportional_string_len_width(const char *text, size_t len) */ void display_calc_proportional_multiline_string_len_width(int &xw, int &yh, const char *text, size_t len) { - const font_t* const fnt = &large_font; + const font_t* const fnt = &default_font; int width = 0; - int w; xw = yh = 0; @@ -4338,12 +4313,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 +4350,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 +4372,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 +4394,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 = &default_font; + + if (x >= cR || y >= cB || y + fnt->get_linespace() <= 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_linespace(); + const KOORD_VAL yy = y + fnt->get_linespace(); + // 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_bitmap(c) + glyph_yoffset + i*GLYPH_BITMAP_HEIGHT; + 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 +4463,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; } @@ -4609,17 +4572,17 @@ void display_ddd_box_rgb(KOORD_VAL x1, KOORD_VAL y1, KOORD_VAL w, KOORD_VAL h, P void display_outline_proportional_rgb(KOORD_VAL xpos, KOORD_VAL ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty, sint32 len) { const int flags = ALIGN_LEFT | DT_CLIP; - display_text_proportional_len_clip_rgb(xpos - 1, ypos - 1 + (12 - large_font_total_height) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); - display_text_proportional_len_clip_rgb(xpos + 1, ypos + 1 + (12 - large_font_total_height) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); - display_text_proportional_len_clip_rgb(xpos, ypos + (12 - large_font_total_height) / 2, text, flags, text_color, dirty, len CLIP_NUM_DEFAULT); + display_text_proportional_len_clip_rgb(xpos - 1, ypos - 1 + (12 - LINESPACE) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); + display_text_proportional_len_clip_rgb(xpos + 1, ypos + 1 + (12 - LINESPACE) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); + display_text_proportional_len_clip_rgb(xpos, ypos + (12 - LINESPACE) / 2, text, flags, text_color, dirty, len CLIP_NUM_DEFAULT); } void display_shadow_proportional_rgb(KOORD_VAL xpos, KOORD_VAL ypos, PIXVAL text_color, PIXVAL shadow_color, const char *text, int dirty, sint32 len) { const int flags = ALIGN_LEFT | DT_CLIP; - display_text_proportional_len_clip_rgb(xpos + 1, ypos + 1 + (12 - large_font_total_height) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); - display_text_proportional_len_clip_rgb(xpos, ypos + (12 - large_font_total_height) / 2, text, flags, text_color, dirty, len CLIP_NUM_DEFAULT); + display_text_proportional_len_clip_rgb(xpos + 1, ypos + 1 + (12 - LINESPACE) / 2, text, flags, shadow_color, dirty, len CLIP_NUM_DEFAULT); + display_text_proportional_len_clip_rgb(xpos, ypos + (12 - LINESPACE) / 2, text, flags, text_color, dirty, len CLIP_NUM_DEFAULT); } @@ -4643,7 +4606,7 @@ void display_ddd_box_clip_rgb(KOORD_VAL x1, KOORD_VAL y1, KOORD_VAL w, KOORD_VAL */ void display_ddd_proportional_clip(KOORD_VAL xpos, KOORD_VAL ypos, KOORD_VAL width, KOORD_VAL hgt, FLAGGED_PIXVAL ddd_color, FLAGGED_PIXVAL text_color, const char *text, int dirty CLIP_NUM_DEF) { - int halfheight = large_font_total_height / 2 + 1; + const int halfheight = LINESPACE / 2 + 1; PIXVAL lighter = display_blend_colors(ddd_color, color_idx_to_rgb(COL_WHITE), 25); PIXVAL darker = display_blend_colors(ddd_color, color_idx_to_rgb(COL_BLACK), 25); @@ -4656,7 +4619,7 @@ void display_ddd_proportional_clip(KOORD_VAL xpos, KOORD_VAL ypos, KOORD_VAL wid display_vline_wh_clip_rgb( xpos - 2, ypos - halfheight - 1 - hgt, halfheight * 2 + 2, lighter, dirty ); display_vline_wh_clip_rgb( xpos + width - 3, ypos - halfheight - 1 - hgt, halfheight * 2 + 2, darker, dirty ); - display_text_proportional_len_clip_rgb( xpos + 2, ypos - 5 + (12 - large_font_total_height) / 2, text, ALIGN_LEFT | DT_CLIP, text_color, dirty, -1); + display_text_proportional_len_clip_rgb( xpos + 2, ypos - 5 + (12 - LINESPACE) / 2, text, ALIGN_LEFT | DT_CLIP, text_color, dirty, -1); } @@ -5107,9 +5070,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 +5163,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 && default_font.is_loaded(); }