Update C sources

This commit is contained in:
Milan Nikolic 2023-11-04 13:06:19 +01:00
parent 443f35cfac
commit 15fba7e9ac
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
29 changed files with 31985 additions and 22038 deletions

View file

@ -3,25 +3,28 @@
* rtext - Basic functions to load fonts and draw text
*
* CONFIGURATION:
* #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build
*
* #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build
* #define SUPPORT_DEFAULT_FONT
* Load default raylib font on initialization to be used by DrawText() and MeasureText().
* If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
*
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
* 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
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
* 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
*
* #define SUPPORT_DEFAULT_FONT
* Load default raylib font on initialization to be used by DrawText() and MeasureText().
* If no default font loaded, DrawTextEx() and MeasureTextEx() are required.
* #define SUPPORT_FONT_ATLAS_WHITE_REC
* On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle
* at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow
* drawing text and shapes with a single draw call [SetShapesTexture()].
*
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size
*
* #define MAX_TEXTSPLIT_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer)
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size
*
* #define MAX_TEXTSPLIT_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer)
*
* DEPENDENCIES:
* stb_truetype - Load TTF file and rasterize characters data
@ -68,12 +71,21 @@
#include <ctype.h> // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
#if defined(SUPPORT_FILEFORMAT_TTF)
#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
#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#include "external/stb_truetype.h" // Required for: ttf font data reading
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif
#endif
//----------------------------------------------------------------------------------
@ -113,8 +125,9 @@ static Font defaultFont = { 0 };
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
#endif
static int textLineSpacing = 15; // Text vertical line spacing in pixels
#if defined(SUPPORT_DEFAULT_FONT)
extern void LoadFontDefault(void);
@ -125,7 +138,6 @@ extern void UnloadFontDefault(void);
// Module Functions Definition
//----------------------------------------------------------------------------------
#if defined(SUPPORT_DEFAULT_FONT)
// Load raylib default font
extern void LoadFontDefault(void)
{
@ -346,18 +358,18 @@ Font LoadFont(const char *fileName)
// Load Font from TTF 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 *fontChars, int glyphCount)
Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount)
{
Font font = { 0 };
// Loading file to memory
unsigned int fileSize = 0;
unsigned char *fileData = LoadFileData(fileName, &fileSize);
int dataSize = 0;
unsigned char *fileData = LoadFileData(fileName, &dataSize);
if (fileData != NULL)
{
// Loading font from memory data
font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, glyphCount);
font = LoadFontFromMemory(GetFileExtension(fileName), fileData, dataSize, fontSize, codepoints, codepointCount);
UnloadFileData(fileData);
}
@ -492,7 +504,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
}
// Load font from memory buffer, fileType refers to extension: i.e. ".ttf"
Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount)
Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount)
{
Font font = { 0 };
@ -504,9 +516,9 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
TextIsEqual(fileExtLower, ".otf"))
{
font.baseSize = fontSize;
font.glyphCount = (glyphCount > 0)? glyphCount : 95;
font.glyphCount = (codepointCount > 0)? codepointCount : 95;
font.glyphPadding = 0;
font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.glyphCount, FONT_DEFAULT);
font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT);
if (font.glyphs != NULL)
{
@ -550,7 +562,7 @@ bool IsFontReady(Font font)
// Load font data for further use
// NOTE: Requires TTF font memory data and can generate SDF data
GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type)
GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type)
{
// NOTE: Using some SDF generation default values,
// trades off precision with ability to handle *smaller* sizes
@ -588,25 +600,25 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
// In case no chars count provided, default to 95
glyphCount = (glyphCount > 0)? glyphCount : 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 (fontChars == NULL)
if (codepoints == NULL)
{
fontChars = (int *)RL_MALLOC(glyphCount*sizeof(int));
for (int i = 0; i < glyphCount; i++) fontChars[i] = i + 32;
codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int));
for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32;
genFontChars = true;
}
chars = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo));
chars = (GlyphInfo *)RL_MALLOC(codepointCount*sizeof(GlyphInfo));
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < glyphCount; i++)
for (int i = 0; i < codepointCount; i++)
{
int chw = 0, chh = 0; // Character width and height (on generation)
int ch = fontChars[i]; // Character value to get info for
int ch = codepoints[i]; // Character value to get info for
chars[i].value = ch;
// Render a unicode codepoint to a bitmap
@ -666,7 +678,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz
}
else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
if (genFontChars) RL_FREE(fontChars);
if (genFontChars) RL_FREE(codepoints);
}
#endif
@ -676,17 +688,17 @@ 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)
Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphCount, int fontSize, int padding, int packMethod)
Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod)
{
Image atlas = { 0 };
if (chars == NULL)
if (glyphs == NULL)
{
TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas");
return atlas;
}
*charRecs = NULL;
*glyphRecs = NULL;
// In case no chars count provided we suppose default of 95
glyphCount = (glyphCount > 0)? glyphCount : 95;
@ -694,17 +706,48 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// NOTE: Rectangles memory is loaded here!
Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle));
// Calculate image size based on required pixel area
// NOTE 1: Image is forced to be squared and POT... very conservative!
// NOTE 2: SDF font characters already contain an internal padding,
// so image size would result bigger than default font type
float requiredArea = 0;
for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding));
float guessSize = sqrtf(requiredArea)*1.4f;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
// Calculate image size based on total glyph width and glyph row count
int totalWidth = 0;
int maxGlyphWidth = 0;
for (int i = 0; i < glyphCount; i++)
{
if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width;
totalWidth += glyphs[i].image.width + 4*padding;
}
//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE
#if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE)
int rowCount = 0;
int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images
// NOTE: maxGlyphWidth is maximum possible space left at the end of row
while (totalWidth > (imageSize - maxGlyphWidth)*rowCount)
{
imageSize *= 2; // Double the size of image (to keep POT)
rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size
}
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
#else
// No need for a so-conservative atlas generation
float totalArea = totalWidth*fontSize*1.2f;
float imageMinSize = sqrtf(totalArea);
int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2)));
if (totalArea < ((imageSize*imageSize)/2))
{
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize/2; // Atlas bitmap height
}
else
{
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
}
#endif
atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1;
@ -720,25 +763,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// NOTE: Using simple packaging, one char after another
for (int i = 0; i < glyphCount; i++)
{
// Copy pixel data from fc.data to atlas
for (int y = 0; y < chars[i].image.height; y++)
{
for (int x = 0; x < chars[i].image.width; x++)
{
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
}
}
// Fill chars rectangles in atlas info
recs[i].x = (float)offsetX;
recs[i].y = (float)offsetY;
recs[i].width = (float)chars[i].image.width;
recs[i].height = (float)chars[i].image.height;
// Move atlas position X for next character drawing
offsetX += (chars[i].image.width + 2*padding);
if (offsetX >= (atlas.width - chars[i].image.width - 2*padding))
// Check remaining space for glyph
if (offsetX >= (atlas.width - glyphs[i].image.width - 2*padding))
{
offsetX = padding;
@ -761,6 +787,24 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
break;
}
}
// Copy pixel data from glyph image to atlas
for (int y = 0; y < glyphs[i].image.height; y++)
{
for (int x = 0; x < glyphs[i].image.width; x++)
{
((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
}
}
// Fill chars rectangles in atlas info
recs[i].x = (float)offsetX;
recs[i].y = (float)offsetY;
recs[i].width = (float)glyphs[i].image.width;
recs[i].height = (float)glyphs[i].image.height;
// Move atlas position X for next character drawing
offsetX += (glyphs[i].image.width + 2*padding);
}
}
else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect)
@ -775,8 +819,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
for (int i = 0; i < glyphCount; i++)
{
rects[i].id = i;
rects[i].w = chars[i].image.width + 2*padding;
rects[i].h = chars[i].image.height + 2*padding;
rects[i].w = glyphs[i].image.width + 2*padding;
rects[i].h = glyphs[i].image.height + 2*padding;
}
// Package rectangles into atlas
@ -787,17 +831,17 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// It returns char rectangles in atlas
recs[i].x = rects[i].x + (float)padding;
recs[i].y = rects[i].y + (float)padding;
recs[i].width = (float)chars[i].image.width;
recs[i].height = (float)chars[i].image.height;
recs[i].width = (float)glyphs[i].image.width;
recs[i].height = (float)glyphs[i].image.height;
if (rects[i].was_packed)
{
// Copy pixel data from fc.data to atlas
for (int y = 0; y < chars[i].image.height; y++)
for (int y = 0; y < glyphs[i].image.height; y++)
{
for (int x = 0; x < chars[i].image.width; x++)
for (int x = 0; x < glyphs[i].image.width; x++)
{
((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x];
((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x];
}
}
}
@ -809,6 +853,19 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
RL_FREE(context);
}
#if defined(SUPPORT_FONT_ATLAS_WHITE_REC)
// Add a 3x3 white rectangle at the bottom-right corner of the generated atlas,
// useful to use as the white texture to draw shapes with raylib, using this rectangle
// shapes and text can be backed into a single draw call: SetShapesTexture()
for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++)
{
((unsigned char *)atlas.data)[k - 0] = 255;
((unsigned char *)atlas.data)[k - 1] = 255;
((unsigned char *)atlas.data)[k - 2] = 255;
k -= atlas.width;
}
#endif
// Convert image data from GRAYSCALE to GRAY_ALPHA
unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
@ -822,7 +879,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
atlas.data = dataGrayAlpha;
atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
*charRecs = recs;
*glyphRecs = recs;
return atlas;
}
@ -918,7 +975,7 @@ bool ExportFontAsCode(Font font, const char *fileName)
byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal));
for (int i = 0; i < compDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]);
byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]);
MemFree(compData);
RL_FREE(compData);
#else
// Save font image data (uncompressed)
byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n");
@ -1018,7 +1075,7 @@ void DrawFPS(int posX, int posY)
if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS
else if (fps < 15) color = RED; // Low FPS
DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color);
DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, color);
}
// Draw text (using default font)
@ -1059,15 +1116,10 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
int codepoint = GetCodepointNext(&text[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint);
// NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all the bad bytes using the '?' symbol moving one byte
if (codepoint == 0x3f) codepointByteCount = 1;
if (codepoint == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textOffsetY += textLineSpacing;
textOffsetX = 0.0f;
}
else
@ -1124,22 +1176,21 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
}
// Draw multiple character (codepoints)
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint)
void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint)
{
int textOffsetY = 0; // Offset between lines (on linebreak '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
for (int i = 0; i < count; i++)
for (int i = 0; i < codepointCount; i++)
{
int index = GetGlyphIndex(font, codepoints[i]);
if (codepoints[i] == '\n')
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textOffsetY += textLineSpacing;
textOffsetX = 0.0f;
}
else
@ -1155,6 +1206,12 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos
}
}
// Set vertical line spacing when drawing with line-breaks
void SetTextLineSpacing(int spacing)
{
textLineSpacing = spacing;
}
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
@ -1193,7 +1250,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
int letter = 0; // Current character
int index = 0; // Index position in sprite font
for (int i = 0; i < size; i++)
for (int i = 0; i < size;)
{
byteCounter++;
@ -1201,10 +1258,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
letter = GetCodepointNext(&text[i], &next);
index = GetGlyphIndex(font, letter);
// NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
// but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1
if (letter == 0x3f) next = 1;
i += next - 1;
i += next;
if (letter != '\n')
{
@ -1216,7 +1270,9 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
byteCounter = 0;
textWidth = 0;
textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines
// NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup
textHeight += (float)textLineSpacing;
}
if (tempByteCounter < byteCounter) tempByteCounter = byteCounter;
@ -1224,7 +1280,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure
textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing);
textSize.y = textHeight*scaleFactor;
return textSize;
@ -1234,17 +1290,17 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
// NOTE: If codepoint is not found in the font it fallbacks to '?'
int GetGlyphIndex(Font font, int codepoint)
{
#ifndef GLYPH_NOTFOUND_CHAR_FALLBACK
#define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?'
#endif
int index = 0;
// Support charsets with any characters order
#define SUPPORT_UNORDERED_CHARSET
#if defined(SUPPORT_UNORDERED_CHARSET)
int index = GLYPH_NOTFOUND_CHAR_FALLBACK;
int fallbackIndex = 0; // Get index of fallback glyph '?'
// Look for character index in the unordered charset
for (int i = 0; i < font.glyphCount; i++)
{
if (font.glyphs[i].value == 63) fallbackIndex = i;
if (font.glyphs[i].value == codepoint)
{
index = i;
@ -1252,10 +1308,12 @@ int GetGlyphIndex(Font font, int codepoint)
}
}
return index;
if ((index == 0) && (font.glyphs[0].value != codepoint)) index = fallbackIndex;
#else
return (codepoint - 32);
index = codepoint - 32;
#endif
return index;
}
// Get glyph font info data for a codepoint (unicode character)
@ -1286,10 +1344,12 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint)
// Get text length in bytes, check for \0 character
unsigned int TextLength(const char *text)
{
unsigned int length = 0; //strlen(text)
unsigned int length = 0;
if (text != NULL)
{
// NOTE: Alternative: use strlen(text)
while (*text++) length++;
}
@ -1313,15 +1373,24 @@ const char *TextFormat(const char *text, ...)
va_list args;
va_start(args, text);
vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
va_end(args);
// If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured
if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH)
{
// Inserting "..." at the end of the string to mark as truncated
char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0"
sprintf(truncBuffer, "...");
}
index += 1; // Move to next buffer for next function call
if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0;
return currentBuffer;
}
// Get integer value from text
// NOTE: This function replaces atoi() [stdlib.h]
int TextToInteger(const char *text)
@ -1335,7 +1404,7 @@ int TextToInteger(const char *text)
text++;
}
for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0');
return value*sign;
}
@ -1348,6 +1417,8 @@ int TextCopy(char *dst, const char *src)
if ((src != NULL) && (dst != NULL))
{
// NOTE: Alternative: use strcpy(dst, src)
while (*src != '\0')
{
*dst = *src;
@ -1393,6 +1464,8 @@ const char *TextSubtext(const char *text, int position, int length)
if (length >= textLength) length = textLength;
// NOTE: Alternative: memcpy(buffer, text + position, length)
for (int c = 0 ; c < length ; c++)
{
*(buffer + c) = *(text + position);
@ -1569,7 +1642,8 @@ int TextFindIndex(const char *text, const char *find)
}
// Get upper case version of provided string
// REQUIRES: toupper()
// WARNING: Limited functionality, only basic characters set
// TODO: Support UTF-8 diacritics to upper-case, check codepoints
const char *TextToUpper(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1577,17 +1651,10 @@ const char *TextToUpper(const char *text)
if (text != NULL)
{
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++)
{
if (text[i] != '\0')
{
buffer[i] = (char)toupper(text[i]);
//if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
// TODO: Support UTF-8 diacritics to upper-case
//if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32;
}
else { buffer[i] = '\0'; break; }
if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32;
else buffer[i] = text[i];
}
}
@ -1595,7 +1662,7 @@ const char *TextToUpper(const char *text)
}
// Get lower case version of provided string
// REQUIRES: tolower()
// WARNING: Limited functionality, only basic characters set
const char *TextToLower(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1603,14 +1670,10 @@ const char *TextToLower(const char *text)
if (text != NULL)
{
for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++)
{
if (text[i] != '\0')
{
buffer[i] = (char)tolower(text[i]);
//if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
}
else { buffer[i] = '\0'; break; }
if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32;
else buffer[i] = text[i];
}
}
@ -1618,7 +1681,7 @@ const char *TextToLower(const char *text)
}
// Get Pascal case notation version of provided string
// REQUIRES: toupper()
// WARNING: Limited functionality, only basic characters set
const char *TextToPascal(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@ -1626,20 +1689,19 @@ const char *TextToPascal(const char *text)
if (text != NULL)
{
buffer[0] = (char)toupper(text[0]);
// Upper case first character
if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32;
else buffer[0] = text[0];
for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++)
// Check for next separator to upper case another character
for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++)
{
if (text[j] != '\0')
if (text[j] != '_') buffer[i] = text[j];
else
{
if (text[j] != '_') buffer[i] = text[j];
else
{
j++;
buffer[i] = (char)toupper(text[j]);
}
j++;
if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32;
}
else { buffer[i] = '\0'; break; }
}
}
@ -1651,7 +1713,7 @@ const char *TextToPascal(const char *text)
// WARNING: Allocated memory must be manually freed
char *LoadUTF8(const int *codepoints, int length)
{
// We allocate enough memory fo fit all possible codepoints
// We allocate enough memory to fit all possible codepoints
// NOTE: 5 bytes for every codepoint should be enough
char *text = (char *)RL_CALLOC(length*5, 1);
const char *utf8 = NULL;
@ -1720,10 +1782,9 @@ int GetCodepointCount(const char *text)
while (*ptr != '\0')
{
int next = 0;
int letter = GetCodepointNext(ptr, &next);
GetCodepointNext(ptr, &next);
if (letter == 0x3f) ptr += 1;
else ptr += next;
ptr += next;
length++;
}
@ -1884,28 +1945,31 @@ int GetCodepointNext(const char *text, int *codepointSize)
{
const char *ptr = text;
int codepoint = 0x3f; // Codepoint (defaults to '?')
*codepointSize = 0;
*codepointSize = 1;
// Get current codepoint and bytes processed
if (0xf0 == (0xf8 & ptr[0]))
{
// 4 byte UTF-8 codepoint
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]);
*codepointSize = 4;
}
else if (0xe0 == (0xf0 & ptr[0]))
{
// 3 byte UTF-8 codepoint */
if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks
codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]);
*codepointSize = 3;
}
else if (0xc0 == (0xe0 & ptr[0]))
{
// 2 byte UTF-8 codepoint
if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks
codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]);
*codepointSize = 2;
}
else
else if (0x00 == (0x80 & ptr[0]))
{
// 1 byte UTF-8 codepoint
codepoint = ptr[0];
@ -1938,7 +2002,6 @@ int GetCodepointPrevious(const char *text, int *codepointSize)
// Module specific Functions Definition
//----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT)
// Read a line from memory
// REQUIRES: memcpy()
// NOTE: Returns the number of bytes read
@ -1968,7 +2031,9 @@ static Font LoadBMFont(const char *fileName)
int imHeight = 0;
char imFileName[129] = { 0 };
int base = 0; // Useless data
int base = 0; // Useless data
int readBytes = 0; // Data bytes read
int readVars = 0; // Variables filled by sscanf()
char *fileText = LoadFileText(fileName);
@ -1977,32 +2042,30 @@ static Font LoadBMFont(const char *fileName)
char *fileTextPtr = fileText;
// NOTE: We skip first line, it contains no useful information
int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
fileTextPtr += (lineBytes + 1);
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
fileTextPtr += (readBytes + 1);
// Read line data
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "lineHeight");
sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
fileTextPtr += (lineBytes + 1);
readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
fileTextPtr += (readBytes + 1);
if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed
TRACELOGD("FONT: [%s] Loaded font info:", fileName);
TRACELOGD(" > Base size: %i", fontSize);
TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight);
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file");
sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
fileTextPtr += (lineBytes + 1);
readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
fileTextPtr += (readBytes + 1);
TRACELOGD(" > Texture filename: %s", imFileName);
if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "count");
sscanf(searchPoint, "count=%i", &glyphCount);
fileTextPtr += (lineBytes + 1);
readVars = sscanf(searchPoint, "count=%i", &glyphCount);
fileTextPtr += (readBytes + 1);
TRACELOGD(" > Chars count: %i", glyphCount);
if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read
// Compose correct path using route of .fnt file (fileName) and imFileName
char *imPath = NULL;
@ -2060,22 +2123,26 @@ static Font LoadBMFont(const char *fileName)
for (int i = 0; i < glyphCount; i++)
{
lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%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);
fileTextPtr += (lineBytes + 1);
fileTextPtr += (readBytes + 1);
if (readVars == 8) // 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 };
// Get character rectangle in the font atlas texture
font.recs[i] = (Rectangle){ (float)charX, (float)charY, (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;
// 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 imFont data
font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]);
}
else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName);
}
UnloadImage(imFont);