Mercurial > hg > dmlib
view tools/fontconv.c @ 2208:90ec1ec89c56
Revamp the palette handling in lib64gfx somewhat, add helper functions to
lib64util for handling external palette file options and add support for
specifying one of the "internal" palettes or external (.act) palette file to
gfxconv and 64vw.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 14 Jun 2019 05:01:12 +0300 |
parents | e3f0eaf23f4f |
children | 8ca515ab9c84 |
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("Unimplemented option 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; }