|
|
@ -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, p">(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++) |
|
|
|