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 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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 @@ - +