diff src/dmtext_bm.c @ 1957:ef08af6887b7

Revamp the bitmap font system to use single SDL_Surface for the font graphics instead of N surfaces for each separate glyph.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 30 Jun 2018 05:24:12 +0300
parents ebcb7713bb6a
children 934cc71c97eb
line wrap: on
line diff
--- a/src/dmtext_bm.c	Sat Jun 30 05:22:15 2018 +0300
+++ b/src/dmtext_bm.c	Sat Jun 30 05:24:12 2018 +0300
@@ -7,26 +7,33 @@
 #include "dmtext.h"
 
 
-void dmDrawBMTextConst(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt)
+void dmDrawBMTextConst(SDL_Surface *screen, const DMBitmapFont *font,
+    const BOOL condensed, const int mode, int xc, int yc, const char *fmt)
 {
     const char *ptr = fmt;
-    DMUnscaledBlitFunc blit = NULL;
+    DMUnscaledBlitFunc blit = dmGetUnscaledBlitFunc(font->glyphs->format, screen->format, mode);
+    SDL_Surface surf;
+    Uint8 *orig = font->glyphs->pixels;
+
+    memcpy(&surf, font->glyphs, sizeof(SDL_Surface));
 
     while (*ptr)
     {
-        int ch = *ptr++;
-        SDL_Surface *glyph;
-
-        if (ch >= 0 && ch < font->nglyphs && (glyph = font->glyphs[ch]) != NULL)
+        unsigned char ch = *ptr++;
+        if (ch < font->maxglyph)
         {
-            if (blit == NULL)
-                blit = dmGetUnscaledBlitFunc(glyph->format, screen->format, mode);
+            DMBitmapGlyph *glyph = &font->glyphMap[ch];
+            if (glyph->index >= 0)
+            {
+                surf.pixels = orig + glyph->index * font->gsize;
+                surf.w = glyph->width;
+                surf.h = glyph->height;
 
-            if (blit != NULL)
-            {
-                blit(glyph, xc, yc, screen);
-                xc += condensed ? glyph->w : font->width;
+                blit(&surf, xc, yc, screen);
+                xc += condensed ? glyph->width : font->width;
             }
+            else
+                xc += font->width;
         }
         else
             xc += font->width;
@@ -34,7 +41,9 @@
 }
 
 
-void dmDrawBMTextVA(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, va_list ap)
+void dmDrawBMTextVA(SDL_Surface *screen, const DMBitmapFont *font,
+    const BOOL condensed, const int mode, const int xc, const int yc,
+    const char *fmt, va_list ap)
 {
     char tmp[512];
     vsnprintf(tmp, sizeof(tmp), fmt, ap);
@@ -43,7 +52,9 @@
 }
 
 
-void dmDrawBMText(SDL_Surface *screen, DMBitmapFont *font, BOOL condensed, int mode, int xc, int yc, const char *fmt, ...)
+void dmDrawBMText(SDL_Surface *screen, const DMBitmapFont *font,
+    const BOOL condensed, const int mode, const int xc, const int yc,
+    const char *fmt, ...)
 {
     va_list ap;
 
@@ -53,42 +64,53 @@
 }
 
 
-DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int width, const int height)
+DMBitmapFont *dmNewBitmapFont(const int nglyphs, const int maxglyph,
+    const int width, const int height, const int bpp)
 {
     DMBitmapFont *font = dmMalloc0(sizeof(DMBitmapFont));
-    if (font == NULL)
+    if (font == NULL || (bpp != 8 && bpp != 32))
         return NULL;
 
     font->width = width;
     font->height = height;
     font->nglyphs = nglyphs;
-    font->glyphs = dmCalloc(nglyphs, sizeof(SDL_Surface *));
-    if (font->glyphs == NULL)
+    font->maxglyph = maxglyph;
+
+    if ((font->glyphMap = dmCalloc(font->maxglyph, sizeof(DMBitmapGlyph))) == NULL)
+        goto error;
+
+    if ((font->glyphs = SDL_CreateRGBSurfaceWithFormat(
+        0, width, height * (nglyphs + 1), bpp,
+        bpp == 8 ? SDL_PIXELFORMAT_INDEX8 : SDL_PIXELFORMAT_RGBA32)) == NULL)
+        goto error;
+
+    font->gsize = font->height * font->glyphs->pitch;
+
+    for (int i = 0; i < font->maxglyph; i++)
     {
-        dmFree(font);
-        return NULL;
+        DMBitmapGlyph *glyph = &font->glyphMap[i];
+        glyph->width  = width;
+        glyph->height = height;
+        glyph->index  = -1;     // means that this index is empty/unused
     }
 
     return font;
+
+error:
+    dmFreeBitmapFont(font);
+    return NULL;
 }
 
 
 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;
-        }
-    }
+    if (font->glyphs != NULL)
+        SDL_FreeSurface(font->glyphs);
 
+    dmFree(font->glyphMap);
     dmFree(font);
     return DMERR_OK;
 }
