summaryrefslogtreecommitdiff
path: root/libs/raylib/src/text.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/raylib/src/text.c')
-rw-r--r--libs/raylib/src/text.c696
1 files changed, 403 insertions, 293 deletions
diff --git a/libs/raylib/src/text.c b/libs/raylib/src/text.c
index f83eed8..3a49963 100644
--- a/libs/raylib/src/text.c
+++ b/libs/raylib/src/text.c
@@ -16,7 +16,7 @@
* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
* TextSplit() function static buffer max size
*
-* #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT
+* #define MAX_TEXTSPLIT_COUNT
* TextSplit() function static substrings pointers array (pointing to static buffer)
*
*
@@ -27,7 +27,7 @@
*
* LICENSE: zlib/libpng
*
-* Copyright (c) 2013-2020 Ramon Santamaria (@raysan5)
+* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
@@ -54,12 +54,12 @@
#endif
#include <stdlib.h> // Required for: malloc(), free()
-#include <stdio.h> // Required for: FILE, fopen(), fclose(), fgets()
-#include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy(), strcat(), strncat(), sscanf()
+#include <stdio.h> // Required for: vsprintf()
+#include <string.h> // Required for: strcmp(), strstr(), strcpy(), strncpy() [Used in TextReplace()], sscanf() [Used in LoadBMFont()]
#include <stdarg.h> // Required for: va_list, va_start(), vsprintf(), va_end() [Used in TextFormat()]
#include <ctype.h> // Requried for: toupper(), tolower() [Used in TextToUpper(), TextToLower()]
-#include "utils.h" // Required for: fopen() Android mapping
+#include "utils.h" // Required for: LoadFileText()
#if defined(SUPPORT_FILEFORMAT_TTF)
#define STB_RECT_PACK_IMPLEMENTATION
@@ -73,17 +73,15 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
- // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal()
-
-#define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints
-
-#if !defined(TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH)
- #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH 1024 // Size of static buffer: TextSplit()
+#ifndef MAX_TEXT_BUFFER_LENGTH
+ #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions:
+ // TextFormat(), TextSubtext(), TextToUpper(), TextToLower(), TextToPascal(), TextSplit()
#endif
-
-#if !defined(TEXTSPLIT_MAX_SUBSTRINGS_COUNT)
- #define TEXTSPLIT_MAX_SUBSTRINGS_COUNT 128 // Size of static pointers array: TextSplit()
+#ifndef MAX_TEXT_UNICODE_CHARS
+ #define MAX_TEXT_UNICODE_CHARS 512 // Maximum number of unicode codepoints: GetCodepoints()
+#endif
+#ifndef MAX_TEXTSPLIT_COUNT
+ #define MAX_TEXTSPLIT_COUNT 128 // Maximum number of substrings to split: TextSplit()
#endif
//----------------------------------------------------------------------------------
@@ -95,8 +93,9 @@
// Global variables
//----------------------------------------------------------------------------------
#if defined(SUPPORT_DEFAULT_FONT)
-static Font defaultFont = { 0 }; // Default font provided by raylib
-// NOTE: defaultFont is loaded on InitWindow and disposed on CloseWindow [module: core]
+// Default font provided by raylib
+// NOTE: Default font is loaded on InitWindow() and disposed on CloseWindow() [module: core]
+static Font defaultFont = { 0 };
#endif
//----------------------------------------------------------------------------------
@@ -129,7 +128,8 @@ extern void LoadFontDefault(void)
// NOTE: Using UTF8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement
// Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl
- defaultFont.charsCount = 224; // Number of chars included in our default font
+ defaultFont.charsCount = 224; // Number of chars included in our default font
+ defaultFont.charsPadding = 0; // Characters padding
// Default font is directly defined here (data generated from a sprite font image)
// This way, we reconstruct Font without creating large global variables
@@ -192,33 +192,31 @@ extern void LoadFontDefault(void)
// Re-construct image from defaultFontData and generate OpenGL texture
//----------------------------------------------------------------------
- int imWidth = 128;
- int imHeight = 128;
-
- Color *imagePixels = (Color *)RL_MALLOC(imWidth*imHeight*sizeof(Color));
-
- for (int i = 0; i < imWidth*imHeight; i++) imagePixels[i] = BLANK; // Initialize array
-
- int counter = 0; // Font data elements counter
-
- // Fill imgData with defaultFontData (convert from bit to pixel!)
- for (int i = 0; i < imWidth*imHeight; i += 32)
+ Image imFont = {
+ .data = calloc(128*128, 2), // 2 bytes per pixel (gray + alpha)
+ .width = 128,
+ .height = 128,
+ .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
+ .mipmaps = 1
+ };
+
+ // Fill image.data with defaultFontData (convert from bit to pixel!)
+ for (int i = 0, counter = 0; i < imFont.width*imFont.height; i += 32)
{
for (int j = 31; j >= 0; j--)
{
- if (BIT_CHECK(defaultFontData[counter], j)) imagePixels[i+j] = WHITE;
+ if (BIT_CHECK(defaultFontData[counter], j))
+ {
+ // NOTE: We are unreferencing data as short, so,
+ // we must consider data as little-endian order (alpha + gray)
+ ((unsigned short *)imFont.data)[i + j] = 0xffff;
+ }
+ else ((unsigned short *)imFont.data)[i + j] = 0x00ff;
}
counter++;
-
- if (counter > 512) counter = 0; // Security check...
}
- Image imFont = LoadImageEx(imagePixels, imWidth, imHeight);
- ImageFormat(&imFont, UNCOMPRESSED_GRAY_ALPHA);
-
- RL_FREE(imagePixels);
-
defaultFont.texture = LoadTextureFromImage(imFont);
// Reconstruct charSet using charsWidth[], charsHeight, charsDivisor, charsCount
@@ -295,15 +293,24 @@ Font GetFontDefault()
// Load Font from file into GPU memory (VRAM)
Font LoadFont(const char *fileName)
{
- // Default hardcoded values for ttf file loading
- #define DEFAULT_TTF_FONTSIZE 32 // Font first character (32 - space)
- #define DEFAULT_TTF_NUMCHARS 95 // ASCII 32..126 is 95 glyphs
- #define DEFAULT_FIRST_CHAR 32 // Expected first char for image sprite font
+ // Default values for ttf font generation
+#ifndef FONT_TTF_DEFAULT_SIZE
+ #define FONT_TTF_DEFAULT_SIZE 32 // TTF font generation default char size (char-height)
+#endif
+#ifndef FONT_TTF_DEFAULT_NUMCHARS
+ #define FONT_TTF_DEFAULT_NUMCHARS 95 // TTF font generation default charset: 95 glyphs (ASCII 32..126)
+#endif
+#ifndef FONT_TTF_DEFAULT_FIRST_CHAR
+ #define FONT_TTF_DEFAULT_FIRST_CHAR 32 // TTF font generation default first char for image sprite font (32-Space)
+#endif
+#ifndef FONT_TTF_DEFAULT_CHARS_PADDING
+ #define FONT_TTF_DEFAULT_CHARS_PADDING 4 // TTF font generation default chars padding
+#endif
Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF)
- if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, DEFAULT_TTF_FONTSIZE, NULL, DEFAULT_TTF_NUMCHARS);
+ if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
@@ -312,7 +319,7 @@ Font LoadFont(const char *fileName)
#endif
{
Image image = LoadImage(fileName);
- if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, DEFAULT_FIRST_CHAR);
+ if (image.data != NULL) font = LoadFontFromImage(image, MAGENTA, FONT_TTF_DEFAULT_FIRST_CHAR);
UnloadImage(image);
}
@@ -321,7 +328,7 @@ Font LoadFont(const char *fileName)
TRACELOG(LOG_WARNING, "FONT: [%s] Failed to load font texture -> Using default font", fileName);
font = GetFontDefault();
}
- else SetTextureFilter(font.texture, FILTER_POINT); // By default we set point filter (best performance)
+ else SetTextureFilter(font.texture, TEXTURE_FILTER_POINT); // By default we set point filter (best performance)
return font;
}
@@ -333,29 +340,18 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
{
Font font = { 0 };
-#if defined(SUPPORT_FILEFORMAT_TTF)
- font.baseSize = fontSize;
- font.charsCount = (charsCount > 0)? charsCount : 95;
- font.chars = LoadFontData(fileName, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
+ // Loading file to memory
+ unsigned int fileSize = 0;
+ unsigned char *fileData = LoadFileData(fileName, &fileSize);
- if (font.chars != NULL)
+ if (fileData != NULL)
{
- Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, 2, 0);
- font.texture = LoadTextureFromImage(atlas);
+ // Loading font from memory data
+ font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, charsCount);
- // Update chars[i].image to use alpha, required to be used on ImageDrawText()
- for (int i = 0; i < font.charsCount; i++)
- {
- UnloadImage(font.chars[i].image);
- font.chars[i].image = ImageFromImage(atlas, font.recs[i]);
- }
-
- UnloadImage(atlas);
+ RL_FREE(fileData);
}
else font = GetFontDefault();
-#else
- font = GetFontDefault();
-#endif
return font;
}
@@ -363,6 +359,10 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int charsCou
// Load an Image font file (XNA style)
Font LoadFontFromImage(Image image, Color key, int firstChar)
{
+#ifndef MAX_GLYPHS_FROM_IMAGE
+ #define MAX_GLYPHS_FROM_IMAGE 256 // Maximum number of glyphs supported on image scan
+#endif
+
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
int charSpacing = 0;
@@ -371,15 +371,12 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
int x = 0;
int y = 0;
- // Default number of characters supported
- #define MAX_FONTCHARS 256
-
// We allocate a temporal arrays for chars data measures,
// once we get the actual number of chars, we copy data to a sized arrays
- int tempCharValues[MAX_FONTCHARS];
- Rectangle tempCharRecs[MAX_FONTCHARS];
+ int tempCharValues[MAX_GLYPHS_FROM_IMAGE];
+ Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE];
- Color *pixels = GetImageData(image);
+ Color *pixels = LoadImageColors(image);
// Parse image data to get charSpacing and lineSpacing
for (y = 0; y < image.height; y++)
@@ -435,19 +432,24 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
}
// NOTE: We need to remove key color borders from image to avoid weird
- // artifacts on texture scaling when using FILTER_BILINEAR or FILTER_TRILINEAR
+ // artifacts on texture scaling when using TEXTURE_FILTER_BILINEAR or TEXTURE_FILTER_TRILINEAR
for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
// Create a new image with the processed color data (key color replaced by BLANK)
- Image fontClear = LoadImageEx(pixels, image.width, image.height);
-
- RL_FREE(pixels); // Free pixels array memory
+ Image fontClear = {
+ .data = pixels,
+ .width = image.width,
+ .height = image.height,
+ .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
+ .mipmaps = 1
+ };
// Create spritefont with all data parsed from image
Font font = { 0 };
font.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
font.charsCount = index;
+ font.charsPadding = 0;
// We got tempCharValues and tempCharsRecs populated with chars data
// Now we move temp data to sized charValues and charRecs arrays
@@ -477,33 +479,78 @@ Font LoadFontFromImage(Image image, Color key, int firstChar)
return font;
}
+// 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 charsCount)
+{
+ Font font = { 0 };
+
+ char fileExtLower[16] = { 0 };
+ strcpy(fileExtLower, TextToLower(fileType));
+
+#if defined(SUPPORT_FILEFORMAT_TTF)
+ if (TextIsEqual(fileExtLower, ".ttf") ||
+ TextIsEqual(fileExtLower, ".otf"))
+ {
+ font.baseSize = fontSize;
+ font.charsCount = (charsCount > 0)? charsCount : 95;
+ font.charsPadding = 0;
+ font.chars = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.charsCount, FONT_DEFAULT);
+
+ if (font.chars != NULL)
+ {
+ font.charsPadding = FONT_TTF_DEFAULT_CHARS_PADDING;
+
+ Image atlas = GenImageFontAtlas(font.chars, &font.recs, font.charsCount, font.baseSize, font.charsPadding, 0);
+ font.texture = LoadTextureFromImage(atlas);
+
+ // Update chars[i].image to use alpha, required to be used on ImageDrawText()
+ for (int i = 0; i < font.charsCount; i++)
+ {
+ UnloadImage(font.chars[i].image);
+ font.chars[i].image = ImageFromImage(atlas, font.recs[i]);
+ }
+
+ UnloadImage(atlas);
+ }
+ else font = GetFontDefault();
+ }
+#else
+ font = GetFontDefault();
+#endif
+
+ return font;
+}
+
// Load font data for further use
-// NOTE: Requires TTF font and can generate SDF data
-CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int charsCount, int type)
+// NOTE: Requires TTF font memory data and can generate SDF data
+CharInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int charsCount, int type)
{
// NOTE: Using some SDF generation default values,
// trades off precision with ability to handle *smaller* sizes
- #define SDF_CHAR_PADDING 4
- #define SDF_ON_EDGE_VALUE 128
- #define SDF_PIXEL_DIST_SCALE 64.0f
-
- #define BITMAP_ALPHA_THRESHOLD 80
+#ifndef FONT_SDF_CHAR_PADDING
+ #define FONT_SDF_CHAR_PADDING 4 // SDF font generation char padding
+#endif
+#ifndef FONT_SDF_ON_EDGE_VALUE
+ #define FONT_SDF_ON_EDGE_VALUE 128 // SDF font generation on edge value
+#endif
+#ifndef FONT_SDF_PIXEL_DIST_SCALE
+ #define FONT_SDF_PIXEL_DIST_SCALE 64.0f // SDF font generation pixel distance scale
+#endif
+#ifndef FONT_BITMAP_ALPHA_THRESHOLD
+ #define FONT_BITMAP_ALPHA_THRESHOLD 80 // Bitmap (B&W) font generation alpha threshold
+#endif
CharInfo *chars = NULL;
#if defined(SUPPORT_FILEFORMAT_TTF)
- // Load font data (including pixel data) from TTF file
- // NOTE: Loaded information should be enough to generate
- // font image atlas, using any packaging method
- unsigned int dataSize = 0;
- unsigned char *fileData = LoadFileData(fileName, &dataSize);
-
+ // Load font data (including pixel data) from TTF memory file
+ // NOTE: Loaded information should be enough to generate font image atlas, using any packaging method
if (fileData != NULL)
{
int genFontChars = false;
stbtt_fontinfo fontInfo = { 0 };
- if (stbtt_InitFont(&fontInfo, fileData, 0)) // Init font for data reading
+ if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Init font for data reading
{
// Calculate font scale factor
float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
@@ -541,7 +588,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
if (type != FONT_SDF) chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
- else if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, SDF_CHAR_PADDING, SDF_ON_EDGE_VALUE, SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
+ else if (ch != 32) chars[i].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, ch, FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY);
else chars[i].image.data = NULL;
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
@@ -551,15 +598,22 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
chars[i].image.width = chw;
chars[i].image.height = chh;
chars[i].image.mipmaps = 1;
- chars[i].image.format = UNCOMPRESSED_GRAYSCALE;
+ chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
chars[i].offsetY += (int)((float)ascent*scaleFactor);
// NOTE: We create an empty image for space character, it could be further required for atlas packing
if (ch == 32)
{
- chars[i].image = GenImageColor(chars[i].advanceX, fontSize, BLANK);
- ImageFormat(&chars[i].image, UNCOMPRESSED_GRAYSCALE);
+ Image imSpace = {
+ .data = calloc(chars[i].advanceX*fontSize, 2),
+ .width = chars[i].advanceX,
+ .height = fontSize,
+ .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE,
+ .mipmaps = 1
+ };
+
+ chars[i].image = imSpace;
}
if (type == FONT_BITMAP)
@@ -568,7 +622,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
// NOTE: For optimum results, bitmap font should be generated at base pixel size
for (int p = 0; p < chw*chh; p++)
{
- if (((unsigned char *)chars[i].image.data)[p] < BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0;
+ if (((unsigned char *)chars[i].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) ((unsigned char *)chars[i].image.data)[p] = 0;
else ((unsigned char *)chars[i].image.data)[p] = 255;
}
}
@@ -585,7 +639,6 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
}
else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data");
- RL_FREE(fileData);
if (genFontChars) RL_FREE(fontChars);
}
#endif
@@ -600,6 +653,12 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
{
Image atlas = { 0 };
+ if (chars == NULL)
+ {
+ TraceLog(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas");
+ return atlas;
+ }
+
*charRecs = NULL;
// In case no chars count provided we suppose default of 95
@@ -620,7 +679,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
atlas.width = imageSize; // Atlas bitmap width
atlas.height = imageSize; // Atlas bitmap height
atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp)
- atlas.format = UNCOMPRESSED_GRAYSCALE;
+ atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
atlas.mipmaps = 1;
// DEBUG: We can see padding in the generated image setting a gray background...
@@ -652,7 +711,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
// Move atlas position X for next character drawing
offsetX += (chars[i].image.width + 2*padding);
- if (offsetX >= (atlas.width - chars[i].image.width - padding))
+ if (offsetX >= (atlas.width - chars[i].image.width - 2*padding))
{
offsetX = padding;
@@ -714,7 +773,6 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
// TODO: Crop image if required for smaller size
// Convert image data from GRAYSCALE to GRAY_ALPHA
- // WARNING: ImageAlphaMask(&atlas, atlas) does not work in this case, requires manual operation
unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels
for (int i = 0, k = 0; i < atlas.width*atlas.height; i++, k += 2)
@@ -725,7 +783,7 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
RL_FREE(atlas.data);
atlas.data = dataGrayAlpha;
- atlas.format = UNCOMPRESSED_GRAY_ALPHA;
+ atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
*charRecs = recs;
@@ -733,27 +791,39 @@ Image GenImageFontAtlas(const CharInfo *chars, Rectangle **charRecs, int charsCo
}
#endif
+// Unload font chars info data (RAM)
+void UnloadFontData(CharInfo *chars, int charsCount)
+{
+ for (int i = 0; i < charsCount; i++) UnloadImage(chars[i].image);
+
+ RL_FREE(chars);
+}
+
// Unload Font from GPU memory (VRAM)
void UnloadFont(Font font)
{
// NOTE: Make sure font is not default font (fallback)
if (font.texture.id != GetFontDefault().texture.id)
{
- for (int i = 0; i < font.charsCount; i++) UnloadImage(font.chars[i].image);
-
+ UnloadFontData(font.chars, font.charsCount);
UnloadTexture(font.texture);
- RL_FREE(font.chars);
RL_FREE(font.recs);
TRACELOGD("FONT: Unloaded font data from RAM and VRAM");
}
}
-// Shows current FPS on top-left corner
+// Draw current FPS
// NOTE: Uses default font
void DrawFPS(int posX, int posY)
{
- DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, LIME);
+ Color color = LIME; // good fps
+ int fps = GetFPS();
+
+ if (fps < 30 && fps >= 15) color = ORANGE; // warning FPS
+ else if (fps < 15) color = RED; // bad FPS
+
+ DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color);
}
// Draw text (using default font)
@@ -774,20 +844,6 @@ void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
}
}
-// Draw one character (codepoint)
-void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float scale, Color tint)
-{
- // Character index position in sprite font
- // NOTE: In case a codepoint is not available in the font, index returned points to '?'
- int index = GetGlyphIndex(font, codepoint);
-
- // Character rectangle on screen
- // NOTE: Quad is scaled proportionally to base character width-height
- Rectangle rec = { position.x, position.y, font.recs[index].width*scale, font.recs[index].height*scale };
-
- DrawTexturePro(font.texture, font.recs[index], rec, (Vector2){ 0, 0 }, 0.0f, tint);
-}
-
// Draw text using Font
// NOTE: chars spacing is NOT proportional to fontSize
void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
@@ -799,7 +855,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
- for (int i = 0; i < length; i++)
+ for (int i = 0; i < length;)
{
// Get next codepoint from byte string and glyph index in font
int codepointByteCount = 0;
@@ -821,19 +877,14 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
{
if ((codepoint != ' ') && (codepoint != '\t'))
{
- Rectangle rec = { position.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
- position.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
- font.recs[index].width*scaleFactor,
- font.recs[index].height*scaleFactor };
-
- DrawTexturePro(font.texture, font.recs[index], rec, (Vector2){ 0, 0 }, 0.0f, tint);
+ DrawTextCodepoint(font, codepoint, (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
}
if (font.chars[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
else textOffsetX += ((float)font.chars[index].advanceX*scaleFactor + spacing);
}
- i += (codepointByteCount - 1); // Move text bytes counter to next codepoint
+ i += codepointByteCount; // Move text bytes counter to next codepoint
}
}
@@ -846,7 +897,7 @@ void DrawTextRec(Font font, const char *text, Rectangle rec, float fontSize, flo
// Draw text using font inside rectangle limits with support for text selection
void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint)
{
- int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
+ int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop
int textOffsetY = 0; // Offset between lines (on line break '\n')
float textOffsetX = 0.0f; // Offset X to next character to draw
@@ -945,18 +996,14 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
bool isGlyphSelected = false;
if ((selectStart >= 0) && (k >= selectStart) && (k < (selectStart + selectLength)))
{
- DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, glyphWidth, (int)((float)font.baseSize*scaleFactor) }, selectBackTint);
+ DrawRectangleRec((Rectangle){ rec.x + textOffsetX - 1, rec.y + textOffsetY, (float)glyphWidth, (float)font.baseSize*scaleFactor }, selectBackTint);
isGlyphSelected = true;
}
- // Draw current chracter glyph
+ // Draw current character glyph
if ((codepoint != ' ') && (codepoint != '\t'))
{
- DrawTexturePro(font.texture, font.recs[index],
- (Rectangle){ rec.x + textOffsetX + font.chars[index].offsetX*scaleFactor,
- rec.y + textOffsetY + font.chars[index].offsetY*scaleFactor,
- font.recs[index].width*scaleFactor, font.recs[index].height*scaleFactor },
- (Vector2){ 0, 0 }, 0.0f, (!isGlyphSelected)? tint : selectTint);
+ DrawTextCodepoint(font, codepoint, (Vector2){ rec.x + textOffsetX, rec.y + textOffsetY }, fontSize, isGlyphSelected? selectTint : tint);
}
}
@@ -978,6 +1025,30 @@ void DrawTextRecEx(Font font, const char *text, Rectangle rec, float fontSize, f
}
}
+// Draw one character (codepoint)
+void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint)
+{
+ // Character index position in sprite font
+ // NOTE: In case a codepoint is not available in the font, index returned points to '?'
+ int index = GetGlyphIndex(font, codepoint);
+ float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor
+
+ // Character destination rectangle on screen
+ // NOTE: We consider charsPadding on drawing
+ Rectangle dstRec = { position.x + font.chars[index].offsetX*scaleFactor - (float)font.charsPadding*scaleFactor,
+ position.y + font.chars[index].offsetY*scaleFactor - (float)font.charsPadding*scaleFactor,
+ (font.recs[index].width + 2.0f*font.charsPadding)*scaleFactor,
+ (font.recs[index].height + 2.0f*font.charsPadding)*scaleFactor };
+
+ // Character source rectangle from font texture atlas
+ // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects
+ Rectangle srcRec = { font.recs[index].x - (float)font.charsPadding, font.recs[index].y - (float)font.charsPadding,
+ font.recs[index].width + 2.0f*font.charsPadding, font.recs[index].height + 2.0f*font.charsPadding };
+
+ // Draw the character texture on the screen
+ DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
+}
+
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
@@ -1053,11 +1124,14 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
// Returns index position for a unicode character on spritefont
int GetGlyphIndex(Font font, int codepoint)
{
-#define TEXT_CHARACTER_NOTFOUND 63 // Character: '?'
+#ifndef GLYPH_NOTFOUND_CHAR_FALLBACK
+ #define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?'
+#endif
-#define UNORDERED_CHARSET
-#if defined(UNORDERED_CHARSET)
- int index = TEXT_CHARACTER_NOTFOUND;
+// Support charsets with any characters order
+#define SUPPORT_UNORDERED_CHARSET
+#if defined(SUPPORT_UNORDERED_CHARSET)
+ int index = GLYPH_NOTFOUND_CHAR_FALLBACK;
for (int i = 0; i < font.charsCount; i++)
{
@@ -1077,40 +1151,6 @@ int GetGlyphIndex(Font font, int codepoint)
//----------------------------------------------------------------------------------
// Text strings management functions
//----------------------------------------------------------------------------------
-
-// Copy one string to another, returns bytes copied
-int TextCopy(char *dst, const char *src)
-{
- int bytes = 0;
-
- if (dst != NULL)
- {
- while (*src != '\0')
- {
- *dst = *src;
- dst++;
- src++;
-
- bytes++;
- }
-
- *dst = '\0';
- }
-
- return bytes;
-}
-
-// Check if two text string are equal
-// REQUIRES: strcmp()
-bool TextIsEqual(const char *text1, const char *text2)
-{
- bool result = false;
-
- if (strcmp(text1, text2) == 0) result = true;
-
- return result;
-}
-
// Get text length in bytes, check for \0 character
unsigned int TextLength(const char *text)
{
@@ -1128,7 +1168,9 @@ unsigned int TextLength(const char *text)
// WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times
const char *TextFormat(const char *text, ...)
{
- #define MAX_TEXTFORMAT_BUFFERS 4
+#ifndef MAX_TEXTFORMAT_BUFFERS
+ #define MAX_TEXTFORMAT_BUFFERS 4 // Maximum number of static buffers for text formatting
+#endif
// We create an array of buffers so strings don't expire until MAX_TEXTFORMAT_BUFFERS invocations
static char buffers[MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH] = { 0 };
@@ -1139,7 +1181,7 @@ const char *TextFormat(const char *text, ...)
va_list args;
va_start(args, text);
- vsprintf(currentBuffer, text, args);
+ vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args);
va_end(args);
index += 1; // Move to next buffer for next function call
@@ -1148,6 +1190,58 @@ const char *TextFormat(const char *text, ...)
return currentBuffer;
}
+// Get integer value from text
+// NOTE: This function replaces atoi() [stdlib.h]
+int TextToInteger(const char *text)
+{
+ int value = 0;
+ int sign = 1;
+
+ if ((text[0] == '+') || (text[0] == '-'))
+ {
+ if (text[0] == '-') sign = -1;
+ text++;
+ }
+
+ for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
+
+ return value*sign;
+}
+
+#if defined(SUPPORT_TEXT_MANIPULATION)
+// Copy one string to another, returns bytes copied
+int TextCopy(char *dst, const char *src)
+{
+ int bytes = 0;
+
+ if (dst != NULL)
+ {
+ while (*src != '\0')
+ {
+ *dst = *src;
+ dst++;
+ src++;
+
+ bytes++;
+ }
+
+ *dst = '\0';
+ }
+
+ return bytes;
+}
+
+// Check if two text string are equal
+// REQUIRES: strcmp()
+bool TextIsEqual(const char *text1, const char *text2)
+{
+ bool result = false;
+
+ if (strcmp(text1, text2) == 0) result = true;
+
+ return result;
+}
+
// Get a piece of a text string
const char *TextSubtext(const char *text, int position, int length)
{
@@ -1179,6 +1273,9 @@ const char *TextSubtext(const char *text, int position, int length)
// WARNING: Internally allocated memory must be freed by the user (if return != NULL)
char *TextReplace(char *text, const char *replace, const char *by)
{
+ // Sanity checks and initialization
+ if (!text || !replace || !by) return NULL;
+
char *result;
char *insertPoint; // Next insert point
@@ -1188,13 +1285,9 @@ char *TextReplace(char *text, const char *replace, const char *by)
int lastReplacePos; // Distance between replace and end of last replace
int count; // Number of replacements
- // Sanity checks and initialization
- if (!text || !replace) return NULL;
-
replaceLen = TextLength(replace);
if (replaceLen == 0) return NULL; // Empty replace causes infinite loop during count
- if (!by) by = ""; // Replace by nothing if not provided
byLen = TextLength(by);
// Count the number of replacements needed
@@ -1213,7 +1306,7 @@ char *TextReplace(char *text, const char *replace, const char *by)
while (count--)
{
insertPoint = strstr(text, replace);
- lastReplacePos = insertPoint - text;
+ lastReplacePos = (int)(insertPoint - text);
temp = strncpy(temp, text, lastReplacePos) + lastReplacePos;
temp = strcpy(temp, by) + byLen;
text += lastReplacePos + replaceLen; // Move to next "end of replace"
@@ -1244,29 +1337,32 @@ char *TextInsert(const char *text, const char *insert, int position)
}
// Join text strings with delimiter
-// REQUIRES: strcat()
+// REQUIRES: memset(), memcpy()
const char *TextJoin(const char **textList, int count, const char *delimiter)
{
static char text[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(text, 0, MAX_TEXT_BUFFER_LENGTH);
+ char *textPtr = text;
int totalLength = 0;
int delimiterLen = TextLength(delimiter);
for (int i = 0; i < count; i++)
{
- int textListLength = TextLength(textList[i]);
+ int textLength = TextLength(textList[i]);
// Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
- if ((totalLength + textListLength) < MAX_TEXT_BUFFER_LENGTH)
+ if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH)
{
- strcat(text, textList[i]);
- totalLength += textListLength;
+ memcpy(textPtr, textList[i], textLength);
+ totalLength += textLength;
+ textPtr += textLength;
if ((delimiterLen > 0) && (i < (count - 1)))
{
- strcat(text, delimiter);
+ memcpy(textPtr, delimiter, delimiterLen);
totalLength += delimiterLen;
+ textPtr += delimiterLen;
}
}
}
@@ -1275,17 +1371,18 @@ const char *TextJoin(const char **textList, int count, const char *delimiter)
}
// Split string into multiple strings
+// REQUIRES: memset()
const char **TextSplit(const char *text, char delimiter, int *count)
{
// NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter)
// inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated,
// all used memory is static... it has some limitations:
- // 1. Maximum number of possible split strings is set by TEXTSPLIT_MAX_SUBSTRINGS_COUNT
- // 2. Maximum size of text to split is TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH
+ // 1. Maximum number of possible split strings is set by MAX_TEXTSPLIT_COUNT
+ // 2. Maximum size of text to split is MAX_TEXT_BUFFER_LENGTH
- static const char *result[TEXTSPLIT_MAX_SUBSTRINGS_COUNT] = { NULL };
- static char buffer[TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH] = { 0 };
- memset(buffer, 0, TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH);
+ static const char *result[MAX_TEXTSPLIT_COUNT] = { NULL };
+ static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
+ memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
result[0] = buffer;
int counter = 0;
@@ -1295,7 +1392,7 @@ const char **TextSplit(const char *text, char delimiter, int *count)
counter = 1;
// Count how many substrings we have on text and point to every one
- for (int i = 0; i < TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH; i++)
+ for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++)
{
buffer[i] = text[i];
if (buffer[i] == '\0') break;
@@ -1305,7 +1402,7 @@ const char **TextSplit(const char *text, char delimiter, int *count)
result[counter] = buffer + i + 1;
counter++;
- if (counter == TEXTSPLIT_MAX_SUBSTRINGS_COUNT) break;
+ if (counter == MAX_TEXTSPLIT_COUNT) break;
}
}
}
@@ -1330,12 +1427,13 @@ int TextFindIndex(const char *text, const char *find)
char *ptr = strstr(text, find);
- if (ptr != NULL) position = ptr - text;
+ if (ptr != NULL) position = (int)(ptr - text);
return position;
}
// Get upper case version of provided string
+// REQUIRES: toupper()
const char *TextToUpper(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@@ -1357,6 +1455,7 @@ const char *TextToUpper(const char *text)
}
// Get lower case version of provided string
+// REQUIRES: tolower()
const char *TextToLower(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@@ -1375,6 +1474,7 @@ const char *TextToLower(const char *text)
}
// Get Pascal case notation version of provided string
+// REQUIRES: toupper()
const char *TextToPascal(const char *text)
{
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
@@ -1398,25 +1498,9 @@ const char *TextToPascal(const char *text)
return buffer;
}
-// Get integer value from text
-// NOTE: This function replaces atoi() [stdlib.h]
-int TextToInteger(const char *text)
-{
- int value = 0;
- int sign = 1;
-
- if ((text[0] == '+') || (text[0] == '-'))
- {
- if (text[0] == '-') sign = -1;
- text++;
- }
-
- for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0');
-
- return value*sign;
-}
-
-// Encode text codepoint into utf8 text (memory must be freed!)
+// Encode text codepoint into utf8 text
+// REQUIRES: memcpy()
+// WARNING: Allocated memory should be manually freed
char *TextToUtf8(int *codepoints, int length)
{
// We allocate enough memory fo fit all possible codepoints
@@ -1428,7 +1512,7 @@ char *TextToUtf8(int *codepoints, int length)
for (int i = 0, bytes = 0; i < length; i++)
{
utf8 = CodepointToUtf8(codepoints[i], &bytes);
- strncpy(text + size, utf8, bytes);
+ memcpy(text + size, utf8, bytes);
size += bytes;
}
@@ -1440,7 +1524,46 @@ char *TextToUtf8(int *codepoints, int length)
return text;
}
+// Encode codepoint into utf8 text (char array length returned as parameter)
+RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength)
+{
+ static char utf8[6] = { 0 };
+ int length = 0;
+
+ if (codepoint <= 0x7f)
+ {
+ utf8[0] = (char)codepoint;
+ length = 1;
+ }
+ else if (codepoint <= 0x7ff)
+ {
+ utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
+ utf8[1] = (char)((codepoint & 0x3f) | 0x80);
+ length = 2;
+ }
+ else if (codepoint <= 0xffff)
+ {
+ utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
+ utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
+ utf8[2] = (char)((codepoint & 0x3f) | 0x80);
+ length = 3;
+ }
+ else if (codepoint <= 0x10ffff)
+ {
+ utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
+ utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
+ utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
+ utf8[3] = (char)((codepoint & 0x3f) | 0x80);
+ length = 4;
+ }
+
+ *byteLength = length;
+
+ return utf8;
+}
+
// Get all codepoints in a string, codepoints count returned by parameters
+// REQUIRES: memset()
int *GetCodepoints(const char *text, int *count)
{
static int codepoints[MAX_TEXT_UNICODE_CHARS] = { 0 };
@@ -1481,14 +1604,14 @@ int GetCodepointsCount(const char *text)
return len;
}
-
+#endif // SUPPORT_TEXT_MANIPULATION
// Returns next codepoint in a UTF8 encoded text, scanning until '\0' is found
// When a invalid UTF8 byte is encountered we exit as soon as possible and a '?'(0x3f) codepoint is returned
// Total number of bytes processed are returned as a parameter
// NOTE: the standard says U+FFFD should be returned in case of errors
// but that character is not supported by the default font in raylib
-// TODO: optimize this code for speed!!
+// TODO: Optimize this code for speed!!
int GetNextCodepoint(const char *text, int *bytesProcessed)
{
/*
@@ -1595,50 +1718,24 @@ int GetNextCodepoint(const char *text, int *bytesProcessed)
return code;
}
-// Encode codepoint into utf8 text (char array length returned as parameter)
-RLAPI const char *CodepointToUtf8(int codepoint, int *byteLength)
-{
- static char utf8[6] = { 0 };
- int length = 0;
-
- if (codepoint <= 0x7f)
- {
- utf8[0] = (char)codepoint;
- length = 1;
- }
- else if (codepoint <= 0x7ff)
- {
- utf8[0] = (char)(((codepoint >> 6) & 0x1f) | 0xc0);
- utf8[1] = (char)((codepoint & 0x3f) | 0x80);
- length = 2;
- }
- else if (codepoint <= 0xffff)
- {
- utf8[0] = (char)(((codepoint >> 12) & 0x0f) | 0xe0);
- utf8[1] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
- utf8[2] = (char)((codepoint & 0x3f) | 0x80);
- length = 3;
- }
- else if (codepoint <= 0x10ffff)
- {
- utf8[0] = (char)(((codepoint >> 18) & 0x07) | 0xf0);
- utf8[1] = (char)(((codepoint >> 12) & 0x3f) | 0x80);
- utf8[2] = (char)(((codepoint >> 6) & 0x3f) | 0x80);
- utf8[3] = (char)((codepoint & 0x3f) | 0x80);
- length = 4;
- }
-
- *byteLength = length;
-
- return utf8;
-}
-//----------------------------------------------------------------------------------
-
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
#if defined(SUPPORT_FILEFORMAT_FNT)
+
+// 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;
+ memcpy(buffer, origin, count);
+ return count;
+}
+
// Load a BMFont file (AngelCode font file)
+// REQUIRES: strstr(), sscanf(), strrchr(), memcpy()
static Font LoadBMFont(const char *fileName)
{
#define MAX_BUFFER_SIZE 256
@@ -1649,84 +1746,97 @@ static Font LoadBMFont(const char *fileName)
char *searchPoint = NULL;
int fontSize = 0;
- int texWidth = 0;
- int texHeight = 0;
- char texFileName[129];
int charsCount = 0;
+ int imWidth = 0;
+ int imHeight = 0;
+ char imFileName[129];
+
int base = 0; // Useless data
- FILE *fntFile = NULL;
+ char *fileText = LoadFileText(fileName);
- fntFile = fopen(fileName, "rt");
+ if (fileText == NULL) return font;
- if (fntFile == NULL)
- {
- TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open FNT file", fileName);
- return font;
- }
+ char *fileTextPtr = fileText;
// NOTE: We skip first line, it contains no useful information
- fgets(buffer, MAX_BUFFER_SIZE, fntFile);
- //searchPoint = strstr(buffer, "size");
- //sscanf(searchPoint, "size=%i", &fontSize);
+ int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
+ fileTextPtr += (lineBytes + 1);
- fgets(buffer, MAX_BUFFER_SIZE, fntFile);
+ // Read line data
+ lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "lineHeight");
- sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &texWidth, &texHeight);
+ sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight);
+ fileTextPtr += (lineBytes + 1);
TRACELOGD("FONT: [%s] Loaded font info:", fileName);
- TRACELOGD(" > Base size: %i", fontSize);
- TRACELOGD(" > Texture scale: %ix%i", texWidth, texHeight);
+ TRACELOGD(" > Base size: %i", fontSize);
+ TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight);
- fgets(buffer, MAX_BUFFER_SIZE, fntFile);
+ lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "file");
- sscanf(searchPoint, "file=\"%128[^\"]\"", texFileName);
+ sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName);
+ fileTextPtr += (lineBytes + 1);
- TRACELOGD(" > Texture filename: %s", texFileName);
+ TRACELOGD(" > Texture filename: %s", imFileName);
- fgets(buffer, MAX_BUFFER_SIZE, fntFile);
+ lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE);
searchPoint = strstr(buffer, "count");
sscanf(searchPoint, "count=%i", &charsCount);
+ fileTextPtr += (lineBytes + 1);
TRACELOGD(" > Chars count: %i", charsCount);
- // Compose correct path using route of .fnt file (fileName) and texFileName
- char *texPath = NULL;
+ // Compose correct path using route of .fnt file (fileName) and imFileName
+ char *imPath = NULL;
char *lastSlash = NULL;
lastSlash = strrchr(fileName, '/');
- if (lastSlash == NULL)
+ if (lastSlash == NULL) lastSlash = strrchr(fileName, '\\');
+
+ if (lastSlash != NULL)
{
- lastSlash = strrchr(fileName, '\\');
+ // NOTE: We need some extra space to avoid memory corruption on next allocations!
+ imPath = 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;
- // NOTE: We need some extra space to avoid memory corruption on next allocations!
- texPath = RL_MALLOC(TextLength(fileName) - TextLength(lastSlash) + TextLength(texFileName) + 4);
-
- // NOTE: strcat() and strncat() required a '\0' terminated string to work!
- *texPath = '\0';
- strncat(texPath, fileName, TextLength(fileName) - TextLength(lastSlash) + 1);
- strncat(texPath, texFileName, TextLength(texFileName));
-
- TRACELOGD(" > Texture loading path: %s", texPath);
+ TRACELOGD(" > Image loading path: %s", imPath);
- Image imFont = LoadImage(texPath);
+ Image imFont = LoadImage(imPath);
- if (imFont.format == UNCOMPRESSED_GRAYSCALE)
+ if (imFont.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
{
// Convert image to GRAYSCALE + ALPHA, using the mask as the alpha channel
- ImageAlphaMask(&imFont, imFont);
- for (int p = 0; p < (imFont.width*imFont.height*2); p += 2) ((unsigned char *)(imFont.data))[p] = 0xff;
+ Image imFontAlpha = {
+ .data = calloc(imFont.width*imFont.height, 2),
+ .width = imFont.width,
+ .height = imFont.height,
+ .format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA,
+ .mipmaps = 1
+ };
+
+ for (int p = 0, i = 0; p < (imFont.width*imFont.height*2); p += 2, i++)
+ {
+ ((unsigned char *)(imFontAlpha.data))[p] = 0xff;
+ ((unsigned char *)(imFontAlpha.data))[p + 1] = ((unsigned char *)imFont.data)[i];
+ }
+
+ UnloadImage(imFont);
+ imFont = imFontAlpha;
}
font.texture = LoadTextureFromImage(imFont);
- RL_FREE(texPath);
+ if (lastSlash != NULL) RL_FREE(imPath);
// Fill font characters info data
font.baseSize = fontSize;
font.charsCount = charsCount;
+ font.charsPadding = 0;
font.chars = (CharInfo *)RL_MALLOC(charsCount*sizeof(CharInfo));
font.recs = (Rectangle *)RL_MALLOC(charsCount*sizeof(Rectangle));
@@ -1734,9 +1844,10 @@ static Font LoadBMFont(const char *fileName)
for (int i = 0; i < charsCount; i++)
{
- fgets(buffer, MAX_BUFFER_SIZE, fntFile);
+ 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",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
+ fileTextPtr += (lineBytes + 1);
// Get character rectangle in the font atlas texture
font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
@@ -1752,8 +1863,7 @@ static Font LoadBMFont(const char *fileName)
}
UnloadImage(imFont);
-
- fclose(fntFile);
+ RL_FREE(fileText);
if (font.texture.id == 0)
{