Reviewed ImageDraw() and ImageResizeCanvas()

Added security checks in case provided image to functions hasn't been properly loaded... just to avoid program crashes.
This commit is contained in:
Ray 2019-07-23 22:24:25 +02:00
parent b44b7dd310
commit 2433f30b4b

View File

@ -408,7 +408,11 @@ Texture2D LoadTextureFromImage(Image image)
{ {
Texture2D texture = { 0 }; Texture2D texture = { 0 };
if ((image.data != NULL) && (image.width != 0) && (image.height != 0))
{
texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps); 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.width = image.width;
texture.height = image.height; texture.height = image.height;
@ -888,6 +892,9 @@ Image ImageCopy(Image image)
// NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5) // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
void ImageToPOT(Image *image, Color fillColor) 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 Color *pixels = GetImageData(*image); // Get pixels data
// Calculate next power-of-two values // Calculate next power-of-two values
@ -931,6 +938,9 @@ void ImageToPOT(Image *image, Color fillColor)
// Convert image data to desired format // Convert image data to desired format
void ImageFormat(Image *image, int newFormat) 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 ((newFormat != 0) && (image->format != newFormat))
{ {
if ((image->format < COMPRESSED_DXT1_RGB) && (newFormat < COMPRESSED_DXT1_RGB)) 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 // NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
void ImageAlphaClear(Image *image, Color color, float threshold) 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); 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; 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 // Premultiply alpha channel
void ImageAlphaPremultiply(Image *image) 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; float alpha = 0.0f;
Color *pixels = GetImageData(*image); 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 // NOTE: Security checks are performed in case rectangle goes out of bounds
void ImageCrop(Image *image, Rectangle crop) 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 // Security checks to make sure cropping rectangle is inside margins
if ((crop.x + crop.width) > image->width) if ((crop.x + crop.width) > image->width)
{ {
@ -1323,6 +1342,9 @@ void ImageCrop(Image *image, Rectangle crop)
// Crop image depending on alpha value // Crop image depending on alpha value
void ImageAlphaCrop(Image *image, float threshold) 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); Color *pixels = GetImageData(*image);
int xMin = 65536; // Define a big enough number 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) // STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL (high-quality Catmull-Rom)
void ImageResize(Image *image, int newWidth, int newHeight) 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 // Get data as Color pixels array to work with it
Color *pixels = GetImageData(*image); Color *pixels = GetImageData(*image);
Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); 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 // Resize and image to new size using Nearest-Neighbor scaling algorithm
void ImageResizeNN(Image *image,int newWidth,int newHeight) 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 *pixels = GetImageData(*image);
Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); 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 // Resize canvas and fill with color
// NOTE: Resize offset is relative to the top-left corner of the original image // 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)) 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)) if ((newWidth > image->width) && (newHeight > image->height))
{ {
Image imTemp = GenImageColor(newWidth, newHeight, color); Image imTemp = GenImageColor(newWidth, newHeight, color);
Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; 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); ImageDraw(&imTemp, *image, srcRec, dstRec);
ImageFormat(&imTemp, image->format); 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)) 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); ImageCrop(image, crop);
} }
else // One side is bigger and the other is smaller 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); Image imTemp = GenImageColor(newWidth, newHeight, color);
Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height }; 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) if (newWidth < image->width)
{ {
srcRec.x = offsetX; srcRec.x = offsetX;
srcRec.width = newWidth; srcRec.width = newWidth;
dstRec.x = 0.0f; dstRec.x = 0.0f;
} }
@ -1453,11 +1504,9 @@ void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, in
{ {
srcRec.y = offsetY; srcRec.y = offsetY;
srcRec.height = newHeight; srcRec.height = newHeight;
dstRec.y = 0.0f; dstRec.y = 0.0f;
} }
// TODO: ImageDraw() could be buggy?
ImageDraw(&imTemp, *image, srcRec, dstRec); ImageDraw(&imTemp, *image, srcRec, dstRec);
ImageFormat(&imTemp, image->format); ImageFormat(&imTemp, image->format);
UnloadImage(*image); 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 // NOTE 3: Mipmaps format is the same as base image
void ImageMipmaps(Image *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 mipCount = 1; // Required mipmap levels count (including base level)
int mipWidth = image->width; // Base image width int mipWidth = image->width; // Base image width
int mipHeight = image->height; // Base image height 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 // dithered data is stored in the LSB part of the unsigned short
void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp) 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) if (image->format >= COMPRESSED_DXT1_RGB)
{ {
TraceLog(LOG_WARNING, "Compressed data formats can not be dithered"); 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) // 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) 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) // Security checks to avoid size and rectangle issues (out of bounds)
// Check that srcRec is inside src image // 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 Image srcCopy = ImageCopy(src); // Make a copy of source image to work with it
ImageCrop(&srcCopy, srcRec); // Crop source image to desired source rectangle 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 // 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) if ((dstRec.x + dstRec.width) > dst->width)
{ {
ImageCrop(&srcCopy, (Rectangle) { 0, 0, dst->width - dstRec.x, dstRec.height });
dstRec.width = dst->width - dstRec.x; 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)
{ {
ImageCrop(&srcCopy, (Rectangle) { 0, -dstRec.y, dstRec.width, dstRec.height + dstRec.y });
dstRec.height = dstRec.height + dstRec.y;
dstRec.y = 0;
}
if (dstRec.y > (dst->height - dstRec.height))
{
ImageCrop(&srcCopy, (Rectangle) { 0, 0, dstRec.width, dst->height - dstRec.y });
dstRec.height = dst->height - dstRec.y; dstRec.height = dst->height - dstRec.y;
TraceLog(LOG_WARNING, "Destination rectangle height out of bounds, rescaled height: %i", dstRec.height);
cropRequired = true;
}
if (cropRequired)
{
// Crop destination rectangle if out of bounds
Rectangle crop = { 0, 0, dstRec.width, dstRec.height };
ImageCrop(&srcCopy, crop);
} }
// Get image data as Color pixels array to work with it // Get image data as Color pixels array to work with it
Color *dstPixels = GetImageData(*dst); Color *dstPixels = GetImageData(*dst);
Color *srcPixels = GetImageData(srcCopy); 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 Vector4 fsrc, fdst, fout; // float based versions of pixel data
// Blit pixels, copy source image into destination // 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 j = (int)dstRec.y; j < (int)(dstRec.y + dstRec.height); j++)
{ {
for (int i = (int)dstRec.x; i < (int)(dstRec.x + dstRec.width); i++) 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); *dst = LoadImageEx(dstPixels, (int)dst->width, (int)dst->height);
ImageFormat(dst, dst->format); 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 // Draw rectangle within an image
void ImageDrawRectangle(Image *dst, Rectangle rec, Color color) 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); Image imRec = GenImageColor((int)rec.width, (int)rec.height, color);
ImageDraw(dst, imRec, (Rectangle){ 0, 0, rec.width, rec.height }, rec); ImageDraw(dst, imRec, (Rectangle){ 0, 0, rec.width, rec.height }, rec);
UnloadImage(imRec); UnloadImage(imRec);
@ -1937,6 +2001,9 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text,
// Flip image vertically // Flip image vertically
void ImageFlipVertical(Image *image) 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 *srcPixels = GetImageData(*image);
Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color));
@ -1961,6 +2028,9 @@ void ImageFlipVertical(Image *image)
// Flip image horizontally // Flip image horizontally
void ImageFlipHorizontal(Image *image) 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 *srcPixels = GetImageData(*image);
Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); Color *dstPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color));
@ -1985,6 +2055,9 @@ void ImageFlipHorizontal(Image *image)
// Rotate image clockwise 90deg // Rotate image clockwise 90deg
void ImageRotateCW(Image *image) 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 *srcPixels = GetImageData(*image);
Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color));
@ -2011,6 +2084,9 @@ void ImageRotateCW(Image *image)
// Rotate image counter-clockwise 90deg // Rotate image counter-clockwise 90deg
void ImageRotateCCW(Image *image) 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 *srcPixels = GetImageData(*image);
Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color)); Color *rotPixels = (Color *)RL_MALLOC(image->width*image->height*sizeof(Color));
@ -2037,6 +2113,9 @@ void ImageRotateCCW(Image *image)
// Modify image color: tint // Modify image color: tint
void ImageColorTint(Image *image, Color color) 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); Color *pixels = GetImageData(*image);
float cR = (float)color.r/255; float cR = (float)color.r/255;
@ -2072,6 +2151,9 @@ void ImageColorTint(Image *image, Color color)
// Modify image color: invert // Modify image color: invert
void ImageColorInvert(Image *image) 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); Color *pixels = GetImageData(*image);
for (int y = 0; y < image->height; y++) for (int y = 0; y < image->height; y++)
@ -2102,6 +2184,9 @@ void ImageColorGrayscale(Image *image)
// NOTE: Contrast values between -100 and 100 // NOTE: Contrast values between -100 and 100
void ImageColorContrast(Image *image, float contrast) 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;
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 // NOTE: Brightness values between -255 and 255
void ImageColorBrightness(Image *image, int brightness) 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;
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 // Modify image color: replace color
void ImageColorReplace(Image *image, Color color, Color replace) 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); Color *pixels = GetImageData(*image);
for (int y = 0; y < image->height; y++) for (int y = 0; y < image->height; y++)