# HG changeset patch # User Matti Hamalainen # Date 1530325452 -10800 # Node ID ef08af6887b7221a690bd26ddeac40448c7e1b9d # Parent 6147dd3fa5d22a314efad1ecdb571c0ebced3cb9 Revamp the bitmap font system to use single SDL_Surface for the font graphics instead of N surfaces for each separate glyph. diff -r 6147dd3fa5d2 -r ef08af6887b7 src/dmtext.h --- a/src/dmtext.h Sat Jun 30 05:22:15 2018 +0300 +++ b/src/dmtext.h Sat Jun 30 05:24:12 2018 +0300 @@ -24,14 +24,13 @@ // DMFONT format constants #define DMFONT_MAGIC "DMFONT" -#define DMFONT_VERSION 0x0100 +#define DMFONT_VERSION 0x0200 #define DMFONT_MIN_WIDTH 3 #define DMFONT_MIN_HEIGHT 3 #define DMFONT_MAX_WIDTH 128 #define DMFONT_MAX_HEIGHT 128 #define DMFONT_MAX_GLYPHS 1024 - #define DMFONT_NPALETTE 256 // Legacy TSFONT loading support @@ -41,29 +40,52 @@ typedef struct { - int width, height; // Dimensions - int nglyphs; // Size of glyphs array - SDL_Surface **glyphs; // NOTE! Not all glyphs may be allocated + int width, height, // Dimensions of this glyph + index; // Index to surface, value of "maxglyph" means unused +} DMBitmapGlyph; + + +typedef struct +{ + int width, height; // Dimensions + int nglyphs, // How many glyphs worth of data is allocated for the surface + maxglyph; // Number of character indices, e.g. typically 256 + size_t gsize; // Size of one glyph in bytes, e.g. glyphs->pitch * font->height + + DMBitmapGlyph *glyphMap; + SDL_Surface *glyphs; // Surface containing glyphs } DMBitmapFont; -DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int width, const int height); +DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int maxglyph, const int width, const int height, const int bpp); int dmFreeBitmapFont(DMBitmapFont *font); int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont); int dmSetBitmapFontPalette(DMBitmapFont *font, const SDL_Color *pal, const int start, const int size); -void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *str); -void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, va_list ap); -void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, ...); +void dmDrawBMTextConst(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, int xc, int yc, const char *str); +void dmDrawBMTextVA(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, const int xc, const int yc, const char *fmt, va_list ap); +void dmDrawBMText(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, const int xc, const int yc, const char *fmt, ...); -static inline SDL_Surface *dmGetBMGlyph(DMBitmapFont *font, int ch) +static inline void dmInitializeGetBMGlyphSurface(SDL_Surface *surf, DMBitmapFont *font) { + memcpy(surf, font->glyphs, sizeof(SDL_Surface)); +} + +static inline void dmGetBMGlyph(SDL_Surface *surf, DMBitmapFont *font, int ch) +{ + DMBitmapGlyph *glyph; if (ch < 0 || ch >= font->nglyphs || ch == '\n' || ch == '\r') ch = 32; - return font->glyphs[ch]; + glyph = &font->glyphMap[ch]; + surf->w = glyph->width; + surf->h = glyph->height; + surf->pixels = font->glyphs->pixels + font->gsize * glyph->index; } #endif diff -r 6147dd3fa5d2 -r ef08af6887b7 src/dmtext_bm.c --- a/src/dmtext_bm.c Sat Jun 30 05:22:15 2018 +0300 +++ b/src/dmtext_bm.c Sat Jun 30 05:24:12 2018 +0300 @@ -7,26 +7,33 @@ #include "dmtext.h" -void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt) +void dmDrawBMTextConst(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, int xc, int yc, const char *fmt) { const char *ptr = fmt; - DMUnscaledBlitFunc blit = NULL; + DMUnscaledBlitFunc blit = dmGetUnscaledBlitFunc(font->glyphs->format, screen->format, mode); + SDL_Surface surf; + Uint8 *orig = font->glyphs->pixels; + + memcpy(&surf, font->glyphs, sizeof(SDL_Surface)); while (*ptr) { - int ch = *ptr++; - SDL_Surface *glyph; - - if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL) + unsigned char ch = *ptr++; + if (ch < font->maxglyph) { - if (blit == NULL) - blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode); + DMBitmapGlyph *glyph = &font->glyphMap[ch]; + if (glyph->index >= 0) + { + surf.pixels = orig + glyph->index * font->gsize; + surf.w = glyph->width; + surf.h = glyph->height; - if (blit != NULL) - { - blit(glyph, xc, yc, screen); - xc += condensed ? glyph->w : font->width; + blit(&surf, xc, yc, screen); + xc += condensed ? glyph->width : font->width; } + else + xc += font->width; } else xc += font->width; @@ -34,7 +41,9 @@ } -void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, va_list ap) +void dmDrawBMTextVA(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, const int xc, const int yc, + const char *fmt, va_list ap) { char tmp[512]; vsnprintf(tmp, sizeof(tmp), fmt, ap); @@ -43,7 +52,9 @@ } -void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, ...) +void dmDrawBMText(SDL_Surface *screen, const DMBitmapFont *font, + const BOOL condensed, const int mode, const int xc, const int yc, + const char *fmt, ...) { va_list ap; @@ -53,42 +64,53 @@ } -DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int width, const int height) +DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int maxglyph, + const int width, const int height, const int bpp) { DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont)); - if (font == NULL) + if (font == NULL || (bpp != 8 && bpp != 32)) return NULL; font->width = width; font->height = height; font->nglyphs = nglyphs; - font->glyphs = dmCalloc(nglyphs, sizeof(SDL_Surface *)); - if (font->glyphs == NULL) + font->maxglyph = maxglyph; + + if ((font->glyphMap = dmCalloc(font->maxglyph, sizeof(DMBitmapGlyph))) == NULL) + goto error; + + if ((font->glyphs = SDL_CreateRGBSurfaceWithFormat( + 0, width, height * (nglyphs + 1), bpp, + bpp == 8 ? SDL_PIXELFORMAT_INDEX8 : SDL_PIXELFORMAT_RGBA32)) == NULL) + goto error; + + font->gsize = font->height * font->glyphs->pitch; + + for (int i = 0; i < font->maxglyph; i++) { - dmFree(font); - return NULL; + DMBitmapGlyph *glyph = &font->glyphMap[i]; + glyph->width = width; + glyph->height = height; + glyph->index = -1; // means that this index is empty/unused } return font; + +error: + dmFreeBitmapFont(font); + return NULL; } int dmFreeBitmapFont(DMBitmapFont *font) { - int i; - if (font == NULL) return DMERR_NULLPTR; - for (i = 0; i < font->nglyphs; i++) - { - if (font->glyphs[i] != NULL) - { - SDL_FreeSurface(font->glyphs[i]); - font->glyphs[i] = NULL; - } - } + if (font->glyphs != NULL) + SDL_FreeSurface(font->glyphs); + dmFree(font->glyphMap); dmFree(font); return DMERR_OK; } @@ -100,22 +122,13 @@ */ int dmSetBitmapFontPalette(DMBitmapFont *font, const SDL_Color *pal, const int start, const int size) { - int i; - if (font == NULL) return DMERR_NULLPTR; if (start < 0 || size < 1) return DMERR_INVALID_ARGS; - for (i = 0; i < font->nglyphs; i++) - { - SDL_Surface *glyph = font->glyphs[i]; - if (glyph != NULL) - { - SDL_SetPaletteColors(glyph->format->palette, pal, start, size); - } - } + SDL_SetPaletteColors(font->glyphs->format->palette, pal, start, size); return DMERR_OK; } @@ -123,32 +136,34 @@ //#define FN_DEBUG -int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont) + +int dmLoadBitmapFont(DMResource *fp, DMBitmapFont **pfont) { DMBitmapFont *font; char magic[8]; Uint16 version, nglyphs, maxglyph; - int width, height; + Uint8 width, height, bpp; BOOL tsfont = FALSE; // Check magic and version - if (!dmf_read_str(res, (Uint8 *) &magic, 6) || - !dmf_read_le16(res, &version)) + if (!dmf_read_str(fp, (Uint8 *) &magic, 6) || + !dmf_read_le16(fp, &version)) return DMERR_FREAD; // Check if it is a legacy TSFONT file if (memcmp(magic, TSFONT_MAGIC, 6) == 0) { // Yep, we handle these a bit differently - int encoding = dmfgetc(res); + int encoding = dmfgetc(fp); tsfont = TRUE; - if (version > TSFONT_VERSION) - return DMERR_VERSION; +#ifdef FN_DEBUG + fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n", + version >> 8, version & 0xff, version, encoding); +#endif -#ifdef FN_DEBUG - fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n", version >> 8, version & 0xff, version, encoding); -#endif + if (version > TSFONT_VERSION || version < 0x0200) + return DMERR_VERSION; // There were only two encodings, 0 = none and 1 = RLE // of which RLE was never actually used ... derp. @@ -156,140 +171,138 @@ return DMERR_NOT_SUPPORTED; } else + if (memcmp(magic, DMFONT_MAGIC, 6) == 0) { - if (memcmp(magic, DMFONT_MAGIC, 6) != 0) - return DMERR_INVALID; +#ifdef FN_DEBUG + fprintf(stderr, "DMFONT v%d.%d (0x%04x)\n", + version >> 8, version & 0xff, version); +#endif if (version > DMFONT_VERSION) return DMERR_VERSION; } + else + return DMERR_INVALID; // Read other header data if (tsfont) { - // TSFONT only has number of glyphs stored in the file - nglyphs = dmfgetc(res); - - // Maximum glyph number - maxglyph = 256; - } - else - { - dmf_read_le16(res, &nglyphs); - dmf_read_le16(res, &maxglyph); - } + // TSFONT has number of glyphs and dimensions + Uint8 tmp, unused; + if (!dmf_read_byte(fp, &tmp) || + !dmf_read_byte(fp, &width) || + !dmf_read_byte(fp, &height) || + !dmf_read_byte(fp, &unused)) + return DMERR_FREAD; - width = dmfgetc(res); - height = dmfgetc(res); - -#ifdef FN_DEBUG - fprintf(stderr, "nglyphs=%d (0x%02x), maxglyph=%d (0x%02x) width=%d, height=%d\n", - nglyphs, nglyphs, maxglyph, maxglyph, width, height); -#endif - - if (tsfont) - { - // TSFONT color assignments (boolean) .. we discard this. - dmfgetc(res); + nglyphs = tmp; + maxglyph = 256; + bpp = 8; // Very old TSFONTs have some extra data that is not used // .. can't actually even remember what it was for. if (version == 0x0200) { - int i; - for (i = 0; i < 32; i++) - dmfgetc(res); + for (int i = 0; i < 32; i++) + { + if (!dmf_read_byte(fp, &unused)) + return DMERR_FREAD; + } } } + else + { + // DMFONT has Uint16 values for nglyphs and maxglyph, plus BPP + if (!dmf_read_le16(fp, &nglyphs) || + !dmf_read_le16(fp, &maxglyph) || + !dmf_read_byte(fp, &width) || + !dmf_read_byte(fp, &height) || + !dmf_read_byte(fp, &bpp)) + return DMERR_FREAD; + } + +#ifdef FN_DEBUG + fprintf(stderr, "nglyphs=%d, maxglyph=%d, width=%d, height=%d, bpp=%d\n", + nglyphs, maxglyph, width, height, bpp); +#endif if (width < DMFONT_MIN_WIDTH || height < DMFONT_MIN_HEIGHT || width > DMFONT_MAX_WIDTH || height > DMFONT_MAX_HEIGHT || nglyphs > DMFONT_MAX_GLYPHS || + nglyphs > maxglyph || maxglyph > DMFONT_MAX_GLYPHS || maxglyph < 1) return DMERR_INVALID_DATA; + if (bpp != 8 && bpp != 32) + return DMERR_NOT_SUPPORTED; + // Allocate font - if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL) + if ((*pfont = font = dmNewBitmapFont(nglyphs, maxglyph, width, height, bpp)) == NULL) return DMERR_MALLOC; - // Read glyph data, if any - if (nglyphs > 0) + // Setup palette for 8bpp fonts + if (bpp == 8) { - int n, i; - Uint32 BitsPerPixel, Rmask, Gmask, Bmask, Amask; SDL_Color pal[DMFONT_NPALETTE]; - - // Setup palette for 8bpp fonts - for (n = 0; n < DMFONT_NPALETTE; n++) + for (int n = 0; n < DMFONT_NPALETTE; n++) { - pal[n].r = pal[n].g = pal[n].b = 0; + pal[n].r = pal[n].g = pal[n].b = n > 0 ? 255 : 0; pal[n].a = n > 0 ? 255 : 0; } + SDL_SetPaletteColors(font->glyphs->format->palette, pal, 0, DMFONT_NPALETTE); + } - if (tsfont) - { - BitsPerPixel = 8; - Rmask = Gmask = Bmask = Amask = 0; - } - else - { - BitsPerPixel = dmfgetc(res); - dmf_read_le32(res, &Rmask); - dmf_read_le32(res, &Gmask); - dmf_read_le32(res, &Bmask); - dmf_read_le32(res, &Amask); - } + // Read glyph data, if any + for (int i = 0; i < nglyphs; i++) + { + DMBitmapGlyph *glyph; + Uint8 gwidth, gheight; + Uint16 gindex; + Uint8 *pixels; - for (i = 0; i < nglyphs; i++) - { - int y; - Uint16 index; - Uint8 *pixels; - SDL_Surface *glyph; + // TSFONT format has only byte sized index + if (tsfont) + gindex = dmfgetc(fp); + else + dmf_read_le16(fp, &gindex); - // TSFONT format has only byte sized index - if (tsfont) - index = dmfgetc(res); - else - dmf_read_le16(res, &index); - - // Read dimensions - width = dmfgetc(res); - height = dmfgetc(res); + // Read dimensions + if (!dmf_read_byte(fp, &gwidth) || + !dmf_read_byte(fp, &gheight)) + return DMERR_FREAD; #ifdef FN_DEBUG - fprintf(stderr, "#%d @ %d - w=%d, h=%d\n", i, index, width, height); + fprintf(stderr, "#%d @ %d - %d x %d\n", i, gindex, gwidth, gheight); #endif - if (width < DMFONT_MIN_WIDTH || - height < DMFONT_MIN_HEIGHT || - width > DMFONT_MAX_WIDTH || - height > DMFONT_MAX_HEIGHT || - index >= maxglyph) - return DMERR_INVALID_DATA; - - // Allocate bitmap - font->glyphs[index] = glyph = SDL_CreateRGBSurface( - SDL_SWSURFACE, width, height, - BitsPerPixel, Rmask, Gmask, Bmask, Amask); + // Check the glyph data + if (gwidth < DMFONT_MIN_WIDTH || + gheight < DMFONT_MIN_HEIGHT || + gwidth > DMFONT_MAX_WIDTH || + gheight > DMFONT_MAX_HEIGHT || + gwidth > width || + gheight > height || + gindex >= maxglyph) + return DMERR_INVALID_DATA; - if (glyph == NULL) - return DMERR_MALLOC; - - if (BitsPerPixel == 8) - SDL_SetPaletteColors(glyph->format->palette, pal, 0, DMFONT_NPALETTE); + // Set glyph data + glyph = &font->glyphMap[gindex]; + glyph->width = gwidth; + glyph->height = gheight; + glyph->index = i; - // Read pixel data - pixels = glyph->pixels; - for (y = 0; y < glyph->h; y++) - { - if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w) - return DMERR_FREAD; - pixels += glyph->pitch; - } + // Read pixel data + pixels = font->glyphs->pixels + (i * font->gsize); + for (int y = 0; y < glyph->height; y++) + { + if (dmfread(pixels, font->glyphs->format->BytesPerPixel, + glyph->width, fp) != (size_t) glyph->width) + return DMERR_FREAD; + + pixels += font->glyphs->pitch; } } diff -r 6147dd3fa5d2 -r ef08af6887b7 tools/fontconv.c --- a/tools/fontconv.c Sat Jun 30 05:22:15 2018 +0300 +++ b/tools/fontconv.c Sat Jun 30 05:24:12 2018 +0300 @@ -16,7 +16,8 @@ char *optInFilename = NULL, *optOutFilename = NULL; int optSplitWidth = 8, - optSplitHeight = 8; + optSplitHeight = 8, + optBPP = 32; SDL_Color optColor = { 255, 255, 255, 100 }; @@ -28,6 +29,7 @@ { 2, 's', "size", "Set glyph dimensions (-s W:H) for image->font conversion", OPT_ARGREQ }, #ifdef DM_GFX_TTF_TEXT { 3, 'c', "color", "TTF font rendering color (def: 0xFFFFFF)", OPT_ARGREQ }, + { 4, 'b', "bpp", "Render font in 8 or 32 bits per pixel (default 32)", OPT_ARGREQ }, #endif }; @@ -100,6 +102,21 @@ } break; + case 4: + if (sscanf(optArg, "%d", &optBPP) != 1) + { + dmErrorMsg("Invalid argument for -b option, '%s'.\n", + optArg); + return FALSE; + } + if (optBPP != 8 && optBPP != 32) + { + dmErrorMsg("Invalid bit depth %d, must be 8 or 32.\n", + optBPP); + return FALSE; + } + break; + default: dmErrorMsg("Unknown argument '%s'.\n", currArg); return FALSE; @@ -111,10 +128,10 @@ BOOL argHandleFile(char *currArg) { - if (!optInFilename) + if (optInFilename == NULL) optInFilename = currArg; else - if (!optOutFilename) + if (optOutFilename == NULL) optOutFilename = currArg; else { @@ -128,49 +145,45 @@ int dmCreateBitmapFontFromImage(SDL_Surface *image, int width, int height, DMBitmapFont **pfont) { - int nglyph, xc, yc, xglyphs, yglyphs; + int nglyph, xglyphs, yglyphs; DMBitmapFont *font; - if (image->w < width || width < 2 || image->h < height || height < 2) + if (image->w < width || width < 2 || + image->h < height || height < 2) return DMERR_INVALID_ARGS; xglyphs = image->w / width; yglyphs = image->h / height; - if ((font = dmNewBitmapFont(xglyphs * yglyphs, width, height)) == NULL) + if ((font = dmNewBitmapFont( + xglyphs * yglyphs, + xglyphs * yglyphs, + width, height, image->format->BitsPerPixel)) == NULL) return DMERR_MALLOC; - dmMsg(1, "%d x %d split as %d x %d blocks => %d x %d = %d glyphs.\n", + dmMsg(1, "%d x %d split as %d x %d blocks => %d x %d = %d glyphs, bpp=%d.\n", image->w, image->h, width, height, - xglyphs, yglyphs, xglyphs * yglyphs); + xglyphs, yglyphs, + xglyphs * yglyphs, image->format->BitsPerPixel); nglyph = 0; - for (yc = 0; yc < yglyphs; yc++) - for (xc = 0; xc < xglyphs; xc++) + for (int yc = 0; yc < yglyphs; yc++) + for (int xc = 0; xc < xglyphs; xc++) { - SDL_Surface *glyph = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, - image->format->BitsPerPixel, - image->format->Rmask, - image->format->Gmask, - image->format->Bmask, - image->format->Amask); + DMBitmapGlyph *glyph = &font->glyphMap[nglyph++]; + SDL_Rect src, dst; - if (glyph == NULL) - { - dmFreeBitmapFont(font); - return DMERR_MALLOC; - } + src.x = xc * width; + src.y = yc * height; + dst.w = src.w = width; + dst.h = src.h = height; - SDL_Rect r; - r.x = xc * width; - r.y = yc * height; - r.w = width; - r.h = height; + dst.x = 0; + dst.y = nglyph * height; + glyph->index = nglyph; - SDL_BlitSurface(image, &r, glyph, NULL); - - font->glyphs[nglyph++] = glyph; + SDL_BlitSurface(image, &src, font->glyphs, &dst); } *pfont = font; @@ -178,82 +191,51 @@ } -int dmSaveBitmapFont(DMResource *res, DMBitmapFont *font) +int dmSaveBitmapFont(DMResource *fp, DMBitmapFont *font) { - SDL_Surface *glyph; - int maxglyph, nglyphs, n; if (font == NULL) return DMERR_NULLPTR; - if (font->nglyphs > DMFONT_MAX_GLYPHS || + if (font->nglyphs > font->maxglyph || + font->maxglyph > DMFONT_MAX_GLYPHS || font->width > DMFONT_MAX_WIDTH || font->height > DMFONT_MAX_HEIGHT || font->width < DMFONT_MIN_WIDTH || font->height < DMFONT_MIN_HEIGHT) return DMERR_INVALID_DATA; - // Count number of actually existing glyphs - for (maxglyph = nglyphs = n = 0; n < font->nglyphs; n++) - { - SDL_Surface *glyph = font->glyphs[n]; - if (glyph != NULL) - { - maxglyph = n; - if (glyph->w < DMFONT_MIN_WIDTH || - glyph->h < DMFONT_MIN_HEIGHT || - glyph->w > DMFONT_MAX_WIDTH || - glyph->h > DMFONT_MAX_HEIGHT) - continue; - nglyphs++; - } - } - - if (nglyphs == 0) - return DMERR_INVALID_DATA; - // Write the DMFONT header - if (!dmf_write_str(res, (Uint8 *) DMFONT_MAGIC, 6)) + if (!dmf_write_str(fp, (Uint8 *) DMFONT_MAGIC, 6) || + !dmf_write_le16(fp, DMFONT_VERSION) || + !dmf_write_le16(fp, font->nglyphs) || + !dmf_write_le16(fp, font->maxglyph) || + !dmf_write_byte(fp, font->width) || + !dmf_write_byte(fp, font->height) || + !dmf_write_byte(fp, font->glyphs->format->BitsPerPixel)) return DMERR_FWRITE; - dmf_write_le16(res, DMFONT_VERSION); - dmf_write_le16(res, nglyphs); - dmf_write_le16(res, maxglyph + 1); - dmfputc(font->width, res); - dmfputc(font->height, res); - - // Store glyph format data - glyph = font->glyphs[maxglyph]; - dmfputc(glyph->format->BitsPerPixel, res); - dmf_write_le32(res, glyph->format->Rmask); - dmf_write_le32(res, glyph->format->Gmask); - dmf_write_le32(res, glyph->format->Bmask); - dmf_write_le32(res, glyph->format->Amask); - - for (n = 0; n < font->nglyphs; n++) + // Write the glyph data + for (int index = 0; index < font->maxglyph; index++) { - glyph = font->glyphs[n]; - if (glyph != NULL) + DMBitmapGlyph *glyph = &font->glyphMap[index]; + if (glyph->index < font->maxglyph) { - int y; - Uint8 *pixels = glyph->pixels; - - if (glyph->w < DMFONT_MIN_WIDTH || - glyph->h < DMFONT_MIN_HEIGHT || - glyph->w > DMFONT_MAX_WIDTH || - glyph->h > DMFONT_MAX_HEIGHT) - continue; + Uint8 *pixels = font->glyphs->pixels + font->gsize * glyph->index; // Each glyph has its table index and w/h stored - dmf_write_le16(res, n); - dmfputc(glyph->w, res); - dmfputc(glyph->h, res); + if (!dmf_write_le16(fp, index) || + !dmf_write_byte(fp, glyph->width) || + !dmf_write_byte(fp, glyph->height)) + return DMERR_FWRITE; // Write the pixel data - for (y = 0; y < glyph->h; y++) + for (int y = 0; y < glyph->height; y++) { - if (dmfwrite(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w) + if (dmfwrite(pixels, font->glyphs->format->BytesPerPixel, + glyph->width, fp) != (size_t) glyph->width) return DMERR_FWRITE; - pixels += glyph->pitch; + + pixels += font->glyphs->pitch; } } } @@ -273,7 +255,7 @@ TTF_Font *ttf = NULL; #endif - dmInitProg("fontconv", "Bitmap font converter", "0.3", NULL, NULL); + dmInitProg("fontconv", "Bitmap font converter", "0.4", NULL, NULL); dmVerbosity = 1; // Parse arguments @@ -282,7 +264,7 @@ exit(1); // Check arguments - if (!optInFilename || !optOutFilename) + if (optInFilename == NULL || optOutFilename == NULL) { dmErrorMsg("Input or output file not specified!\n"); return 1; @@ -292,7 +274,7 @@ if (TTF_Init() < 0) { dmErrorMsg("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); - goto error_exit; + goto out; } initTTF = TRUE; #endif @@ -308,29 +290,85 @@ if ((res = dmLoadBitmapFont(inFile, &font)) == DMERR_OK) { - dmMsg(1, "Input is a TSFONT/DMFONT font file.\n"); + dmMsg(1, "Input is a TSFONT/DMFONT font file, %d x %d, %d glyphs (%d max).\n", + font->width, font->height, font->nglyphs, font->maxglyph); + } + else + if (res != DMERR_INVALID) + { + dmErrorMsg("Input is a TSFONT/DMFONT font file, but there is an error: %s\n", + dmErrorStr(res)); + goto out; } #ifdef DM_GFX_TTF_TEXT else - if ((ttf = TTF_OpenFont(optInFilename, optSplitWidth)) != NULL) + if ((ttf = TTF_OpenFont(optInFilename, optSplitWidth - 1)) != NULL) { - int i; - dmMsg(1, "Input is a TTF TrueType font, rendering at %d x %d.\n", - optSplitWidth, optSplitHeight); + int gmin = 34, gmax = 127; + + dmMsg(1, "Input is a TTF TrueType font, rendering at %d x %d, %d bpp.\n", + optSplitWidth, optSplitHeight, optBPP); + + dmMsg(1, "Rendering glyph range %d to %d inclusive.\n", + gmin, gmax); TTF_SetFontStyle(ttf, TTF_STYLE_NORMAL); - if ((font = dmNewBitmapFont(256, optSplitWidth - 1, optSplitHeight+4)) == NULL) + // Create the bitmap font + if ((font = dmNewBitmapFont(gmax - gmin + 1, 256, + optSplitWidth - 6, optSplitHeight + 2, optBPP)) == NULL) { - goto error_exit; + dmErrorMsg("Could not allocate bitmap font!\n"); + goto out; } - for (i = 0; i < 255; i++) + // Render glyphs from the normal ASCII range only + for (int index = 0, nglyph = gmin; nglyph <= gmax; nglyph++) { + SDL_Surface *tmp; char str[2]; - str[0] = i; + str[0] = nglyph; str[1] = 0; - font->glyphs[i] = TTF_RenderText_Blended(ttf, str, optColor); + + // Render the glyph from TTF to surface + if (optBPP == 8) + tmp = TTF_RenderText_Solid(ttf, str, optColor); + else + tmp = TTF_RenderText_Blended(ttf, str, optColor); + + if (tmp != NULL) + { + DMBitmapGlyph *glyph = &font->glyphMap[nglyph]; + int minx, miny, advance; + SDL_Rect src, dst; + + if (TTF_GlyphMetrics(ttf, nglyph, &minx, NULL, &miny, NULL, &advance) == -1) + { + dmErrorMsg("Could not get TTF glyph metrics for character '%c' (%d).\n", + nglyph, nglyph); + goto out; + } + + src.x = 0; + src.y = 0; + src.w = tmp->w; + src.h = tmp->h; + + dst.x = 0; + dst.y = index * font->height; + dst.w = tmp->w; + dst.h = tmp->h; +// dst.h = font->height; + + // Set glyph data + glyph->width = font->width; + glyph->height = font->height; + glyph->index = index; + + SDL_BlitSurface(tmp, NULL, font->glyphs, &dst); + SDL_FreeSurface(tmp); + index++; + } } } #endif @@ -341,7 +379,7 @@ if ((fontbmap = dmLoadImage(inFile)) == NULL) { dmErrorMsg("Could not load image file '%s'.\n", optInFilename); - goto error_exit; + goto out; } dmMsg(1, "Input is a bitmap image (%d x %d, %d bpp), splitting to %d x %d.\n", @@ -352,23 +390,51 @@ { dmErrorMsg("Could not create a font from image, %d: %s\n", res, dmErrorStr(res)); - goto error_exit; + goto out; } } if (font == NULL) { dmErrorMsg("No font loaded.\n"); - goto error_exit; + goto out; } - dmMsg(1, "Outputting a DMFONT format bitmap font.\n"); + // Count number of actually existing glyphs despite that we should have + // that information in font->nglyphs. Also sanity check the glyphs. + font->nglyphs = 0; + for (int n = 0; n < font->maxglyph; n++) + { + DMBitmapGlyph *glyph = &font->glyphMap[n]; + + if (glyph->width < DMFONT_MIN_WIDTH || + glyph->height < DMFONT_MIN_HEIGHT || + glyph->width > DMFONT_MAX_WIDTH || + glyph->height > DMFONT_MAX_HEIGHT || + glyph->width > font->width || + glyph->height > font->height) + { + dmErrorMsg("Invalid glyph #%d: %d x %d (font %d x %d)\n", + n, + glyph->width, glyph->height, + font->width, font->height); + goto out; + } + + if (glyph->index < font->maxglyph) + font->nglyphs++; + } + + dmMsg(1, "Outputting a DMFONT format bitmap font, %d x %d with %d glyphs (%d max), %d bpp.\n", + font->width, font->height, + font->nglyphs, font->maxglyph, + font->glyphs->format->BitsPerPixel); if ((res = dmf_open_stdio(optOutFilename, "wb", &outFile)) != DMERR_OK) { dmErrorMsg("Error creating file '%s', %d: %s\n", optInFilename, res, dmErrorStr(res)); - goto error_exit; + goto out; } res = dmSaveBitmapFont(outFile, font); @@ -380,7 +446,7 @@ res, dmErrorStr(res)); } -error_exit: +out: #ifdef DM_GFX_TTF_TEXT if (initTTF) diff -r 6147dd3fa5d2 -r ef08af6887b7 tools/libgutil.c --- a/tools/libgutil.c Sat Jun 30 05:22:15 2018 +0300 +++ b/tools/libgutil.c Sat Jun 30 05:24:12 2018 +0300 @@ -117,12 +117,15 @@ void dmDrawBMTextConstQ(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt) { const char *ptr = fmt; - DMUnscaledBlitFunc blit = NULL; + DMUnscaledBlitFunc blit = dmGetUnscaledBlitFunc(font->glyphs->format, screen->format, mode); + Uint8 *orig = font->glyphs->pixels; + SDL_Surface surf; + + memcpy(&surf, font->glyphs, sizeof(SDL_Surface)); while (*ptr) { int ch = toupper(*ptr++); - SDL_Surface *glyph; if (ch == '_') { @@ -130,16 +133,20 @@ continue; } - if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL) + if (ch < font->maxglyph && ch != ' ') { - if (blit == NULL) - blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode); + DMBitmapGlyph *glyph = &font->glyphMap[ch]; - blit(glyph, xc, yc, screen); - xc += font->width; + if (glyph->index >= 0) + { + surf.pixels = orig + glyph->index * font->gsize; + surf.w = glyph->width; + surf.h = glyph->height; + + blit(&surf, xc, yc, screen); + } } - else - xc += font->width; + xc += font->width; } }