From 7d5b61ce016fe70b8ce3248fdd0554899688489d Mon Sep 17 00:00:00 2001 From: Colin James Wood <82226641+NighthowlerStudios@users.noreply.github.com> Date: Wed, 20 May 2026 08:12:20 +0100 Subject: [PATCH 1/6] [rlsw][SEGFAULT] Fix triangle and quad spans applying pixels out of bounds (#5849) * fix triangle and quad spans applying pixels out of bounds * remove off by one errors on x/y LoopMax * apply the RASTER_QUAD offset at the loop start so it increments correctly * fix missing endif * remove include guard to allow dyMin usage * early exit if nothing to draw on a span * incorporate dxStart into xSubstep to make xOffset calculate a single time * remove ghost comment * early exit for quads, with a float cast on the left and top distance calculation * remove duplicate xLoopEnd --- src/external/rlsw.h | 84 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 7e153adbc..85e4e8740 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -5385,6 +5385,17 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t int xEnd = (int)end->position[0]; if (xStart == xEnd) return; + // Intercept the span bounds to ensure we don't write before the framebuffer. + int xLoopStart = (xStart >= 0)? xStart : 0; + int xLoopEnd = (xEnd <= RLSW.colorBuffer->width)? xEnd : RLSW.colorBuffer->width; + // Nothing to draw. + if (xLoopStart >= xLoopEnd) return; + + // Get the current row and skip if outside the framebuffer. + // Maybe this check is better suited elsewhere? + int y = (int)start->position[1]; + if (y < 0 || y >= RLSW.colorBuffer->height) return; + // Compute the inverse horizontal distance along the X axis float dxRcp = sw_rcp(end->position[0] - start->position[0]); @@ -5405,27 +5416,29 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t #endif // Compute the subpixel distance to traverse before the first pixel + // Also step further into them to move away from the colorbuffer edge. float xSubstep = 1.0f - sw_fract(start->position[0]); + float dxStart = (float)(xLoopStart - xStart); + float xOffset = xSubstep + dxStart; - // Initializing the interpolation starting values - float w = start->position[3] + dWdx*xSubstep; + // Initializing the interpolation starting values. + float w = start->position[3] + dWdx*xOffset; float color[4] = { - start->color[0] + dCdx[0]*xSubstep, - start->color[1] + dCdx[1]*xSubstep, - start->color[2] + dCdx[2]*xSubstep, - start->color[3] + dCdx[3]*xSubstep + start->color[0] + dCdx[0]*xOffset, + start->color[1] + dCdx[1]*xOffset, + start->color[2] + dCdx[2]*xOffset, + start->color[3] + dCdx[3]*xOffset }; #ifdef SW_ENABLE_DEPTH_TEST - float z = start->position[2] + dZdx*xSubstep; + float z = start->position[2] + dZdx*xOffset; #endif #ifdef SW_ENABLE_TEXTURE - float u = start->texcoord[0] + dUdx*xSubstep; - float v = start->texcoord[1] + dVdx*xSubstep; + float u = start->texcoord[0] + dUdx*xOffset; + float v = start->texcoord[1] + dVdx*xOffset; #endif // Pre-calculate the starting pointers for the framebuffer row - int y = (int)start->position[1]; - int baseOffset = y*RLSW.colorBuffer->width + xStart; + int baseOffset = y*RLSW.colorBuffer->width + xLoopStart; uint8_t *cPtr = (uint8_t *)(RLSW.colorBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; #ifdef SW_ENABLE_DEPTH_TEST uint8_t *dPtr = (uint8_t *)(RLSW.depthBuffer->pixels) + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; @@ -5433,12 +5446,12 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t #define SW_AFFINE_BLOCK 16 - int x = xStart; - while (x < xEnd) + int x = xLoopStart; + while (x < xLoopEnd) { // Clamp last block to remaining pixels int blockEnd = x + SW_AFFINE_BLOCK; - if (blockEnd > xEnd) blockEnd = xEnd; + if (blockEnd > xLoopEnd) blockEnd = xLoopEnd; float blockLenF = (float)(blockEnd - x); float blockLenRcp = sw_rcp(blockLenF); @@ -5673,6 +5686,14 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, int xMax = (int)br->position[0]; int yMax = (int)br->position[1]; + // Exit early if no pixels to draw. Use these later for loop boundaries + int yLoopMin = (yMin >= 0)? yMin : 0; + int xLoopMin = (xMin >= 0)? xMin : 0; + int yLoopMax = (yMax <= RLSW.colorBuffer->height)? yMax : RLSW.colorBuffer->height; + int xLoopMax = (xMax <= RLSW.colorBuffer->width)? xMax : RLSW.colorBuffer->width; + + if (yLoopMin >= yLoopMax || xLoopMin >= xLoopMax) return; + float w = (float)(xMax - xMin); float h = (float)(yMax - yMin); if ((w <= 0) || (h <= 0)) return; @@ -5726,21 +5747,44 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, uint8_t *dPixels = RLSW.depthBuffer->pixels; #endif - for (int y = yMin; y < yMax; y++) + // Calculate the distance the in-bounds boundary is from the quad's edges, only on the left and top. + float dxMin = (float)(xLoopMin - xMin); + float dyMin = (float)(yLoopMin - yMin); + + // Correct our start by how far we clipped outside the framebuffer. + cRow[0] += dCdx[0]*dxMin + dCdy[0]*dyMin; + cRow[1] += dCdx[1]*dxMin + dCdy[1]*dyMin; + cRow[2] += dCdx[2]*dxMin + dCdy[2]*dyMin; + cRow[3] += dCdx[3]*dxMin + dCdy[3]*dyMin; + #ifdef SW_ENABLE_DEPTH_TEST + zRow += dZdy*dyMin + dZdx*dxMin; + #endif + #ifdef SW_ENABLE_TEXTURE + uRow += dUdy*dyMin + dUdx*dxMin; + vRow += dVdy*dyMin + dVdx*dxMin; + #endif + + for (int y = yLoopMin; y < yLoopMax; y++) { - int baseOffset = y*stride + xMin; + int baseOffset = y*stride + xLoopMin; uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; #ifdef SW_ENABLE_DEPTH_TEST uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; + // Copy the cursors so we increment them ourselves without destroying the offset maths. float z = zRow; #endif #ifdef SW_ENABLE_TEXTURE float u = uRow; float v = vRow; #endif - float color[4] = { cRow[0], cRow[1], cRow[2], cRow[3] }; + float color[4] = { + cRow[0], + cRow[1], + cRow[2], + cRow[3] + }; - for (int x = xMin; x < xMax; x++) + for (int x = xLoopMin; x < xLoopMax; x++) { float srcColor[4] = { color[0], color[1], color[2], color[3] }; @@ -5779,6 +5823,7 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, #ifdef SW_ENABLE_DEPTH_TEST discard: #endif + // We move one pixel over without touching the original "start offset" color[0] += dCdx[0]; color[1] += dCdx[1]; color[2] += dCdx[2]; @@ -5801,6 +5846,9 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, cPtr += SW_FRAMEBUFFER_COLOR_SIZE; } + // The for loop is clamped to the right side of the screen. + // However, these cursor start vars are still on the left. + // That's fine. We just need to advance to the next row. cRow[0] += dCdy[0]; cRow[1] += dCdy[1]; cRow[2] += dCdy[2]; From 0d78f10161f9d3df38b10bd33444451b7ee11eda Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 May 2026 09:16:16 +0200 Subject: [PATCH 2/6] Reviewed comments formating --- src/external/rlsw.h | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/external/rlsw.h b/src/external/rlsw.h index 85e4e8740..ffbdaae81 100644 --- a/src/external/rlsw.h +++ b/src/external/rlsw.h @@ -5385,13 +5385,12 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t int xEnd = (int)end->position[0]; if (xStart == xEnd) return; - // Intercept the span bounds to ensure we don't write before the framebuffer. + // Intercept the span bounds to ensure to not write before the framebuffer int xLoopStart = (xStart >= 0)? xStart : 0; int xLoopEnd = (xEnd <= RLSW.colorBuffer->width)? xEnd : RLSW.colorBuffer->width; - // Nothing to draw. - if (xLoopStart >= xLoopEnd) return; + if (xLoopStart >= xLoopEnd) return; // Nothing to draw - // Get the current row and skip if outside the framebuffer. + // Get the current row and skip if outside the framebuffer // Maybe this check is better suited elsewhere? int y = (int)start->position[1]; if (y < 0 || y >= RLSW.colorBuffer->height) return; @@ -5416,12 +5415,12 @@ static void SW_RASTER_TRIANGLE_SPAN(const sw_vertex_t *start, const sw_vertex_t #endif // Compute the subpixel distance to traverse before the first pixel - // Also step further into them to move away from the colorbuffer edge. + // Also step further into them to move away from the colorbuffer edge float xSubstep = 1.0f - sw_fract(start->position[0]); float dxStart = (float)(xLoopStart - xStart); float xOffset = xSubstep + dxStart; - // Initializing the interpolation starting values. + // Initializing the interpolation starting values float w = start->position[3] + dWdx*xOffset; float color[4] = { start->color[0] + dCdx[0]*xOffset, @@ -5747,11 +5746,11 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, uint8_t *dPixels = RLSW.depthBuffer->pixels; #endif - // Calculate the distance the in-bounds boundary is from the quad's edges, only on the left and top. + // Calculate the distance the in-bounds boundary is from the quad's edges, only on the left and top float dxMin = (float)(xLoopMin - xMin); float dyMin = (float)(yLoopMin - yMin); - // Correct our start by how far we clipped outside the framebuffer. + // Correct our start by how far it's clipped outside the framebuffer cRow[0] += dCdx[0]*dxMin + dCdy[0]*dyMin; cRow[1] += dCdx[1]*dxMin + dCdy[1]*dyMin; cRow[2] += dCdx[2]*dxMin + dCdy[2]*dyMin; @@ -5770,7 +5769,7 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, uint8_t *cPtr = cPixels + baseOffset*SW_FRAMEBUFFER_COLOR_SIZE; #ifdef SW_ENABLE_DEPTH_TEST uint8_t *dPtr = dPixels + baseOffset*SW_FRAMEBUFFER_DEPTH_SIZE; - // Copy the cursors so we increment them ourselves without destroying the offset maths. + // Copy the cursors without destroying the offset maths float z = zRow; #endif #ifdef SW_ENABLE_TEXTURE @@ -5823,7 +5822,7 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, #ifdef SW_ENABLE_DEPTH_TEST discard: #endif - // We move one pixel over without touching the original "start offset" + // Move one pixel over without touching the original "start offset" color[0] += dCdx[0]; color[1] += dCdx[1]; color[2] += dCdx[2]; @@ -5846,9 +5845,9 @@ static void SW_RASTER_QUAD(const sw_vertex_t *a, const sw_vertex_t *b, cPtr += SW_FRAMEBUFFER_COLOR_SIZE; } - // The for loop is clamped to the right side of the screen. - // However, these cursor start vars are still on the left. - // That's fine. We just need to advance to the next row. + // The for loop is clamped to the right side of the screen + // However, these cursor start vars are still on the left + // That's fine, advancing to the next row cRow[0] += dCdy[0]; cRow[1] += dCdy[1]; cRow[2] += dCdy[2]; From f65d5ad7a9682696f96ef7fbca13609126139a71 Mon Sep 17 00:00:00 2001 From: ghera Date: Sun, 24 May 2026 08:55:08 +0200 Subject: [PATCH 3/6] rshapes: fix auto segment rounded-corner math and rounding (#5883) --- src/rshapes.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 69c1dacea..5ef988e38 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -361,7 +361,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); if (segments <= 0) segments = minSegments; } @@ -453,7 +453,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); if (segments <= 0) segments = minSegments; } @@ -579,7 +579,7 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); - segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); if (segments <= 0) segments = minSegments; } @@ -670,7 +670,7 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/outerRadius, 2) - 1); - segments = (int)((endAngle - startAngle)*ceilf(2*PI/th)/360); + segments = (int)ceilf((endAngle - startAngle)*(2*PI/th)/360.0f); if (segments <= 0) segments = minSegments; } @@ -960,7 +960,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)(ceilf(2*PI/th)/2.0f); + segments = (int)ceilf((2*PI/th)/4.0f); if (segments <= 0) segments = 4; } @@ -1195,7 +1195,7 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f { // Calculate the maximum angle between segments based on the error rate (usually 0.5f) float th = acosf(2*powf(1 - SMOOTH_CIRCLE_ERROR_RATE/radius, 2) - 1); - segments = (int)(ceilf(2*PI/th)/2.0f); + segments = (int)ceilf((2*PI/th)/4.0f); if (segments <= 0) segments = 4; } From f17babfe8af98e7a6bf565f790cccf43177187c6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 May 2026 09:24:26 +0200 Subject: [PATCH 4/6] REVIEWED: #5879 --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index e8164073f..286f33c28 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1164,7 +1164,7 @@ RLAPI bool ChangeDirectory(const char *dirPath); // Change wo RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI bool IsFileNameValid(const char *fileName); // Check if fileName is valid for the platform/OS RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths, files and directories, no subdirs scan -RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and subdir scan; some filters available: `*.*`,`FILES*`,`DIRS*` +RLAPI FilePathList LoadDirectoryFilesEx(const char *basePath, const char *filter, bool scanSubdirs); // Load directory filepaths with extension filtering and subdir scan; some filters available: '*.*','FILES*','DIRS*' RLAPI void UnloadDirectoryFiles(FilePathList files); // Unload filepaths RLAPI bool IsFileDropped(void); // Check if a file has been dropped into window RLAPI FilePathList LoadDroppedFiles(void); // Load dropped filepaths From 7dd72e73284f7d6ed8d9e292ea421071117757a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 24 May 2026 07:25:01 +0000 Subject: [PATCH 5/6] rlparser: update raylib_api.* by CI --- tools/rlparser/output/raylib_api.json | 2 +- tools/rlparser/output/raylib_api.lua | 2 +- tools/rlparser/output/raylib_api.txt | 2 +- tools/rlparser/output/raylib_api.xml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/rlparser/output/raylib_api.json b/tools/rlparser/output/raylib_api.json index aa018051a..b42fd2d61 100644 --- a/tools/rlparser/output/raylib_api.json +++ b/tools/rlparser/output/raylib_api.json @@ -4696,7 +4696,7 @@ }, { "name": "LoadDirectoryFilesEx", - "description": "Load directory filepaths with extension filtering and subdir scan; some filters available: `*.*`,`FILES*`,`DIRS*`", + "description": "Load directory filepaths with extension filtering and subdir scan; some filters available: '*.*','FILES*','DIRS*'", "returnType": "FilePathList", "params": [ { diff --git a/tools/rlparser/output/raylib_api.lua b/tools/rlparser/output/raylib_api.lua index e88db0b7e..859ef38ba 100644 --- a/tools/rlparser/output/raylib_api.lua +++ b/tools/rlparser/output/raylib_api.lua @@ -4207,7 +4207,7 @@ return { }, { name = "LoadDirectoryFilesEx", - description = "Load directory filepaths with extension filtering and subdir scan; some filters available: `*.*`,`FILES*`,`DIRS*`", + description = "Load directory filepaths with extension filtering and subdir scan; some filters available: '*.*','FILES*','DIRS*'", returnType = "FilePathList", params = { {type = "const char *", name = "basePath"}, diff --git a/tools/rlparser/output/raylib_api.txt b/tools/rlparser/output/raylib_api.txt index 0447edd23..0051c6a14 100644 --- a/tools/rlparser/output/raylib_api.txt +++ b/tools/rlparser/output/raylib_api.txt @@ -1789,7 +1789,7 @@ Function 146: LoadDirectoryFiles() (1 input parameters) Function 147: LoadDirectoryFilesEx() (3 input parameters) Name: LoadDirectoryFilesEx Return type: FilePathList - Description: Load directory filepaths with extension filtering and subdir scan; some filters available: `*.*`,`FILES*`,`DIRS*` + Description: Load directory filepaths with extension filtering and subdir scan; some filters available: '*.*','FILES*','DIRS*' Param[1]: basePath (type: const char *) Param[2]: filter (type: const char *) Param[3]: scanSubdirs (type: bool) diff --git a/tools/rlparser/output/raylib_api.xml b/tools/rlparser/output/raylib_api.xml index e9db25846..fdc1481ec 100644 --- a/tools/rlparser/output/raylib_api.xml +++ b/tools/rlparser/output/raylib_api.xml @@ -1125,7 +1125,7 @@ - + From 808e6b9b20f76de5af1f512ae2a76af01627de74 Mon Sep 17 00:00:00 2001 From: keyle Date: Mon, 25 May 2026 01:36:12 +1000 Subject: [PATCH 6/6] Raylib-lua version to 5.5 (#5884) raylib-lua from 5.0 to 5.5 in bindings. --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 7d350a9b0..710a7a1ed 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -49,7 +49,7 @@ Some people ported raylib to other languages in the form of bindings or wrappers | [Raylib.jl](https://github.com/chengchingwen/Raylib.jl) | 4.2 | [Julia](https://julialang.org) | Zlib | | [kaylib](https://github.com/electronstudio/kaylib) | 3.7 | [Kotlin/native](https://kotlinlang.org) | **???** | | [KaylibKit](https://codeberg.org/Kenta/KaylibKit) | 4.5 | [Kotlin/native](https://kotlinlang.org) | Zlib | -| [raylib-lua](https://github.com/TSnake41/raylib-lua) | 5.0 | [Lua](http://www.lua.org) | ISC | +| [raylib-lua](https://github.com/TSnake41/raylib-lua) | 5.5 | [Lua](http://www.lua.org) | ISC | | [raylib-lua-bindings (WIP)](https://github.com/legendaryredfox/raylib-lua-bindings) | 5.5 | [Lua](http://www.lua.org) | ISC | | [ReiLua](https://github.com/nullstare/ReiLua) | 5.5 | [Lua](http://www.lua.org) | MIT | | [raylib-lua-sol](https://github.com/RobLoach/raylib-lua-sol) | 5.5 | [Lua](http://www.lua.org) | Zlib |