From 7d07d21ee53d4b38bd102c9fdf7534b43c062fe5 Mon Sep 17 00:00:00 2001 From: 0_stamina Date: Sun, 31 Aug 2025 22:56:31 -0700 Subject: [PATCH 01/26] Update Camera3D.fovy description in raylib.h (#5164) Previously stated it was the near plane width in orthographic, in actuality it's the height. The wording has been fixed, with added clarity that it's in world units so no conversion is needed. --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index c45168763..a460ed240 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -327,7 +327,7 @@ typedef struct Camera3D { Vector3 position; // Camera position Vector3 target; // Camera target it looks-at Vector3 up; // Camera up vector (rotation over its axis) - float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic + float fovy; // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane height in world units in orthographic int projection; // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC } Camera3D; From 223feca13ab0d51a75f7cdb6a4fc2a16dcbf2308 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 05:56:44 +0000 Subject: [PATCH 02/26] Update raylib_api.* by CI --- tools/parser/output/raylib_api.json | 2 +- tools/parser/output/raylib_api.lua | 2 +- tools/parser/output/raylib_api.txt | 2 +- tools/parser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 3617d6392..45d14a5b3 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -753,7 +753,7 @@ { "type": "float", "name": "fovy", - "description": "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic" + "description": "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane height in world units in orthographic" }, { "type": "int", diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index 00abc0c77..3c975531f 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -753,7 +753,7 @@ return { { type = "float", name = "fovy", - description = "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic" + description = "Camera field-of-view aperture in Y (degrees) in perspective, used as near plane height in world units in orthographic" }, { type = "int", diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index 12af97c08..d8eeb6541 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -394,7 +394,7 @@ Struct 13: Camera3D (5 fields) Field[1]: Vector3 position // Camera position Field[2]: Vector3 target // Camera target it looks-at Field[3]: Vector3 up // Camera up vector (rotation over its axis) - Field[4]: float fovy // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane width in orthographic + Field[4]: float fovy // Camera field-of-view aperture in Y (degrees) in perspective, used as near plane height in world units in orthographic Field[5]: int projection // Camera projection: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC Struct 14: Camera2D (4 fields) Name: Camera2D diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 0a053abc6..9f887912c 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -151,7 +151,7 @@ - + From 998a18b641dc333ebda0a40060de69c6bbb07546 Mon Sep 17 00:00:00 2001 From: wileyanderssen Date: Mon, 1 Sep 2025 12:17:51 +0300 Subject: [PATCH 03/26] removed macro that does nothing (#5163) --- src/rtextures.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 624cf574a..ea32f2c04 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -658,8 +658,6 @@ bool ExportImage(Image image, const char *fileName) result = SaveFileData(fileName, fileData, dataSize); RL_FREE(fileData); } -#else - if (false) { } #endif #if defined(SUPPORT_FILEFORMAT_BMP) else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); From 12ce72c32d6946bef6b763aacfc5efb23641c98a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 11:24:53 +0200 Subject: [PATCH 04/26] REVIEWED: `ExportImage()` --- src/rtextures.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index ea32f2c04..2f8b60f17 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -422,7 +422,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i { Image image = { 0 }; - // Security check for input data + // Security checks for input data if ((fileData == NULL) || (dataSize == 0)) { TRACELOG(LOG_WARNING, "IMAGE: Invalid file data"); @@ -650,8 +650,9 @@ bool ExportImage(Image image, const char *fileName) allocatedData = true; } + if (false) { 0 } // Required to attach following 'else' cases #if defined(SUPPORT_FILEFORMAT_PNG) - if (IsFileExtension(fileName, ".png")) + else if (IsFileExtension(fileName, ".png")) { int dataSize = 0; unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize); @@ -701,7 +702,8 @@ bool ExportImage(Image image, const char *fileName) // NOTE: It's up to the user to track image parameters result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); } - + else TRACELOG(LOG_WARNING, "IMAGE: Export image format requested not supported"); + if (allocatedData) RL_FREE(imgData); #endif // SUPPORT_IMAGE_EXPORT From 0a92c863c412b9eb2fe2d0ab17a8fe88fa8775ce Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 11:27:11 +0200 Subject: [PATCH 05/26] Reverted const codepoints to avoid warnings --- src/raylib.h | 4 ++-- src/rtext.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index a460ed240..b5de3c71d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1466,9 +1466,9 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked) RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info diff --git a/src/rtext.c b/src/rtext.c index db143b098..f54c71aef 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -404,7 +404,7 @@ Font LoadFont(const char *fileName) // Load Font from TTF or BDF 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, const int *codepoints, int codepointCount) +Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; @@ -549,7 +549,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, const int *codepoints, int codepointCount) +Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; From 917533224f50235e64290c59d21a0a55953dcacf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 1 Sep 2025 09:27:38 +0000 Subject: [PATCH 06/26] Update raylib_api.* by CI --- tools/parser/output/raylib_api.json | 4 ++-- tools/parser/output/raylib_api.lua | 4 ++-- tools/parser/output/raylib_api.txt | 4 ++-- tools/parser/output/raylib_api.xml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 45d14a5b3..8828ffb18 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -9041,7 +9041,7 @@ "name": "fontSize" }, { - "type": "const int *", + "type": "int *", "name": "codepoints" }, { @@ -9091,7 +9091,7 @@ "name": "fontSize" }, { - "type": "const int *", + "type": "int *", "name": "codepoints" }, { diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index 3c975531f..d0cce09eb 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -6550,7 +6550,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "int", name = "fontSize"}, - {type = "const int *", name = "codepoints"}, + {type = "int *", name = "codepoints"}, {type = "int", name = "codepointCount"} } }, @@ -6573,7 +6573,7 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "const int *", name = "codepoints"}, + {type = "int *", name = "codepoints"}, {type = "int", name = "codepointCount"} } }, diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index d8eeb6541..82fdc2c6a 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -3468,7 +3468,7 @@ Function 395: LoadFontEx() (4 input parameters) Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height Param[1]: fileName (type: const char *) Param[2]: fontSize (type: int) - Param[3]: codepoints (type: const int *) + Param[3]: codepoints (type: int *) Param[4]: codepointCount (type: int) Function 396: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage @@ -3485,7 +3485,7 @@ Function 397: LoadFontFromMemory() (6 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: fontSize (type: int) - Param[5]: codepoints (type: const int *) + Param[5]: codepoints (type: int *) Param[6]: codepointCount (type: int) Function 398: IsFontValid() (1 input parameters) Name: IsFontValid diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 9f887912c..f2e3b3b4f 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -2286,7 +2286,7 @@ - + @@ -2299,7 +2299,7 @@ - + From 708c1539e4b82e515fb32167c319cd06bec52c3b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 11:38:57 +0200 Subject: [PATCH 07/26] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 2f8b60f17..dc6695452 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -650,7 +650,7 @@ bool ExportImage(Image image, const char *fileName) allocatedData = true; } - if (false) { 0 } // Required to attach following 'else' cases + if (false) { } // Required to attach following 'else' cases #if defined(SUPPORT_FILEFORMAT_PNG) else if (IsFileExtension(fileName, ".png")) { From 1777da9056ac84bb7410392e103c6a0964570d67 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 20:37:23 +0200 Subject: [PATCH 08/26] REVIEWED: Avoid realloc() calls, small security improvement --- src/rcore.c | 19 ++++++++++--------- src/rmodels.c | 18 ++++++++++++++---- src/rtext.c | 16 ++++++++++------ src/rtextures.c | 11 +++++------ 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 4360a23ee..d1b1a8219 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2562,19 +2562,20 @@ unsigned char *DecompressData(const unsigned char *compData, int compDataSize, i #if defined(SUPPORT_COMPRESSION_API) // Decompress data from a valid DEFLATE stream - data = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1); + unsigned char *data0 = (unsigned char *)RL_CALLOC(MAX_DECOMPRESSION_SIZE*1024*1024, 1); int length = sinflate(data, MAX_DECOMPRESSION_SIZE*1024*1024, compData, compDataSize); - // WARNING: RL_REALLOC can make (and leave) data copies in memory, be careful with sensitive compressed data! - // TODO: Use a different approach, create another buffer, copy data manually to it and wipe original buffer memory - unsigned char *temp = (unsigned char *)RL_REALLOC(data, length); - - if (temp != NULL) data = temp; - else TRACELOG(LOG_WARNING, "SYSTEM: Failed to re-allocate required decompression memory"); + // WARNING: RL_REALLOC can make (and leave) data copies in memory, + // that can be a security concern in case of compression of sensitive data + // So, we use a second buffer to copy data manually, wiping original buffer memory + data = (unsigned char *)RL_CALLOC(length, 1); + memcpy(data, data0, length); + memset(data0, 0, MAX_DECOMPRESSION_SIZE*1024*1024); // Wipe memory, is memset() safe? + RL_FREE(data0); + + TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, length); *dataSize = length; - - TRACELOG(LOG_INFO, "SYSTEM: Decompress data: Comp. size: %i -> Original size: %i", compDataSize, *dataSize); #endif return data; diff --git a/src/rmodels.c b/src/rmodels.c index ff71b79d6..5a824ce44 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -6573,13 +6573,23 @@ static Model LoadM3D(const char *fileName) // Materials are grouped together if (mi != m3d->face[i].materialid) { - // there should be only one material switch per material kind, but be bulletproof for non-optimal model files + // There should be only one material switch per material kind, + // but be bulletproof for non-optimal model files if (k + 1 >= model.meshCount) { model.meshCount++; - model.meshes = (Mesh *)RL_REALLOC(model.meshes, model.meshCount*sizeof(Mesh)); - memset(&model.meshes[model.meshCount - 1], 0, sizeof(Mesh)); - model.meshMaterial = (int *)RL_REALLOC(model.meshMaterial, model.meshCount*sizeof(int)); + + // Create a second buffer for mesh re-allocation + Mesh *tempMeshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); + memcpy(tempMeshes, model.meshes, (model.meshCount - 1)*sizeof(Mesh)); + RL_FREE(model.meshes); + model.meshes = tempMeshes; + + // Create a second buffer for material re-allocation + int *tempMeshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + memcpy(tempMeshMaterial, model.meshMaterial, (model.meshCount - 1)*sizeof(int)); + RL_FREE(model.meshMaterial); + model.meshMaterial = tempMeshMaterial; } k++; diff --git a/src/rtext.c b/src/rtext.c index f54c71aef..ffa432ed3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1920,10 +1920,11 @@ char *LoadUTF8(const int *codepoints, int length) size += bytes; } - // Resize memory to text length + string NULL terminator - void *ptr = RL_REALLOC(text, size + 1); - - if (ptr != NULL) text = (char *)ptr; + // Create second buffer and copy data manually to it + char *temp = (char *)RL_CALLOC(size + 1, 1); + memcpy(temp, text, size); + RL_FREE(text); + text = temp; return text; } @@ -1951,8 +1952,11 @@ int *LoadCodepoints(const char *text, int *count) i += codepointSize; } - // Re-allocate buffer to the actual number of codepoints loaded - codepoints = (int *)RL_REALLOC(codepoints, codepointCount*sizeof(int)); + // Create second buffer and copy data manually to it + int *temp = (int *)RL_CALLOC(codepointCount, sizeof(int)); + for (int i = 0; i < codepointCount; i++) temp[i] = codepoints[i]; + RL_FREE(codepoints); + codepoints = temp; *count = codepointCount; diff --git a/src/rtextures.c b/src/rtextures.c index dc6695452..a4c34b000 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2400,10 +2400,11 @@ void ImageMipmaps(Image *image) if (image->mipmaps < mipCount) { - void *temp = RL_REALLOC(image->data, mipSize); - - if (temp != NULL) image->data = temp; // Assign new pointer (new size) to store mipmaps data - else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated"); + // Create second buffer and copy data manually to it + void *temp = RL_CALLOC(mipSize, 1); + memcpy(temp, image->data, GetPixelDataSize(image->width, image->height, image->format)); + RL_FREE(image->data); + image->data = temp; // Pointer to allocated memory point where store next mipmap level data unsigned char *nextmip = image->data; @@ -2429,9 +2430,7 @@ void ImageMipmaps(Image *image) if (i < image->mipmaps) continue; TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip); - ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter - memcpy(nextmip, imCopy.data, mipSize); } From e18d167c8f50a101ecce904ef9103190db3676d0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 20:47:59 +0200 Subject: [PATCH 09/26] REVIEWED: New examples added renaming, avoid duplicated .sln GUIDs --- examples/core/core_3d_camera_fps.c | 12 ++--- ...ler.vcxproj => core_3d_camera_fps.vcxproj} | 0 .../examples/text_unicode_ranges.vcxproj | 2 +- projects/VS2022/raylib.sln | 54 +++++++++---------- 4 files changed, 34 insertions(+), 34 deletions(-) rename projects/VS2022/examples/{core_3d_fps_controller.vcxproj => core_3d_camera_fps.vcxproj} (100%) diff --git a/examples/core/core_3d_camera_fps.c b/examples/core/core_3d_camera_fps.c index a3f1d5716..1efb66770 100644 --- a/examples/core/core_3d_camera_fps.c +++ b/examples/core/core_3d_camera_fps.c @@ -18,7 +18,7 @@ #include "raylib.h" #include "raymath.h" -#include "rcamera.h" +//#include "rcamera.h" //---------------------------------------------------------------------------------- // Defines and Macros @@ -68,7 +68,7 @@ static Vector2 lean = { 0 }; // Module functions declaration //---------------------------------------------------------------------------------- static void DrawLevel(void); -static void UpdateCameraAngle(Camera *camera); +static void UpdateCameraFPS(Camera *camera); static void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold); //------------------------------------------------------------------------------------ @@ -84,7 +84,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d first-person camera controller"); // Initialize camera variables - // NOTE: UpdateCameraAngle() takes care of the rest + // NOTE: UpdateCameraFPS() takes care of the rest Camera camera = { 0 }; camera.fovy = 60.0f; camera.projection = CAMERA_PERSPECTIVE; @@ -94,7 +94,7 @@ int main(void) player.position.z, }; - UpdateCameraAngle(&camera); // Update camera parameters + UpdateCameraFPS(&camera); // Update camera parameters DisableCursor(); // Limit cursor to relative movement inside the window @@ -138,7 +138,7 @@ int main(void) lean.x = Lerp(lean.x, sideway*0.02f, 10.0f*delta); lean.y = Lerp(lean.y, forward*0.015f, 10.0f*delta); - UpdateCameraAngle(&camera); + UpdateCameraFPS(&camera); //---------------------------------------------------------------------------------- // Draw @@ -238,7 +238,7 @@ void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed } // Update camera -static void UpdateCameraAngle(Camera *camera) +static void UpdateCameraFPS(Camera *camera) { const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f }; const Vector3 targetOffset = (Vector3){ 0.0f, 0.0f, -1.0f }; diff --git a/projects/VS2022/examples/core_3d_fps_controller.vcxproj b/projects/VS2022/examples/core_3d_camera_fps.vcxproj similarity index 100% rename from projects/VS2022/examples/core_3d_fps_controller.vcxproj rename to projects/VS2022/examples/core_3d_camera_fps.vcxproj diff --git a/projects/VS2022/examples/text_unicode_ranges.vcxproj b/projects/VS2022/examples/text_unicode_ranges.vcxproj index c9ddd9a1c..730bcb1d0 100644 --- a/projects/VS2022/examples/text_unicode_ranges.vcxproj +++ b/projects/VS2022/examples/text_unicode_ranges.vcxproj @@ -51,7 +51,7 @@ - {6B1A933E-71B8-4C1F-9E79-02D98830E671} + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} Win32Proj text_unicode_ranges 10.0 diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 21d7c63b6..227d52297 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -337,7 +337,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_fps", "examp EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_normal_map", "examples\shaders_normal_map.vcxproj", "{6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_unicode_ranges", "examples\text_unicode_ranges.vcxproj", "{6B1A933E-71B8-4C1F-9E79-02D98830E671}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "text_unicode_ranges", "examples\text_unicode_ranges.vcxproj", "{6777EC3C-077C-42FC-B4AD-B799CE55CCE4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -4135,30 +4135,30 @@ Global {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x64.Build.0 = Release|x64 {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.ActiveCfg = Release|Win32 {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3}.Release|x86.Build.0 = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|ARM64.Build.0 = Debug|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.ActiveCfg = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x64.Build.0 = Debug|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.ActiveCfg = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Debug|x86.Build.0 = Debug|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.ActiveCfg = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|ARM64.Build.0 = Release|ARM64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.ActiveCfg = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x64.Build.0 = Release|x64 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.ActiveCfg = Release|Win32 - {6B1A933E-71B8-4C1F-9E79-02D98830E671}.Release|x86.Build.0 = Release|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.ActiveCfg = Debug.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|ARM64.Build.0 = Debug.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|ARM64.Build.0 = Debug|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.ActiveCfg = Debug|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x64.Build.0 = Debug|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.ActiveCfg = Debug|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Debug|x86.Build.0 = Debug|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.ActiveCfg = Release.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|ARM64.Build.0 = Release.DLL|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.ActiveCfg = Release|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|ARM64.Build.0 = Release|ARM64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.ActiveCfg = Release|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x64.Build.0 = Release|x64 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.ActiveCfg = Release|Win32 + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -4327,9 +4327,9 @@ Global {C54703BF-D68A-480D-BE27-49B62E45D582} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {9CD8BCAD-F212-4BCC-BA98-899743CE3279} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {0981CA28-E4A5-4DF1-987F-A41D09131EFC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6BFF72EA-7362-4A3B-B6E5-9A3655BBBDA3} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} - {6B1A933E-71B8-4C1F-9E79-02D98830E671} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} + {6777EC3C-077C-42FC-B4AD-B799CE55CCE4} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 864459cbd2ebe8837d574af699d5fd68d1d58d14 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 Sep 2025 20:59:37 +0200 Subject: [PATCH 10/26] Update core_3d_camera_fps.c --- examples/core/core_3d_camera_fps.c | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/core/core_3d_camera_fps.c b/examples/core/core_3d_camera_fps.c index 1efb66770..7fbd4b137 100644 --- a/examples/core/core_3d_camera_fps.c +++ b/examples/core/core_3d_camera_fps.c @@ -18,7 +18,6 @@ #include "raylib.h" #include "raymath.h" -//#include "rcamera.h" //---------------------------------------------------------------------------------- // Defines and Macros From b6ae3802602c22fb0c35330dd1b4c58b72f5506f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 12:10:16 +0200 Subject: [PATCH 11/26] REVIEWED: Examples comments, consistent code sections --- examples/audio/audio_sound_positioning.c | 9 +++-- examples/audio/audio_stream_effects.c | 4 ++- examples/core/core_2d_camera_platformer.c | 5 ++- examples/core/core_3d_camera_fps.c | 9 ++--- examples/core/core_basic_window_web.c | 2 +- examples/core/core_high_dpi.c | 4 +-- examples/core/core_loading_thread.c | 15 ++++++--- examples/core/core_random_sequence.c | 9 +++-- examples/models/models_rlgl_solar_system.c | 3 +- examples/others/rlgl_standalone.c | 6 ++-- examples/shaders/shaders_basic_pbr.c | 20 ++++++----- examples/shaders/shaders_hybrid_render.c | 35 +++++++++++--------- examples/shaders/shaders_rounded_rectangle.c | 6 ++-- examples/shaders/shaders_spotlight.c | 6 ++++ examples/shaders/shaders_write_depth.c | 13 ++++---- examples/shapes/shapes_digital_clock.c | 1 - examples/shapes/shapes_double_pendulum.c | 10 +++--- examples/shapes/shapes_rectangle_advanced.c | 6 ++++ examples/text/text_draw_3d.c | 4 +-- examples/text/text_rectangle_bounds.c | 4 +-- examples/text/text_unicode.c | 28 ++++++++-------- examples/text/text_unicode_ranges.c | 13 +++++--- examples/textures/textures_image_kernel.c | 4 +-- examples/textures/textures_textured_curve.c | 1 - projects/CMake/core_basic_window.c | 2 +- projects/VSCode/main.c | 6 ++-- 26 files changed, 131 insertions(+), 94 deletions(-) diff --git a/examples/audio/audio_sound_positioning.c b/examples/audio/audio_sound_positioning.c index 5bef2d671..9f09420a2 100644 --- a/examples/audio/audio_sound_positioning.c +++ b/examples/audio/audio_sound_positioning.c @@ -19,7 +19,9 @@ #include "raymath.h" -// Sound positioning function +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, float maxDist); //------------------------------------------------------------------------------------ @@ -94,7 +96,10 @@ int main(void) //-------------------------------------------------------------------------------------- } -// Sound positioning function +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ +// Set sound 3d position static void SetSoundPosition(Camera listener, Sound sound, Vector3 position, float maxDist) { // Calculate direction vector and distance between listener and sound source diff --git a/examples/audio/audio_stream_effects.c b/examples/audio/audio_stream_effects.c index b848c8c31..0d06e1e03 100644 --- a/examples/audio/audio_stream_effects.c +++ b/examples/audio/audio_stream_effects.c @@ -17,7 +17,9 @@ #include // Required for: NULL -// Required delay effect variables +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- static float *delayBuffer = NULL; static unsigned int delayBufferSize = 0; static unsigned int delayReadIndex = 2; diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 9a6ec9ade..45bad4015 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -22,6 +22,9 @@ #define PLAYER_JUMP_SPD 350.0f #define PLAYER_HOR_SPD 200.0f +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- typedef struct Player { Vector2 position; float speed; @@ -35,7 +38,7 @@ typedef struct EnvItem { } EnvItem; //---------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- void UpdatePlayer(Player *player, EnvItem *envItems, int envItemsLength, float delta); void UpdateCameraCenter(Camera2D *camera, Player *player, EnvItem *envItems, int envItemsLength, float delta, int width, int height); diff --git a/examples/core/core_3d_camera_fps.c b/examples/core/core_3d_camera_fps.c index 7fbd4b137..980660b30 100644 --- a/examples/core/core_3d_camera_fps.c +++ b/examples/core/core_3d_camera_fps.c @@ -64,7 +64,7 @@ static float headLerp = STAND_HEIGHT; static Vector2 lean = { 0 }; //---------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- static void DrawLevel(void); static void UpdateCameraFPS(Camera *camera); @@ -172,9 +172,10 @@ int main(void) } //---------------------------------------------------------------------------------- -// Module functions definition +// Module Functions Definition //---------------------------------------------------------------------------------- -void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed, bool crouchHold) +// Update body considering current world state +void UpdateBody(Body *body, float rot, char side, char forward, bool jumpPressed, bool crouchHold) { Vector2 input = (Vector2){ (float)side, (float)-forward }; @@ -236,7 +237,7 @@ void UpdateBody(Body* body, float rot, char side, char forward, bool jumpPressed } } -// Update camera +// Update camera for FPS behaviour static void UpdateCameraFPS(Camera *camera) { const Vector3 up = (Vector3){ 0.0f, 1.0f, 0.0f }; diff --git a/examples/core/core_basic_window_web.c b/examples/core/core_basic_window_web.c index c4aa18177..7877337a3 100644 --- a/examples/core/core_basic_window_web.c +++ b/examples/core/core_basic_window_web.c @@ -32,7 +32,7 @@ const int screenWidth = 800; const int screenHeight = 450; //---------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- void UpdateDrawFrame(void); // Update and Draw one frame diff --git a/examples/core/core_high_dpi.c b/examples/core/core_high_dpi.c index 8de5fa261..636442401 100644 --- a/examples/core/core_high_dpi.c +++ b/examples/core/core_high_dpi.c @@ -14,7 +14,7 @@ #include "raylib.h" //------------------------------------------------------------------------------------ -// Module functions declaration +// Module Functions Declaration //------------------------------------------------------------------------------------ static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color); @@ -120,7 +120,7 @@ int main(void) } //------------------------------------------------------------------------------------ -// Module functions definition +// Module Functions Definition //------------------------------------------------------------------------------------ static void DrawTextCenter(const char *text, int x, int y, int fontSize, Color color) { diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 1fa84640d..3d04f94a7 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -20,16 +20,18 @@ // WARNING: This example does not build on Windows with MSVC compiler #include "pthread.h" // POSIX style threads management -#include // C11 atomic data types - +#include // Required for: C11 atomic data types #include // Required for: clock() // Using C11 atomics for synchronization // NOTE: A plain bool (or any plain data type for that matter) can't be used for inter-thread synchronization -static atomic_bool dataLoaded = false; // Data Loaded completion indicator -static void *LoadDataThread(void *arg); // Loading data thread function declaration +static atomic_bool dataLoaded = false; // Data Loaded completion indicator +static atomic_int dataProgress = 0; // Data progress accumulator -static atomic_int dataProgress = 0; // Data progress accumulator +//------------------------------------------------------------------------------------ +// Module Functions Declaration +//------------------------------------------------------------------------------------ +static void *LoadDataThread(void *arg); // Loading data thread function declaration //------------------------------------------------------------------------------------ // Program main entry point @@ -134,6 +136,9 @@ int main(void) return 0; } +//------------------------------------------------------------------------------------ +// Module Functions Definition +//------------------------------------------------------------------------------------ // Loading data thread function definition static void *LoadDataThread(void *arg) { diff --git a/examples/core/core_random_sequence.c b/examples/core/core_random_sequence.c index be63e2f9b..3121debc7 100644 --- a/examples/core/core_random_sequence.c +++ b/examples/core/core_random_sequence.c @@ -18,15 +18,18 @@ #include "raylib.h" #include "raymath.h" -#include // Required for: malloc() and free() +#include // Required for: malloc(), free() +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- typedef struct ColorRect { Color c; Rectangle r; } ColorRect; //------------------------------------------------------------------------------------ -// Module functions declaration +// Module Functions Declaration //------------------------------------------------------------------------------------ static Color GenerateRandomColor(); static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight); @@ -114,7 +117,7 @@ int main(void) } //------------------------------------------------------------------------------------ -// Module functions definition +// Module Functions Definition //------------------------------------------------------------------------------------ static Color GenerateRandomColor() { diff --git a/examples/models/models_rlgl_solar_system.c b/examples/models/models_rlgl_solar_system.c index ee1e713c2..d8d86d69a 100644 --- a/examples/models/models_rlgl_solar_system.c +++ b/examples/models/models_rlgl_solar_system.c @@ -128,9 +128,8 @@ int main(void) } //-------------------------------------------------------------------------------------------- -// Module Functions Definitions (local) +// Module Functions Definition //-------------------------------------------------------------------------------------------- - // Draw sphere without any matrix transformation // NOTE: Sphere is drawn in world position ( 0, 0, 0 ) with radius 1.0f void DrawSphereBasic(Color color) diff --git a/examples/others/rlgl_standalone.c b/examples/others/rlgl_standalone.c index eddb63a0f..288f06419 100644 --- a/examples/others/rlgl_standalone.c +++ b/examples/others/rlgl_standalone.c @@ -77,7 +77,7 @@ #define DARKGRAY (Color){ 80, 80, 80, 255 } // Dark Gray //---------------------------------------------------------------------------------- -// Structures Definition +// Types and Structures Definition //---------------------------------------------------------------------------------- // Color, 4 components, R8G8B8A8 (32bit) typedef struct Color { @@ -97,7 +97,7 @@ typedef struct Camera { } Camera; //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- static void ErrorCallback(int error, const char *description); static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); @@ -260,7 +260,7 @@ int main(void) } //---------------------------------------------------------------------------------- -// Module specific Functions Definitions +// Module Functions Definitions //---------------------------------------------------------------------------------- // GLFW3: Error callback diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c index 1844a43bc..ec15ab206 100644 --- a/examples/shaders/shaders_basic_pbr.c +++ b/examples/shaders/shaders_basic_pbr.c @@ -34,7 +34,6 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- - // Light type typedef enum { LIGHT_DIRECTIONAL = 0, @@ -66,7 +65,7 @@ typedef struct { static int lightCount = 0; // Current number of dynamic lights that have been created //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- // Create a light and get shader locations static Light CreateLight(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader); @@ -76,7 +75,7 @@ static Light CreateLight(int type, Vector3 position, Vector3 target, Color color static void UpdateLight(Shader shader, Light light); //---------------------------------------------------------------------------------- -// Main Entry Point +// Program main entry point //---------------------------------------------------------------------------------- int main() { @@ -231,9 +230,9 @@ int main() Vector4 floorEmissiveColor = ColorNormalize(floor.materials[0].maps[MATERIAL_MAP_EMISSION].color); SetShaderValue(shader, emissiveColorLoc, &floorEmissiveColor, SHADER_UNIFORM_VEC4); - // Set floor metallic and roughness values - SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); - SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); + // Set floor metallic and roughness values + SetShaderValue(shader, metallicValueLoc, &floor.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, roughnessValueLoc, &floor.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); DrawModel(floor, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); // Draw floor model @@ -244,9 +243,9 @@ int main() float emissiveIntensity = 0.01f; SetShaderValue(shader, emissiveIntensityLoc, &emissiveIntensity, SHADER_UNIFORM_FLOAT); - // Set old car metallic and roughness values - SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); - SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); + // Set old car metallic and roughness values + SetShaderValue(shader, metallicValueLoc, &car.materials[0].maps[MATERIAL_MAP_METALNESS].value, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, roughnessValueLoc, &car.materials[0].maps[MATERIAL_MAP_ROUGHNESS].value, SHADER_UNIFORM_FLOAT); DrawModel(car, (Vector3){ 0.0f, 0.0f, 0.0f }, 0.25f, WHITE); // Draw car model @@ -293,6 +292,9 @@ int main() return 0; } +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- // Create light with provided data // NOTE: It updated the global lightCount and it's limited to MAX_LIGHTS static Light CreateLight(int type, Vector3 position, Vector3 target, Color color, float intensity, Shader shader) diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index d3c3a910b..9882306a0 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -16,32 +16,35 @@ ********************************************************************************************/ #include "raylib.h" + #include "rlgl.h" -#include "math.h" // Used for tan() -#include "raymath.h" // Used to calculate camera Direction +#include "raymath.h" + +#include // Required for: tanf() #if defined(PLATFORM_DESKTOP) -#define GLSL_VERSION 330 + #define GLSL_VERSION 330 #else // PLATFORM_ANDROID, PLATFORM_WEB -#define GLSL_VERSION 100 + #define GLSL_VERSION 100 #endif +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + unsigned int camPos; + unsigned int camDir; + unsigned int screenCenter; +} RayLocs; + //------------------------------------------------------------------------------------ -// Declare custom functions required for the example +// Module Functions Declaration //------------------------------------------------------------------------------------ // Load custom render texture, create a writable depth texture buffer static RenderTexture2D LoadRenderTextureDepthTex(int width, int height); // Unload render texture from GPU memory (VRAM) static void UnloadRenderTextureDepthTex(RenderTexture2D target); -//------------------------------------------------------------------------------------ -// Declare custom Structs -//------------------------------------------------------------------------------------ - -typedef struct { - unsigned int camPos, camDir, screenCenter; -}RayLocs ; - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -153,10 +156,10 @@ int main(void) } //------------------------------------------------------------------------------------ -// Define custom functions required for the example +// Module Functions Definition //------------------------------------------------------------------------------------ // Load custom render texture, create a writable depth texture buffer -RenderTexture2D LoadRenderTextureDepthTex(int width, int height) +static RenderTexture2D LoadRenderTextureDepthTex(int width, int height) { RenderTexture2D target = { 0 }; @@ -195,7 +198,7 @@ RenderTexture2D LoadRenderTextureDepthTex(int width, int height) } // Unload render texture from GPU memory (VRAM) -void UnloadRenderTextureDepthTex(RenderTexture2D target) +static void UnloadRenderTextureDepthTex(RenderTexture2D target) { if (target.id > 0) { diff --git a/examples/shaders/shaders_rounded_rectangle.c b/examples/shaders/shaders_rounded_rectangle.c index 654fba49b..ddfab299c 100644 --- a/examples/shaders/shaders_rounded_rectangle.c +++ b/examples/shaders/shaders_rounded_rectangle.c @@ -23,9 +23,9 @@ #define GLSL_VERSION 100 #endif -//------------------------------------------------------------------------------------ -// Structs definition -//------------------------------------------------------------------------------------ +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- // Rounded rectangle data typedef struct { Vector4 cornerRadius; // Individual corner radius (top-left, top-right, bottom-left, bottom-right) diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index 1ab7664b5..1188ea8da 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -43,6 +43,9 @@ #define MAX_SPOTS 3 // NOTE: It must be the same as define in shader #define MAX_STARS 400 +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- // Spot data typedef struct Spot { Vector2 position; @@ -62,6 +65,9 @@ typedef struct Star { Vector2 speed; } Star; +//-------------------------------------------------------------------------------------- +// Module Functions Declaration +//-------------------------------------------------------------------------------------- static void UpdateStar(Star *s); static void ResetStar(Star *s); diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 54e504a48..a80eb83e5 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -25,9 +25,9 @@ #define GLSL_VERSION 100 #endif -//------------------------------------------------------------------------------------ -// Declare custom functions required for the example -//------------------------------------------------------------------------------------ +//-------------------------------------------------------------------------------------- +// Module Functions Declaration +//-------------------------------------------------------------------------------------- // Load custom render texture, create a writable depth texture buffer static RenderTexture2D LoadRenderTextureDepthTex(int width, int height); @@ -109,9 +109,10 @@ int main(void) return 0; } -//------------------------------------------------------------------------------------ -// Define custom functions required for the example -//------------------------------------------------------------------------------------ +//-------------------------------------------------------------------------------------- +// Module Functions Definition +//-------------------------------------------------------------------------------------- + // Load custom render texture, create a writable depth texture buffer RenderTexture2D LoadRenderTextureDepthTex(int width, int height) { diff --git a/examples/shapes/shapes_digital_clock.c b/examples/shapes/shapes_digital_clock.c index 5f13dd2a8..20c43ed7b 100644 --- a/examples/shapes/shapes_digital_clock.c +++ b/examples/shapes/shapes_digital_clock.c @@ -128,7 +128,6 @@ int main(void) //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Update clock time static void UpdateClock(Clock *clock) { diff --git a/examples/shapes/shapes_double_pendulum.c b/examples/shapes/shapes_double_pendulum.c index e2817f24b..200f9ad7e 100644 --- a/examples/shapes/shapes_double_pendulum.c +++ b/examples/shapes/shapes_double_pendulum.c @@ -19,9 +19,6 @@ #include // Required for: sin(), cos(), PI -//---------------------------------------------------------------------------------- -// Macro Helpers -//---------------------------------------------------------------------------------- // Constant for Simulation #define SIMULATION_STEPS 30 #define G 9.81 @@ -156,13 +153,16 @@ int main(void) return 0; } -// Calculate Pendulum End Point +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- +// Calculate pendulum end point static Vector2 CalculatePendulumEndPoint(float l, float theta) { return (Vector2){ 10*l*sin(theta), 10*l*cos(theta) }; } -// Calculate Double Pendulum End Point +// Calculate double pendulum end point static Vector2 CalculateDoublePendulumEndPoint(float l1, float theta1, float l2, float theta2) { Vector2 endpoint1 = CalculatePendulumEndPoint(l1, theta1); diff --git a/examples/shapes/shapes_rectangle_advanced.c b/examples/shapes/shapes_rectangle_advanced.c index c4fc648b3..5bd15cc55 100644 --- a/examples/shapes/shapes_rectangle_advanced.c +++ b/examples/shapes/shapes_rectangle_advanced.c @@ -21,6 +21,9 @@ #include +//-------------------------------------------------------------------------------------- +// Module Functions Declaration +//-------------------------------------------------------------------------------------- // Draw rectangle with rounded edges and horizontal gradient, with options to choose side of roundness static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, float roundnessRight, int segments, Color left, Color right); @@ -83,6 +86,9 @@ int main(void) return 0; } +//-------------------------------------------------------------------------------------- +// Module Functions Definition +//-------------------------------------------------------------------------------------- // Draw rectangle with rounded edges and horizontal gradient, with options to choose side of roundness // NOTE: Adapted from both 'DrawRectangleRounded()' and 'DrawRectangleGradientH()' raylib [rshapes] implementations static void DrawRectangleRoundedGradientH(Rectangle rec, float roundnessLeft, float roundnessRight, int segments, Color left, Color right) diff --git a/examples/text/text_draw_3d.c b/examples/text/text_draw_3d.c index 532596c88..0202b49f7 100644 --- a/examples/text/text_draw_3d.c +++ b/examples/text/text_draw_3d.c @@ -41,7 +41,7 @@ #endif //-------------------------------------------------------------------------------------- -// Globals +// Global variables //-------------------------------------------------------------------------------------- #define LETTER_BOUNDRY_SIZE 0.25f #define TEXT_MAX_LAYERS 32 @@ -51,7 +51,7 @@ bool SHOW_LETTER_BOUNDRY = false; bool SHOW_TEXT_BOUNDRY = false; //-------------------------------------------------------------------------------------- -// Data Types definition +// Types and Structures Definition //-------------------------------------------------------------------------------------- // Configuration structure for waving the text typedef struct WaveTextConfig { diff --git a/examples/text/text_rectangle_bounds.c b/examples/text/text_rectangle_bounds.c index 66fe53234..e180ae475 100644 --- a/examples/text/text_rectangle_bounds.c +++ b/examples/text/text_rectangle_bounds.c @@ -18,7 +18,7 @@ #include "raylib.h" //---------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- // Draw text using font inside rectangle limits static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); @@ -135,7 +135,7 @@ tempor incididunt ut labore et dolore magna aliqua. Nec ullamcorper sit amet ris } //-------------------------------------------------------------------------------------- -// Module functions definition +// Module Functions Definition //-------------------------------------------------------------------------------------- // Draw text using font inside rectangle limits diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index 54bdd4f86..aa8527fa9 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -24,6 +24,18 @@ #define EMOJI_PER_WIDTH 8 #define EMOJI_PER_HEIGHT 4 +//-------------------------------------------------------------------------------------- +// Global variables +//-------------------------------------------------------------------------------------- +// Arrays that holds the random emojis +struct { + int index; // Index inside `emojiCodepoints` + int message; // Message index + Color color; // Emoji color +} emoji[EMOJI_PER_WIDTH*EMOJI_PER_HEIGHT] = { 0 }; + +static int hovered = -1, selected = -1; + // String containing 180 emoji codepoints separated by a '\0' char const char *const emojiCodepoints = "\xF0\x9F\x8C\x80\x00\xF0\x9F\x98\x80\x00\xF0\x9F\x98\x82\x00\xF0\x9F\xA4\xA3\x00\xF0\x9F\x98\x83\x00\xF0\x9F\x98\x86\x00\xF0\x9F\x98\x89\x00" "\xF0\x9F\x98\x8B\x00\xF0\x9F\x98\x8E\x00\xF0\x9F\x98\x8D\x00\xF0\x9F\x98\x98\x00\xF0\x9F\x98\x97\x00\xF0\x9F\x98\x99\x00\xF0\x9F\x98\x9A\x00\xF0\x9F\x99\x82\x00" @@ -133,25 +145,13 @@ struct { }; //-------------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //-------------------------------------------------------------------------------------- static void RandomizeEmoji(void); // Fills the emoji array with random emojis static void DrawTextBoxed(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint); // Draw text using font inside rectangle limits static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, float fontSize, float spacing, bool wordWrap, Color tint, int selectStart, int selectLength, Color selectTint, Color selectBackTint); // Draw text using font inside rectangle limits with support for text selection -//-------------------------------------------------------------------------------------- -// Global variables -//-------------------------------------------------------------------------------------- -// Arrays that holds the random emojis -struct { - int index; // Index inside `emojiCodepoints` - int message; // Message index - Color color; // Emoji color -} emoji[EMOJI_PER_WIDTH*EMOJI_PER_HEIGHT] = { 0 }; - -static int hovered = -1, selected = -1; - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -329,7 +329,7 @@ static void RandomizeEmoji(void) } //-------------------------------------------------------------------------------------- -// Module functions definition +// Module Functions Definition //-------------------------------------------------------------------------------------- // Draw text using font inside rectangle limits diff --git a/examples/text/text_unicode_ranges.c b/examples/text/text_unicode_ranges.c index d3cb4b5e5..d1ae1a4b5 100644 --- a/examples/text/text_unicode_ranges.c +++ b/examples/text/text_unicode_ranges.c @@ -19,17 +19,20 @@ #include +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- typedef struct { - int* data; + int *data; int count; int capacity; } CodepointsArray; //-------------------------------------------------------------------------------------- -// Module functions declaration +// Module Functions Declaration //-------------------------------------------------------------------------------------- -static void AddRange(CodepointsArray* array, int start, int stop); -static Font LoadUnicodeFont(const char* fileName, int fontSize); +static void AddCodepointRange(CodepointsArray* array, int start, int stop); +//static Font LoadUnicodeFont(const char* fileName, int fontSize); //------------------------------------------------------------------------------------ // Program main entry point @@ -95,7 +98,7 @@ int main(void) } //-------------------------------------------------------------------------------------- -// Module functions definition +// Module Functions Definition //-------------------------------------------------------------------------------------- static void AddRange(CodepointsArray* array, int start, int stop) { diff --git a/examples/textures/textures_image_kernel.c b/examples/textures/textures_image_kernel.c index 99285e15e..3c89bd620 100644 --- a/examples/textures/textures_image_kernel.c +++ b/examples/textures/textures_image_kernel.c @@ -20,7 +20,7 @@ #include "raylib.h" //------------------------------------------------------------------------------------ -// Module functions declaration +// Module Functions Declaration //------------------------------------------------------------------------------------ static void NormalizeKernel(float *kernel, int size); @@ -131,7 +131,7 @@ int main(void) } //------------------------------------------------------------------------------------ -// Module functions definition +// Module Functions Definition //------------------------------------------------------------------------------------ static void NormalizeKernel(float *kernel, int size) { diff --git a/examples/textures/textures_textured_curve.c b/examples/textures/textures_textured_curve.c index 245aee3c7..feeefc7b6 100644 --- a/examples/textures/textures_textured_curve.c +++ b/examples/textures/textures_textured_curve.c @@ -155,7 +155,6 @@ int main() //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Draw textured curve using Spline Cubic Bezier static void DrawTexturedCurve(void) { diff --git a/projects/CMake/core_basic_window.c b/projects/CMake/core_basic_window.c index 86709f7a9..cb23a6f87 100644 --- a/projects/CMake/core_basic_window.c +++ b/projects/CMake/core_basic_window.c @@ -31,7 +31,7 @@ int screenHeight = 450; void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- -// Main Entry Point +// Program main entry point //---------------------------------------------------------------------------------- int main() { diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index cad32c2ef..ea394de58 100644 --- a/projects/VSCode/main.c +++ b/projects/VSCode/main.c @@ -26,18 +26,18 @@ #endif //---------------------------------------------------------------------------------- -// Local Variables Definition (local to this module) +// Global Variables Definition (local to this module) //---------------------------------------------------------------------------------- Camera camera = { 0 }; Vector3 cubePosition = { 0 }; //---------------------------------------------------------------------------------- -// Local Functions Declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- static void UpdateDrawFrame(void); // Update and draw one frame //---------------------------------------------------------------------------------- -// Main entry point +// Program main entry point //---------------------------------------------------------------------------------- int main() { From 8f32c502a7b612e8ba258b5172b2bc6e46b1576e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 12:10:40 +0200 Subject: [PATCH 12/26] REVIEWED: Code sections definition --- src/platforms/rcore_desktop_sdl.c | 2 +- src/platforms/rcore_drm.c | 2 +- src/platforms/rcore_web.c | 2 +- src/raudio.c | 5 ++--- src/raylib.h | 4 ++-- src/rlgl.h | 6 +++--- src/rmodels.c | 5 ++--- src/rshapes.c | 7 +++---- src/rtext.c | 6 ++---- src/rtextures.c | 5 ++--- 10 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c9b346fcf..33788d720 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -111,7 +111,7 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- -// Local Variables Definition +// Global Variables Definition //---------------------------------------------------------------------------------- #define SCANCODE_MAPPED_NUM 232 static const KeyboardKey mapScancodeToKey[SCANCODE_MAPPED_NUM] = { diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index a2151ea38..b15df2dea 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -156,7 +156,7 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- -// Local Variables Definition +// Global Variables Definition //---------------------------------------------------------------------------------- // NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 2712aa142..a32f9c59a 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -86,7 +86,7 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- -// Local Variables Definition +// Global Variables Definition //---------------------------------------------------------------------------------- static const char cursorLUT[11][12] = { "default", // 0 MOUSE_CURSOR_DEFAULT diff --git a/src/raudio.c b/src/raudio.c index e9d76fa99..c883132d9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -407,7 +407,7 @@ static AudioData AUDIO = { // Global AUDIO context }; //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage); @@ -2352,9 +2352,8 @@ void DetachAudioMixedProcessor(AudioCallback process) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- - // Log callback function static void OnLog(void *pUserData, ma_uint32 level, const char *pMessage) { diff --git a/src/raylib.h b/src/raylib.h index b5de3c71d..1d7321b46 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -115,7 +115,7 @@ #endif //---------------------------------------------------------------------------------- -// Some basic Defines +// Defines and Macros //---------------------------------------------------------------------------------- #ifndef PI #define PI 3.14159265358979323846f @@ -201,7 +201,7 @@ #define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 } // My own White (raylib logo) //---------------------------------------------------------------------------------- -// Structures Definition +// Types and Structures Definition //---------------------------------------------------------------------------------- // Boolean type #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) diff --git a/src/rlgl.h b/src/rlgl.h index 324d8aac5..884110c56 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1039,7 +1039,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #endif //---------------------------------------------------------------------------------- -// Types and Structures Definition +// Module Types and Structures Definition //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) @@ -1145,7 +1145,7 @@ static PFNGLVERTEXATTRIBDIVISOREXTPROC glVertexAttribDivisor = NULL; #endif //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Functions Declaration //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) static void rlLoadShaderDefault(void); // Load default shader @@ -4864,7 +4864,7 @@ const char *rlGetPixelFormatName(unsigned int format) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Functions Definition //---------------------------------------------------------------------------------- #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) // Load default shader (just vertex positioning and texture coloring) diff --git a/src/rmodels.c b/src/rmodels.c index 5a824ce44..3ac416847 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -144,7 +144,7 @@ // ... //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_OBJ) static Model LoadOBJ(const char *fileName); // Load OBJ mesh data @@ -171,7 +171,6 @@ static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *mate //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Draw a line in 3D world space void DrawLine3D(Vector3 startPos, Vector3 endPos, Color color) { @@ -4244,7 +4243,7 @@ RayCollision GetRayCollisionQuad(Ray ray, Vector3 p1, Vector3 p2, Vector3 p3, Ve } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_IQM) || defined(SUPPORT_FILEFORMAT_GLTF) // Build pose from parent joints diff --git a/src/rshapes.c b/src/rshapes.c index 38ad75c7d..14e3f856a 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -83,14 +83,13 @@ static Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used o static Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static float EaseCubicInOut(float t, float b, float c, float d); // Cubic easing //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Set texture and rectangle to be used on shapes drawing // NOTE: It can be useful when using basic shapes and one single font, // defining a font char white rectangle would allow drawing everything in a single draw call @@ -2371,7 +2370,7 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol } // Check if circle collides with a line created between two points [p1] and [p2] -RLAPI bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2) +bool CheckCollisionCircleLine(Vector2 center, float radius, Vector2 p1, Vector2 p2) { float dx = p1.x - p2.x; float dy = p1.y - p2.y; @@ -2420,7 +2419,7 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- // Cubic easing in-out diff --git a/src/rtext.c b/src/rtext.c index ffa432ed3..ffcaa3d46 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -141,7 +141,7 @@ static int textLineSpacing = 2; // Text vertical line spacing in //... //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) @@ -2197,7 +2197,7 @@ int GetCodepointPrevious(const char *text, int *codepointSize) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) || defined(SUPPORT_FILEFORMAT_BDF) // Read a line from memory @@ -2380,11 +2380,9 @@ static Font LoadBMFont(const char *fileName) return font; } - #endif #if defined(SUPPORT_FILEFORMAT_BDF) - // Convert hexadecimal to decimal (single digit) static unsigned char HexToInt(char hex) { diff --git a/src/rtextures.c b/src/rtextures.c index a4c34b000..18ee1af99 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -258,7 +258,7 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font, required by ImageDrawText() //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static float HalfToFloat(unsigned short x); static unsigned short FloatToHalf(float x); @@ -267,7 +267,6 @@ static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data f //---------------------------------------------------------------------------------- // Module Functions Definition //---------------------------------------------------------------------------------- - // Load image from file into CPU memory (RAM) Image LoadImage(const char *fileName) { @@ -5406,7 +5405,7 @@ int GetPixelDataSize(int width, int height, int format) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- // Convert half-float (stored as unsigned short) to float // REF: https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308 From 8b3c68f8b5bf11dfb97f99061d45f0438b14ebaa Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 12:18:19 +0200 Subject: [PATCH 13/26] Update core_random_sequence.c --- examples/core/core_random_sequence.c | 65 ++++++++++++---------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/examples/core/core_random_sequence.c b/examples/core/core_random_sequence.c index 3121debc7..67990df32 100644 --- a/examples/core/core_random_sequence.c +++ b/examples/core/core_random_sequence.c @@ -24,8 +24,8 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct ColorRect { - Color c; - Rectangle r; + Color color; + Rectangle rect; } ColorRect; //------------------------------------------------------------------------------------ @@ -34,7 +34,6 @@ typedef struct ColorRect { static Color GenerateRandomColor(); static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight); static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount); -static void DrawTextCenterKeyHelp(const char *key, const char *text, int posX, int posY, int fontSize, Color color); //------------------------------------------------------------------------------------ // Program main entry point @@ -66,7 +65,9 @@ int main(void) { rectCount++; rectSize = (float)screenWidth/rectCount; - free(rectangles); + RL_FREE(rectangles); + + // Re-generate random sequence with new count rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f*screenHeight); } @@ -76,7 +77,9 @@ int main(void) { rectCount--; rectSize = (float)screenWidth/rectCount; - free(rectangles); + RL_FREE(rectangles); + + // Re-generate random sequence with new count rectangles = GenerateRandomColorRectSequence((float)rectCount, rectSize, (float)screenWidth, 0.75f*screenHeight); } } @@ -88,20 +91,20 @@ int main(void) ClearBackground(RAYWHITE); - int fontSize = 20; for (int i = 0; i < rectCount; i++) { - DrawRectangleRec(rectangles[i].r, rectangles[i].c); - DrawTextCenterKeyHelp("SPACE", "to shuffle the sequence.", 10, screenHeight - 96, fontSize, BLACK); - DrawTextCenterKeyHelp("UP", "to add a rectangle and generate a new sequence.", 10, screenHeight - 64, fontSize, BLACK); - DrawTextCenterKeyHelp("DOWN", "to remove a rectangle and generate a new sequence.", 10, screenHeight - 32, fontSize, BLACK); + DrawRectangleRec(rectangles[i].rect, rectangles[i].color); + + DrawText("Press SPACE to shuffle the sequence", 10, screenHeight - 96, 20, BLACK); + + DrawText("Press SPACE to shuffle the current sequence", 10, screenHeight - 96, 20, BLACK); + DrawText("Press UP to add a rectangle and generate a new sequence", 10, screenHeight - 64, 20, BLACK); + DrawText("Press DOWN to remove a rectangle and generate a new sequence", 10, screenHeight - 32, 20, BLACK); } - const char *rectCountText = TextFormat("%d rectangles", rectCount); - int rectCountTextSize = MeasureText(rectCountText, fontSize); - DrawText(rectCountText, screenWidth - rectCountTextSize - 10, 10, fontSize, BLACK); + DrawText(TextFormat("Count: %d rectangles", rectCount), 10, 10, 20, MAROON); - DrawFPS(10, 10); + DrawFPS(screenWidth - 80, 10); EndDrawing(); //---------------------------------------------------------------------------------- @@ -133,7 +136,8 @@ static Color GenerateRandomColor() static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWidth, float screenWidth, float screenHeight) { - ColorRect *rectangles = (ColorRect *)malloc((int)rectCount*sizeof(ColorRect)); + ColorRect *rectangles = (ColorRect *)RL_CALLOC((int)rectCount, sizeof(ColorRect)); + int *seq = LoadRandomSequence((unsigned int)rectCount, 0, (unsigned int)rectCount - 1); float rectSeqWidth = rectCount*rectWidth; float startX = (screenWidth - rectSeqWidth)*0.5f; @@ -142,8 +146,8 @@ static ColorRect *GenerateRandomColorRectSequence(float rectCount, float rectWid { int rectHeight = (int)Remap((float)seq[i], 0, rectCount - 1, 0, screenHeight); - rectangles[i].c = GenerateRandomColor(); - rectangles[i].r = CLITERAL(Rectangle){ startX + i*rectWidth, screenHeight - rectHeight, rectWidth, (float)rectHeight }; + rectangles[i].color = GenerateRandomColor(); + rectangles[i].rect = CLITERAL(Rectangle){ startX + i*rectWidth, screenHeight - rectHeight, rectWidth, (float)rectHeight }; } UnloadRandomSequence(seq); @@ -162,28 +166,13 @@ static void ShuffleColorRectSequence(ColorRect *rectangles, int rectCount) // Swap only the color and height ColorRect tmp = *r1; - r1->c = r2->c; - r1->r.height = r2->r.height; - r1->r.y = r2->r.y; - r2->c = tmp.c; - r2->r.height = tmp.r.height; - r2->r.y = tmp.r.y; + r1->color = r2->color; + r1->rect.height = r2->rect.height; + r1->rect.y = r2->rect.y; + r2->color = tmp.color; + r2->rect.height = tmp.rect.height; + r2->rect.y = tmp.rect.y; } UnloadRandomSequence(seq); } - -static void DrawTextCenterKeyHelp(const char *key, const char *text, int posX, int posY, int fontSize, Color color) -{ - int spaceSize = MeasureText(" ", fontSize); - int pressSize = MeasureText("Press", fontSize); - int keySize = MeasureText(key, fontSize); - int textSizeCurrent = 0; - - DrawText("Press", posX, posY, fontSize, color); - textSizeCurrent += pressSize + 2*spaceSize; - DrawText(key, posX + textSizeCurrent, posY, fontSize, RED); - DrawRectangle(posX + textSizeCurrent, posY + fontSize, keySize, 3, RED); - textSizeCurrent += keySize + 2*spaceSize; - DrawText(text, posX + textSizeCurrent, posY, fontSize, color); -} From aa475b10461018f6f2ed02cfe0bc628d6a8b9698 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 12:34:04 +0200 Subject: [PATCH 14/26] REVIEWED: Code sections description for consistency --- examples/models/rlights.h | 2 +- examples/shaders/rlights.h | 2 +- examples/shapes/raygui.h | 4 ++-- src/external/rprand.h | 6 +++--- src/rcamera.h | 2 +- src/rgestures.h | 4 ++-- src/utils.c | 4 ++-- tools/rexm/rexm.c | 7 +++---- 8 files changed, 15 insertions(+), 16 deletions(-) diff --git a/examples/models/rlights.h b/examples/models/rlights.h index e38b0015a..aba907b5f 100644 --- a/examples/models/rlights.h +++ b/examples/models/rlights.h @@ -109,7 +109,7 @@ void UpdateLightValues(Shader shader, Light light); // Send light proper static int lightsCount = 0; // Current amount of created lights //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- // ... diff --git a/examples/shaders/rlights.h b/examples/shaders/rlights.h index e38b0015a..aba907b5f 100644 --- a/examples/shaders/rlights.h +++ b/examples/shaders/rlights.h @@ -109,7 +109,7 @@ void UpdateLightValues(Shader shader, Light light); // Send light proper static int lightsCount = 0; // Current amount of created lights //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- // ... diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 85c477870..a3fc51f0f 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1480,7 +1480,7 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co #endif // RAYGUI_STANDALONE //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only) @@ -4479,7 +4479,7 @@ void GuiSetIconScale(int scale) #endif // !RAYGUI_NO_ICONS //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- // Load style from memory diff --git a/src/external/rprand.h b/src/external/rprand.h index 95147d270..cdef03c01 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -156,13 +156,13 @@ static uint32_t rprand_state[4] = { // Xoshiro128** state, initializ }; //---------------------------------------------------------------------------------- -// Module internal functions declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static uint32_t rprand_xoshiro(void); // Xoshiro128** generator (uses global rprand_state) static uint64_t rprand_splitmix64(void); // SplitMix64 generator (uses seed to generate rprand_state) //---------------------------------------------------------------------------------- -// Module functions definition +// Module Functions Definition //---------------------------------------------------------------------------------- // Set rprand_state for Xoshiro128** // NOTE: We use a custom generation algorithm using SplitMix64 @@ -236,7 +236,7 @@ void rprand_unload_sequence(int *sequence) } //---------------------------------------------------------------------------------- -// Module internal functions definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- static inline uint32_t rprand_rotate_left(const uint32_t x, int k) { diff --git a/src/rcamera.h b/src/rcamera.h index 62c1225ec..3e9f83095 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -216,7 +216,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera *camera, float aspect); //... //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- //... diff --git a/src/rgestures.h b/src/rgestures.h index 1bf4e0555..389df64a1 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -238,7 +238,7 @@ static GesturesData GESTURES = { }; //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static float rgVector2Angle(Vector2 initialPosition, Vector2 finalPosition); static float rgVector2Distance(Vector2 v1, Vector2 v2); @@ -481,7 +481,7 @@ float GetGesturePinchAngle(void) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- // Get angle from two-points vector with X-axis static float rgVector2Angle(Vector2 v1, Vector2 v2) diff --git a/src/utils.c b/src/utils.c index 26e7a1bab..310ebfc78 100644 --- a/src/utils.c +++ b/src/utils.c @@ -82,7 +82,7 @@ static const char *internalDataPath = NULL; // Android internal data pat #endif //---------------------------------------------------------------------------------- -// Module specific Functions Declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), @@ -482,7 +482,7 @@ FILE *android_fopen(const char *fileName, const char *mode) #endif // PLATFORM_ANDROID //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) static int android_read(void *cookie, char *data, int dataSize) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 9ab4950c8..d1bfb1a62 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -129,7 +129,7 @@ static const char *exCollectionFilePath = NULL; // Env: REXM_EXAMPLES_COLLECTION static const char *exVSProjectSolutionFile = NULL; // Env REXM_EXAMPLES_VS2022_SLN_FILE //---------------------------------------------------------------------------------- -// Module specific functions declaration +// Module Internal Functions Declaration //---------------------------------------------------------------------------------- static int FileTextFind(const char *fileName, const char *find); static int FileTextReplace(const char *fileName, const char *find, const char *replace); @@ -1283,9 +1283,8 @@ int main(int argc, char *argv[]) } //---------------------------------------------------------------------------------- -// Module specific functions definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- - // Update required files from examples collection static int UpdateRequiredFiles(void) { @@ -1772,7 +1771,7 @@ static int FileMove(const char *srcPath, const char *dstPath) // Get example info from example file header // NOTE: Expecting the example to follow raylib_example_template.c -rlExampleInfo *LoadExampleInfo(const char *exFileName) +static rlExampleInfo *LoadExampleInfo(const char *exFileName) { rlExampleInfo *exInfo = (rlExampleInfo *)RL_CALLOC(1, sizeof(rlExampleInfo)); From 29ce5d8aa9d261eba395e24437e08c6bd744616e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 17:45:39 +0200 Subject: [PATCH 15/26] WARNING: BREAKING: `LoadFontData()` redesigned, added parameter This redesign is a big improvement on font loading time and memory requirements. It only loads glyphs available on font from requested codepoints and only processes those glyphs for packaging. When processing +10K codepoints (CJK), the loading time improves considerably. --- src/raylib.h | 6 +- src/rtext.c | 176 ++++++++++++++++++++++++++++++--------------------- 2 files changed, 107 insertions(+), 75 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 1d7321b46..41bc1926e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1466,11 +1466,11 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height +RLAPI Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI bool IsFontValid(Font font); // Check if a font is valid (font data loaded, WARNING: GPU texture not checked) -RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use +RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount, int type, int *glyphCount); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) diff --git a/src/rtext.c b/src/rtext.c index ffcaa3d46..009a264f3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -171,7 +171,7 @@ extern void LoadFontDefault(void) // NOTE: Using UTF-8 encoding table for Unicode U+0000..U+00FF Basic Latin + Latin-1 Supplement // Ref: http://www.utf8-chartable.de/unicode-utf8-table.pl - defaultFont.glyphCount = 224; // Number of chars included in our default font + defaultFont.glyphCount = 224; // Number of glyphs included in our default font defaultFont.glyphPadding = 0; // Characters padding // Default font is directly defined here (data generated from a sprite font image) @@ -365,7 +365,7 @@ Font LoadFont(const char *fileName) #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 + #define FONT_TTF_DEFAULT_CHARS_PADDING 4 // TTF font generation default glyphs padding #endif Font font = { 0 }; @@ -404,7 +404,7 @@ Font LoadFont(const char *fileName) // Load Font from TTF or BDF 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 *codepoints, int codepointCount) +Font LoadFontEx(const char *fileName, int fontSize, const int *codepoints, int codepointCount) { Font font = { 0 }; @@ -440,8 +440,8 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) int x = 0; int y = 0; - // We allocate a temporal arrays for chars data measures, - // once we get the actual number of chars, we copy data to a sized arrays + // We allocate a temporal arrays for glyphs data measures, + // once we get the actual number of glyphs, we copy data to a sized arrays int tempCharValues[MAX_GLYPHS_FROM_IMAGE] = { 0 }; Rectangle tempCharRecs[MAX_GLYPHS_FROM_IMAGE] = { 0 }; @@ -520,7 +520,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) font.glyphCount = index; font.glyphPadding = 0; - // We got tempCharValues and tempCharsRecs populated with chars data + // We got tempCharValues and tempCharsRecs populated with glyphs data // Now we move temp data to sized charValues and charRecs arrays font.glyphs = (GlyphInfo *)RL_MALLOC(font.glyphCount*sizeof(GlyphInfo)); font.recs = (Rectangle *)RL_MALLOC(font.glyphCount*sizeof(Rectangle)); @@ -549,7 +549,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 *codepoints, int codepointCount) +Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount) { Font font = { 0 }; @@ -557,21 +557,21 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int strncpy(fileExtLower, TextToLower(fileType), 16 - 1); font.baseSize = fontSize; - font.glyphCount = (codepointCount > 0)? codepointCount : 95; font.glyphPadding = 0; #if defined(SUPPORT_FILEFORMAT_TTF) if (TextIsEqual(fileExtLower, ".ttf") || TextIsEqual(fileExtLower, ".otf")) { - font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT); + font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, (codepointCount > 0)? codepointCount : 95, FONT_DEFAULT, &font.glyphCount); } else #endif #if defined(SUPPORT_FILEFORMAT_BDF) if (TextIsEqual(fileExtLower, ".bdf")) { - font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, font.glyphCount, &font.baseSize); + font.glyphs = LoadFontDataBDF(fileData, dataSize, codepoints, (codepointCount > 0)? codepointCount : 95, &font.baseSize); + font.glyphCount = (codepointCount > 0)? codepointCount : 95; } else #endif @@ -620,7 +620,7 @@ bool IsFontValid(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 *codepoints, int codepointCount, int type) +GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, const int *codepoints, int codepointCount, int type, int *glyphCount) { // NOTE: Using some SDF generation default values, // trades off precision with ability to handle *smaller* sizes @@ -637,7 +637,8 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz #define FONT_BITMAP_ALPHA_THRESHOLD 80 // Bitmap (B&W) font generation alpha threshold #endif - GlyphInfo *chars = NULL; + GlyphInfo *glyphs = NULL; + int glyphCounter = 0; #if defined(SUPPORT_FILEFORMAT_TTF) // Load font data (including pixel data) from TTF memory file @@ -646,6 +647,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz { bool genFontChars = false; stbtt_fontinfo fontInfo = { 0 }; + int *requiredCodepoints = (int *)codepoints; if (stbtt_InitFont(&fontInfo, (unsigned char *)fileData, 0)) // Initialize font for data reading { @@ -662,21 +664,29 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Fill fontChars in case not provided externally // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) - if (codepoints == NULL) + if (requiredCodepoints == NULL) { - codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); - for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; + requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); + for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32; genFontChars = true; } - chars = (GlyphInfo *)RL_CALLOC(codepointCount, sizeof(GlyphInfo)); + // Check available glyphs on provided font before loading them + for (int i = 0, index; i < codepointCount; i++) + { + index = stbtt_FindGlyphIndex(&fontInfo, requiredCodepoints[i]); + if (index > 0) glyphCounter++; + } - // NOTE: Using simple packaging, one char after another + // WARNING: Allocating space for maximum number of codepoints + glyphs = (GlyphInfo *)RL_CALLOC(glyphCounter, sizeof(GlyphInfo)); + glyphCounter = 0; // Reset to reuse + + int k = 0; for (int i = 0; i < codepointCount; i++) { - int chw = 0, chh = 0; // Character width and height (on generation) - int ch = codepoints[i]; // Character value to get info for - chars[i].value = ch; + int cpWidth = 0, cpHeight = 0; // Codepoint width and height (on generation) + int cp = requiredCodepoints[i]; // Codepoint value to get info for // Render a unicode codepoint to a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap @@ -685,76 +695,96 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Check if a glyph is available in the font // WARNING: if (index == 0), glyph not found, it could fallback to default .notdef glyph (if defined in font) - int index = stbtt_FindGlyphIndex(&fontInfo, ch); + int index = stbtt_FindGlyphIndex(&fontInfo, cp); if (index > 0) { + // NOTE: Only storing glyphs for codepoints found in the font + glyphs[k].value = cp; + switch (type) { case FONT_DEFAULT: - case FONT_BITMAP: chars[i].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, ch, &chw, &chh, &chars[i].offsetX, &chars[i].offsetY); break; - case FONT_SDF: 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); break; + case FONT_BITMAP: glyphs[k].image.data = stbtt_GetCodepointBitmap(&fontInfo, scaleFactor, scaleFactor, cp, &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY); break; + case FONT_SDF: + { + if (cp != 32) + { + glyphs[k].image.data = stbtt_GetCodepointSDF(&fontInfo, scaleFactor, cp, + FONT_SDF_CHAR_PADDING, FONT_SDF_ON_EDGE_VALUE, FONT_SDF_PIXEL_DIST_SCALE, + &cpWidth, &cpHeight, &glyphs[k].offsetX, &glyphs[k].offsetY); + } + } break; + //case FONT_MSDF: default: break; } - if (chars[i].image.data != NULL) // Glyph data has been found in the font + if (glyphs[k].image.data != NULL) // Glyph data has been found in the font { - stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); - chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor); + stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL); + glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor); - if (chh > fontSize) TRACELOG(LOG_WARNING, "FONT: Character [0x%08x] size is bigger than expected font size", ch); + if (cpHeight > fontSize) TRACELOG(LOG_WARNING, "FONT: [0x%04x] Glyph height is bigger than requested font size: %i > %i", cp, cpHeight, (int)fontSize); - // Load characters images - chars[i].image.width = chw; - chars[i].image.height = chh; - chars[i].image.mipmaps = 1; - chars[i].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + // Load glyph image + glyphs[k].image.width = cpWidth; + glyphs[k].image.height = cpHeight; + glyphs[k].image.mipmaps = 1; + glyphs[k].image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; - chars[i].offsetY += (int)((float)ascent*scaleFactor); + glyphs[k].offsetY += (int)((float)ascent*scaleFactor); } + //else TRACELOG(LOG_WARNING, "FONT: Glyph [0x%08x] has no image data available", cp); // Only reported for 0x20 and 0x3000 - // NOTE: We create an empty image for space character, - // it could be further required for atlas packing - if (ch == 32) + // We create an empty image for Space character (0x20), useful for sprite font generation + // NOTE: Another space to consider: 0x3000 (CJK - Ideographic Space) + if ((cp == 0x20) || (cp == 0x3000)) { - stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL); - chars[i].advanceX = (int)((float)chars[i].advanceX*scaleFactor); + stbtt_GetCodepointHMetrics(&fontInfo, cp, &glyphs[k].advanceX, NULL); + glyphs[k].advanceX = (int)((float)glyphs[k].advanceX*scaleFactor); Image imSpace = { - .data = RL_CALLOC(chars[i].advanceX*fontSize, 2), - .width = chars[i].advanceX, + .data = RL_CALLOC(glyphs[k].advanceX*fontSize, 2), + .width = glyphs[k].advanceX, .height = fontSize, .mipmaps = 1, .format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE }; - chars[i].image = imSpace; + glyphs[k].image = imSpace; } if (type == FONT_BITMAP) { // Aliased bitmap (black & white) font generation, avoiding anti-aliasing // NOTE: For optimum results, bitmap font should be generated at base pixel size - for (int p = 0; p < chw*chh; p++) + for (int p = 0; p < cpWidth*cpHeight; p++) { - 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; + if (((unsigned char *)glyphs[k].image.data)[p] < FONT_BITMAP_ALPHA_THRESHOLD) + ((unsigned char *)glyphs[k].image.data)[p] = 0; + else ((unsigned char *)glyphs[k].image.data)[p] = 255; } } + + k++; + glyphCounter++; } else { - // TODO: Use some fallback glyph for codepoints not found in the font + // WARNING: Glyph not found on font, optionally use a fallback glyph } } + + if (glyphCounter < codepointCount) TRACELOG(LOG_WARNING, "FONT: Requested codepoints glyphs found: [%i/%i]", k, codepointCount); } else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data"); - if (genFontChars) RL_FREE(codepoints); + if (genFontChars) RL_FREE(requiredCodepoints); } #endif - return chars; + *glyphCount = glyphCounter; + return glyphs; } // Generate image font atlas using chars info @@ -1239,7 +1269,7 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz (font.recs[index].height + 2.0f*font.glyphPadding)*scaleFactor }; // Character source rectangle from font texture atlas - // NOTE: We consider chars padding when drawing, it could be required for outline/glow shader effects + // NOTE: We consider glyphs padding when drawing, it could be required for outline/glow shader effects Rectangle srcRec = { font.recs[index].x - (float)font.glyphPadding, font.recs[index].y - (float)font.glyphPadding, font.recs[index].width + 2.0f*font.glyphPadding, font.recs[index].height + 2.0f*font.glyphPadding }; @@ -1292,7 +1322,7 @@ int MeasureText(const char *text, int fontSize) // Check if default font has been loaded if (GetFontDefault().texture.id != 0) { - int defaultFontSize = 10; // Default Font chars height in pixel + int defaultFontSize = 10; // Default Font glyphs height in pixel if (fontSize < defaultFontSize) fontSize = defaultFontSize; int spacing = fontSize/defaultFontSize; @@ -2394,7 +2424,7 @@ static unsigned char HexToInt(char hex) // Load font data for further use // NOTE: Requires BDF font memory data -static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, int *codepoints, int codepointCount, int *outFontSize) +static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, const int *codepoints, int codepointCount, int *outFontSize) { #define MAX_BUFFER_SIZE 256 @@ -2428,7 +2458,9 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i int charBByoff0 = 0; // Character bounding box Y0 offset int charDWidthX = 0; // Character advance X int charDWidthY = 0; // Character advance Y (unused) - GlyphInfo *charGlyphInfo = NULL; // Pointer to output glyph info (NULL if not set) + + GlyphInfo *glyphs = NULL; // Pointer to output glyph info (NULL if not set) + int *requiredCodepoints = codepoints; if (fileData == NULL) return glyphs; @@ -2437,10 +2469,10 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i // Fill fontChars in case not provided externally // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) - if (codepoints == NULL) + if (requiredCodepoints == NULL) { - codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); - for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; + requiredCodepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); + for (int i = 0; i < codepointCount; i++) requiredCodepoints[i] = i + 32; genFontChars = true; } @@ -2466,11 +2498,11 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i if (charBitmapStarted) { - if (charGlyphInfo != NULL) + if (glyphs != NULL) { int pixelY = charBitmapNextRow++; - if (pixelY >= charGlyphInfo->image.height) break; + if (pixelY >= glyphs->image.height) break; for (int x = 0; x < readBytes; x++) { @@ -2480,9 +2512,9 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i { int pixelX = ((x*4) + bitX); - if (pixelX >= charGlyphInfo->image.width) break; + if (pixelX >= glyphs->image.width) break; - if ((byte & (8 >> bitX)) > 0) ((unsigned char *)charGlyphInfo->image.data)[(pixelY*charGlyphInfo->image.width) + pixelX] = 255; + if ((byte & (8 >> bitX)) > 0) ((unsigned char *)glyphs->image.data)[(pixelY*glyphs->image.width) + pixelX] = 255; } } } @@ -2514,30 +2546,30 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i if (strstr(buffer, "BITMAP") != NULL) { // Search for glyph index in codepoints - charGlyphInfo = NULL; + glyphs = NULL; for (int codepointIndex = 0; codepointIndex < codepointCount; codepointIndex++) { if (codepoints[codepointIndex] == charEncoding) { - charGlyphInfo = &glyphs[codepointIndex]; + glyphs = &glyphs[codepointIndex]; break; } } // Init glyph info - if (charGlyphInfo != NULL) + if (glyphs != NULL) { - charGlyphInfo->value = charEncoding; - charGlyphInfo->offsetX = charBBxoff0 + fontBByoff0; - charGlyphInfo->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent); - charGlyphInfo->advanceX = charDWidthX; + glyphs->value = charEncoding; + glyphs->offsetX = charBBxoff0 + fontBByoff0; + glyphs->offsetY = fontBBh - (charBBh + charBByoff0 + fontBByoff0 + fontAscent); + glyphs->advanceX = charDWidthX; - charGlyphInfo->image.data = RL_CALLOC(charBBw*charBBh, 1); - charGlyphInfo->image.width = charBBw; - charGlyphInfo->image.height = charBBh; - charGlyphInfo->image.mipmaps = 1; - charGlyphInfo->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; + glyphs->image.data = RL_CALLOC(charBBw*charBBh, 1); + glyphs->image.width = charBBw; + glyphs->image.height = charBBh; + glyphs->image.mipmaps = 1; + glyphs->image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; } charBitmapStarted = true; @@ -2588,14 +2620,14 @@ static GlyphInfo *LoadFontDataBDF(const unsigned char *fileData, int dataSize, i { charStarted = true; charEncoding = -1; - charGlyphInfo = NULL; + glyphs = NULL; charBBw = 0; charBBh = 0; charBBxoff0 = 0; charBByoff0 = 0; charDWidthX = 0; charDWidthY = 0; - charGlyphInfo = NULL; + glyphs = NULL; charBitmapStarted = false; charBitmapNextRow = 0; continue; From b16d14f5ed7b07c0854806e41cb88684c377d042 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 17:45:52 +0200 Subject: [PATCH 16/26] Update text_font_sdf.c --- examples/text/text_font_sdf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 5a24c1013..d81e8b447 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -50,7 +50,7 @@ int main(void) // Loading font data from memory data // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 95 (autogenerate chars array) - fontDefault.glyphs = LoadFontData(fileData, fileSize, 16, 0, 95, FONT_DEFAULT); + fontDefault.glyphs = LoadFontData(fileData, fileSize, 16, 0, 95, FONT_DEFAULT, &fontDefault.glyphCount); // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 4 px, pack method: 0 (default) Image atlas = GenImageFontAtlas(fontDefault.glyphs, &fontDefault.recs, 95, 16, 4, 0); fontDefault.texture = LoadTextureFromImage(atlas); @@ -61,7 +61,8 @@ int main(void) fontSDF.baseSize = 16; fontSDF.glyphCount = 95; // Parameters > font size: 16, no glyphs array provided (0), glyphs count: 0 (defaults to 95) - fontSDF.glyphs = LoadFontData(fileData, fileSize, 16, 0, 0, FONT_SDF); + fontSDF.glyphs = LoadFontData(fileData, fileSize, 16, 0, 0, FONT_SDF, & + fontSDF.glyphCount); // Parameters > glyphs count: 95, font size: 16, glyphs padding in image: 0 px, pack method: 1 (Skyline algorythm) atlas = GenImageFontAtlas(fontSDF.glyphs, &fontSDF.recs, 95, 16, 0, 1); fontSDF.texture = LoadTextureFromImage(atlas); From 17d9927a4fcf24984b2b6ff2c18608c21fd03194 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:46:09 +0000 Subject: [PATCH 17/26] Update raylib_api.* by CI --- tools/parser/output/raylib_api.json | 10 +++++++--- tools/parser/output/raylib_api.lua | 9 +++++---- tools/parser/output/raylib_api.txt | 9 +++++---- tools/parser/output/raylib_api.xml | 9 +++++---- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 8828ffb18..4ce190714 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -9041,7 +9041,7 @@ "name": "fontSize" }, { - "type": "int *", + "type": "const int *", "name": "codepoints" }, { @@ -9091,7 +9091,7 @@ "name": "fontSize" }, { - "type": "int *", + "type": "const int *", "name": "codepoints" }, { @@ -9129,7 +9129,7 @@ "name": "fontSize" }, { - "type": "int *", + "type": "const int *", "name": "codepoints" }, { @@ -9139,6 +9139,10 @@ { "type": "int", "name": "type" + }, + { + "type": "int *", + "name": "glyphCount" } ] }, diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index d0cce09eb..e52fda62d 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -6550,7 +6550,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "codepoints"}, + {type = "const int *", name = "codepoints"}, {type = "int", name = "codepointCount"} } }, @@ -6573,7 +6573,7 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "codepoints"}, + {type = "const int *", name = "codepoints"}, {type = "int", name = "codepointCount"} } }, @@ -6593,9 +6593,10 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "codepoints"}, + {type = "const int *", name = "codepoints"}, {type = "int", name = "codepointCount"}, - {type = "int", name = "type"} + {type = "int", name = "type"}, + {type = "int *", name = "glyphCount"} } }, { diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index 82fdc2c6a..b164b7e88 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -3468,7 +3468,7 @@ Function 395: LoadFontEx() (4 input parameters) Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set, font size is provided in pixels height Param[1]: fileName (type: const char *) Param[2]: fontSize (type: int) - Param[3]: codepoints (type: int *) + Param[3]: codepoints (type: const int *) Param[4]: codepointCount (type: int) Function 396: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage @@ -3485,23 +3485,24 @@ Function 397: LoadFontFromMemory() (6 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: fontSize (type: int) - Param[5]: codepoints (type: int *) + Param[5]: codepoints (type: const int *) Param[6]: codepointCount (type: int) Function 398: IsFontValid() (1 input parameters) Name: IsFontValid Return type: bool Description: Check if a font is valid (font data loaded, WARNING: GPU texture not checked) Param[1]: font (type: Font) -Function 399: LoadFontData() (6 input parameters) +Function 399: LoadFontData() (7 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use Param[1]: fileData (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: fontSize (type: int) - Param[4]: codepoints (type: int *) + Param[4]: codepoints (type: const int *) Param[5]: codepointCount (type: int) Param[6]: type (type: int) + Param[7]: glyphCount (type: int *) Function 400: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index f2e3b3b4f..43835334d 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -2286,7 +2286,7 @@ - + @@ -2299,19 +2299,20 @@ - + - + - + + From 0fd5a1a3f5adce1a77b6859877a29c12b5a0d178 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 17:47:25 +0200 Subject: [PATCH 18/26] REDESIGNED: example: `text_unicode_ranges` --- examples/text/text_unicode_ranges.c | 222 +++++++++++++++------------- 1 file changed, 118 insertions(+), 104 deletions(-) diff --git a/examples/text/text_unicode_ranges.c b/examples/text/text_unicode_ranges.c index d1ae1a4b5..190d72732 100644 --- a/examples/text/text_unicode_ranges.c +++ b/examples/text/text_unicode_ranges.c @@ -11,7 +11,7 @@ * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2019-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) +* Copyright (c) 2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -19,20 +19,11 @@ #include -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -typedef struct { - int *data; - int count; - int capacity; -} CodepointsArray; - //-------------------------------------------------------------------------------------- // Module Functions Declaration //-------------------------------------------------------------------------------------- -static void AddCodepointRange(CodepointsArray* array, int start, int stop); -//static Font LoadUnicodeFont(const char* fileName, int fontSize); +// Add codepoint range to existing font +static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop); //------------------------------------------------------------------------------------ // Program main entry point @@ -46,9 +37,12 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [text] example - unicode ranges"); - // Load font with Unicode support - Font fontUni = LoadUnicodeFont("resources/NotoSansTC-Regular.ttf", 32); - SetTextureFilter(fontUni.texture, TEXTURE_FILTER_BILINEAR); + // Load font with default Unicode range: Basic ASCII [32-127] + Font font = LoadFont("resources/NotoSansTC-Regular.ttf"); + SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + + int unicodeRange = 0; // Track the ranges of codepoints added to font + int prevUnicodeRange = 0; // Previous Unicode range to avoid reloading every frame SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -58,7 +52,76 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - //... + if (unicodeRange != prevUnicodeRange) + { + UnloadFont(font); + + // Load font with default Unicode range: Basic ASCII [32-127] + font = LoadFont("resources/NotoSansTC-Regular.ttf"); + + // Add required ranges to loaded font + switch (unicodeRange) + { + /* + case 5: + { + // Unicode range: Devanari, Arabic, Hebrew + // WARNING: Glyphs not available on provided font! + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x900, 0x97f); // Devanagari + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x600, 0x6ff); // Arabic + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x5d0, 0x5ea); // Hebrew + } + */ + case 4: + { + // Unicode range: CJK (Japanese and Chinese) + // WARNING: Loading thousands of codepoints requires lot of time! + // A better strategy is prefilter the required codepoints for the text + // in the game and just load the required ones + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x4e00, 0x9fff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3400, 0x4dbf); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3000, 0x303f); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x3040, 0x309f); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x30A0, 0x30ff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x31f0, 0x31ff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xff00, 0xffef); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xac00, 0xd7af); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1100, 0x11ff); + } + case 3: + { + // Unicode range: Cyrillic + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x400, 0x4ff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x500, 0x52f); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2de0, 0x2Dff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xa640, 0xA69f); + } + case 2: + { + // Unicode range: Greek + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x370, 0x3ff); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1f00, 0x1fff); + } + case 1: + { + // Unicode range: European Languages + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0xc0, 0x17f); + AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x180, 0x24f); + //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x1e00, 0x1eff); + //AddCodepointRange(&font, "resources/NotoSansTC-Regular.ttf", 0x2c60, 0x2c7f); + } + default: break; + } + + prevUnicodeRange = unicodeRange; + } + + if (IsKeyPressed(KEY_ZERO)) unicodeRange = 0; + else if (IsKeyPressed(KEY_ONE)) unicodeRange = 1; + else if (IsKeyPressed(KEY_TWO)) unicodeRange = 2; + else if (IsKeyPressed(KEY_THREE)) unicodeRange = 3; + else if (IsKeyPressed(KEY_FOUR)) unicodeRange = 4; + //else if (IsKeyPressed(KEY_FIVE)) unicodeRange = 5; //---------------------------------------------------------------------------------- // Draw @@ -66,22 +129,34 @@ int main(void) BeginDrawing(); ClearBackground(RAYWHITE); + + DrawText("ADD CODEPOINTS: 1,2,3,4", 20, 20, 20, MAROON); // Render test strings in different languages - DrawTextEx(fontUni, "English: Hello World!", (Vector2){ 50, 50 }, 32, 1, DARKGRAY); // English - DrawTextEx(fontUni, "Español: Hola mundo!", (Vector2){ 50, 100 }, 32, 1, DARKGRAY); // Spanish - DrawTextEx(fontUni, "Ελληνικά: Γειά σου κόσμε!", (Vector2){ 50, 150 }, 32, 1, DARKGRAY); // Greek - DrawTextEx(fontUni, "Русский: Привет мир!", (Vector2){ 50, 200 }, 32, 0, DARKGRAY); // Russian - DrawTextEx(fontUni, "中文: 你好世界!", (Vector2){ 50, 250 }, 32, 1, DARKGRAY); // Chinese - DrawTextEx(fontUni, "日本語: こんにちは世界!", (Vector2){ 50, 300 }, 32, 1, DARKGRAY); // Japanese - DrawTextEx(fontUni, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY); // Devanagari + DrawTextEx(font, "> English: Hello World!", (Vector2){ 50, 70 }, 32, 1, DARKGRAY); // English + DrawTextEx(font, "> Español: Hola mundo!", (Vector2){ 50, 120 }, 32, 1, DARKGRAY); // Spanish + DrawTextEx(font, "> Ελληνικά: Γειά σου κόσμε!", (Vector2){ 50, 170 }, 32, 1, DARKGRAY); // Greek + DrawTextEx(font, "> Русский: Привет мир!", (Vector2){ 50, 220 }, 32, 0, DARKGRAY); // Russian + DrawTextEx(font, "> 中文: 你好世界!", (Vector2){ 50, 270 }, 32, 1, DARKGRAY); // Chinese + DrawTextEx(font, "> 日本語: こんにちは世界!", (Vector2){ 50, 320 }, 32, 1, DARKGRAY); // Japanese + //DrawTextEx(font, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY); // Devanagari (glyphs not available in font) + // Draw font texture scaled to screen DrawRectangle(400, 16, 380, 400, BLACK); - DrawTexturePro(fontUni.texture, (Rectangle){ 0, 0, fontUni.texture.width, fontUni.texture.height }, - (Rectangle){ 400, 16, 380, 400 }, (Vector2){ 0, 0 }, 0.0f, WHITE); + DrawTexturePro(font.texture, (Rectangle){ 0, 0, font.texture.width, font.texture.height }, + (Rectangle){ 400, 16, font.texture.width*0.5f, font.texture.height*0.5f }, (Vector2){ 0, 0 }, 0.0f, WHITE); + + DrawText(TextFormat("ATLAS SIZE: %ix%i px", font.texture.width, font.texture.height), 20, 380, 20, BLUE); // Display font attribution DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", screenWidth - 300, screenHeight - 20, 10, GRAY); + + if (prevUnicodeRange != unicodeRange) + { + DrawRectangle(0, 0, screenWidth, screenHeight, Fade(WHITE, 0.8f)); + DrawRectangle(0, 125, screenWidth, 200, GRAY); + DrawText("LOADING CODEPOINTS...", 150, 210, 40, BLACK); + } EndDrawing(); //---------------------------------------------------------------------------------- @@ -89,7 +164,7 @@ int main(void) // De-Initialization //-------------------------------------------------------------------------------------- - UnloadFont(fontUni); // Unload font resource + UnloadFont(font); // Unload font resource CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- @@ -100,87 +175,26 @@ int main(void) //-------------------------------------------------------------------------------------- // Module Functions Definition //-------------------------------------------------------------------------------------- -static void AddRange(CodepointsArray* array, int start, int stop) +// Add codepoint range to existing font +static void AddCodepointRange(Font *font, const char *fontPath, int start, int stop) { int rangeSize = stop - start + 1; + int currentRangeSize = font->glyphCount; - if ((array->count + rangeSize) > array->capacity) - { - array->capacity = array->count + rangeSize + 1024; - array->data = (int *)MemRealloc(array->data, array->capacity*sizeof(int)); - - if (!array->data) - { - TraceLog(LOG_ERROR, "FONTUTIL: Memory allocation failed"); - exit(1); - } - } + // TODO: Load glyphs from provided vector font (if available), + // add them to existing font, regenerating font image and texture - for (int i = start; i <= stop; i++) array->data[array->count++] = i; + int updatedCodepointCount = currentRangeSize + rangeSize; + int *updatedCodepoints = (int *)RL_CALLOC(updatedCodepointCount, sizeof(int)); + + // Get current codepoint list + for (int i = 0; i < currentRangeSize; i++) updatedCodepoints[i] = font->glyphs[i].value; + + // Add new codepoints to list (provided range) + for (int i = currentRangeSize; i < updatedCodepointCount; i++) + updatedCodepoints[i] = start + (i - currentRangeSize); + + UnloadFont(*font); + *font = LoadFontEx(fontPath, 32, updatedCodepoints, updatedCodepointCount); } -Font LoadUnicodeFont(const char *fileName, int fontSize) -{ - CodepointsArray cp = { 0 }; - cp.capacity = 2048; - cp.data = (int *)MemAlloc(cp.capacity*sizeof(int)); - - if (!cp.data) - { - TraceLog(LOG_ERROR, "FONTUTIL: Initial allocation failed"); - return GetFontDefault(); - } - - // Unicode range: Basic ASCII - AddRange(&cp, 32, 126); - - // Unicode range: European Languages - AddRange(&cp, 0xC0, 0x17F); - AddRange(&cp, 0x180, 0x24F); - AddRange(&cp, 0x1E00, 0x1EFF); - AddRange(&cp, 0x2C60, 0x2C7F); - - // Unicode range: Greek - AddRange(&cp, 0x370, 0x3FF); - AddRange(&cp, 0x1F00, 0x1FFF); - - // Unicode range: Cyrillic - AddRange(&cp, 0x400, 0x4FF); - AddRange(&cp, 0x500, 0x52F); - AddRange(&cp, 0x2DE0, 0x2DFF); - AddRange(&cp, 0xA640, 0xA69F); - - // Unicode range: CJK - AddRange(&cp, 0x4E00, 0x9FFF); - AddRange(&cp, 0x3400, 0x4DBF); - AddRange(&cp, 0x3000, 0x303F); - AddRange(&cp, 0x3040, 0x309F); - AddRange(&cp, 0x30A0, 0x30FF); - AddRange(&cp, 0x31F0, 0x31FF); - AddRange(&cp, 0xFF00, 0xFFEF); - AddRange(&cp, 0xAC00, 0xD7AF); - AddRange(&cp, 0x1100, 0x11FF); - - // Unicode range: Other - // WARNING: Not available on provided font - AddRange(&cp, 0x900, 0x97F); // Devanagari - AddRange(&cp, 0x600, 0x6FF); // Arabic - AddRange(&cp, 0x5D0, 0x5EA); // Hebrew - - Font font = {0}; - - if (FileExists(fileName)) - { - font = LoadFontEx(fileName, fontSize, cp.data, cp.count); - } - - if (font.texture.id == 0) - { - font = GetFontDefault(); - TraceLog(LOG_WARNING, "FONTUTIL: Using default font"); - } - - MemFree(cp.data); - - return font; -} From 9fab2c8a134b9aad46a79922e65870605af1ff0a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:34:58 +0200 Subject: [PATCH 19/26] REVIEWED: example: `text_unicode_ranges` --- examples/text/text_unicode_ranges.c | 11 +++++++---- examples/text/text_unicode_ranges.png | Bin 0 -> 91782 bytes 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 examples/text/text_unicode_ranges.png diff --git a/examples/text/text_unicode_ranges.c b/examples/text/text_unicode_ranges.c index 190d72732..76d20ba40 100644 --- a/examples/text/text_unicode_ranges.c +++ b/examples/text/text_unicode_ranges.c @@ -114,6 +114,7 @@ int main(void) } prevUnicodeRange = unicodeRange; + SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); // Set font atlas scale filter } if (IsKeyPressed(KEY_ZERO)) unicodeRange = 0; @@ -130,7 +131,7 @@ int main(void) ClearBackground(RAYWHITE); - DrawText("ADD CODEPOINTS: 1,2,3,4", 20, 20, 20, MAROON); + DrawText("ADD CODEPOINTS: [1][2][3][4]", 20, 20, 20, MAROON); // Render test strings in different languages DrawTextEx(font, "> English: Hello World!", (Vector2){ 50, 70 }, 32, 1, DARKGRAY); // English @@ -142,11 +143,13 @@ int main(void) //DrawTextEx(font, "देवनागरी: होला मुंडो!", (Vector2){ 50, 350 }, 32, 1, DARKGRAY); // Devanagari (glyphs not available in font) // Draw font texture scaled to screen - DrawRectangle(400, 16, 380, 400, BLACK); + float atlasScale = 380.0f/font.texture.width; + DrawRectangle(400, 16, font.texture.width*atlasScale, font.texture.height*atlasScale, BLACK); DrawTexturePro(font.texture, (Rectangle){ 0, 0, font.texture.width, font.texture.height }, - (Rectangle){ 400, 16, font.texture.width*0.5f, font.texture.height*0.5f }, (Vector2){ 0, 0 }, 0.0f, WHITE); + (Rectangle){ 400, 16, font.texture.width*atlasScale, font.texture.height*atlasScale }, (Vector2){ 0, 0 }, 0.0f, WHITE); + DrawRectangleLines(400, 16, 380, 380, RED); - DrawText(TextFormat("ATLAS SIZE: %ix%i px", font.texture.width, font.texture.height), 20, 380, 20, BLUE); + DrawText(TextFormat("ATLAS SIZE: %ix%i px (x%02.1f)", font.texture.width, font.texture.height, atlasScale), 20, 380, 20, BLUE); // Display font attribution DrawText("Font: Noto Sans TC. License: SIL Open Font License 1.1", screenWidth - 300, screenHeight - 20, 10, GRAY); diff --git a/examples/text/text_unicode_ranges.png b/examples/text/text_unicode_ranges.png new file mode 100644 index 0000000000000000000000000000000000000000..5ffffe7186101449d956a74c25bca6d0f8d79755 GIT binary patch literal 91782 zcmeFZ_gfQdyZ$}Nq(DfalY|yTBqAUs0St&U;))20BVt2T5C{@R)Dx#w3 zD6)*b5EZO60ci%zQbbe`P{4+$2-f%Z+0VP5XYXV0_YZi#Uw+5|B*0|uc3tP^JlTeY zOl2|n3o%9k=`o^n*;!?By&eCoy zcRdEfLw)L$d;9GThdO)e_pf;>96h>9;OxwtTVGvWO%xVZCiS&AWKG)i)9}-wtSnl< zJ+%RGkev^1PuP|UUQWt zcu+aeSxINQynb{c&P^oB{B!*Li}k#`G&FQ*#*H7(dB_QxJ@cr)12>OI`|He^ z`pBp#Qi$W!T{f}M<1bw`9Ob&6?(VYA*u=!9fv=yJXYF;I>2Y{MYF6i?3rnWd*qNA^ zsBYi(hih3=cUnc2tbT8^_!jZ$ZEtTabbPnj?KK~iogL*W=5RZUF%$br8!?Nq6=oo@ zRgH(dldn(k^E<3iso*n*kUrZJw(}yCb%ri3E=jF}A3i85>ACmsZ1^U1%c#10xBM%T zcx𝔅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

T-3r@wi#}IQ2 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 literal 0 HcmV?d00001 From bcd46fb36be8c29afce9c9b5568859618e9ffa88 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:38:04 +0200 Subject: [PATCH 20/26] Update rexm.c --- tools/rexm/rexm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index d1bfb1a62..2e0294aa0 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -1257,9 +1257,9 @@ int main(int argc, char *argv[]) printf("////////////////////////////////////////////////////////////////////////////////////////////\n\n"); printf("USAGE:\n\n"); - printf(" > rexm []\n"); + printf(" > rexm []\n\n"); - printf("\COMMANDS:\n\n"); + printf("COMMANDS:\n\n"); printf(" help : Provides command-line usage information\n"); printf(" create : Creates an empty example, from internal template\n"); printf(" add : Add existing example, category extracted from name\n"); From cf61a8b888b6e5823c919937a88e4117714100f9 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:38:20 +0200 Subject: [PATCH 21/26] REXM: Update examples report --- examples/Makefile | 2 +- examples/Makefile.Web | 8 ++++---- tools/rexm/examples_report.md | 4 ++-- tools/rexm/examples_report_issues.md | 1 - 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 25baa9bbe..afd9533c9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -504,10 +504,10 @@ CORE = \ core/core_2d_camera_platformer \ core/core_2d_camera_split_screen \ core/core_3d_camera_first_person \ + core/core_3d_camera_fps \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ - core/core_3d_camera_fps \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 74b94dd6f..be93ca36a 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -504,10 +504,10 @@ CORE = \ core/core_2d_camera_platformer \ core/core_2d_camera_split_screen \ core/core_3d_camera_first_person \ + core/core_3d_camera_fps \ core/core_3d_camera_free \ core/core_3d_camera_mode \ core/core_3d_camera_split_screen \ - core/core_3d_camera_fps \ core/core_3d_picking \ core/core_automation_events \ core/core_basic_screen_manager \ @@ -695,6 +695,9 @@ core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_3d_camera_fps: core/core_3d_camera_fps.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_3d_camera_free: core/core_3d_camera_free.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -704,9 +707,6 @@ core/core_3d_camera_mode: core/core_3d_camera_mode.c core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -core/core_3d_camera_fps: core/core_3d_camera_fps.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index 1c08d9270..9eb828045 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -36,7 +36,7 @@ Example elements validated: | core_3d_camera_free | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_3d_camera_first_person | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_3d_camera_split_screen | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | -| core_3d_fps_controller | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| core_3d_camera_fps | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_3d_picking | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_world_screen | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | core_custom_logging | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | @@ -181,4 +181,4 @@ Example elements validated: | raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | | embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | raymath_vector_angle | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | -| text_unicode_ranges | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_unicode_ranges | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/examples_report_issues.md b/tools/rexm/examples_report_issues.md index 12e8c656e..bfa0125bb 100644 --- a/tools/rexm/examples_report_issues.md +++ b/tools/rexm/examples_report_issues.md @@ -37,4 +37,3 @@ Example elements validated: | raylib_opengl_interop | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | | embedded_files_loading | ✔ | ❌ | ❌ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | raymath_vector_angle | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | -| text_unicode_ranges | ✔ | ✔ | ✔ | ❌ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 1dfff2252dbc6e4a41e92431231d0096fcc10cb2 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:44:27 +0200 Subject: [PATCH 22/26] rexm update --- examples/examples_list.txt | 2 +- tools/rexm/examples_report.md | 1 + tools/rexm/examples_report_issues.md | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/examples_list.txt b/examples/examples_list.txt index a980ff4a4..0a20d8155 100644 --- a/examples/examples_list.txt +++ b/examples/examples_list.txt @@ -100,6 +100,7 @@ text;text_input_box;⭐️⭐️☆☆;1.7;3.5;"Ramon Santamaria";@raysan5 text;text_writing_anim;⭐️⭐️☆☆;1.4;1.4;"Ramon Santamaria";@raysan5 text;text_rectangle_bounds;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor text;text_unicode;⭐️⭐️⭐️⭐️;2.5;4.0;"Vlad Adrian";@demizdor +text;text_unicode_ranges;⭐️⭐️⭐️⭐️;5.5;5.6;"Vlad Adrian";@demizdor text;text_draw_3d;⭐️⭐️⭐️⭐️;3.5;4.0;"Vlad Adrian";@demizdor text;text_codepoints_loading;⭐️⭐️⭐️☆;4.2;4.2;"Ramon Santamaria";@raysan5 models;models_animation;⭐️⭐️☆☆;2.5;3.5;"Culacant";@culacant @@ -168,4 +169,3 @@ others;easings_testbed;⭐️⭐️⭐️☆;2.5;3.0;"Juan Miguel López";@flash others;raylib_opengl_interop;⭐️⭐️⭐️⭐️;3.8;4.0;"Stephan Soller";@arkanis others;embedded_files_loading;⭐️⭐️☆☆;3.0;3.5;"Kristian Holmgren";@defutura others;raymath_vector_angle;⭐️⭐️☆☆;1.0;4.6;"Ramon Santamaria";@raysan5 -text;text_unicode_ranges;⭐⭐⭐⭐️;2.5;4.0;"Vlad Adrian";@demizdor diff --git a/tools/rexm/examples_report.md b/tools/rexm/examples_report.md index 9eb828045..a4bcef40c 100644 --- a/tools/rexm/examples_report.md +++ b/tools/rexm/examples_report.md @@ -113,6 +113,7 @@ Example elements validated: | text_writing_anim | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_rectangle_bounds | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_unicode | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_unicode_ranges | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_draw_3d | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_codepoints_loading | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_animation | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | diff --git a/tools/rexm/examples_report_issues.md b/tools/rexm/examples_report_issues.md index bfa0125bb..9f82a6dfa 100644 --- a/tools/rexm/examples_report_issues.md +++ b/tools/rexm/examples_report_issues.md @@ -26,6 +26,7 @@ Example elements validated: | core_high_dpi | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | shapes_digital_clock | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_font_sdf | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | +| text_unicode_ranges | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_draw_3d | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | text_codepoints_loading | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | models_loading_m3d | ✔ | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | From 4fa8e23efc2d3e2740edc6c8f17e16500ef76e0a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:52:00 +0200 Subject: [PATCH 23/26] Update rexm.c --- tools/rexm/rexm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index 2e0294aa0..c83ae0eaf 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -370,15 +370,13 @@ int main(int argc, char *argv[]) { // Verify example exists in collection to be removed char *exColInfo = LoadFileText(exCollectionFilePath); - if ((TextFindIndex(exColInfo, argv[2]) != -1) && // Example in the collection - (TextFindIndex(exName, "_") != -1)) // Valid example name + if (TextFindIndex(exColInfo, argv[2]) != -1) // Example in the collection { - strcpy(exName, argv[2]); // Register example name for removal strncpy(exCategory, exName, TextFindIndex(exName, "_")); opCode = OP_BUILD; } - else LOG("WARNING: REMOVE: Example not available in the collection\n"); + else LOG("WARNING: BUILD: Example not available in the collection\n"); UnloadFileText(exColInfo); } } From cb6a64843c3ddd383a17237664325629b368759d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 18:52:10 +0200 Subject: [PATCH 24/26] Remove raylib logs --- tools/rexm/rexm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/rexm/rexm.c b/tools/rexm/rexm.c index c83ae0eaf..db1e0fbf7 100644 --- a/tools/rexm/rexm.c +++ b/tools/rexm/rexm.c @@ -181,6 +181,8 @@ static void UpdateWebMetadata(const char *exHtmlPath, const char *exFilePath); //------------------------------------------------------------------------------------ int main(int argc, char *argv[]) { + SetTraceLogLevel(LOG_NONE); + // Path values can be configured with environment variables exBasePath = getenv("REXM_EXAMPLES_BASE_PATH"); exWebPath = getenv("REXM_EXAMPLES_WEB_PATH"); From 4e8d08523fb7c62bce68cd0a2e11d926bf632e79 Mon Sep 17 00:00:00 2001 From: JohnnyCena123 Date: Tue, 2 Sep 2025 19:58:39 +0300 Subject: [PATCH 25/26] add const qualifier to the first arg of TextJoin (#5166) * add const qualifier to the first arg of TextJoin * Update raylib_api.* by CI * Update raylib.h --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Ray --- src/raylib.h | 2 +- src/rtext.c | 2 +- tools/parser/output/raylib_api.json | 2 +- tools/parser/output/raylib_api.lua | 2 +- tools/parser/output/raylib_api.txt | 2 +- tools/parser/output/raylib_api.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 41bc1926e..fa08ed9c0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1515,7 +1515,7 @@ RLAPI const char *TextFormat(const char *text, ...); RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) -RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string, -1 if not found diff --git a/src/rtext.c b/src/rtext.c index 009a264f3..8bccf0e15 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1714,7 +1714,7 @@ char *TextInsert(const char *text, const char *insert, int position) // Join text strings with delimiter // REQUIRES: memset(), memcpy() -char *TextJoin(char **textList, int count, const char *delimiter) +char *TextJoin(const char **textList, int count, const char *delimiter) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 4ce190714..20101ed9f 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -9754,7 +9754,7 @@ "returnType": "char *", "params": [ { - "type": "char **", + "type": "const char **", "name": "textList" }, { diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index e52fda62d..da669c801 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -6933,7 +6933,7 @@ return { description = "Join text strings with delimiter", returnType = "char *", params = { - {type = "char **", name = "textList"}, + {type = "const char **", name = "textList"}, {type = "int", name = "count"}, {type = "const char *", name = "delimiter"} } diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index b164b7e88..bb8c52408 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -3734,7 +3734,7 @@ Function 434: TextJoin() (3 input parameters) Name: TextJoin Return type: char * Description: Join text strings with delimiter - Param[1]: textList (type: char **) + Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) Function 435: TextSplit() (3 input parameters) diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 43835334d..4f91715c1 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -2474,7 +2474,7 @@ - + From 1fa3c15942fc5076f0af7d40307f2c8f54e2fce0 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 Sep 2025 19:00:22 +0200 Subject: [PATCH 26/26] Revert "add const qualifier to the first arg of TextJoin (#5166)" This reverts commit 4e8d08523fb7c62bce68cd0a2e11d926bf632e79. --- src/raylib.h | 2 +- src/rtext.c | 2 +- tools/parser/output/raylib_api.json | 2 +- tools/parser/output/raylib_api.lua | 2 +- tools/parser/output/raylib_api.txt | 2 +- tools/parser/output/raylib_api.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index fa08ed9c0..41bc1926e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1515,7 +1515,7 @@ RLAPI const char *TextFormat(const char *text, ...); RLAPI const char *TextSubtext(const char *text, int position, int length); // Get a piece of a text string RLAPI char *TextReplace(const char *text, const char *replace, const char *by); // Replace text string (WARNING: memory must be freed!) RLAPI char *TextInsert(const char *text, const char *insert, int position); // Insert text in a position (WARNING: memory must be freed!) -RLAPI char *TextJoin(const char **textList, int count, const char *delimiter); // Join text strings with delimiter +RLAPI char *TextJoin(char **textList, int count, const char *delimiter); // Join text strings with delimiter RLAPI char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings, using MAX_TEXTSPLIT_COUNT static strings RLAPI void TextAppend(char *text, const char *append, int *position); // Append text at specific position and move cursor! RLAPI int TextFindIndex(const char *text, const char *find); // Find first text occurrence within a string, -1 if not found diff --git a/src/rtext.c b/src/rtext.c index 8bccf0e15..009a264f3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1714,7 +1714,7 @@ char *TextInsert(const char *text, const char *insert, int position) // Join text strings with delimiter // REQUIRES: memset(), memcpy() -char *TextJoin(const char **textList, int count, const char *delimiter) +char *TextJoin(char **textList, int count, const char *delimiter) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); diff --git a/tools/parser/output/raylib_api.json b/tools/parser/output/raylib_api.json index 20101ed9f..4ce190714 100644 --- a/tools/parser/output/raylib_api.json +++ b/tools/parser/output/raylib_api.json @@ -9754,7 +9754,7 @@ "returnType": "char *", "params": [ { - "type": "const char **", + "type": "char **", "name": "textList" }, { diff --git a/tools/parser/output/raylib_api.lua b/tools/parser/output/raylib_api.lua index da669c801..e52fda62d 100644 --- a/tools/parser/output/raylib_api.lua +++ b/tools/parser/output/raylib_api.lua @@ -6933,7 +6933,7 @@ return { description = "Join text strings with delimiter", returnType = "char *", params = { - {type = "const char **", name = "textList"}, + {type = "char **", name = "textList"}, {type = "int", name = "count"}, {type = "const char *", name = "delimiter"} } diff --git a/tools/parser/output/raylib_api.txt b/tools/parser/output/raylib_api.txt index bb8c52408..b164b7e88 100644 --- a/tools/parser/output/raylib_api.txt +++ b/tools/parser/output/raylib_api.txt @@ -3734,7 +3734,7 @@ Function 434: TextJoin() (3 input parameters) Name: TextJoin Return type: char * Description: Join text strings with delimiter - Param[1]: textList (type: const char **) + Param[1]: textList (type: char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) Function 435: TextSplit() (3 input parameters) diff --git a/tools/parser/output/raylib_api.xml b/tools/parser/output/raylib_api.xml index 4f91715c1..43835334d 100644 --- a/tools/parser/output/raylib_api.xml +++ b/tools/parser/output/raylib_api.xml @@ -2474,7 +2474,7 @@ - +