Mercurial > hg > dmlib
view fontconv.c @ 510:43ea59887c69
Start work on making C64 formats encoding possible by changing DMDecodeOps
to DMEncDecOps and adding fields and op enums for custom encode functions, renaming,
etc. Split generic op sanity checking into a separate function in
preparation for its use in generic encoding function.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 19 Nov 2012 15:06:01 +0200 |
parents | d6c184800384 |
children | f87446a81887 |
line wrap: on
line source
/* * fontconv - Convert bitmap fonts * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include <stdio.h> #include <errno.h> #include "dmlib.h" #include "dmargs.h" #include "dmfile.h" #include "dmimage.h" #include "dmtext.h" #include "dmresw.h" enum { OFMT_DMFONT, OFMT_C }; char *optInFilename = NULL, *optOutFilename = NULL, *optOutName = NULL; int optOutFormat = OFMT_DMFONT, optSplitWidth = 8, optSplitHeight = 8; DMOptArg optList[] = { { 0, '?', "help", "Show this help", OPT_NONE }, { 1, 'v', "verbose", "Be more verbose", OPT_NONE }, { 2, 'o', "output", "Output file (default stdout)", OPT_ARGREQ }, { 3, 's', "size", "Set glyph dimensions (-s WxH) for image->font conversion", OPT_ARGREQ }, { 4, 'C', "csource", "DMFONT as C source", OPT_NONE }, { 5, 'n', "name", "Variable name prefix for C output", OPT_ARGREQ }, }; 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]"); dmArgsPrintHelp(stdout, optList, optListN); exit(0); break; case 1: dmVerbosity++; break; case 2: optOutFilename = optArg; break; case 3: { int w, h; if (sscanf(optArg, "%dx%d", &w, &h) != 2) { dmError("Invalid argument for -s option, '%s'.\n", optArg); return FALSE; } if (w < DMFONT_MIN_WIDTH || w > DMFONT_MAX_WIDTH || h < DMFONT_MIN_HEIGHT || h > DMFONT_MAX_HEIGHT) { dmError("Invalid 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 = w; optSplitHeight = h; } break; case 4: optOutFormat = OFMT_C; break; case 5: optOutName = optArg; break; default: dmError("Unknown argument '%s'.\n", currArg); return FALSE; } return TRUE; } BOOL argHandleFile(char *currArg) { if (!optInFilename) optInFilename = currArg; else { dmError("Too many filename arguments, '%s'\n", currArg); return FALSE; } return TRUE; } static int dm_csrc_ferror(DMResource * f) { return f->error; } static int dm_csrc_fputc(int v, DMResource * f) { if (f->dataSize++ >= 16) { fprintf(f->fh, "\n"); f->dataSize = 0; } fprintf(f->fh, "%3d,", v); f->error = dmGetErrno(); return 1; } static size_t dm_csrc_fwrite(void *ptr, size_t size, size_t nmemb, DMResource * f) { size_t n; Uint8 *p = (Uint8 *) ptr; for (n = 0; n < size * nmemb; n++, p++) { if (f->dataSize++ >= 16) { fprintf(f->fh, "\n"); f->dataSize = 0; } fprintf(f->fh, "%3d,", *p); } f->error = dmGetErrno(); return nmemb; } static int dm_csrc_fopen(DMResource * f) { fprintf(f->fh, "const Uint8 %s[] = {\n", (char *) f->data ); return DMERR_OK; } static void dm_csrc_fclose(DMResource * f) { if (f->fh != NULL) { fprintf(f->fh, "\n};\n" "const unsigned int %s_size = sizeof(%s) / sizeof(%s[0]);\n", (char *)f->data, (char *)f->data, (char *)f->data ); fclose(f->fh); f->fh = NULL; } } DMResourceOps dfCSourceFileOps = { dm_csrc_ferror, NULL, NULL, NULL, NULL, NULL, dm_csrc_fputc, NULL, dm_csrc_fwrite, dm_csrc_fopen, dm_csrc_fclose, NULL }; DMResource * dmf_create_csrc(const char *filename, const char *name) { DMResource *handle = dmres_new(NULL, filename, 0, 0); if (handle == NULL) return NULL; handle->fops = &dfCSourceFileOps; handle->fh = fopen(filename, "w"); handle->error = dmGetErrno(); handle->data = (Uint8 *) dm_strdup(name); if (handle->fh != NULL) { handle->fops->fopen(handle); dmres_ref(handle); return handle; } else { dmres_free(handle); return NULL; } } DMResource * dmf_create_csrc_stream(FILE *fh, const char *name) { DMResource *handle = dmres_new(NULL, NULL, 0, 0); if (handle == NULL) return NULL; handle->fops = &dfCSourceFileOps; handle->fh = fh; handle->error = dmGetErrno(); handle->data = (Uint8 *) dm_strdup(name); handle->fops->fopen(handle); dmres_ref(handle); return handle; } int dmCreateBitmapFontFromImage(SDL_Surface *image, int width, int height, DMBitmapFont **pfont) { int nglyph, xc, yc, xglyphs, yglyphs; DMBitmapFont *font; if (image->w < width || width < 4 || image->h < height || height < 4) return DMERR_INVALID_ARGS; xglyphs = image->w / width; yglyphs = image->h / height; if ((font = dmNewBitmapFont(xglyphs * yglyphs, width, height)) == NULL) return DMERR_MALLOC; dmMsg(1, "%d x %d split as %d x %d blocks => %d x %d = %d glyphs.\n", image->w, image->h, width, height, xglyphs, yglyphs, xglyphs * yglyphs); nglyph = 0; for (yc = 0; yc < yglyphs; yc++) for (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); if (glyph == NULL) { dmFreeBitmapFont(font); return DMERR_MALLOC; } SDL_Rect r; r.x = xc * width; r.y = yc * height; r.w = width; r.h = height; SDL_BlitSurface(image, &r, glyph, NULL); font->glyphs[nglyph++] = glyph; } *pfont = font; return DMERR_OK; } int dmSaveBitmapFont(DMResource *res, DMBitmapFont *font) { int maxglyph, nglyphs, n; if (font == NULL) return DMERR_NULLPTR; if (font->nglyphs > 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++; } } // Write the DMFONT header if (!dmf_write_str(res, (Uint8 *) DMFONT_MAGIC, 6)) 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); if (nglyphs > 0) { int i; SDL_Surface *glyph = font->glyphs[maxglyph]; // If there are actual glyphs stored, save this 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 (i = 0; i < font->nglyphs; i++) { glyph = font->glyphs[i]; if (glyph != NULL) { 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; // Each glyph has its table index and w/h stored dmf_write_le16(res, i); dmfputc(glyph->w, res); dmfputc(glyph->h, res); // Write the pixel data for (y = 0; y < glyph->h; y++) { if (dmfwrite(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w) return DMERR_FWRITE; pixels += glyph->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.2", NULL, NULL); dmVerbosity = 1; // Parse arguments if (!dmArgsProcess(argc, argv, optList, optListN, argHandleOpt, argHandleFile, TRUE)) exit(1); // Check arguments if (!optInFilename) { dmError("Input or output file not specified!\n"); return 1; } #ifdef DM_GFX_TTF_TEXT if (TTF_Init() < 0) { dmError("Could not initialize FreeType/TTF: %s\n", SDL_GetError()); goto error_exit; } initTTF = TRUE; #endif // Open the source file if ((inFile = dmf_create_stdio(optInFilename, "rb")) == NULL) { dmError("Error opening input file '%s', %d: %s\n", optInFilename, errno, strerror(errno)); return 1; } if ((res = dmLoadBitmapFont(inFile, &font)) == DMERR_OK) { dmMsg(1, "Input is a TSFONT/DMFONT font file.\n"); } #ifdef DM_GFX_TTF_TEXT else if ((ttf = TTF_OpenFont(optInFilename, optSplitWidth)) != NULL) { int i; SDL_Color col = { 255, 255, 255, 100 }; dmMsg(1, "Input is a TTF TrueType font, rendering at %d x %d.\n", optSplitWidth, optSplitHeight); TTF_SetFontStyle(ttf, TTF_STYLE_NORMAL); if ((font = dmNewBitmapFont(256, optSplitWidth - 1, optSplitHeight+4)) == NULL) { goto error_exit; } for (i = 0; i < 255; i++) { char str[2]; str[0] = i; str[1] = 0; font->glyphs[i] = TTF_RenderText_Blended(ttf, str, col); } } #endif else { dmfseek(inFile, 0L, SEEK_SET); if ((fontbmap = dmLoadImage(inFile)) == NULL) { dmError("Could not load image file '%s'.\n", optInFilename); goto error_exit; } 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) { dmError("Could not create a font from image, %d: %s\n", res, dmErrorStr(res)); goto error_exit; } } if (font == NULL) { dmError("No font loaded.\n"); goto error_exit; } if (optOutFormat == OFMT_DMFONT) { dmMsg(1, "Outputting a DMFONT format bitmap font.\n"); if (optOutFilename == NULL) outFile = dmf_create_stdio_stream(stdout); else outFile = dmf_create_stdio(optOutFilename, "wb"); } else if (optOutFormat == OFMT_C) { if (optOutName == NULL) { dmError("C source output selected, but variable name not specified.\n"); goto error_exit; } dmMsg(1, "Outputting a DMFONT format bitmap font as C source file.\n"); if (optOutFilename == NULL) outFile = dmf_create_csrc_stream(stdout, optOutName); else outFile = dmf_create_csrc(optOutFilename, optOutName); } if (outFile == NULL) { dmError("Error creating file '%s', %d: %s\n", optInFilename, errno, strerror(errno)); goto error_exit; } res = dmSaveBitmapFont(outFile, font); dmf_close(outFile); if (res != DMERR_OK) { dmError("Error saving font, %d: %s\n", res, dmErrorStr(res)); } error_exit: #ifdef DM_GFX_TTF_TEXT if (initTTF) TTF_Quit(); #endif dmf_close(inFile); dmFreeBitmapFont(font); SDL_FreeSurface(fontbmap); return 0; }