Mercurial > hg > dmlib
view tools/fontconv.c @ 2576:812b16ee49db
I had been living under apparent false impression that "realfft.c"
on which the FFT implementation in DMLIB was basically copied from
was released in public domain at some point, but it could very well
be that it never was. Correct license is (or seems to be) GNU GPL.
Thus I removing the code from DMLIB, and profusely apologize to the
author, Philip Van Baren.
It was never my intention to distribute code based on his original
work under a more liberal license than originally intended.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 11 Mar 2022 16:32:50 +0200 |
parents | 92b93a12c014 |
children | 9807ae37ad69 |
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, 0, "license" , "Print out this program's license agreement", OPT_NONE }, { 2, 'v', "verbose" , "Be more verbose", OPT_NONE }, { 10, 's', "size" , "Set glyph dimensions (-s W:H or -s N) for image->font conversion", OPT_ARGREQ }, #ifdef DM_GFX_TTF_TEXT { 12, 'c', "color" , "TTF font rendering color (def: 0xFFFFFF)", OPT_ARGREQ }, { 14, 'b', "bpp" , "Render font in 8 or 32 bits per pixel (default 32)", OPT_ARGREQ }, #endif }; const int optListN = sizeof(optList) / sizeof(optList[0]); void argShowHelp() { dmPrintBanner(stdout, dmProgName, "[options] <sourcefile.(ttf|fnt|dmf|png)> <outputfile.dmf>"); dmArgsPrintHelp(stdout, optList, optListN, 0, 80 - 2); 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"); } BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { switch (optN) { case 0: argShowHelp(); exit(0); break; case 1: dmPrintLicense(stdout); exit(0); break; case 2: dmVerbosity++; break; case 10: { 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 12: { 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 14: 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 = (Uint8 *) 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 = DMERR_OK; #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)) goto out; // Check arguments if (optInFilename == NULL || optOutFilename == NULL) { argShowHelp(); res = dmError(DMERR_INVALID_ARGS, "No input or output file specified!\n"); goto out; } #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': %s\n", optInFilename, dmErrorStr(res)); goto out; } 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)); goto out; } out: // Cleanup #ifdef DM_GFX_TTF_TEXT if (initTTF) TTF_Quit(); #endif dmf_close(inFile); dmFreeBitmapFont(font); if (fontbmap != NULL) SDL_FreeSurface(fontbmap); return res; }