Update C sources

This commit is contained in:
Milan Nikolic 2022-08-27 16:00:43 +02:00
parent dd222de786
commit b83dec57b5
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
14 changed files with 2900 additions and 1289 deletions

View file

@ -4,6 +4,9 @@
*
* CONFIGURATION:
*
* #define SUPPORT_MODULE_RTEXT
* rtext module is included in the build
*
* #define SUPPORT_FILEFORMAT_FNT
* #define SUPPORT_FILEFORMAT_TTF
* Selected desired fileformats to be supported for loading. Some of those formats are
@ -22,12 +25,12 @@
*
* DEPENDENCIES:
* stb_truetype - Load TTF file and rasterize characters data
* stb_rect_pack - Rectangles packing algorythms, required for font atlas generation
* stb_rect_pack - Rectangles packing algorithms, required for font atlas generation
*
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2021 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2022 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.
@ -53,6 +56,8 @@
#include "config.h" // Defines module configuration flags
#endif
#if defined(SUPPORT_MODULE_RTEXT)
#include "utils.h" // Required for: LoadFileText()
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 2.1, 3.3+ or ES2 -> Only DrawTextPro()
@ -224,7 +229,7 @@ extern void LoadFontDefault(void)
//------------------------------------------------------------------------------
// Allocate space for our characters info data
// NOTE: This memory should be freed at end! --> CloseWindow()
// NOTE: This memory must be freed at end! --> Done by CloseWindow()
defaultFont.glyphs = (GlyphInfo *)RL_MALLOC(defaultFont.glyphCount*sizeof(GlyphInfo));
defaultFont.recs = (Rectangle *)RL_MALLOC(defaultFont.glyphCount*sizeof(Rectangle));
@ -311,7 +316,7 @@ Font LoadFont(const char *fileName)
Font font = { 0 };
#if defined(SUPPORT_FILEFORMAT_TTF)
if (IsFileExtension(fileName, ".ttf;.otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
if (IsFileExtension(fileName, ".ttf") || IsFileExtension(fileName, ".otf")) font = LoadFontEx(fileName, FONT_TTF_DEFAULT_SIZE, NULL, FONT_TTF_DEFAULT_NUMCHARS);
else
#endif
#if defined(SUPPORT_FILEFORMAT_FNT)
@ -329,7 +334,11 @@ 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, 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)
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", FONT_TTF_DEFAULT_SIZE, FONT_TTF_DEFAULT_NUMCHARS);
}
return font;
}
@ -515,7 +524,7 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int
UnloadImage(atlas);
// TRACELOG(LOG_INFO, "FONT: Font loaded successfully (%i glyphs)", font.glyphCount);
TRACELOG(LOG_INFO, "FONT: Data loaded successfully (%i pixel size | %i glyphs)", font.baseSize, font.glyphCount);
}
else font = GetFontDefault();
}
@ -677,20 +686,20 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// 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)*(chars[i].image.height + 2*padding));
float guessSize = sqrtf(requiredArea)*1.3f;
int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT
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
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.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;
// DEBUG: We can see padding in the generated image setting a gray background...
//for (int i = 0; i < atlas.width*atlas.height; i++) ((unsigned char *)atlas.data)[i] = 100;
if (packMethod == 0) // Use basic packing algorythm
if (packMethod == 0) // Use basic packing algorithm
{
int offsetX = padding;
int offsetY = padding;
@ -725,11 +734,23 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// height is bigger than fontSize, it could be up to (fontSize + 8)
offsetY += (fontSize + 2*padding);
if (offsetY > (atlas.height - fontSize - padding)) break;
if (offsetY > (atlas.height - fontSize - padding))
{
for(int j = i + 1; j < glyphCount; j++)
{
TRACELOG(LOG_WARNING, "FONT: Failed to package character (%i)", j);
// make sure remaining recs contain valid data
recs[j].x = 0;
recs[j].y = 0;
recs[j].width = 0;
recs[j].height = 0;
}
break;
}
}
}
}
else if (packMethod == 1) // Use Skyline rect packing algorythm (stb_pack_rect)
else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect)
{
stbrp_context *context = (stbrp_context *)RL_MALLOC(sizeof(*context));
stbrp_node *nodes = (stbrp_node *)RL_MALLOC(glyphCount*sizeof(*nodes));
@ -797,9 +818,12 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC
// Unload font glyphs info data (RAM)
void UnloadFontData(GlyphInfo *glyphs, int glyphCount)
{
for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image);
if (glyphs != NULL)
{
for (int i = 0; i < glyphCount; i++) UnloadImage(glyphs[i].image);
RL_FREE(glyphs);
RL_FREE(glyphs);
}
}
// Unload Font from GPU memory (VRAM)
@ -816,15 +840,170 @@ void UnloadFont(Font font)
}
}
// Export font as code file, returns true on success
bool ExportFontAsCode(Font font, const char *fileName)
{
bool success = false;
#ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20
#endif
#define MAX_FONT_DATA_SIZE 1024*1024 // 1 MB
// Get file name from path
char fileNamePascal[256] = { 0 };
strcpy(fileNamePascal, TextToPascal(GetFileNameWithoutExt(fileName)));
// NOTE: Text data buffer size is estimated considering image data size in bytes
// and requiring 6 char bytes for every byte: "0x00, "
char *txtData = (char *)RL_CALLOC(MAX_FONT_DATA_SIZE, sizeof(char));
int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// FontAsCode exporter v1.0 - Font data exported as an array of bytes //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// ---------------------------------------------------------------------------------- //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// TODO: Fill the information and license of the exported font here: //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Font name: .... //\n");
byteCount += sprintf(txtData + byteCount, "// Font creator: .... //\n");
byteCount += sprintf(txtData + byteCount, "// Font LICENSE: .... //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
// Support font export and initialization
// NOTE: This mechanism is highly coupled to raylib
Image image = LoadImageFromTexture(font.texture);
if (image.format != PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) TRACELOG(LOG_WARNING, "Font export as code: Font image format is not GRAY+ALPHA!");
int imageDataSize = GetPixelDataSize(image.width, image.height, image.format);
// Image data is usually GRAYSCALE + ALPHA and can be reduced to GRAYSCALE
//ImageFormat(&image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
#define SUPPORT_COMPRESSED_FONT_ATLAS
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
// WARNING: Data is compressed using raylib CompressData() DEFLATE,
// it requires to be decompressed with raylib DecompressData(), that requires
// compiling raylib with SUPPORT_COMPRESSION_API config flag enabled
// Compress font image data
int compDataSize = 0;
unsigned char *compData = CompressData(image.data, imageDataSize, &compDataSize);
// Save font image data (compressed)
byteCount += sprintf(txtData + byteCount, "#define COMPRESSED_DATA_SIZE_FONT_%s %i\n\n", TextToUpper(fileNamePascal), compDataSize);
byteCount += sprintf(txtData + byteCount, "// Font image pixels data compressed (DEFLATE)\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: Original pixel data simplified to GRAYSCALE\n");
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);
#else
// Save font image data (uncompressed)
byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: 2 bytes per pixel, GRAY + ALPHA channels\n");
byteCount += sprintf(txtData + byteCount, "static unsigned char fontImageData_%s[%i] = { ", fileNamePascal, imageDataSize);
for (int i = 0; i < imageDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), ((unsigned char *)imFont.data)[i]);
byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", ((unsigned char *)imFont.data)[imageDataSize - 1]);
#endif
// Save font recs data
byteCount += sprintf(txtData + byteCount, "// Font characters rectangles data\n");
byteCount += sprintf(txtData + byteCount, "static const Rectangle fontRecs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
for (int i = 0; i < font.glyphCount; i++)
{
byteCount += sprintf(txtData + byteCount, " { %1.0f, %1.0f, %1.0f , %1.0f },\n", font.recs[i].x, font.recs[i].y, font.recs[i].width, font.recs[i].height);
}
byteCount += sprintf(txtData + byteCount, "};\n\n");
// Save font glyphs data
// NOTE: Glyphs image data not saved (grayscale pixels),
// it could be generated from image and recs
byteCount += sprintf(txtData + byteCount, "// Font glyphs info data\n");
byteCount += sprintf(txtData + byteCount, "// NOTE: No glyphs.image data provided\n");
byteCount += sprintf(txtData + byteCount, "static const GlyphInfo fontGlyphs_%s[%i] = {\n", fileNamePascal, font.glyphCount);
for (int i = 0; i < font.glyphCount; i++)
{
byteCount += sprintf(txtData + byteCount, " { %i, %i, %i, %i, { 0 }},\n", font.glyphs[i].value, font.glyphs[i].offsetX, font.glyphs[i].offsetY, font.glyphs[i].advanceX);
}
byteCount += sprintf(txtData + byteCount, "};\n\n");
// Custom font loading function
byteCount += sprintf(txtData + byteCount, "// Font loading function: %s\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, "static Font LoadFont_%s(void)\n{\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " Font font = { 0 };\n\n");
byteCount += sprintf(txtData + byteCount, " font.baseSize = %i;\n", font.baseSize);
byteCount += sprintf(txtData + byteCount, " font.glyphCount = %i;\n", font.glyphCount);
byteCount += sprintf(txtData + byteCount, " font.glyphPadding = %i;\n\n", font.glyphPadding);
byteCount += sprintf(txtData + byteCount, " // Custom font loading\n");
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
byteCount += sprintf(txtData + byteCount, " // NOTE: Compressed font image data (DEFLATE), it requires DecompressData() function\n");
byteCount += sprintf(txtData + byteCount, " int fontDataSize_%s = 0;\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " unsigned char *data = DecompressData(fontData_%s, COMPRESSED_DATA_SIZE_FONT_%s, &fontDataSize_%s);\n", fileNamePascal, TextToUpper(fileNamePascal), fileNamePascal);
byteCount += sprintf(txtData + byteCount, " Image imFont = { data, %i, %i, 1, %i };\n\n", image.width, image.height, image.format);
#else
byteCount += sprintf(txtData + byteCount, " Image imFont = { fontImageData_%s, %i, %i, 1, %i };\n\n", styleName, image.width, image.height, image.format);
#endif
byteCount += sprintf(txtData + byteCount, " // Load texture from image\n");
byteCount += sprintf(txtData + byteCount, " font.texture = LoadTextureFromImage(imFont);\n");
#if defined(SUPPORT_COMPRESSED_FONT_ATLAS)
byteCount += sprintf(txtData + byteCount, " UnloadImage(imFont); // Uncompressed data can be unloaded from memory\n\n");
#endif
// We have two possible mechanisms to assign font.recs and font.glyphs data,
// that data is already available as global arrays, we two options to assign that data:
// - 1. Data copy. This option consumes more memory and Font MUST be unloaded by user, requiring additional code.
// - 2. Data assignment. This option consumes less memory and Font MUST NOT be unloaded by user because data is on protected DATA segment
//#define SUPPORT_FONT_DATA_COPY
#if defined(SUPPORT_FONT_DATA_COPY)
byteCount += sprintf(txtData + byteCount, " // Copy glyph recs data from global fontRecs\n");
byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n");
byteCount += sprintf(txtData + byteCount, " font.recs = (Rectangle *)malloc(font.glyphCount*sizeof(Rectangle));\n");
byteCount += sprintf(txtData + byteCount, " memcpy(font.recs, fontRecs_%s, font.glyphCount*sizeof(Rectangle));\n\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " // Copy font glyph info data from global fontChars\n");
byteCount += sprintf(txtData + byteCount, " // NOTE: Required to avoid issues if trying to free font\n");
byteCount += sprintf(txtData + byteCount, " font.glyphs = (GlyphInfo *)malloc(font.glyphCount*sizeof(GlyphInfo));\n");
byteCount += sprintf(txtData + byteCount, " memcpy(font.glyphs, fontGlyphs_%s, font.glyphCount*sizeof(GlyphInfo));\n\n", fileNamePascal);
#else
byteCount += sprintf(txtData + byteCount, " // Assign glyph recs and info data directly\n");
byteCount += sprintf(txtData + byteCount, " // WARNING: This font data must not be unloaded\n");
byteCount += sprintf(txtData + byteCount, " font.recs = fontRecs_%s;\n", fileNamePascal);
byteCount += sprintf(txtData + byteCount, " font.glyphs = fontGlyphs_%s;\n\n", fileNamePascal);
#endif
byteCount += sprintf(txtData + byteCount, " return font;\n");
byteCount += sprintf(txtData + byteCount, "}\n");
UnloadImage(image);
// NOTE: Text data size exported is determined by '\0' (NULL) character
success = SaveFileText(fileName, txtData);
RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Font as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export font as code", fileName);
return success;
}
// Draw current FPS
// NOTE: Uses default font
void DrawFPS(int posX, int posY)
{
Color color = LIME; // good fps
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
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);
}
@ -875,7 +1054,7 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f
{
// NOTE: Fixed line spacing of 1.5 line-height
// TODO: Support custom line spacing defined by user
textOffsetY += (int)((font.baseSize + font.baseSize/2)*scaleFactor);
textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor);
textOffsetX = 0.0f;
}
else
@ -931,10 +1110,42 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz
DrawTexturePro(font.texture, srcRec, dstRec, (Vector2){ 0, 0 }, 0.0f, tint);
}
// Draw multiple character (codepoints)
void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint)
{
int textOffsetY = 0; // Offset between lines (on line break '\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++)
{
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);
textOffsetX = 0.0f;
}
else
{
if ((codepoints[i] != ' ') && (codepoints[i] != '\t'))
{
DrawTextCodepoint(font, codepoints[i], (Vector2){ position.x + textOffsetX, position.y + textOffsetY }, fontSize, tint);
}
if (font.glyphs[index].advanceX == 0) textOffsetX += ((float)font.recs[index].width*scaleFactor + spacing);
else textOffsetX += ((float)font.glyphs[index].advanceX*scaleFactor + spacing);
}
}
}
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
Vector2 vec = { 0.0f, 0.0f };
Vector2 textSize = { 0.0f, 0.0f };
// Check if default font has been loaded
if (GetFontDefault().texture.id != 0)
@ -943,15 +1154,19 @@ int MeasureText(const char *text, int fontSize)
if (fontSize < defaultFontSize) fontSize = defaultFontSize;
int spacing = fontSize/defaultFontSize;
vec = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
textSize = MeasureTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing);
}
return (int)vec.x;
return (int)textSize.x;
}
// Measure string size for Font
Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing)
{
Vector2 textSize = { 0 };
if ((font.texture.id == 0) || (text == NULL)) return textSize;
int size = TextLength(text); // Get size in bytes of text
int tempByteCounter = 0; // Used to count longer text line num chars
int byteCounter = 0;
@ -996,11 +1211,10 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing
if (tempTextWidth < textWidth) tempTextWidth = textWidth;
Vector2 vec = { 0 };
vec.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure
vec.y = textHeight*scaleFactor;
textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure
textSize.y = textHeight*scaleFactor;
return vec;
return textSize;
}
// Get index position for a unicode character on font
@ -1179,7 +1393,7 @@ const char *TextSubtext(const char *text, int position, int length)
// Replace text string
// REQUIRES: strlen(), strstr(), strncpy(), strcpy()
// WARNING: Returned buffer must be freed by the user (if return != NULL)
// WARNING: Allocated memory must be manually freed
char *TextReplace(char *text, const char *replace, const char *by)
{
// Sanity checks and initialization
@ -1228,7 +1442,7 @@ char *TextReplace(char *text, const char *replace, const char *by)
}
// Insert text in a specific position, moves all text forward
// WARNING: Allocated memory should be manually freed
// WARNING: Allocated memory must be manually freed
char *TextInsert(const char *text, const char *insert, int position)
{
int textLen = TextLength(text);
@ -1412,8 +1626,8 @@ const char *TextToPascal(const char *text)
// Encode text codepoint into UTF-8 text
// REQUIRES: memcpy()
// WARNING: Allocated memory should be manually freed
char *TextCodepointsToUTF8(int *codepoints, int length)
// WARNING: Allocated memory must be manually freed
char *TextCodepointsToUTF8(const int *codepoints, int length)
{
// We allocate enough memory fo fit all possible codepoints
// NOTE: 5 bytes for every codepoint should be enough
@ -1593,7 +1807,7 @@ int GetCodepoint(const char *text, int *bytesProcessed)
if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) ||
((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; }
if ((octet >= 0xe0) && (0 <= 0xef))
if ((octet >= 0xe0) && (octet <= 0xef))
{
code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f);
*bytesProcessed = 3;
@ -1795,3 +2009,5 @@ static Font LoadBMFont(const char *fileName)
return font;
}
#endif
#endif // SUPPORT_MODULE_RTEXT