Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BM Font Extender #3536

Merged
merged 8 commits into from
Dec 15, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 67 additions & 49 deletions src/rtext.c
Original file line number Diff line number Diff line change
Expand Up @@ -2017,7 +2017,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 };

Expand All @@ -2029,7 +2030,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
Expand All @@ -2048,17 +2050,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");
Expand All @@ -2067,50 +2078,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);
}
Dismissed Show dismissed Hide dismissed

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;
Expand All @@ -2119,33 +2136,33 @@ 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;
font.glyphs[i].offsetX = charOffsetX;
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)
Expand All @@ -2158,6 +2175,7 @@ static Font LoadBMFont(const char *fileName)

return font;
}

#endif

#endif // SUPPORT_MODULE_RTEXT
Loading