From cfe56d97ea6b0cf29f4c80f1ebc9e704921a2a10 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 11 Jul 2026 16:42:57 +0200 Subject: [PATCH] REVIEWED: `GetPixelDataSize()`, avoid allocations >2GB - ROS: `CLN-013` REVIEWED: `GenImageColor()`, `ImageRotate()` --- src/rlgl.h | 18 ++++++++--- src/rtextures.c | 85 ++++++++++++++++++++++++++++--------------------- 2 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index de8f12d1a..77d7090b0 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -5236,7 +5236,9 @@ static int rlGetPixelDataSize(int width, int height, int format) { int blockWidth = (width + 3)/4; int blockHeight = (height + 3)/4; - dataSize = blockWidth*blockHeight*8; + unsigned long long dataSizeBytes = blockWidth*blockHeight*8; + if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes; + } break; case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: case RL_PIXELFORMAT_COMPRESSED_DXT5_RGBA: @@ -5245,13 +5247,17 @@ static int rlGetPixelDataSize(int width, int height, int format) { int blockWidth = (width + 3)/4; int blockHeight = (height + 3)/4; - dataSize = blockWidth*blockHeight*16; + unsigned long long dataSizeBytes = blockWidth*blockHeight*16; + if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes; + } break; case RL_PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: // 4 bytes per each 4x4 block { int blockWidth = (width + 3)/4; int blockHeight = (height + 3)/4; - dataSize = blockWidth*blockHeight*4; + unsigned long long dataSizeBytes = blockWidth*blockHeight*4; + if (dataSizeBytes < INT_MAX) dataSize = (int)dataSizeBytes; + } break; default: break; } @@ -5260,9 +5266,11 @@ static int rlGetPixelDataSize(int width, int height, int format) if ((format >= RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) && (format <= RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) { - double bytesPerPixel = (double)bpp/8.0; - dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes + unsigned long long dataSizeBytes = (width*height*bpp) >> 3; // Get size in bytes (dividing by 8) + 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; } diff --git a/src/rtextures.c b/src/rtextures.c index 610b78674..e860df100 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -843,9 +843,10 @@ bool ExportImageAsCode(Image image, const char *fileName) // Generate image: plain 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 = { .data = pixels, @@ -2691,45 +2692,48 @@ void ImageRotate(Image *image, int degrees) int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius)); int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius)); - int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); + int bytesPerPixel = GetPixelDataSize(width, height, image->format); + unsigned char *rotatedData = (unsigned char *)RL_CALLOC(bytesPerPixel, 1); - for (int y = 0; y < height; y++) + if (rotatedData != NULL) { - for (int x = 0; x < width; x++) + for (int y = 0; y < height; y++) { - float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f; - float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f; - - if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height)) + for (int x = 0; x < width; x++) { - int x1 = (int)floorf(oldX); - int y1 = (int)floorf(oldY); - int x2 = MIN(x1 + 1, image->width - 1); - int y2 = MIN(y1 + 1, image->height - 1); + float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f; + float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f; - float px = oldX - x1; - float py = oldY - y1; - - for (int i = 0; i < bytesPerPixel; i++) + if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height)) { - float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i]; - float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i]; - float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i]; - float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i]; + int x1 = (int)floorf(oldX); + int y1 = (int)floorf(oldY); + int x2 = MIN(x1 + 1, image->width - 1); + int y2 = MIN(y1 + 1, image->height - 1); - float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py; + float px = oldX - x1; + float py = oldY - y1; - rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val; + for (int i = 0; i < bytesPerPixel; i++) + { + float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i]; + float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i]; + float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i]; + float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i]; + + float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py; + + rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val; + } } } } - } - RL_FREE(image->data); - image->data = rotatedData; - image->width = width; - image->height = height; + RL_FREE(image->data); + image->data = rotatedData; + image->width = width; + image->height = height; + } } } @@ -5508,17 +5512,24 @@ int GetPixelDataSize(int width, int height, int format) case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break; default: break; } + + unsigned long long dataSizeBytes = (width*height*bpp) >> 3; // Get size in bytes (dividing by 8) - double bytesPerPixel = (double)bpp/8.0; - dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes - - // Most compressed formats works on 4x4 blocks, - // if texture is smaller, minimum dataSize is 8 or 16 - if ((width < 4) && (height < 4)) + if (dataSizeBytes < INT_MAX) { - 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; + dataSize = (int)dataSizeBytes; + + // Most compressed formats works on 4x4 blocks, + // if texture is smaller, minimum dataSize is 8 or 16 + if ((width < 4) && (height < 4)) + { + 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; + } } + + // 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; }