mirror of https://github.com/raysan5/raylib
[rtext] Add BDF font support (#3735)
* Add BDF font support * Include font ascent in glyph y-offset when loading BDF font
This commit is contained in:
parent
29ff658d92
commit
0932cd3059
|
@ -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()
|
||||
|
|
370
src/rtext.c
370
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)
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
|
||||
}
|
||||
else font = GetFontDefault();
|
||||
}
|
||||
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 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);
|
||||
|
||||
// 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);
|
||||
|
||||
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
|
||||
|
|
Loading…
Reference in New Issue