changeset 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 aa9fbdbcea70
children 1ab3fd8b9afc
files dmtext.h dmtext_bm.c
diffstat 2 files changed, 152 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/dmtext.h	Tue Oct 02 14:25:47 2012 +0300
+++ b/dmtext.h	Tue Oct 02 15:52:44 2012 +0300
@@ -22,19 +22,30 @@
  */
 #ifdef DM_GFX_BM_TEXT
 
-#define DMFONT_MAGIC    "DMFONT"
-#define DMFONT_VERSION  0x0100
+// DMFONT format constants
+#define DMFONT_MAGIC         "DMFONT"
+#define DMFONT_VERSION       0x0100
+
+#define DMFONT_MIN_WIDTH     3
+#define DMFONT_MIN_HEIGHT    3
+#define DMFONT_MAX_WIDTH     128
+#define DMFONT_MAX_HEIGHT    128
+#define DMFONT_MAX_GLYPHS    1024
+
+// Legacy TSFONT loading support
+#define TSFONT_MAGIC         "TSFONT"
+#define TSFONT_VERSION       0x0205
+
 
 typedef struct
 {
-    int width, height;
-
-    int nglyphs;
-    SDL_Surface **glyphs;
+    int width, height;     // Dimensions
+    int nglyphs;           // Size of glyphs array
+    SDL_Surface **glyphs;  // NOTE! Not all glyphs may be allocated
 } DMBitmapFont;
 
 
-DMBitmapFont *dmNewBitmapFont(int nglyphs);
+DMBitmapFont *dmNewBitmapFont(int nglyphs, int width, int height);
 int dmFreeBitmapFont(DMBitmapFont *font);
 int dmLoadBitmapFont(DMResource *res, DMBitmapFont **pfont);
 int dmSaveBitmapFont(DMResource *res, DMBitmapFont *font);
--- a/dmtext_bm.c	Tue Oct 02 14:25:47 2012 +0300
+++ b/dmtext_bm.c	Tue Oct 02 15:52:44 2012 +0300
@@ -69,15 +69,22 @@
 }
 
 
-DMBitmapFont *dmNewBitmapFont(int nglyphs)
+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;
 }
 
@@ -114,12 +121,9 @@
     xglyphs = image->w / width;
     yglyphs = image->h / height;
     
-    if ((font = dmNewBitmapFont(xglyphs * yglyphs)) == NULL)
+    if ((font = dmNewBitmapFont(xglyphs * yglyphs, width, height)) == NULL)
         return DMERR_MALLOC;
 
-    font->width = width;
-    font->height = height;
-
 /*
     fprintf(stderr, "%d x %d split as %d x %d blocks => %d x %d = %d glyphs\n",
         image->w, image->h,
@@ -164,56 +168,114 @@
 {
     DMBitmapFont *font;
     char magic[8];
-    Uint16 version;
-    Uint32 width, height, nglyphs;
+    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);
     
-    if (memcmp(magic, DMFONT_MAGIC, 6) != 0)
-        return DMERR_INVALID;
+    // 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 != DMFONT_VERSION)
-        return DMERR_VERSION;
+        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
-    dmf_read_le32(res, &width);
-    dmf_read_le32(res, &height);
-    dmf_read_le32(res, &nglyphs);
+    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 (width > 128 || height > 128 || nglyphs > 1024)
+    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(nglyphs)) == NULL)
+    if ((*pfont = font = dmNewBitmapFont(maxglyph, width, height)) == NULL)
         return DMERR_MALLOC;
 
-    font->width = width;
-    font->height = height;
-    font->nglyphs = nglyphs;
-
     // Read glyph data, if any
     if (nglyphs > 0)
     {
-        Uint32 i, BitsPerPixel, Rmask, Gmask, Bmask, Amask, pitch;
+        Uint32 i, BitsPerPixel, Rmask, Gmask, Bmask, Amask;
 
-        BitsPerPixel = dmfgetc(res);
-        dmf_read_le32(res, &Rmask);
-        dmf_read_le32(res, &Gmask);
-        dmf_read_le32(res, &Bmask);
-        dmf_read_le32(res, &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;
-            dmf_read_le32(res, &width);
-            dmf_read_le32(res, &height);
-            dmf_read_le32(res, &pitch);
+
+            if (tsfont)
+                index = dmfgetc(res);
+            else
+                dmf_read_le16(res, &index);
+
+            width = dmfgetc(res);
+            height = dmfgetc(res);
             
-            font->glyphs[i] = glyph = SDL_CreateRGBSurface(
+            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,
@@ -225,7 +287,7 @@
             pixels = glyph->pixels;
             for (y = 0; y < glyph->h; y++)
             {
-                if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != glyph->w)
+                if (dmfread(pixels, glyph->format->BytesPerPixel, glyph->w, res) != (size_t) glyph->w)
                     return DMERR_FREAD;
                 pixels += glyph->pitch;
             }
@@ -238,22 +300,37 @@
 
 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_le32(res, font->width);
-    dmf_write_le32(res, font->height);
-    dmf_write_le32(res, font->nglyphs);
+    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);
@@ -262,18 +339,30 @@
 
         for (i = 0; i < font->nglyphs; i++)
         {
-            int y;
             glyph = font->glyphs[i];
-            Uint8 *pixels = glyph->pixels;
+            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;
 
-            dmf_write_le32(res, glyph->w);
-            dmf_write_le32(res, glyph->h);
+                // 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);
 
-            for (y = 0; y < glyph->h; y++)
-            {
-                if (dmfwrite(pixels, glyph->format->BytesPerPixel, glyph->w, res) != glyph->w)
-                    return DMERR_FWRITE;
-                pixels += glyph->pitch;
+                // 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;
+                }
             }
         }
     }