summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanley Fuller <[email protected]>2024-02-04 05:28:12 +1100
committerGitHub <[email protected]>2024-02-03 19:28:12 +0100
commit0932cd3059a181f637cd80c09534d609f1ea3ed1 (patch)
treef3051a28e0e4822c6ed480ff855ecce2c9a88c24
parent29ff658d9223068378269e4a705811f085fafdf4 (diff)
downloadraylib-0932cd3059a181f637cd80c09534d609f1ea3ed1.tar.gz
raylib-0932cd3059a181f637cd80c09534d609f1ea3ed1.zip
[rtext] Add BDF font support (#3735)
* Add BDF font support * Include font ascent in glyph y-offset when loading BDF font
-rw-r--r--src/config.h1
-rw-r--r--src/rtext.c360
2 files changed, 336 insertions, 25 deletions
diff --git a/src/config.h b/src/config.h
index 410e9067..34d2e964 100644
--- a/src/config.h
+++ b/src/config.h
@@ -181,6 +181,7 @@
// Selected desired font fileformats to be supported for loading
#define SUPPORT_FILEFORMAT_FNT 1
#define SUPPORT_FILEFORMAT_TTF 1
+#define SUPPORT_FILEFORMAT_BDF 1
// Support text management functions
// If not defined, still some functions are supported: TextLength(), TextFormat()
diff --git a/src/rtext.c b/src/rtext.c
index 22c2e5e4..0cd90a59 100644
--- a/src/rtext.c
+++ b/src/rtext.c
@@ -12,6 +12,7 @@
*
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
+* #define SUPPORT_FILEFORMAT_BDF
* Selected desired fileformats to be supported for loading. Some of those formats are
* supported by default, to remove support, just comment unrequired #define in this module
*
@@ -70,14 +71,27 @@
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STB_RECT_PACK_IMPLEMENTATION
- #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging
+ #include "external/stb_rect_pack.h" // Required for: ttf/bdf font rectangles packaging
+
+ #include <math.h> // Required for: ttf/bdf font rectangles packaging
+
+ #if defined(__GNUC__) // GCC and Clang
+ #pragma GCC diagnostic pop
+ #endif
+#endif
+
+#if defined(SUPPORT_FILEFORMAT_TTF)
+ #if defined(__GNUC__) // GCC and Clang
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Wunused-function"
+ #endif
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
@@ -127,6 +141,9 @@ static Font defaultFont = { 0 };
#if defined(SUPPORT_FILEFORMAT_FNT)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
+#if defined(SUPPORT_FILEFORMAT_BDF)
+static GlyphInfo *LoadBDFFontData(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int* outFontSize);
+#endif
static int textLineSpacing = 15; // Text vertical line spacing in pixels
#if defined(SUPPORT_DEFAULT_FONT)
@@ -335,6 +352,10 @@ Font LoadFont(const char *fileName)
if (IsFileExtension(fileName, ".fnt")) font = LoadBMFont(fileName);
else
#endif
+#if defined(SUPPORT_FILEFORMAT_BDF)
+ if (IsFileExtension(fileName, ".bdf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
+ else
+#endif
{
Image image = LoadImage(fileName);
if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
@@ -355,7 +376,7 @@ Font LoadFont(const char *fileName)
return font;
}
-// Load Font from TTF font file with generation parameters
+// Load Font from TTF or BDF font file with generation parameters
// NOTE: You can pass an array with desired characters, those characters should be available in the font
// if array is NULL, default char set is selected 32..126
Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount)
@@ -511,35 +532,49 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
char fileExtLower[16] = { 0 };
strcpy(fileExtLower, TextToLower(fileType));
-#if defined(SUPPORT_FILEFORMAT_TTF)
+ font.baseSize = fontSize;
+ font.glyphCount = (codepointCount > 0)? codepointCount : 95;
+ font.glyphPadding = 0;
+
+#if defined(SUPPORT_FILEFORMAT_TTF)
if (TextIsEqual(fileExtLower, ".ttf") ||
TextIsEqual(fileExtLower, ".otf"))
{
- font.baseSize = fontSize;
- font.glyphCount = (codepointCount > 0)? codepointCount : 95;
- font.glyphPadding = 0;
font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT);
+ }
+ else
+#endif
+#if defined(SUPPORT_FILEFORMAT_BDF)
+ if (TextIsEqual(fileExtLower, ".bdf"))
+ {
+ font.glyphs = LoadBDFFontData(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize);
+ }
+ else
+#endif
+ {
+ font.glyphs = NULL;
+ }
- if (font.glyphs != NULL)
- {
- font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
+#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
+ if (font.glyphs != NULL)
+ {
+ font.glyphPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
- Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0);
- font.texture = LoadTextureFromImage(atlas);
+ Image atlas = GenImageFontAtlas(font.glyphs, &font.recs, font.glyphCount, font.baseSize, font.glyphPadding, 0);
+ font.texture = LoadTextureFromImage(atlas);
- // Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
- for (int i = 0; i < font.glyphCount; i++)
- {
- UnloadImage(font.glyphs[i].image);
- font.glyphs[i].image = ImageFromImage(atlas, font.recs[i]);
- }
+ // Update glyphs[i].image to use alpha, required to be used on ImageDrawText()
+ for (int i = 0; i < font.glyphCount; i++)
+ {
+ UnloadImage(font.glyphs[i].image);
+ font.glyphs[i].image = ImageFromImage(atlas, font.recs[i]);
+ }
- UnloadImage(atlas);
+ UnloadImage(atlas);
- TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
- }
- else font = GetFontDefault();
+ TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
+ else font = GetFontDefault();
#else
font = GetFontDefault();
#endif
@@ -699,7 +734,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
// Generate image font atlas using chars info
// NOTE: Packing method: 0-Default, 1-Skyline
-#if defined(SUPPORT_FILEFORMAT_TTF)
+#if defined(SUPPORT_FILEFORMAT_TTF) || defined(SUPPORT_FILEFORMAT_BDF)
Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod)
{
Image atlas = { 0 };
@@ -2036,18 +2071,21 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
-#if defined(SUPPORT_FILEFORMAT_FNT)
+#if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF)
// Read a line from memory
// REQUIRES: memcpy()
// NOTE: Returns the number of bytes read
static int GetLine(const char *origin, char *buffer, int maxLength)
{
int count = 0;
- for (; count < maxLength; count++) if (origin[count] == '\n') break;
+ for (; count < maxLength - 1; count++) if (origin[count] == '\n') break;
memcpy(buffer, origin, count);
+ buffer[count] = '\0';
return count;
}
+#endif
+#if defined(SUPPORT_FILEFORMAT_FNT)
// Load a BMFont file (AngelCode font file)
// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
static Font LoadBMFont(const char *fileName)
@@ -2213,4 +2251,276 @@ static Font LoadBMFont(const char *fileName)
#endif
+#if defined(SUPPORT_FILEFORMAT_BDF)
+
+// Convert hexadecimal to decimal (single digit)
+static char HexToInt(char hex) {
+ if (hex >= '0' && hex <= '9')
+ {
+ return hex - '0';
+ }
+ else if (hex >= 'a' && hex <= 'f')
+ {
+ return hex - 'a' + 10;
+ }
+ else if (hex >= 'A' && hex <= 'F')
+ {
+ return hex - 'A' + 10;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Load font data for further use
+// NOTE: Requires BDF font memory data
+static GlyphInfo *LoadBDFFontData(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int* outFontSize)
+{
+ #define MAX_BUFFER_SIZE 256
+ char buffer[MAX_BUFFER_SIZE] = { 0 };
+
+ GlyphInfo *glyphs = NULL;
+
+ bool genFontChars = false;
+
+ int totalReadBytes = 0; // Data bytes read (total)
+ int readBytes = 0; // Data bytes read (line)
+ int readVars = 0; // Variables filled by sscanf()
+
+ const char *fileText = (const char*)fileData;
+ const char *fileTextPtr = fileText;
+
+ bool fontMalformed = false; // Is the font malformed
+ bool fontStarted = false; // Has font started (STARTFONT)
+ int fontBBw = 0; // Font base character bounding box width
+ int fontBBh = 0; // Font base character bounding box height
+ int fontBBxoff0 = 0; // Font base character bounding box X0 offset
+ int fontBByoff0 = 0; // Font base character bounding box Y0 offset
+ int fontAscent = 0; // Font ascent
+
+ bool charStarted = false; // Has character started (STARTCHAR)
+ bool charBitmapStarted = false; // Has bitmap data started (BITMAP)
+ int charBitmapNextRow = 0; // Y position for the next row of bitmap data
+ int charEncoding = -1; // The unicode value of the character (-1 if not set)
+ int charBBw = 0; // Character bounding box width
+ int charBBh = 0; // Character bounding box height
+ int charBBxoff0 = 0; // Character bounding box X0 offset
+ int charBByoff0 = 0; // Character bounding box Y0 offset
+ int charDWidthX = 0; // Character advance X
+ int charDWidthY = 0; // Character advance Y (unused)
+ GlyphInfo *charGlyphInfo = NULL; // Pointer to output glyph info (NULL if not set)
+
+ if (fileData == NULL) return glyphs;
+
+ // In case no chars count provided, default to 95
+ codepointCount = (codepointCount > 0)? codepointCount : 95;
+
+ // Fill fontChars in case not provided externally
+ // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space)
+ if (codepoints == NULL)
+ {
+ codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
+ for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
+ genFontChars = true;
+ }
+
+ glyphs = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo));
+
+ while (totalReadBytes <= dataSize)
+ {
+ readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ totalReadBytes += (readBytes + 1);
+ fileTextPtr += (readBytes + 1);
+
+ // COMMENT
+ if (strstr(buffer, "COMMENT") != NULL) continue; // Ignore line
+
+ if (charStarted)
+ {
+ // ENDCHAR
+ if (strstr(buffer, "ENDCHAR") != NULL)
+ {
+ charStarted = false;
+ continue;
+ }
+
+ if (charBitmapStarted)
+ {
+ if (charGlyphInfo != NULL) {
+ int pixelY = charBitmapNextRow++;
+ if (pixelY >= charGlyphInfo->image.height)
+ {
+ break;
+ }
+ for (int x = 0; x < readBytes; x++)
+ {
+ char byte = HexToInt(buffer[x]);
+ for (int bitX = 0; bitX < 4; bitX++)
+ {
+ int pixelX = ((x * 4) + bitX);
+ if (pixelX >= charGlyphInfo->image.width)
+ {
+ break;
+ }
+
+ if ((byte & (8 >> bitX)) > 0)
+ {
+ ((unsigned char*)charGlyphInfo->image.data)[(pixelY * charGlyphInfo->image.width) + pixelX] = 255;
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ // ENCODING
+ if (strstr(buffer, "ENCODING") != NULL)
+ {
+ readVars = sscanf(buffer, "ENCODING %i", &charEncoding);
+ continue;
+ }
+
+ // BBX
+ if (strstr(buffer, "BBX") != NULL)
+ {
+ readVars = sscanf(buffer, "BBX %i %i %i %i", &charBBw, &charBBh, &charBBxoff0, &charBByoff0);
+ continue;
+ }
+
+ // DWIDTH
+ if (strstr(buffer, "DWIDTH") != NULL)
+ {
+ readVars = sscanf(buffer, "DWIDTH %i %i", &charDWidthX, &charDWidthY);
+ continue;
+ }
+
+ // BITMAP
+ if (strstr(buffer, "BITMAP") != NULL)
+ {
+ // Search for glyph index in codepoints
+ charGlyphInfo = NULL;
+ for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++)
+ {
+ if (codepoints[codepointIndex] == charEncoding)
+ {
+ charGlyphInfo = &glyphs[codepointIndex];
+ break;
+ }
+ }
+
+ // Init glyph info
+ if (charGlyphInfo != NULL)
+ {
+ charGlyphInfo->value = charEncoding;
+ charGlyphInfo->offsetX = charBBxoff0 + fontBByoff0;
+ charGlyphInfo->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent);
+ charGlyphInfo->advanceX = charDWidthX;
+
+ charGlyphInfo->image.data = RL_CALLOC(charBBw * charBBh, 1);
+ charGlyphInfo->image.width = charBBw;
+ charGlyphInfo->image.height = charBBh;
+ charGlyphInfo->image.mipmaps = 1;
+ charGlyphInfo->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
+ }
+
+ charBitmapStarted = true;
+ charBitmapNextRow = 0;
+
+ continue;
+ }
+ }
+ else if (fontStarted)
+ {
+ // ENDFONT
+ if (strstr(buffer, "ENDFONT") != NULL)
+ {
+ fontStarted = false;
+ break;
+ }
+
+ // SIZE
+ if (strstr(buffer, "SIZE") != NULL)
+ {
+ if (outFontSize != NULL)
+ {
+ readVars = sscanf(buffer, "SIZE %i", outFontSize);
+ }
+ continue;
+ }
+
+ // PIXEL_SIZE
+ if (strstr(buffer, "PIXEL_SIZE") != NULL)
+ {
+ if (outFontSize != NULL)
+ {
+ readVars = sscanf(buffer, "PIXEL_SIZE %i", outFontSize);
+ }
+ continue;
+ }
+
+ // FONTBOUNDINGBOX
+ if (strstr(buffer, "FONTBOUNDINGBOX") != NULL)
+ {
+ readVars = sscanf(buffer, "FONTBOUNDINGBOX %i %i %i %i", &fontBBw, &fontBBh, &fontBBxoff0, &fontBByoff0);
+ continue;
+ }
+
+ // FONT_ASCENT
+ if (strstr(buffer, "FONT_ASCENT") != NULL)
+ {
+ readVars = sscanf(buffer, "FONT_ASCENT %i", &fontAscent);
+ continue;
+ }
+
+ // STARTCHAR
+ if (strstr(buffer, "STARTCHAR") != NULL)
+ {
+ charStarted = true;
+ charEncoding = -1;
+ charGlyphInfo = NULL;
+ charBBw = 0;
+ charBBh = 0;
+ charBBxoff0 = 0;
+ charBByoff0 = 0;
+ charDWidthX = 0;
+ charDWidthY = 0;
+ charGlyphInfo = NULL;
+ charBitmapStarted = false;
+ charBitmapNextRow = 0;
+ continue;
+ }
+ }
+ else
+ {
+ // STARTFONT
+ if (strstr(buffer, "STARTFONT") != NULL)
+ {
+ if (fontStarted)
+ {
+ fontMalformed = true;
+ break;
+ }
+ else
+ {
+ fontStarted = true;
+ continue;
+ }
+ }
+ }
+ }
+
+ if (genFontChars) RL_FREE(codepoints);
+
+ if (fontMalformed)
+ {
+ RL_FREE(glyphs);
+ glyphs = NULL;
+ }
+
+ return glyphs;
+}
+
+#endif
+
#endif // SUPPORT_MODULE_RTEXT