diff --git a/src/rtext.c b/src/rtext.c index dd7c83f39490..52d78fb63350 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -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 }; @@ -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 @@ -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"); @@ -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); } - - 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; @@ -2119,19 +2136,19 @@ 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; @@ -2139,13 +2156,13 @@ static Font LoadBMFont(const char *fileName) 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) @@ -2158,6 +2175,7 @@ static Font LoadBMFont(const char *fileName) return font; } + #endif #endif // SUPPORT_MODULE_RTEXT