26 Commits

Author SHA1 Message Date
Ray
46b9579f0c Update README.md 2026-07-16 15:03:30 +02:00
Ray
8eb613e50a Update rcore_desktop_glfw.c 2026-07-16 12:28:25 +02:00
89b4fbd298 [rcore] fic position on mouse leave for emscripten. Issue raysan5#5945 followup (#5981) 2026-07-16 12:10:03 +02:00
93e64f2210 Fix TextInsertAlloc overflow by correcting loop bounds (#5982)
Co-authored-by: gideons <gse@newspacesystems.com>
2026-07-16 12:08:41 +02:00
a9a0c046f1 Fix pixel data size regressions (#5984)
Co-authored-by: gideons <gse@newspacesystems.com>
2026-07-16 12:07:19 +02:00
d224912bd5 Fix rlsw persistent alpha blending state (#5964) 2026-07-15 21:21:38 +02:00
39706cccbe [rshapes] Fix DrawRectangleRoundedLinesEx not scaling with transform when thick=1 (#5980) 2026-07-15 21:20:18 +02:00
Ray
4640c84920 WARNING: Not updating mouse.previousPosition when SeMousePosition() #5962
This could be a sensible change, not sure if it can break anything...
2026-07-13 19:32:30 +02:00
Ray
af98e3126e Merge branch 'master' of https://github.com/raysan5/raylib 2026-07-13 19:25:08 +02:00
Ray
e063385c47 Fix #5965 2026-07-13 19:24:49 +02:00
7a247ff40f Fix swScissor() Y-axis clipping inversion in software renderer (#5976) 2026-07-13 19:19:02 +02:00
5de8c3521f fix: ImageFromImage rectangle validation use >= and <= (#5979) 2026-07-13 15:13:12 +02:00
b631fb3d05 Fix TextSubtext() returning empty string when position is 0 (#5975) 2026-07-13 15:11:42 +02:00
fb4649a3fe [rcore][desktop] Fix clipboard image memory leak (#5968)
* [rcore][desktop] Fix clipboard image memory leak

* [delete] delete comment GetClipboardImage into  NOTE about where put SDL_free() .
2026-07-13 15:10:27 +02:00
Ray
f00317bead Added required library 2026-07-11 17:01:09 +02:00
Ray
8805ab3ee2 Update rmodels.c 2026-07-11 16:57:49 +02:00
Ray
ebec978443 REVIEWED: TextJoin(), TextSubtext(), TextInsert*() - ROS: CLN-018 2026-07-11 16:45:56 +02:00
Ray
cfe56d97ea REVIEWED: GetPixelDataSize(), avoid allocations >2GB - ROS: CLN-013
REVIEWED: `GenImageColor()`, `ImageRotate()`
2026-07-11 16:42:57 +02:00
Ray
97d2b0146c REVIEWED: Replace assert() by runtime checks - ROS: CLN-012 2026-07-11 16:22:05 +02:00
Ray
c31666fe2a REVIEWED: ImageFromImage() - ROS: CLN-007 2026-07-11 16:20:41 +02:00
Ray
ab35e0f712 REVIEWED: LoadAutomationEventList() - ROS: CLN-005, CLN-006 2026-07-11 16:18:34 +02:00
Ray
c85cd07db0 Update rmodels.c 2026-07-11 12:22:03 +02:00
Ray
f458296a07 small optimization 2026-07-11 12:21:24 +02:00
Ray
8df2f13962 Update raudio.c 2026-07-11 12:20:57 +02:00
cba8f4286d Fix initial macOS window size (#5967) 2026-07-11 12:06:54 +02:00
86c7edfd74 [raudio] Fix memory leak when loading .wav files (#5963)
* [raudio] Fix memory leak when loading .wav files

* [change] Insert a space.
2026-07-11 12:03:41 +02:00
23 changed files with 315 additions and 285 deletions

View File

@ -99,7 +99,7 @@ vec3 ComputePBR()
vec3 N = normalize(fragNormal); vec3 N = normalize(fragNormal);
if (useTexNormal == 1) if (useTexNormal == 1)
{ {
N = texture2D(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb; N = texture2D(normalMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb;
N = normalize(N*2.0 - 1.0); N = normalize(N*2.0 - 1.0);
N = normalize(N*TBN); N = normalize(N*TBN);
} }

View File

@ -97,7 +97,7 @@ vec3 ComputePBR()
vec3 N = normalize(fragNormal); vec3 N = normalize(fragNormal);
if (useTexNormal == 1) if (useTexNormal == 1)
{ {
N = texture2D(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb; N = texture2D(normalMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb;
N = normalize(N*2.0 - 1.0); N = normalize(N*2.0 - 1.0);
N = normalize(N*TBN); N = normalize(N*TBN);
} }

View File

@ -100,7 +100,7 @@ vec3 ComputePBR()
vec3 N = normalize(fragNormal); vec3 N = normalize(fragNormal);
if (useTexNormal == 1) if (useTexNormal == 1)
{ {
N = texture(normalMap, vec2(fragTexCoord.x*tiling.x + offset.y, fragTexCoord.y*tiling.y + offset.y)).rgb; N = texture(normalMap, vec2(fragTexCoord.x*tiling.x + offset.x, fragTexCoord.y*tiling.y + offset.y)).rgb;
N = normalize(N*2.0 - 1.0); N = normalize(N*2.0 - 1.0);
N = normalize(N*TBN); N = normalize(N*TBN);
} }

View File

@ -9,10 +9,10 @@ IDE | Platform(s) | Source | Example(s)
[CMake](https://cmake.org/) | Windows, Linux, macOS, Web | ✔️ | ✔️ [CMake](https://cmake.org/) | Windows, Linux, macOS, Web | ✔️ | ✔️
[CodeBlocks](http://www.codeblocks.org/) | Windows, Linux, macOS | ❌ | ✔️ [CodeBlocks](http://www.codeblocks.org/) | Windows, Linux, macOS | ❌ | ✔️
[Geany](https://www.geany.org/) | Windows, Linux | ✔️ | ✔️ [Geany](https://www.geany.org/) | Windows, Linux | ✔️ | ✔️
[Notepad++](https://notepad-plus-plus.org/) | Windows | ✔️ | ✔️ [Notepad++](https://notepad-plus-plus.org/) | Windows Web | ✔️ | ✔️
[SublimeText](https://www.sublimetext.com/) | Windows, Linux, macOS | ✔️ | ✔️ [SublimeText](https://www.sublimetext.com/) | Windows, Linux, macOS | ✔️ | ✔️
[VS2019](https://www.visualstudio.com) | Windows | ✔️ | ✔️ [VS2022](https://www.visualstudio.com) | Windows | ✔️ | ✔️
[VSCode](https://code.visualstudio.com/) | Windows, macOS | ❌ | ✔️ [VSCode](https://code.visualstudio.com/) | Windows, Linux, macOS | ❌ | ✔️
[Zig](https://ziglang.org) | Windows, Linux, macOS, Web | ✔️ | ✔️ [Zig](https://ziglang.org) | Windows, Linux, macOS, Web | ✔️ | ✔️
scripts | Windows, Linux, macOS | ✔️ | ✔️ scripts | Windows, Linux, macOS | ✔️ | ✔️

8
src/external/rlsw.h vendored
View File

@ -3909,7 +3909,6 @@ static void sw_immediate_set_color(const float color[4])
RLSW.primitive.color[2] = color[2]; RLSW.primitive.color[2] = color[2];
RLSW.primitive.color[3] = color[3]; RLSW.primitive.color[3] = color[3];
RLSW.primitive.hasColorAlpha |= (color[3] < 1.0f);
} }
static void sw_immediate_set_texcoord(const float texcoord[2]) static void sw_immediate_set_texcoord(const float texcoord[2])
@ -3948,6 +3947,9 @@ static void sw_immediate_push_vertex(const float position[4])
for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i]; for (int i = 0; i < 4; i++) vertex->color[i] = RLSW.primitive.color[i];
for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.primitive.texcoord[i]; for (int i = 0; i < 2; i++) vertex->texcoord[i] = RLSW.primitive.texcoord[i];
// Track whether any vertex in this primitive has alpha < 1.0
RLSW.primitive.hasColorAlpha |= (vertex->color[3] < 1.0f);
// Immediate rendering of the primitive if the required number is reached // Immediate rendering of the primitive if the required number is reached
if (RLSW.primitive.vertexCount == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode]) if (RLSW.primitive.vertexCount == SW_PRIMITIVE_VERTEX_COUNT[RLSW.drawMode])
{ {
@ -4307,8 +4309,8 @@ void swScissor(int x, int y, int width, int height)
RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f; RLSW.scClipMin[0] = (2.0f*(float)RLSW.scMin[0]/(float)RLSW.vpSize[0]) - 1.0f;
RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f; RLSW.scClipMax[0] = (2.0f*(float)RLSW.scMax[0]/(float)RLSW.vpSize[0]) - 1.0f;
RLSW.scClipMax[1] = 1.0f - (2.0f*(float)RLSW.scMin[1]/(float)RLSW.vpSize[1]); RLSW.scClipMin[1] = (2.0f*(float)RLSW.scMin[1]/(float)RLSW.vpSize[1]) - 1.0f;
RLSW.scClipMin[1] = 1.0f - (2.0f*(float)RLSW.scMax[1]/(float)RLSW.vpSize[1]); RLSW.scClipMax[1] = (2.0f*(float)RLSW.scMax[1]/(float)RLSW.vpSize[1]) - 1.0f;
} }
void swClearColor(float r, float g, float b, float a) void swClearColor(float r, float g, float b, float a)

View File

@ -1020,7 +1020,12 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
int triangulate) { int triangulate) {
char linebuf[4096]; char linebuf[4096];
const char *token; const char *token;
assert(p_len < 4095);
// @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(p_len < 4095);
if (p_len > 4095) return 0;
memcpy(linebuf, p, p_len); memcpy(linebuf, p, p_len);
linebuf[p_len] = '\0'; linebuf[p_len] = '\0';
@ -1102,7 +1107,11 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
tinyobj_vertex_index_t i1; tinyobj_vertex_index_t i1;
tinyobj_vertex_index_t i2 = f[1]; tinyobj_vertex_index_t i2 = f[1];
assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE); // @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(3 * num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
if (3*num_f > TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
for (k = 2; k < num_f; k++) { for (k = 2; k < num_f; k++) {
i1 = i2; i1 = i2;
@ -1119,7 +1128,13 @@ static int parseLine(Command *command, const char *p, unsigned int p_len,
} else { } else {
unsigned int k = 0; unsigned int k = 0;
assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
// @raysan5:
// WARNING: If -DNDEBUG is set when compiling, assertions will not
// be present in the compiled binary, replacing by a runtime check
//assert(num_f < TINYOBJ_MAX_FACES_PER_F_LINE);
if (num_f > TINYOBJ_MAX_FACES_PER_F_LINE) return 0;
for (k = 0; k < num_f; k++) { for (k = 0; k < num_f; k++) {
command->f[k] = f[k]; command->f[k] = f[k];
} }

View File

@ -726,7 +726,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -1237,7 +1237,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
// NOTE: emscripten not implemented // NOTE: emscripten not implemented
glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y);
@ -1326,7 +1325,7 @@ void PollInputEvents(void)
// Register previous gamepad states // Register previous gamepad states
for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k];
// Get current gamepad state // Get current gamepad state using internal GLFW mapping, instead of the immediate joystick API
// NOTE: There is no callback available, getting it manually // NOTE: There is no callback available, getting it manually
GLFWgamepadstate state = { 0 }; GLFWgamepadstate state = { 0 };
int result = glfwGetGamepadState(i, &state); // This remaps all gamepads so they have their buttons mapped like an xbox controller int result = glfwGetGamepadState(i, &state); // This remaps all gamepads so they have their buttons mapped like an xbox controller
@ -1693,6 +1692,14 @@ int InitPlatform(void)
return -1; return -1;
} }
#if defined(__APPLE__)
// AppKit can constrain the requested window size to the visible work area during creation
int windowWidth = 0;
int windowHeight = 0;
glfwGetWindowSize(platform.handle, &windowWidth, &windowHeight);
if ((windowWidth > 0) && (windowHeight > 0)) CORE.Window.screen = (Size){ windowWidth, windowHeight };
#endif
// NOTE: Not considering scale factor now, considered below // NOTE: Not considering scale factor now, considered below
CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.width = CORE.Window.screen.width;
CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.render.height = CORE.Window.screen.height;

View File

@ -1526,7 +1526,6 @@ void SetMousePosition(int x, int y)
{ {
RGFW_window_moveMouse(platform.window, x, y); RGFW_window_moveMouse(platform.window, x, y);
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -1255,12 +1255,14 @@ Image GetClipboardImage(void)
for (int i = 0; i < SDL_arraysize(imageFormats); i++) for (int i = 0; i < SDL_arraysize(imageFormats); i++)
{ {
// NOTE: This pointer should be free with SDL_free() at some point
fileData = SDL_GetClipboardData(imageFormats[i], &dataSize); fileData = SDL_GetClipboardData(imageFormats[i], &dataSize);
if (fileData) if (fileData)
{ {
image = LoadImageFromMemory(imageExtensions[i], fileData, (int)dataSize); image = LoadImageFromMemory(imageExtensions[i], fileData, (int)dataSize);
SDL_free(fileData);
if (IsImageValid(image)) if (IsImageValid(image))
{ {
TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]); TRACELOG(LOG_INFO, "Clipboard: Got image from clipboard successfully: %s", imageExtensions[i]);
@ -1409,7 +1411,6 @@ void SetMousePosition(int x, int y)
SDL_WarpMouseInWindow(platform.window, x, y); SDL_WarpMouseInWindow(platform.window, x, y);
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -1299,8 +1299,8 @@ void SetMousePosition(int x, int y)
if (!CORE.Input.Mouse.cursorLocked) if (!CORE.Input.Mouse.cursorLocked)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
TRACELOG(LOG_WARNING, "SetMousePosition not implemented"); TRACELOG(LOG_WARNING, "SetMousePosition not implemented at platform level");
} }
else TRACELOG(LOG_WARNING, "INPUT: MOUSE: Cursor not enabled"); else TRACELOG(LOG_WARNING, "INPUT: MOUSE: Cursor not enabled");
} }

View File

@ -1042,7 +1042,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -420,7 +420,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -395,7 +395,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
} }
// Set mouse cursor // Set mouse cursor

View File

@ -1047,7 +1047,6 @@ void SetGamepadVibration(int gamepad, float leftMotor, float rightMotor, float d
void SetMousePosition(int x, int y) void SetMousePosition(int x, int y)
{ {
CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y };
CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition;
if (CORE.Input.Mouse.cursorLocked) CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition; if (CORE.Input.Mouse.cursorLocked) CORE.Input.Mouse.lockedPosition = CORE.Input.Mouse.currentPosition;

View File

@ -1507,8 +1507,16 @@ static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent
{ {
switch (eventType) switch (eventType)
{ {
case EMSCRIPTEN_EVENT_MOUSEENTER: CORE.Input.Mouse.cursorOnScreen = true; break; case EMSCRIPTEN_EVENT_MOUSEENTER: {
case EMSCRIPTEN_EVENT_MOUSELEAVE: CORE.Input.Mouse.cursorOnScreen = false; break; // NOTE: Mouse position updated by EmscriptenMouseMoveCallback(),
// EmscriptenPointerlockCallback(), and EmscriptenTouchCallback()
CORE.Input.Mouse.cursorOnScreen = true;
} break;
case EMSCRIPTEN_EVENT_MOUSELEAVE:
{
CORE.Input.Mouse.cursorOnScreen = false;
CORE.Input.Mouse.currentPosition = (Vector2){ 0 };
} break;
case EMSCRIPTEN_EVENT_MOUSEDOWN: case EMSCRIPTEN_EVENT_MOUSEDOWN:
{ {
// NOTE: Emscripten and raylib buttons indices are not aligned // NOTE: Emscripten and raylib buttons indices are not aligned

View File

@ -822,7 +822,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.sampleRate = wav.sampleRate; wave.sampleRate = wav.sampleRate;
wave.sampleSize = 16; wave.sampleSize = 16;
wave.channels = wav.channels; wave.channels = wav.channels;
wave.data = (short *)RL_MALLOC((size_t)wave.frameCount*wave.channels*sizeof(short)); wave.data = (short *)RL_CALLOC((size_t)wave.frameCount*wave.channels, sizeof(short));
// NOTE: Forcing conversion to 16bit sample size on reading // NOTE: Forcing conversion to 16bit sample size on reading
drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data); drwav_read_pcm_frames_s16(&wav, wave.frameCount, (drwav_int16 *)wave.data);
@ -845,7 +845,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int
wave.sampleSize = 16; // By default, ogg data is 16 bit per sample (short) wave.sampleSize = 16; // By default, ogg data is 16 bit per sample (short)
wave.channels = info.channels; wave.channels = info.channels;
wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames! wave.frameCount = (unsigned int)stb_vorbis_stream_length_in_samples(oggData); // NOTE: It returns frames!
wave.data = (short *)RL_MALLOC(wave.frameCount*wave.channels*sizeof(short)); wave.data = (short *)RL_CALLOC(wave.frameCount*wave.channels, sizeof(short));
// NOTE: Get the number of samples to process (be careful! asking for number of shorts, not bytes!) // NOTE: Get the number of samples to process (be careful! asking for number of shorts, not bytes!)
stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels); stb_vorbis_get_samples_short_interleaved(oggData, info.channels, (short *)wave.data, wave.frameCount*wave.channels);
@ -1258,7 +1258,7 @@ void WaveFormat(Wave *wave, int sampleRate, int sampleSize, int channels)
return; return;
} }
void *data = RL_MALLOC(frameCount*channels*(sampleSize/8)); void *data = RL_CALLOC(frameCount*channels*(sampleSize/8), 1);
frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate); frameCount = (ma_uint32)ma_convert_frames(data, frameCount, formatOut, channels, sampleRate, wave->data, frameCountIn, formatIn, wave->channels, wave->sampleRate);
if (frameCount == 0) if (frameCount == 0)
@ -1772,7 +1772,7 @@ void UnloadMusicStream(Music music)
{ {
if (false) { } if (false) { }
#if SUPPORT_FILEFORMAT_WAV #if SUPPORT_FILEFORMAT_WAV
else if (music.ctxType == MUSIC_AUDIO_WAV) drwav_uninit((drwav *)music.ctxData); else if (music.ctxType == MUSIC_AUDIO_WAV) { drwav_uninit((drwav *)music.ctxData); RL_FREE(music.ctxData); }
#endif #endif
#if SUPPORT_FILEFORMAT_OGG #if SUPPORT_FILEFORMAT_OGG
else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData); else if (music.ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close((stb_vorbis *)music.ctxData);

View File

@ -3273,7 +3273,7 @@ unsigned int *ComputeMD5(const unsigned char *data, int dataSize)
memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer memcpy(msg + newDataSize, &bitsLen, 4); // Append the len in bits at the end of the buffer
// Process the message in successive 512-bit chunks for each 512-bit chunk of message // Process the message in successive 512-bit chunks for each 512-bit chunk of message
for (int offset = 0; offset < newDataSize; offset += (512/8)) for (int offset = 0; offset < newDataSize; offset += 64) // 512/8
{ {
// Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
unsigned int *w = (unsigned int *)(msg + offset); unsigned int *w = (unsigned int *)(msg + offset);
@ -3370,7 +3370,7 @@ unsigned int *ComputeSHA1(const unsigned char *data, int dataSize)
msg[newDataSize - 8] = (unsigned char)(bitsLen >> 56); msg[newDataSize - 8] = (unsigned char)(bitsLen >> 56);
// Process the message in successive 512-bit chunks // Process the message in successive 512-bit chunks
for (int offset = 0; offset < newDataSize; offset += (512/8)) for (int offset = 0; offset < newDataSize; offset += 64) // 512/8
{ {
// Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15 // Break chunk into sixteen 32-bit words w[j], 0 <= j <= 15
unsigned int w[80] = { 0 }; unsigned int w[80] = { 0 };
@ -3601,10 +3601,14 @@ AutomationEventList LoadAutomationEventList(const char *fileName)
case 'c': sscanf(buffer, "c %i", &list.count); break; case 'c': sscanf(buffer, "c %i", &list.count); break;
case 'e': case 'e':
{ {
sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type, if (counter < list.capacity)
{
sscanf(buffer, "e %d %d %d %d %d %d %63[^\n]s", &list.events[counter].frame, &list.events[counter].type,
&list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc); &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc);
counter++; counter++;
}
else TRACELOG(LOG_WARNING, "AUTOMATION: Event goes beyond automated list capacity (MAX: %i): %s", buffer, list.capacity);
} break; } break;
default: break; default: break;
} }

View File

@ -910,6 +910,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad
#include <stdlib.h> // Required for: calloc(), free() #include <stdlib.h> // Required for: calloc(), free()
#include <string.h> // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] #include <string.h> // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading]
#include <math.h> // Required for: sqrtf(), sinf(), cosf(), floor(), log() #include <math.h> // Required for: sqrtf(), sinf(), cosf(), floor(), log()
#include <limits.h> // Required for: INT_MAX
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Defines and Macros // Defines and Macros
@ -5236,7 +5237,9 @@ static int rlGetPixelDataSize(int width, int height, int format)
{ {
int blockWidth = (width + 3)/4; int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4; int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*8; unsigned long long dataSizeBytes = (unsigned long long)blockWidth*blockHeight*8;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break; } break;
case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA:
case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA:
@ -5245,13 +5248,17 @@ static int rlGetPixelDataSize(int width, int height, int format)
{ {
int blockWidth = (width + 3)/4; int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4; int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*16; unsigned long long dataSizeBytes = (unsigned long long)blockWidth*blockHeight*16;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break; } break;
case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block
{ {
int blockWidth = (width + 3)/4; int blockWidth = (width + 3)/4;
int blockHeight = (height + 3)/4; int blockHeight = (height + 3)/4;
dataSize = blockWidth*blockHeight*4; unsigned long long dataSizeBytes = (unsigned long long)blockWidth*blockHeight*4;
if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} break; } break;
default: break; default: break;
} }
@ -5260,10 +5267,12 @@ static int rlGetPixelDataSize(int width, int height, int format)
if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) && if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) &&
(format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) (format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16))
{ {
double bytesPerPixel = (double)bpp/8.0; unsigned long long dataSizeBytes = ((unsigned long long)width*height*bpp) >> 3; // Get size in bytes (dividing by 8)
dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes;
} }
if (dataSize == 0) TRACELOG(LOG_WARNING, "Requested image size is larger than 2GB, it can not be allocated");
return dataSize; return dataSize;
} }

View File

@ -2320,7 +2320,7 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, float frame)
Matrix currentPoseMatrix = { 0 }; Matrix currentPoseMatrix = { 0 };
// Update all bones and bone matrices of model // Update all bones and bone matrices of model
for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++) for (unsigned int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{ {
// Compute interpolated pose between current and next frame // Compute interpolated pose between current and next frame
// NOTE: Storing animation frame data in model.currentPose // NOTE: Storing animation frame data in model.currentPose
@ -2390,7 +2390,7 @@ void UpdateModelAnimationEx(Model model, ModelAnimation animA, float frameA, Mod
Matrix bindPoseMatrix = { 0 }; Matrix bindPoseMatrix = { 0 };
Matrix currentPoseMatrix = { 0 }; Matrix currentPoseMatrix = { 0 };
for (int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++) for (unsigned int boneIndex = 0; boneIndex < model.skeleton.boneCount; boneIndex++)
{ {
// Get frame-interpolation for first animation // Get frame-interpolation for first animation
Vector3 frameATranslation = Vector3Lerp( Vector3 frameATranslation = Vector3Lerp(
@ -5013,7 +5013,7 @@ static Model LoadIQM(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices // Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
UnloadFileData(fileData); UnloadFileData(fileData);
@ -5241,7 +5241,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCou
// Build frameposes // Build frameposes
for (unsigned int frame = 0; frame < anim[a].num_frames; frame++) for (unsigned int frame = 0; frame < anim[a].num_frames; frame++)
{ {
for (int i = 0; i < animations[a].boneCount; i++) for (unsigned int i = 0; i < animations[a].boneCount; i++)
{ {
if (bones[i].parent >= 0) if (bones[i].parent >= 0)
{ {
@ -5316,8 +5316,8 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
{ {
int base64Size = (int)strlen(cgltfImage->uri + i + 1); int base64Size = (int)strlen(cgltfImage->uri + i + 1);
while (cgltfImage->uri[i + base64Size] == '=') base64Size--; // Ignore optional paddings while (cgltfImage->uri[i + base64Size] == '=') base64Size--; // Ignore optional paddings
int numberOfEncodedBits = base64Size*6 - (base64Size*6) % 8 ; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits int numberOfEncodedBits = base64Size*6 - (base64Size*6)%8; // Encoded bits minus extra bits, so it becomes a multiple of 8 bits
int outSize = numberOfEncodedBits/8 ; // Actual encoded bytes int outSize = numberOfEncodedBits >> 3; // Actual encoded bytes
void *data = NULL; void *data = NULL;
cgltf_options options = { 0 }; cgltf_options options = { 0 };
@ -5371,7 +5371,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat
// Load bone info from GLTF skin data // Load bone info from GLTF skin data
static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, unsigned int *boneCount) static BoneInfo *LoadBoneInfoGLTF(cgltf_skin skin, unsigned int *boneCount)
{ {
*boneCount = skin.joints_count; *boneCount = (unsigned int)skin.joints_count;
BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo)); BoneInfo *bones = (BoneInfo *)RL_CALLOC(skin.joints_count, sizeof(BoneInfo));
for (unsigned int i = 0; i < skin.joints_count; i++) for (unsigned int i = 0; i < skin.joints_count; i++)
@ -6144,7 +6144,7 @@ static Model LoadGLTF(const char *fileName)
model.skeleton.bones = LoadBoneInfoGLTF(skin, &model.skeleton.boneCount); model.skeleton.bones = LoadBoneInfoGLTF(skin, &model.skeleton.boneCount);
model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); model.skeleton.bindPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
for (int i = 0; i < model.skeleton.boneCount; i++) for (unsigned int i = 0; i < model.skeleton.boneCount; i++)
{ {
Matrix bindMatrix = { 0 }; Matrix bindMatrix = { 0 };
cgltf_float inverseBindTransform[16] = { 0 }; cgltf_float inverseBindTransform[16] = { 0 };
@ -6309,7 +6309,7 @@ static Model LoadGLTF(const char *fileName)
if ((data->skins_count > 0) && !hasJoints && (node->parent != NULL) && (node->parent->mesh == NULL)) if ((data->skins_count > 0) && !hasJoints && (node->parent != NULL) && (node->parent->mesh == NULL))
{ {
int parentBoneId = -1; int parentBoneId = -1;
for (int joint = 0; joint < model.skeleton.boneCount; joint++) for (unsigned int joint = 0; joint < model.skeleton.boneCount; joint++)
{ {
if (data->skins[0].joints[joint] == node->parent) if (data->skins[0].joints[joint] == node->parent)
{ {
@ -6347,7 +6347,7 @@ static Model LoadGLTF(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices // Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
// Free unused allocated memory in case of no bones defined // Free unused allocated memory in case of no bones defined
@ -6670,7 +6670,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCo
animations[a].keyframePoses[j] = (Transform *)RL_CALLOC(animations[a].boneCount, sizeof(Transform)); animations[a].keyframePoses[j] = (Transform *)RL_CALLOC(animations[a].boneCount, sizeof(Transform));
float time = (float)j / GLTF_FRAMERATE; float time = (float)j / GLTF_FRAMERATE;
for (int k = 0; k < animations[a].boneCount; k++) for (unsigned int k = 0; k < animations[a].boneCount; k++)
{ {
Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]}; Vector3 translation = {skin.joints[k]->translation[0], skin.joints[k]->translation[1], skin.joints[k]->translation[2]};
Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]}; Quaternion rotation = {skin.joints[k]->rotation[0], skin.joints[k]->rotation[1], skin.joints[k]->rotation[2], skin.joints[k]->rotation[3]};
@ -7204,7 +7204,7 @@ static Model LoadM3D(const char *fileName)
// Initialize runtime animation data: current pose and bone matrices // Initialize runtime animation data: current pose and bone matrices
model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform)); model.currentPose = (Transform *)RL_CALLOC(model.skeleton.boneCount, sizeof(Transform));
model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix)); model.boneMatrices = (Matrix *)RL_CALLOC(model.skeleton.boneCount, sizeof(Matrix));
for (int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity(); for (unsigned int j = 0; j < model.skeleton.boneCount; j++) model.boneMatrices[j] = MatrixIdentity();
} }
m3d_free(m3d); m3d_free(m3d);

View File

@ -888,14 +888,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co
// Draw rectangle with rounded edges // Draw rectangle with rounded edges
void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color) void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, Color color)
{ {
// NOTE: For line thicknes <=1.0f using RL_LINES, otherwise using RL_QUADS/RL_TRIANGLES
DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color); DrawRectangleRoundedLinesEx(rec, roundness, segments, 1.0f, color);
} }
// Draw rectangle with rounded edges outline with line thickness // Draw rectangle with rounded edges outline with line thickness
void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float thick, Color color) void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, float thick, Color color)
{ {
if (thick < 0) thick = 0; if (thick <= 0) return;
// Not a rounded rectangle // Not a rounded rectangle
if (roundness <= 0.0f) if (roundness <= 0.0f)
@ -966,8 +965,6 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f };
if (thick > 1)
{
#if SUPPORT_QUADS_DRAW_MODE #if SUPPORT_QUADS_DRAW_MODE
rlSetTexture(GetShapesTexture().id); rlSetTexture(GetShapesTexture().id);
Rectangle shapeRect = GetShapesTextureRectangle(); Rectangle shapeRect = GetShapesTextureRectangle();
@ -1107,35 +1104,6 @@ void DrawRectangleRoundedLinesEx(Rectangle rec, float roundness, int segments, f
rlVertex2f(point[14].x, point[14].y); rlVertex2f(point[14].x, point[14].y);
rlEnd(); rlEnd();
#endif #endif
}
else
{
// Use LINES to draw the outline
rlBegin(RL_LINES);
// Draw all the 4 corners first: Upper Left Corner, Upper Right Corner, Lower Right Corner, Lower Left Corner
for (int k = 0; k < 4; ++k) // Hope the compiler is smart enough to unroll this loop
{
float angle = angles[k];
const Vector2 center = centers[k];
for (int i = 0; i < segments; i++)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius);
rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius);
angle += stepLength;
}
}
// And now the remaining 4 lines
for (int i = 0; i < 8; i += 2)
{
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(point[i].x, point[i].y);
rlVertex2f(point[i + 1].x, point[i + 1].y);
}
rlEnd();
}
} }
// Draw a polygon of n sides // Draw a polygon of n sides

View File

@ -1699,7 +1699,7 @@ const char *TextSubtext(const char *text, int position, int length)
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if (text != NULL) if ((text != NULL) && (position >= 0) && (length > 0))
{ {
int textLength = TextLength(text); int textLength = TextLength(text);
@ -1975,10 +1975,11 @@ char *TextInsert(const char *text, const char *insert, int position)
{ {
static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 };
memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH); memset(buffer, 0, MAX_TEXT_BUFFER_LENGTH);
if ((text != NULL) && (insert != NULL))
{
int textLen = TextLength(text); int textLen = TextLength(text);
if ((text != NULL) && (insert != NULL) && (position >= 0))
{
if (position > textLen) position = textLen; // End of text string
int insertLen = TextLength(insert); int insertLen = TextLength(insert);
if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1)) if ((textLen + insertLen) < (MAX_TEXT_BUFFER_LENGTH - 1))
@ -1987,7 +1988,7 @@ char *TextInsert(const char *text, const char *insert, int position)
for (int i = 0; i < position; i++) buffer[i] = text[i]; for (int i = 0; i < position; i++) buffer[i] = text[i];
for (int i = position; i < insertLen + position; i++) buffer[i] = insert[i - position]; for (int i = position; i < insertLen + position; i++) buffer[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i]; for (int i = (insertLen + position); i < (textLen + insertLen); i++) buffer[i] = text[i - insertLen];
buffer[textLen + insertLen] = '\0'; // Add EOL buffer[textLen + insertLen] = '\0'; // Add EOL
} }
@ -2002,17 +2003,18 @@ char *TextInsert(const char *text, const char *insert, int position)
char *TextInsertAlloc(const char *text, const char *insert, int position) char *TextInsertAlloc(const char *text, const char *insert, int position)
{ {
char *result = NULL; char *result = NULL;
if ((text != NULL) && (insert != NULL))
{
int textLen = TextLength(text); int textLen = TextLength(text);
if ((text != NULL) && (insert != NULL) && (position >= 0))
{
if (position > textLen) position = textLen; // End of text string
int insertLen = TextLength(insert); int insertLen = TextLength(insert);
result = (char *)RL_MALLOC(textLen + insertLen + 1); result = (char *)RL_MALLOC(textLen + insertLen + 1);
for (int i = 0; i < position; i++) result[i] = text[i]; for (int i = 0; i < position; i++) result[i] = text[i];
for (int i = position; i < insertLen + position; i++) result[i] = insert[i - position]; for (int i = position; i < (insertLen + position); i++) result[i] = insert[i - position];
for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i]; for (int i = (insertLen + position); i < (textLen + insertLen); i++) result[i] = text[i - insertLen];
result[textLen + insertLen] = '\0'; // Add EOL result[textLen + insertLen] = '\0'; // Add EOL
} }
@ -2036,7 +2038,7 @@ char *TextJoin(char **textList, int count, const char *delimiter)
int textLength = TextLength(textList[i]); int textLength = TextLength(textList[i]);
// Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH // Make sure joined text could fit inside MAX_TEXT_BUFFER_LENGTH
if ((totalLength + textLength) < MAX_TEXT_BUFFER_LENGTH) if ((totalLength + textLength + delimiterLen) < MAX_TEXT_BUFFER_LENGTH)
{ {
memcpy(textPtr, textList[i], textLength); memcpy(textPtr, textList[i], textLength);
totalLength += textLength; totalLength += textLength;

View File

@ -70,6 +70,7 @@
#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()/LoadImageAnimFromMemory()/ExportImageToMemory()] #include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()/LoadImageAnimFromMemory()/ExportImageToMemory()]
#include <math.h> // Required for: fabsf() [Used in DrawTextureRec()] #include <math.h> // Required for: fabsf() [Used in DrawTextureRec()]
#include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()] #include <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
#include <limits.h> // Required for: INT_MAX
// Support only desired texture formats on stb_image // Support only desired texture formats on stb_image
#if !SUPPORT_FILEFORMAT_BMP #if !SUPPORT_FILEFORMAT_BMP
@ -843,9 +844,10 @@ bool ExportImageAsCode(Image image, const char *fileName)
// Generate image: plain color // Generate image: plain color
Image GenImageColor(int width, int height, Color color) Image GenImageColor(int width, int height, Color color)
{ {
Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color)); int dataSize = GetPixelDataSize(width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
Color *pixels = (Color *)RL_CALLOC(dataSize, 1);
for (int i = 0; i < width*height; i++) pixels[i] = color; for (int i = 0; (pixels != NULL) && (i < width*height); i++) pixels[i] = color;
Image image = { Image image = {
.data = pixels, .data = pixels,
@ -1228,6 +1230,11 @@ Image ImageFromImage(Image image, Rectangle rec)
{ {
Image result = { 0 }; Image result = { 0 };
// Basic rectangle validation: size smaller than image size
if ((rec.x >= 0) && (rec.y >= 0) && (rec.width > 0) && (rec.height > 0) &&
(((int)rec.x + (int)rec.width) <= image.width) &&
(((int)rec.y + (int)rec.height) <= image.height))
{
int bytesPerPixel = GetPixelDataSize(1, 1, image.format); int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
result.width = (int)rec.width; result.width = (int)rec.width;
@ -1240,6 +1247,8 @@ Image ImageFromImage(Image image, Rectangle rec)
{ {
memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel); memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
} }
}
else TRACELOG(LOG_WARNING, "IMAGE: Rectangle provided for ImageToImage not valid");
return result; return result;
} }
@ -2684,9 +2693,12 @@ void ImageRotate(Image *image, int degrees)
int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius)); int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius));
int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius)); int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius));
int dataSize = GetPixelDataSize(width, height, image->format);
int bytesPerPixel = GetPixelDataSize(1, 1, image->format); int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); unsigned char *rotatedData = (unsigned char *)RL_CALLOC(dataSize, 1);
if (rotatedData != NULL)
{
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
@ -2724,6 +2736,7 @@ void ImageRotate(Image *image, int degrees)
image->width = width; image->width = width;
image->height = height; image->height = height;
} }
}
} }
// Rotate image clockwise 90deg // Rotate image clockwise 90deg
@ -5502,8 +5515,11 @@ int GetPixelDataSize(int width, int height, int format)
default: break; default: break;
} }
double bytesPerPixel = (double)bpp/8.0; unsigned long long dataSizeBytes = ((unsigned long long)width*height*bpp) >> 3; // Get size in bytes (dividing by 8)
dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
if (dataSizeBytes < INT_MAX)
{
dataSize = (int)dataSizeBytes;
// Most compressed formats works on 4x4 blocks, // Most compressed formats works on 4x4 blocks,
// if texture is smaller, minimum dataSize is 8 or 16 // if texture is smaller, minimum dataSize is 8 or 16
@ -5512,6 +5528,10 @@ int GetPixelDataSize(int width, int height, int format)
if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8; if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16; else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
} }
}
// NOTE: In case required image data larger than 2GB, no memory allocated at all (NULL)
if (dataSize == 0) TRACELOG(LOG_WARNING, "Requested image size is larger than 2GB, it can not be allocated");
return dataSize; return dataSize;
} }