diff --git a/src/config.h b/src/config.h index 410e9067e0fa..34d2e964804a 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 270d0121bc2e..2f3609a23ea5 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 // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()] #include // 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 // 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) @@ -334,6 +351,10 @@ Font LoadFont(const char *fileName) #if defined(SUPPORT_FILEFORMAT_FNT) 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); @@ -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