Mercurial > hg > dmlib
diff tools/fontconv.c @ 1957:ef08af6887b7
Revamp the bitmap font system to use single SDL_Surface for the font
graphics instead of N surfaces for each separate glyph.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 30 Jun 2018 05:24:12 +0300 |
parents | 73545a442ffe |
children | b99e04c356ec |
line wrap: on
line diff
--- 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)