Mercurial > hg > dmlib
view tools/fontconv.c @ 2087:05532d716f50
Bump copyright years.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 14 Jan 2019 07:20:52 +0200 |
parents | 2ca6a13b091b |
children | e3f0eaf23f4f |
line wrap: on
line source
/* * fontconv - Convert bitmap fonts * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012-2015 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include <stdio.h> #include "dmlib.h" #include "dmargs.h" #include "dmfile.h" #include "dmimage.h" #include "dmtext.h" #include "dmres.h" char *optInFilename = NULL, *optOutFilename = NULL; int optSplitWidth = 8, optSplitHeight = 8, optBPP = 32; SDL_Color optColor = { 255, 255, 255, 100 }; static const DMOptArg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 's', "size", "Set glyph dimensions (-s W:H or -s N) 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 }; const int optListN = sizeof(optList) / sizeof(optList[0]); BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: dmPrintBanner(stdout, dmProgName, "[options] <sourcefile.(ttf|fnt|dmf|png)> <outputfile.dmf>"); dmArgsPrintHelp(stdout, optList, optListN, 0); printf( "\n" "This utility can be used to convert TSFONT files to bitmap DMFONT (DMF)\n" "files, render TrueType TTF to DMFONT at desired glyph resolution, or\n" "cut a PNG (or JPEG) image to glyphs of desired size.\n"); exit(0); break; case 1: dmVerbosity++; break; case 2: { unsigned int fontW, fontH; char *sep = strchr(optArg, ':'); if (sep != NULL) { char *tmpStr = dm_strndup(optArg, sep - optArg); if (!dmGetIntVal(tmpStr, &fontW, NULL) || !dmGetIntVal(sep + 1, &fontH, NULL)) { dmErrorMsg("Invalid font width or height value ('%s')\n", optArg); dmFree(tmpStr); return FALSE; } dmFree(tmpStr); } else { if (!dmGetIntVal(optArg, &fontW, NULL)) { dmErrorMsg("Invalid font size value ('%s')\n", optArg); return FALSE; } fontH = fontW; } if (fontW < DMFONT_MIN_WIDTH || fontW > DMFONT_MAX_WIDTH || fontH < DMFONT_MIN_HEIGHT || fontH > DMFONT_MAX_HEIGHT) { dmErrorMsg("Invalid font dimensions, must be %d < W %d, %d < H < %d.\n", DMFONT_MIN_WIDTH , DMFONT_MAX_WIDTH, DMFONT_MIN_HEIGHT , DMFONT_MAX_HEIGHT); return FALSE; } optSplitWidth = fontW; optSplitHeight = fontH; } break; case 3: { unsigned int colR, colG, colB, colA = 100; if (optArg[0] == '#' || optArg[0] == '$') optArg++; else if (optArg[0] == '0' && optArg[1] == 'x') optArg += 2; if (sscanf(optArg, "%02x%02x%02x", &colR, &colG, &colB) != 3 && sscanf(optArg, "%02x%02x%02x%02x", &colR, &colG, &colB, &colA) != 4) { dmErrorMsg("Invalid RGB hex representation '%s'.\n", optArg); return FALSE; } optColor.r = colR; optColor.g = colG; optColor.b = colB; optColor.a = colA; } 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; } return TRUE; } BOOL argHandleFile(char *currArg) { if (optInFilename == NULL) optInFilename = currArg; else if (optOutFilename == NULL) optOutFilename = currArg; else { dmErrorMsg("Too many filename arguments, '%s'\n", currArg); return FALSE; } return TRUE; } int dmCreateBitmapFontFromImage(SDL_Surface *image, int width, int height, DMBitmapFont **pfont) { int nglyph, xglyphs, yglyphs; DMBitmapFont *font; 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, 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, bpp=%d.\n", image->w, image->h, width, height, xglyphs, yglyphs, xglyphs * yglyphs, image->format->BitsPerPixel); nglyph = 0; for (int yc = 0; yc < yglyphs; yc++) for (int xc = 0; xc < xglyphs; xc++) { DMBitmapGlyph *glyph = &font->glyphMap[nglyph++]; SDL_Rect src, dst; src.x = xc * width; src.y = yc * height; dst.w = src.w = width; dst.h = src.h = height; dst.x = 0; dst.y = nglyph * height; glyph->index = nglyph; SDL_BlitSurface(image, &src, font->glyphs, &dst); } *pfont = font; return DMERR_OK; } int dmSaveBitmapFont(DMResource *fp, DMBitmapFont *font) { if (font == NULL) return DMERR_NULLPTR; 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; // Write the DMFONT header 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; // Write the glyph data for (int index = 0; index < font->maxglyph; index++) { DMBitmapGlyph *glyph = &font->glyphMap[index]; if (glyph->index >= 0) { Uint8 *pixels = font->glyphs->pixels + font->gsize * glyph->index; // Each glyph has its table index and w/h stored 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 (int y = 0; y < glyph->height; y++) { if (dmfwrite(pixels, font->glyphs->format->BytesPerPixel, glyph->width, fp) != (size_t) glyph->width) return DMERR_FWRITE; pixels += font->glyphs->pitch; } } } return DMERR_OK; } int main(int argc, char *argv[]) { DMResource *inFile = NULL, *outFile = NULL; DMBitmapFont *font = NULL; SDL_Surface *fontbmap = NULL; int res; #ifdef DM_GFX_TTF_TEXT BOOL initTTF = FALSE; TTF_Font *ttf = NULL; #endif dmInitProg("fontconv", "Bitmap font converter", "0.4", NULL, NULL); // Parse arguments if (!dmArgsProcess(argc, argv, optList, optListN, argHandleOpt, argHandleFile, OPTH_BAILOUT)) exit(1); // Check arguments if (optInFilename == NULL || optOutFilename == NULL) { dmErrorMsg("Input or output file not specified!\n"); return 1; } #ifdef DM_GFX_TTF_TEXT if (TTF_Init() < 0) { dmErrorMsg("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); goto out; } initTTF = TRUE; #endif // Open the source file if ((res = dmf_open_stdio(optInFilename, "rb", &inFile)) != DMERR_OK) { dmErrorMsg("Error opening input file '%s', %d: %s\n", optInFilename, res, dmErrorStr(res)); return 1; } if ((res = dmLoadBitmapFont(inFile, &font)) == DMERR_OK) { 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 - 1)) != NULL) { 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); // Create the bitmap font if ((font = dmNewBitmapFont(gmax - gmin + 1, 256, optSplitWidth - 6, optSplitHeight + 2, optBPP)) == NULL) { dmErrorMsg("Could not allocate bitmap font!\n"); goto out; } // 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] = nglyph; str[1] = 0; // 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 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; } dst.x = 0; dst.y = index * font->height; dst.w = font->width; 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 else { dmfreset(inFile); if ((fontbmap = dmLoadImage(inFile)) == NULL) { dmErrorMsg("Could not load image file '%s'.\n", optInFilename); goto out; } dmMsg(1, "Input is a bitmap image (%d x %d, %d bpp), splitting to %d x %d.\n", fontbmap->w, fontbmap->h, fontbmap->format->BitsPerPixel, optSplitWidth, optSplitHeight); if ((res = dmCreateBitmapFontFromImage(fontbmap, optSplitWidth, optSplitHeight, &font)) != DMERR_OK) { dmErrorMsg("Could not create a font from image, %d: %s\n", res, dmErrorStr(res)); goto out; } } if (font == NULL) { dmErrorMsg("No font loaded.\n"); goto out; } // 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->index >= 0) { font->nglyphs++; 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; } } } 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 out; } res = dmSaveBitmapFont(outFile, font); dmf_close(outFile); if (res != DMERR_OK) { dmErrorMsg("Error saving font, %d: %s\n", res, dmErrorStr(res)); } out: #ifdef DM_GFX_TTF_TEXT if (initTTF) TTF_Quit(); #endif dmf_close(inFile); dmFreeBitmapFont(font); SDL_FreeSurface(fontbmap); return 0; }