@@ -100,22 +122,13 @@
  */
 int dmSetBitmapFontPalette(DMBitmapFont *font, const SDL_Color *pal, const int start, const int size)
 {
-    int i;
-
     if (font == NULL)
         return DMERR_NULLPTR;
 
     if (start < 0 || size < 1)
         return DMERR_INVALID_ARGS;
 
-    for (i = 0; i < font->nglyphs; i++)
-    {
-        SDL_Surface *glyph = font->glyphs[i];
-        if (glyph != NULL)
-        {
-            SDL_SetPaletteColors(glyph->format->palette, pal, start, size);
-        }
-    }
+    SDL_SetPaletteColors(font->glyphs->format->palette, pal, start, size);
 
     return DMERR_OK;
 }
@@ -123,32 +136,34 @@
 
 //#define FN_DEBUG
 
-int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont)
+
+int dmLoadBitmapFont(DMResource *fp, DMBitmapFont **pfont)
 {
     DMBitmapFont *font;
     char magic[8];
     Uint16 version, nglyphs, maxglyph;
-    int width, height;
+    Uint8 width, height, bpp;
     BOOL tsfont = FALSE;
 
     // Check magic and version
-    if (!dmf_read_str(res, (Uint8 *) &magic, 6) ||
-        !dmf_read_le16(res, &version))
+    if (!dmf_read_str(fp, (Uint8 *) &magic, 6) ||
+        !dmf_read_le16(fp, &version))
         return DMERR_FREAD;
 
     // 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);
+        int encoding = dmfgetc(fp);
         tsfont = TRUE;
 
-        if (version > TSFONT_VERSION)
-            return DMERR_VERSION;
+#ifdef FN_DEBUG
+        fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n",
+            version >> 8, version & 0xff, version, encoding);
+#endif
 
-#ifdef FN_DEBUG
-        fprintf(stderr, "TSFONT v%d.%d (0x%04x), encoding=%d\n", version >> 8, version & 0xff, version, encoding);
-#endif
+        if (version > TSFONT_VERSION || version < 0x0200)
+            return DMERR_VERSION;
 
         // There were only two encodings, 0 = none and 1 = RLE
         // of which RLE was never actually used ... derp.
@@ -156,140 +171,138 @@
             return DMERR_NOT_SUPPORTED;
     }
     else
+    if (memcmp(magic, DMFONT_MAGIC, 6) == 0)
     {
-        if (memcmp(magic, DMFONT_MAGIC, 6) != 0)
-            return DMERR_INVALID;
+#ifdef FN_DEBUG
+        fprintf(stderr, "DMFONT v%d.%d (0x%04x)\n",
+            version >> 8, version & 0xff, version);
+#endif
 
         if (version > DMFONT_VERSION)
             return DMERR_VERSION;
     }
+    else
+        return DMERR_INVALID;
 
     // Read other header data
     if (tsfont)
     {
-        // TSFONT only has number of glyphs stored in the file
-        nglyphs = dmfgetc(res);
-
-        // Maximum glyph number
-        maxglyph = 256;
-    }
-    else
-    {
-        dmf_read_le16(res, &nglyphs);
-        dmf_read_le16(res, &maxglyph);
-    }
+        // TSFONT has number of glyphs and dimensions
+        Uint8 tmp, unused;
+        if (!dmf_read_byte(fp, &tmp) ||
+            !dmf_read_byte(fp, &width) ||
+            !dmf_read_byte(fp, &height) ||
+            !dmf_read_byte(fp, &unused))
+            return DMERR_FREAD;
 
-    width = dmfgetc(res);
-    height = dmfgetc(res);
-
-#ifdef FN_DEBUG
-    fprintf(stderr, "nglyphs=%d (0x%02x), maxglyph=%d (0x%02x) width=%d, height=%d\n",
-        nglyphs, nglyphs, maxglyph, maxglyph, width, height);
-#endif
-
-    if (tsfont)
-    {
-        // TSFONT color assignments (boolean) .. we discard this.
-        dmfgetc(res);
+        nglyphs = tmp;
+        maxglyph = 256;
+        bpp = 8;
 
         // Very old TSFONTs have some extra data that is not used
         // .. can't actually even remember what it was for.
         if (version == 0x0200)
         {
-            int i;
-            for (i = 0; i < 32; i++)
-                dmfgetc(res);
+            for (int i = 0; i < 32; i++)
+            {
+                if (!dmf_read_byte(fp, &unused))
+                    return DMERR_FREAD;
+            }
         }
     }
