From 2433f30b4b81030c6c3fd6cc17ee394403208fe0 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 23 Jul 2019 22:24:25 +0200 Subject: [PATCH] Reviewed ImageDraw() and ImageResizeCanvas() Added security checks in case provided image to functions hasn't been properly loaded... just to avoid program crashes. --- src/textures.c | 155 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 32 deletions(-) diff --git a/src/textures.c b/src/textures.c index 6f6487fe..d9ad1ad0 100644 --- a/src/textures.c +++ b/src/textures.c @@ -408,7 +408,11 @@ Texture2D LoadTextureFromImage(Image image) { Texture2D texture = { 0 }; - texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); + if ((image.data != NULL) && (image.width != 0) && (image.height != 0)) + { + texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); + } + else TraceLog(LOG_WARNING, "Texture could not be loaded from Image"); texture.width = image.width; texture.height = image.height; @@ -888,6 +892,9 @@ Image ImageCopy(Image image) // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5) void ImageToPOT(Image *image, Color fillColor) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); // Get pixels data // Calculate next power-of-two values @@ -931,6 +938,9 @@ void ImageToPOT(Image *image, Color fillColor) // Convert image data to desired format void ImageFormat(Image *image, int newFormat) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + if ((newFormat != 0) && (image->format != newFormat)) { if ((image->format < COMPRESSED_DXT1_RGB) && (newFormat < COMPRESSED_DXT1_RGB)) @@ -1153,6 +1163,9 @@ void ImageAlphaMask(Image *image, Image alphaMask) // NOTE: Threshold defines the alpha limit, 0.0f to 1.0f void ImageAlphaClear(Image *image, Color color, float threshold) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); for (int i = 0; i < image->width*image->height; i++) if (pixels[i].a <= (unsigned char)(threshold*255.0f)) pixels[i] = color; @@ -1168,6 +1181,9 @@ void ImageAlphaClear(Image *image, Color color, float threshold) // Premultiply alpha channel void ImageAlphaPremultiply(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + float alpha = 0.0f; Color *pixels = GetImageData(*image); @@ -1274,6 +1290,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layoutType) // NOTE: Security checks are performed in case rectangle goes out of bounds void ImageCrop(Image *image, Rectangle crop) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + // Security checks to make sure cropping rectangle is inside margins if ((crop.x + crop.width) > image->width) { @@ -1323,6 +1342,9 @@ void ImageCrop(Image *image, Rectangle crop) // Crop image depending on alpha value void ImageAlphaCrop(Image *image, float threshold) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); int xMin = 65536; // Define a big enough number @@ -1358,6 +1380,9 @@ void ImageAlphaCrop(Image *image, float threshold) // STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom) void ImageResize(Image *image, int newWidth, int newHeight) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + // Get data as Color pixels array to work with it Color *pixels = GetImageData(*image); Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); @@ -1379,6 +1404,9 @@ void ImageResize(Image *image, int newWidth, int newHeight) // Resize and image to new size using Nearest-Neighbor scaling algorithm void ImageResizeNN(Image *image,int newWidth,int newHeight) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); @@ -1411,18 +1439,42 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight) // Resize canvas and fill with color // NOTE: Resize offset is relative to the top-left corner of the original image -void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, int offsetY, Color color) +void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color color) { - // TODO: Review different scaling situations + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; if ((newWidth != image->width) || (newHeight != image->height)) { + // Support offsets out of canvas new size -> original image is cropped + if (offsetX < 0) + { + ImageCrop(image, (Rectangle) { -offsetX, 0, image->width + offsetX, image->height }); + offsetX = 0; + } + else if (offsetX > (newWidth - image->width)) + { + ImageCrop(image, (Rectangle) { 0, 0, image->width - (offsetX - (newWidth - image->width)), image->height }); + offsetX = newWidth - image->width; + } + + if (offsetY < 0) + { + ImageCrop(image, (Rectangle) { 0, -offsetY, image->width, image->height + offsetY }); + offsetY = 0; + } + else if (offsetY > (newHeight - image->height)) + { + ImageCrop(image, (Rectangle) { 0, 0, image->width, image->height - (offsetY - (newHeight - image->height)) }); + offsetY = newHeight - image->height; + } + if ((newWidth > image->width) && (newHeight > image->height)) { Image imTemp = GenImageColor(newWidth, newHeight, color); Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; - Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height }; + Rectangle dstRec = { (float)offsetX, (float)offsetY, srcRec.width, srcRec.height }; ImageDraw(&imTemp, *image, srcRec, dstRec); ImageFormat(&imTemp, image->format); @@ -1431,7 +1483,7 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in } else if ((newWidth < image->width) && (newHeight < image->height)) { - Rectangle crop = { (float)offsetX, (float)offsetY, newWidth, newHeight }; + Rectangle crop = { (float)offsetX, (float)offsetY, (float)newWidth, (float)newHeight }; ImageCrop(image, crop); } else // One side is bigger and the other is smaller @@ -1439,13 +1491,12 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in Image imTemp = GenImageColor(newWidth, newHeight, color); Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; - Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)newWidth, (float)newHeight }; + Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)image->width, (float)image->height }; if (newWidth < image->width) { srcRec.x = offsetX; srcRec.width = newWidth; - dstRec.x = 0.0f; } @@ -1453,11 +1504,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in { srcRec.y = offsetY; srcRec.height = newHeight; - dstRec.y = 0.0f; } - // TODO: ImageDraw() could be buggy? ImageDraw(&imTemp, *image, srcRec, dstRec); ImageFormat(&imTemp, image->format); UnloadImage(*image); @@ -1472,6 +1521,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in // NOTE 3: Mipmaps format is the same as base image void ImageMipmaps(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + int mipCount = 1; // Required mipmap levels count (including base level) int mipWidth = image->width; // Base image width int mipHeight = image->height; // Base image height @@ -1546,6 +1598,9 @@ void ImageMipmaps(Image *image) // dithered data is stored in the LSB part of the unsigned short void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + if (image->format >= COMPRESSED_DXT1_RGB) { TraceLog(LOG_WARNING, "Compressed data formats can not be dithered"); @@ -1702,10 +1757,11 @@ Color *ImageExtractPalette(Image image, int maxPaletteSize, int *extractCount) } // Draw an image (source) within an image (destination) -// TODO: Feel this function could be simplified... void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) { - bool cropRequired = false; + // Security check to avoid program crash + if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) || + (src.data == NULL) || (src.width == 0) || (src.height == 0)) return; // Security checks to avoid size and rectangle issues (out of bounds) // Check that srcRec is inside src image @@ -1727,45 +1783,50 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle - // Check that dstRec is inside dst image - // TODO: Allow negative position within destination with cropping - if (dstRec.x < 0) dstRec.x = 0; - if (dstRec.y < 0) dstRec.y = 0; - // Scale source image in case destination rec size is different than source rec size - if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height)) ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); - + if (((int)dstRec.width != (int)srcRec.width) || ((int)dstRec.height != (int)srcRec.height)) + { + ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height); + } + + // Check that dstRec is inside dst image + // Allow negative position within destination with cropping + if (dstRec.x < 0) + { + ImageCrop(&srcCopy, (Rectangle) { -dstRec.x, 0, dstRec.width + dstRec.x, dstRec.height }); + dstRec.width = dstRec.width + dstRec.x; + dstRec.x = 0; + } + if ((dstRec.x + dstRec.width) > dst->width) { + ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height }); dstRec.width = dst->width - dstRec.x; - TraceLog(LOG_WARNING, "Destination rectangle width out of bounds, rescaled width: %i", dstRec.width); - cropRequired = true; } - if ((dstRec.y + dstRec.height) > dst->height) + if (dstRec.y < 0) { - dstRec.height = dst->height - dstRec.y; - TraceLog(LOG_WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height); - cropRequired = true; + ImageCrop(&srcCopy, (Rectangle) { 0, -dstRec.y, dstRec.width, dstRec.height + dstRec.y }); + dstRec.height = dstRec.height + dstRec.y; + dstRec.y = 0; } - - if (cropRequired) + + if (dstRec.y > (dst->height - dstRec.height)) { - // Crop destination rectangle if out of bounds - Rectangle crop = { 0, 0, dstRec.width, dstRec.height }; - ImageCrop(&srcCopy, crop); + ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y }); + dstRec.height = dst->height - dstRec.y; } // Get image data as Color pixels array to work with it Color *dstPixels = GetImageData(*dst); Color *srcPixels = GetImageData(srcCopy); - UnloadImage(srcCopy); // Source copy not required any more... + UnloadImage(srcCopy); // Source copy not required any more Vector4 fsrc, fdst, fout; // float based versions of pixel data // Blit pixels, copy source image into destination - // TODO: Probably out-of-bounds blitting could be considered here instead of so much cropping... + // TODO: Maybe out-of-bounds blitting could be considered here instead of so much cropping for (int j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++) { for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++) @@ -1799,7 +1860,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec) } } - UnloadImage(*dst); // NOTE: Only dst->data is unloaded + UnloadImage(*dst); *dst = LoadImageEx(dstPixels, (int)dst->width, (int)dst->height); ImageFormat(dst, dst->format); @@ -1900,6 +1961,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Draw rectangle within an image void ImageDrawRectangle(Image *dst, Rectangle rec, Color color) { + // Security check to avoid program crash + if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return; + Image imRec = GenImageColor((int)rec.width, (int)rec.height, color); ImageDraw(dst, imRec, (Rectangle){ 0, 0, rec.width, rec.height }, rec); UnloadImage(imRec); @@ -1937,6 +2001,9 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text, // Flip image vertically void ImageFlipVertical(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *srcPixels = GetImageData(*image); Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); @@ -1961,6 +2028,9 @@ void ImageFlipVertical(Image *image) // Flip image horizontally void ImageFlipHorizontal(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *srcPixels = GetImageData(*image); Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); @@ -1985,6 +2055,9 @@ void ImageFlipHorizontal(Image *image) // Rotate image clockwise 90deg void ImageRotateCW(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *srcPixels = GetImageData(*image); Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); @@ -2011,6 +2084,9 @@ void ImageRotateCW(Image *image) // Rotate image counter-clockwise 90deg void ImageRotateCCW(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *srcPixels = GetImageData(*image); Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); @@ -2037,6 +2113,9 @@ void ImageRotateCCW(Image *image) // Modify image color: tint void ImageColorTint(Image *image, Color color) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); float cR = (float)color.r/255; @@ -2072,6 +2151,9 @@ void ImageColorTint(Image *image, Color color) // Modify image color: invert void ImageColorInvert(Image *image) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); for (int y = 0; y < image->height; y++) @@ -2102,6 +2184,9 @@ void ImageColorGrayscale(Image *image) // NOTE: Contrast values between -100 and 100 void ImageColorContrast(Image *image, float contrast) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + if (contrast < -100) contrast = -100; if (contrast > 100) contrast = 100; @@ -2156,6 +2241,9 @@ void ImageColorContrast(Image *image, float contrast) // NOTE: Brightness values between -255 and 255 void ImageColorBrightness(Image *image, int brightness) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + if (brightness < -255) brightness = -255; if (brightness > 255) brightness = 255; @@ -2195,6 +2283,9 @@ void ImageColorBrightness(Image *image, int brightness) // Modify image color: replace color void ImageColorReplace(Image *image, Color color, Color replace) { + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + Color *pixels = GetImageData(*image); for (int y = 0; y < image->height; y++)