|
|
@ -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); |
|
|
|
} |
|
|
|
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 |