+    else
+    {
+        // DMFONT has Uint16 values for nglyphs and maxglyph, plus BPP
+        if (!dmf_read_le16(fp, &nglyphs) ||
+            !dmf_read_le16(fp, &maxglyph) ||
+            !dmf_read_byte(fp, &width) ||
+            !dmf_read_byte(fp, &height) ||
+            !dmf_read_byte(fp, &bpp))
+            return DMERR_FREAD;
+    }
+
+#ifdef FN_DEBUG
+    fprintf(stderr, "nglyphs=%d, maxglyph=%d, width=%d, height=%d, bpp=%d\n",
+        nglyphs, maxglyph, width, height, bpp);
+#endif
 
     if (width < DMFONT_MIN_WIDTH ||
         height < DMFONT_MIN_HEIGHT ||
         width > DMFONT_MAX_WIDTH ||
         height > DMFONT_MAX_HEIGHT ||
         nglyphs > DMFONT_MAX_GLYPHS ||
+        nglyphs > maxglyph ||
         maxglyph > DMFONT_MAX_GLYPHS ||
         maxglyph < 1)
         return DMERR_INVALID_DATA;
 
+    if (bpp != 8 && bpp != 32)
+        return DMERR_NOT_SUPPORTED;
+
     // Allocate font
-    if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL)
+    if ((*pfont = font = dmNewBitmapFont(nglyphs, maxglyph, width, height, bpp)) == NULL)
         return DMERR_MALLOC;
 
-    // Read glyph data, if any
-    if (nglyphs > 0)
+    // Setup palette for 8bpp fonts
+    if (bpp == 8)
     {
-        int n, i;
-        Uint32 BitsPerPixel, Rmask, Gmask, Bmask, Amask;
         SDL_Color pal[DMFONT_NPALETTE];
-
-        // Setup palette for 8bpp fonts
-        for (n = 0; n < DMFONT_NPALETTE; n++)
+        for (int n = 0; n < DMFONT_NPALETTE; n++)
         {
-            pal[n].r = pal[n].g = pal[n].b = 0;
+            pal[n].r = pal[n].g = pal[n].b = n > 0 ? 255 : 0;
             pal[n].a = n > 0 ? 255 : 0;
         }
+        SDL_SetPaletteColors(font->glyphs->format->palette, pal, 0, DMFONT_NPALETTE);
+    }
 
-        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);
-        }
+    // Read glyph data, if any
+    for (int i = 0; i < nglyphs; i++)
+    {
+        DMBitmapGlyph *glyph;
+        Uint8 gwidth, gheight;
+        Uint16 gindex;
+        Uint8 *pixels;
 
-        for (i = 0; i < nglyphs; i++)
-        {
-            int y;
-            Uint16 index;
-            Uint8 *pixels;
-            SDL_Surface *glyph;
+        // TSFONT format has only byte sized index
+        if (tsfont)
+            gindex = dmfgetc(fp);
+        else
+            dmf_read_le16(fp, &gindex);
 
-            // TSFONT format has only byte sized index
-            if (tsfont)
-                index = dmfgetc(res);
-            else
-                dmf_read_le16(res, &index);
-
-            // Read dimensions
-            width = dmfgetc(res);
-            height = dmfgetc(res);
+        // Read dimensions
+        if (!dmf_read_byte(fp, &gwidth) ||
+            !dmf_read_byte(fp, &gheight))
+            return DMERR_FREAD;
 
 #ifdef FN_DEBUG
-            fprintf(stderr, "#%d @ %d - w=%d, h=%d\n", i, index, width, height);
+        fprintf(stderr, "#%d @ %d - %d x %d\n", i, gindex, gwidth, gheight);
 #endif
 
-            if (width < DMFONT_MIN_WIDTH ||
-                height < DMFONT_MIN_HEIGHT ||
-                width > DMFONT_MAX_WIDTH ||
-                height > DMFONT_MAX_HEIGHT ||
-                index >= maxglyph)
-                return DMERR_INVALID_DATA;
-
-            // Allocate bitmap
-            font->glyphs[index] = glyph = SDL_CreateRGBSurface(
-                SDL_SWSURFACE, width, height,
-                BitsPerPixel, Rmask, Gmask, Bmask, Amask);
+        // Check the glyph data
+        if (gwidth < DMFONT_MIN_WIDTH ||
+            gheight < DMFONT_MIN_HEIGHT ||
+            gwidth > DMFONT_MAX_WIDTH ||
+            gheight > DMFONT_MAX_HEIGHT ||
+            gwidth > width ||
+            gheight > height ||
+            gindex >= maxglyph)
+            return DMERR_INVALID_DATA;
 
-            if (glyph == NULL)
-                return DMERR_MALLOC;
-
-            if (BitsPerPixel == 8)
-                SDL_SetPaletteColors(glyph->format->palette, pal, 0, DMFONT_NPALETTE);
+        // Set glyph data
+        glyph = &font->glyphMap[gindex];
+        glyph->width  = gwidth;
+        glyph->height = gheight;
+        glyph->index  = i;
 
-            // Read pixel data
-            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;
-            }
+        // Read pixel data
+        pixels = font->glyphs->pixels + (i * font->gsize);
+        for (int y = 0; y < glyph->height; y++)
+        {
+            if (dmfread(pixels, font->glyphs->format->BytesPerPixel,
+                glyph->width, fp) != (size_t) glyph->width)
+                return DMERR_FREAD;
+
+            pixels += font->glyphs->pitch;
         }
     }