Update C sources
This commit is contained in:
parent
443f35cfac
commit
15fba7e9ac
29 changed files with 31985 additions and 22038 deletions
405
raylib/rtext.c
405
raylib/rtext.c
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue