Mercurial > hg > dmlib
view dmtext_bm.c @ 89:b10884934aca
Various improvements in bitmapped font support, and addition of legacy
TSFONT loading support (untested as of now).
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 02 Oct 2012 15:52:44 +0300 |
parents | 7d201aed1fd9 |
children | e1e308167991 |
line wrap: on
line source
/* * DMLib * -- Bitmap and TTF text & font support * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2012 Tecnic Software productions (TNSP) */ #include "dmtext.h" #include "dmresw.h" void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt) { const char *ptr = fmt; DMScaledBlitFunc blit = dmGetScaledBlitFunc(font->glyphs[0]->format, screen->format, mode); while (*ptr) { SDL_Surface *glyph; int pos; int ch = *ptr++; if (ch == '_') { xc += 4; continue; } else if (ch >= 'A' && ch <= 'Z') pos = ch - 'A' + 256 + 1; else if (ch >= 'a' && ch <= 'z') pos = ch - 'a' + 1; else if (ch >= '0' && ch <= '9') pos = ch - '0' + 48; else pos = ch; if (pos >= 0 && pos < font->nglyphs) { glyph = font->glyphs[pos]; blit(glyph, xc, yc, glyph->w, glyph->h, screen); xc += glyph->w; } else xc += font->width; } } void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, va_list ap) { char *tmp = dm_strdup_vprintf(fmt, ap); if (tmp != NULL) { dmDrawBMTextConst(screen, font, mode, xc, yc, tmp); dmFree(tmp); } } void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, int mode, int xc, int yc, const char *fmt, ...) { va_list ap; va_start(ap, fmt); dmDrawBMTextVA(screen, font, mode, xc, yc, fmt, ap); va_end(ap); } DMBitmapFont *dmNewBitmapFont(int nglyphs, int width, int height) { DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont)); if (font == NULL) return NULL; font->width = width; font->height = height; font->nglyphs = nglyphs; font->glyphs = dmCalloc(nglyphs, sizeof(SDL_Surface *)); if (font->glyphs == NULL) { dmFree(font); return NULL; } return font; } int dmFreeBitmapFont(DMBitmapFont *font) { int i; if (font == NULL) return DMERR_NULLPTR; for (i = 0; i < font->nglyphs; i++) { if (font->glyphs[i] != NULL) { SDL_FreeSurface(font->glyphs[i]); font->glyphs[i] = NULL; } } dmFree(font); return DMERR_OK; } 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; /* fprintf(stderr, "%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 dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont) { DMBitmapFont *font; char magic[8]; Uint16 version, nglyphs, maxglyph; int width, height; BOOL tsfont = FALSE; // Check magic and version dmf_read_str(res, (Uint8 *) &magic, 6); dmf_read_le16(res, &version); // Check if it is a legacy TSFONT file if (memcmp(magic, TSFONT_MAGIC, 6) == 0) { // Yep, we handle these a bit differently int encoding = dmfgetc(res); tsfont = TRUE; if (version > TSFONT_VERSION) return DMERR_VERSION; // There were only two encodings, 0 = none and 1 = RLE // of which RLE was never actually used ... derp. if (encoding != 0) return DMERR_NOT_SUPPORTED; } else { if (memcmp(magic, DMFONT_MAGIC, 6) != 0) return DMERR_INVALID; if (version > DMFONT_VERSION) return DMERR_VERSION; } // Check other data if (tsfont) { nglyphs = dmfgetc(res); maxglyph = 256; } else { dmf_read_le16(res, &nglyphs); dmf_read_le16(res, &maxglyph); } width = dmfgetc(res); height = dmfgetc(res); if (tsfont) { // TSFONT color assigns (boolean) .. we discard this. dmfgetc(res); } if (width < DMFONT_MIN_WIDTH || height < DMFONT_MIN_HEIGHT || width > DMFONT_MAX_WIDTH || height > DMFONT_MAX_HEIGHT || nglyphs > DMFONT_MAX_GLYPHS || maxglyph > DMFONT_MAX_GLYPHS || maxglyph < 1) return DMERR_INVALID_DATA; // Allocate font if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL) return DMERR_MALLOC; // Read glyph data, if any if (nglyphs > 0) { Uint32 i, BitsPerPixel, Rmask, Gmask, Bmask, Amask; if (tsfont) { BitsPerPixel = 8; Rmask = Gmask = Bmask = Amask = 0; } else { BitsPerPixel = dmfgetc(res); dmf_read_le32(res, &Rmask); dmf_read_le32(res, &Gmask); dmf_read_le32(res, &Bmask); dmf_read_le32(res, &Amask); } for (i = 0; i < nglyphs; i++) { int y; Uint16 index; Uint8 *pixels; SDL_Surface *glyph; if (tsfont) index = dmfgetc(res); else dmf_read_le16(res, &index); width = dmfgetc(res); height = dmfgetc(res); if (width < DMFONT_MIN_WIDTH || height < DMFONT_MIN_HEIGHT || width > DMFONT_MAX_WIDTH || height > DMFONT_MAX_HEIGHT || index > DMFONT_MAX_GLYPHS) return DMERR_INVALID_DATA; font->glyphs[index] = glyph = SDL_CreateRGBSurface( SDL_SWSURFACE, width, height, BitsPerPixel, Rmask, Gmask, Bmask, Amask); if (glyph == NULL) return DMERR_MALLOC; pixels = glyph->pixels; for (y = 0; y < glyph->h; y++) { if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w) return DMERR_FREAD; pixels += glyph->pitch; } } } return DMERR_OK; } int dmSaveBitmapFont(DMResource *res, DMBitmapFont *font) { int count, 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 (count = n = 0; n < font->nglyphs; n++) if (font->glyphs[n] != NULL) count++; // 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, count); // # of glyphs actually dmf_write_le16(res, font->nglyphs); // Max glyph # dmfputc(font->width, res); dmfputc(font->height, res); if (font->nglyphs > 0) { int i; SDL_Surface *glyph = font->glyphs[0]; // If there are actual glyphs stored, save thi 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) return DMERR_INVALID_DATA; // Each glyph has its table index and w/h stored dmf_write_le16(res, i); dmf_write_le16(res, glyph->w); dmf_write_le16(res, glyph->h); // 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; }