Browse Source

BM Font Extender (#3536)

* loadbmfont

* bmfontextender

* bm font

* Modify LoadBM Font

* Delete loadbmfontex.diff

* REVIEWED: `LoadBMFont()` PR

* Update rtext.c

* Update rtext.c

---------

Co-authored-by: Dongkun Lee <[email protected]>
Co-authored-by: Ray <[email protected]>
Dongkun Lee 1 year ago
parent
commit
9de79861ea
1 changed files with 67 additions and 49 deletions
  1. 67 49
      src/rtext.c

+ 67 - 49
src/rtext.c

@@ -2051,7 +2051,8 @@ static int GetLine(const char *origin, char *buffer, int maxLength)
 // REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
 static Font LoadBMFont(const char *fileName)
 {
-    #define MAX_BUFFER_SIZE     256
+    #define MAX_BUFFER_SIZE       256
+    #define MAX_FONT_IMAGE_PAGES    8
 
     Font font = { 0 };
 
@@ -2063,7 +2064,8 @@ static Font LoadBMFont(const char *fileName)
 
     int imWidth = 0;
     int imHeight = 0;
-    char imFileName[129] = { 0 };
+    int pageCount = 1;
+    char imFileName[MAX_FONT_IMAGE_PAGES][129] = { 0 };
 
     int base = 0;       // Useless data
     int readBytes = 0;  // Data bytes read
@@ -2082,17 +2084,26 @@ static Font LoadBMFont(const char *fileName)
     // Read line data
     readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
     searchPoint = strstr(buffer, "lineHeight");
-    readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
+    readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i pages=%i", &fontSize, &base, &imWidth, &imHeight, &pageCount);
     fileTextPtr += (readBytes + 1);
     
     if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
+    
+    if (pageCount > MAX_FONT_IMAGE_PAGES)
+    {
+        TRACELOG(LOG_WARNING, "FONT: [%s] Font defines more pages than supported: %i/%i", fileName, pageCount, MAX_FONT_IMAGE_PAGES);
+        pageCount = MAX_FONT_IMAGE_PAGES;
+    }
 
-    readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
-    searchPoint = strstr(buffer, "file");
-    readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
-    fileTextPtr += (readBytes + 1);
+    for (int i = 0; i < pageCount; i++)
+    {
+        readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+        searchPoint = strstr(buffer, "file");
+        readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName[i]);
+        fileTextPtr += (readBytes + 1);
 
-    if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
+        if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
+    }
 
     readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
     searchPoint = strstr(buffer, "count");
@@ -2101,50 +2112,56 @@ static Font LoadBMFont(const char *fileName)
 
     if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
 
-    // Compose correct path using route of .fnt file (fileName) and imFileName
-    char *imPath = NULL;
-    char *lastSlash = NULL;
+    // Load all required images for further compose
+    Image *imFonts = (Image *)RL_CALLOC(pageCount, sizeof(Image)); // Font atlases, multiple images
 
-    lastSlash = strrchr(fileName, '/');
-    if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
-
-    if (lastSlash != NULL)
+    for (int i = 0; i < pageCount; i++)
     {
-        // NOTE: We need some extra space to avoid memory corruption on next allocations!
-        imPath = (char *)RL_CALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(imFileName) + 4, 1);
-        memcpy(imPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
-        memcpy(imPath + TextLength(fileName) - TextLength(lastSlash) + 1, imFileName, TextLength(imFileName));
-    }
-    else imPath = imFileName;
+        imFonts[i] = LoadImage(TextFormat("%s/%s", GetDirectoryPath(fileName), imFileName[i]));
 
-    TRACELOGD("    > Image loading path: %s", imPath);
+        if (imFonts[i].format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
+        {
+            // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
+            Image imFontAlpha = {
+                .data = RL_CALLOC(imFonts[i].width*imFonts[i].height, 2),
+                .width = imFonts[i].width,
+                .height = imFonts[i].height,
+                .mipmaps = 1,
+                .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
+            };
+
+            for (int p = 0, pi = 0; p < (imFonts[i].width*imFonts[i].height*2); p += 2, pi++)
+            {
+                ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
+                ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFonts[i].data)[pi];
+            }
+
+            UnloadImage(imFonts[i]);
+            imFonts[i] = imFontAlpha;
+        }
+    }
 
-    Image imFont = LoadImage(imPath);
+    Image fullFont = imFonts[0];
+    for (int i = 1; i < pageCount; i++) UnloadImage(imFonts[i]);
 
-    if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
+    // If multiple atlas, then merge atlas
+    // NOTE: WARNING: This process could be really slow!
+    if (pageCount > 1)
     {
-        // Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
-        Image imFontAlpha = {
-            .data = RL_CALLOC(imFont.width*imFont.height, 2),
-            .width = imFont.width,
-            .height = imFont.height,
-            .mipmaps = 1,
-            .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA
-        };
+        // Resize font atlas to draw additional images 
+        ImageResizeCanvas(&fullFont, imWidth, imHeight*pageCount, 0, 0, BLACK); 
 
-        for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
+        for (int i = 1; i < pageCount; i++)
         {
-            ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
-            ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
+            Rectangle srcRec = { 0.0f, 0.0f, (float)imWidth, (float)imHeight };
+            Rectangle destRec = { 0.0f, (float)imHeight*(float)i, (float)imWidth, (float)imHeight };
+            ImageDraw(&fullFont, imFonts[i], srcRec, destRec, WHITE);              
         }
-
-        UnloadImage(imFont);
-        imFont = imFontAlpha;
     }
+    
+    RL_FREE(imFonts);
 
-    font.texture = LoadTextureFromImage(imFont);
-
-    if (lastSlash != NULL) RL_FREE(imPath);
+    font.texture = LoadTextureFromImage(fullFont);
 
     // Fill font characters info data
     font.baseSize = fontSize;
@@ -2153,19 +2170,19 @@ static Font LoadBMFont(const char *fileName)
     font.glyphs = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
     font.recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
 
-    int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
+    int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX, pageID;
 
     for (int i = 0; i < glyphCount; i++)
     {
         readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
-        readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
-                       &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
+        readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i page=%i",
+                       &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX, &pageID);
         fileTextPtr += (readBytes + 1);
         
-        if (readVars == 8)  // Make sure all char data has been properly read
+        if (readVars == 9)  // Make sure all char data has been properly read
         {
             // Get character rectangle in the font atlas texture
-            font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
+            font.recs[i] = (Rectangle){ (float)charX, (float)charY + (float)imHeight*pageID, (float)charWidth, (float)charHeight };
 
             // Save data properly in sprite font
             font.glyphs[i].value = charId;
@@ -2173,13 +2190,13 @@ static Font LoadBMFont(const char *fileName)
             font.glyphs[i].offsetY = charOffsetY;
             font.glyphs[i].advanceX = charAdvanceX;
 
-            // Fill character image data from imFont data
-            font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]);
+            // Fill character image data from full font data
+            font.glyphs[i].image = ImageFromImage(fullFont, font.recs[i]);
         }
         else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
     }
 
-    UnloadImage(imFont);
+    UnloadImage(fullFont);
     UnloadFileText(fileText);
 
     if (font.texture.id == 0)
@@ -2192,6 +2209,7 @@ static Font LoadBMFont(const char *fileName)
 
     return font;
 }
+
 #endif
 
 #endif      // SUPPORT_MODULE_RTEXT