Update C sources

This commit is contained in:
Milan Nikolic 2024-05-07 21:54:39 +02:00
parent 2a66186c7d
commit 1868520849
No known key found for this signature in database
GPG key ID: 9229D0EAA3AA4E75
27 changed files with 4582 additions and 2234 deletions

View file

@ -42,7 +42,7 @@
*
* LICENSE: zlib/libpng
*
* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5)
* Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
@ -71,10 +71,10 @@
#if defined(SUPPORT_MODULE_RTEXTURES)
#include "utils.h" // Required for: TRACELOG()
#include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2
#include "rlgl.h" // OpenGL abstraction layer to multiple versions
#include <stdlib.h> // Required for: malloc(), free()
#include <string.h> // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()]
#include <stdlib.h> // Required for: malloc(), calloc(), free()
#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 <stdio.h> // Required for: sprintf() [Used in ExportImageAsCode()]
@ -212,15 +212,25 @@
#define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
#define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-function"
#endif
#define STB_IMAGE_RESIZE_IMPLEMENTATION
#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()]
#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()]
#if defined(__GNUC__) // GCC and Clang
#pragma GCC diagnostic pop
#endif
#if defined(SUPPORT_FILEFORMAT_SVG)
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"
#define NANOSVG_IMPLEMENTATION // Expands implementation
#include "external/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "external/nanosvgrast.h"
#endif
//----------------------------------------------------------------------------------
@ -283,9 +293,12 @@ Image LoadImage(const char *fileName)
unsigned char *fileData = LoadFileData(fileName, &dataSize);
// Loading image from memory data
if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize);
if (fileData != NULL)
{
image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize);
RL_FREE(fileData);
UnloadFileData(fileData);
}
return image;
}
@ -301,18 +314,22 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
if (fileData != NULL)
{
unsigned char *dataPtr = fileData;
unsigned int size = GetPixelDataSize(width, height, format);
int size = GetPixelDataSize(width, height, format);
if (headerSize > 0) dataPtr += headerSize;
if (size <= dataSize) // Security check
{
// Offset file data to expected raw image by header size
if ((headerSize > 0) && ((headerSize + size) <= dataSize)) dataPtr += headerSize;
image.data = RL_MALLOC(size); // Allocate required memory in bytes
memcpy(image.data, dataPtr, size); // Copy required data to image
image.width = width;
image.height = height;
image.mipmaps = 1;
image.format = format;
image.data = RL_MALLOC(size); // Allocate required memory in bytes
memcpy(image.data, dataPtr, size); // Copy required data to image
image.width = width;
image.height = height;
image.mipmaps = 1;
image.format = format;
}
RL_FREE(fileData);
UnloadFileData(fileData);
}
return image;
@ -368,8 +385,8 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height)
int offsetX = 0;
int offsetY = 0;
if (scaleHeight > scaleWidth) offsetY = (height - svgImage->height*scale) / 2;
else offsetX = (width - svgImage->width*scale) / 2;
if (scaleHeight > scaleWidth) offsetY = (height - svgImage->height*scale)/2;
else offsetX = (width - svgImage->width*scale)/2;
// Rasterize
struct NSVGrasterizer *rast = nsvgCreateRasterizer();
@ -421,7 +438,7 @@ Image LoadImageAnim(const char *fileName, int *frames)
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
RL_FREE(fileData);
UnloadFileData(fileData);
RL_FREE(delays); // NOTE: Frames delays are discarded
}
}
@ -438,12 +455,56 @@ Image LoadImageAnim(const char *fileName, int *frames)
return image;
}
// Load animated image data
// - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
// - Number of frames is returned through 'frames' parameter
// - All frames are returned in RGBA format
// - Frames delay data is discarded
Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames)
{
Image image = { 0 };
int frameCount = 0;
// Security check for input data
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
#if defined(SUPPORT_FILEFORMAT_GIF)
if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0))
{
if (fileData != NULL)
{
int comp = 0;
int *delays = NULL;
image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
RL_FREE(delays); // NOTE: Frames delays are discarded
}
}
#else
if (false) { }
#endif
else
{
image = LoadImageFromMemory(fileType, fileData, dataSize);
frameCount = 1;
}
*frames = frameCount;
return image;
}
// Load image from memory buffer, fileType refers to extension: i.e. ".png"
// WARNING: File extension must be provided in lower-case
Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
{
Image image = { 0 };
// Security check for input data
if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
if ((false)
#if defined(SUPPORT_FILEFORMAT_PNG)
|| (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)
@ -633,10 +694,11 @@ Image LoadImageFromTexture(Texture2D texture)
// Load image from screen buffer and (screenshot)
Image LoadImageFromScreen(void)
{
Vector2 scale = GetWindowScaleDPI();
Image image = { 0 };
image.width = GetScreenWidth();
image.height = GetScreenHeight();
image.width = (int)(GetScreenWidth()*scale.x);
image.height = (int)(GetScreenHeight()*scale.y);
image.mipmaps = 1;
image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
image.data = rlReadScreenPixels(image.width, image.height);
@ -647,11 +709,15 @@ Image LoadImageFromScreen(void)
// Check if an image is ready
bool IsImageReady(Image image)
{
return ((image.data != NULL) && // Validate pixel data available
(image.width > 0) &&
(image.height > 0) && // Validate image size
(image.format > 0) && // Validate image format
(image.mipmaps > 0)); // Validate image mipmaps (at least 1 for basic mipmap level)
bool result = false;
if ((image.data != NULL) && // Validate pixel data available
(image.width > 0) &&
(image.height > 0) && // Validate image size
(image.format > 0) && // Validate image format
(image.mipmaps > 0)) result = true; // Validate image mipmaps (at least 1 for basic mipmap level)
return result;
}
// Unload image from CPU memory (RAM)
@ -666,6 +732,7 @@ bool ExportImage(Image image, const char *fileName)
{
int result = 0;
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result;
#if defined(SUPPORT_IMAGE_EXPORT)
@ -753,6 +820,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS
unsigned char *fileData = NULL;
*dataSize = 0;
// Security check for input data
if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL;
#if defined(SUPPORT_IMAGE_EXPORT)
@ -800,7 +868,7 @@ bool ExportImageAsCode(Image image, const char *fileName)
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2023 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
@ -856,8 +924,8 @@ Image GenImageColor(int width, int height, Color color)
#if defined(SUPPORT_IMAGE_GENERATION)
// Generate image: linear gradient
// The direction value specifies the direction of the gradient (in degrees)
// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right).
// The gradient effectively rotates counter-clockwise by the specified amount.
// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right)
// The gradient effectively rotates counter-clockwise by the specified amount
Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end)
{
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
@ -950,8 +1018,8 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner,
float distY = fabsf(y - centerY);
// Normalize the distances by the dimensions of the gradient rectangle
float normalizedDistX = distX / centerX;
float normalizedDistY = distY / centerY;
float normalizedDistX = distX/centerX;
float normalizedDistY = distY/centerY;
// Calculate the total normalized Manhattan distance
float manhattanDist = fmaxf(normalizedDistX, normalizedDistY);
@ -1008,6 +1076,7 @@ Image GenImageChecked(int width, int height, int checksX, int checksY, Color col
}
// Generate image: white noise
// NOTE: It requires GetRandomValue(), defined in [rcore]
Image GenImageWhiteNoise(int width, int height, float factor)
{
Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
@ -1546,7 +1615,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// Scale image depending on text size
if (textSize.y != imSize.y)
{
float scaleFactor = textSize.y / imSize.y;
float scaleFactor = textSize.y/imSize.y;
TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
// Using nearest-neighbor scaling algorithm for default font
@ -1600,7 +1669,6 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight)
UnloadImageColors(pixels);
}
// Resize and image to new size
// NOTE: Uses stb default scaling filters (both bicubic):
// STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM
@ -1693,8 +1761,22 @@ void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, i
int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
// TODO: Fill resized canvas with fill color (must be formatted to image->format)
// Fill resized canvas with fill color
// Set first pixel with image->format
SetPixelColor(resizedData, fill, image->format);
// Fill remaining bytes of first row
for (int x = 1; x < newWidth; x++)
{
memcpy(resizedData + x*bytesPerPixel, resizedData, bytesPerPixel);
}
// Copy the first row into the other rows
for (int y = 1; y < newHeight; y++)
{
memcpy(resizedData + y*newWidth*bytesPerPixel, resizedData, newWidth*bytesPerPixel);
}
// Copy old image to resized canvas
int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
for (int y = 0; y < (int)srcRec.height; y++)
@ -1930,8 +2012,9 @@ void ImageAlphaPremultiply(Image *image)
ImageFormat(image, format);
}
// Apply box blur
void ImageBlurGaussian(Image *image, int blurSize) {
// Apply box blur to image
void ImageBlurGaussian(Image *image, int blurSize)
{
// Security check to avoid program crash
if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
@ -1943,7 +2026,8 @@ void ImageBlurGaussian(Image *image, int blurSize) {
Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
for (int i = 0; i < (image->height)*(image->width); i++) {
for (int i = 0; i < (image->height*image->width); i++)
{
pixelsCopy1[i].x = pixels[i].r;
pixelsCopy1[i].y = pixels[i].g;
pixelsCopy1[i].z = pixels[i].b;
@ -1951,7 +2035,8 @@ void ImageBlurGaussian(Image *image, int blurSize) {
}
// Repeated convolution of rectangular window signal by itself converges to a gaussian distribution
for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++) {
for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++)
{
// Horizontal motion blur
for (int row = 0; row < image->height; row++)
{
@ -1959,9 +2044,9 @@ void ImageBlurGaussian(Image *image, int blurSize) {
float avgG = 0.0f;
float avgB = 0.0f;
float avgAlpha = 0.0f;
int convolutionSize = blurSize+1;
int convolutionSize = blurSize;
for (int i = 0; i < blurSize+1; i++)
for (int i = 0; i < blurSize; i++)
{
avgR += pixelsCopy1[row*image->width + i].x;
avgG += pixelsCopy1[row*image->width + i].y;
@ -1969,19 +2054,14 @@ void ImageBlurGaussian(Image *image, int blurSize) {
avgAlpha += pixelsCopy1[row*image->width + i].w;
}
pixelsCopy2[row*image->width].x = avgR/convolutionSize;
pixelsCopy2[row*image->width].y = avgG/convolutionSize;
pixelsCopy2[row*image->width].z = avgB/convolutionSize;
pixelsCopy2[row*image->width].w = avgAlpha/convolutionSize;
for (int x = 1; x < image->width; x++)
for (int x = 0; x < image->width; x++)
{
if (x-blurSize >= 0)
if (x-blurSize-1 >= 0)
{
avgR -= pixelsCopy1[row*image->width + x-blurSize].x;
avgG -= pixelsCopy1[row*image->width + x-blurSize].y;
avgB -= pixelsCopy1[row*image->width + x-blurSize].z;
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize].w;
avgR -= pixelsCopy1[row*image->width + x-blurSize-1].x;
avgG -= pixelsCopy1[row*image->width + x-blurSize-1].y;
avgB -= pixelsCopy1[row*image->width + x-blurSize-1].z;
avgAlpha -= pixelsCopy1[row*image->width + x-blurSize-1].w;
convolutionSize--;
}
@ -1999,7 +2079,7 @@ void ImageBlurGaussian(Image *image, int blurSize) {
pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
}
}
}
// Vertical motion blur
for (int col = 0; col < image->width; col++)
@ -2008,9 +2088,9 @@ void ImageBlurGaussian(Image *image, int blurSize) {
float avgG = 0.0f;
float avgB = 0.0f;
float avgAlpha = 0.0f;
int convolutionSize = blurSize+1;
int convolutionSize = blurSize;
for (int i = 0; i < blurSize+1; i++)
for (int i = 0; i < blurSize; i++)
{
avgR += pixelsCopy2[i*image->width + col].x;
avgG += pixelsCopy2[i*image->width + col].y;
@ -2018,19 +2098,14 @@ void ImageBlurGaussian(Image *image, int blurSize) {
avgAlpha += pixelsCopy2[i*image->width + col].w;
}
pixelsCopy1[col].x = (unsigned char) (avgR/convolutionSize);
pixelsCopy1[col].y = (unsigned char) (avgG/convolutionSize);
pixelsCopy1[col].z = (unsigned char) (avgB/convolutionSize);
pixelsCopy1[col].w = (unsigned char) (avgAlpha/convolutionSize);
for (int y = 1; y < image->height; y++)
for (int y = 0; y < image->height; y++)
{
if (y-blurSize >= 0)
if (y-blurSize-1 >= 0)
{
avgR -= pixelsCopy2[(y-blurSize)*image->width + col].x;
avgG -= pixelsCopy2[(y-blurSize)*image->width + col].y;
avgB -= pixelsCopy2[(y-blurSize)*image->width + col].z;
avgAlpha -= pixelsCopy2[(y-blurSize)*image->width + col].w;
avgR -= pixelsCopy2[(y-blurSize-1)*image->width + col].x;
avgG -= pixelsCopy2[(y-blurSize-1)*image->width + col].y;
avgB -= pixelsCopy2[(y-blurSize-1)*image->width + col].z;
avgAlpha -= pixelsCopy2[(y-blurSize-1)*image->width + col].w;
convolutionSize--;
}
if (y+blurSize < image->height)
@ -2050,7 +2125,6 @@ void ImageBlurGaussian(Image *image, int blurSize) {
}
}
// Reverse premultiply
for (int i = 0; i < (image->width)*(image->height); i++)
{
@ -2082,6 +2156,135 @@ void ImageBlurGaussian(Image *image, int blurSize) {
ImageFormat(image, format);
}
// The kernel matrix is assumed to be square. Only supply the width of the kernel
void ImageKernelConvolution(Image *image, float* kernel, int kernelSize)
{
if ((image->data == NULL) || (image->width == 0) || (image->height == 0) || kernel == NULL) return;
int kernelWidth = (int)sqrtf((float)kernelSize);
if (kernelWidth*kernelWidth != kernelSize)
{
TRACELOG(LOG_WARNING, "IMAGE: Convolution kernel must be square to be applied");
return;
}
Color *pixels = LoadImageColors(*image);
Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
for (int i = 0; i < kernelSize; i++)
{
temp[i].x = 0.0f;
temp[i].y = 0.0f;
temp[i].z = 0.0f;
temp[i].w = 0.0f;
}
float rRes = 0.0f;
float gRes = 0.0f;
float bRes = 0.0f;
float aRes = 0.0f;
int startRange = 0, endRange = 0;
if (kernelWidth%2 == 0)
{
startRange = -kernelWidth/2;
endRange = kernelWidth/2;
}
else
{
startRange = -kernelWidth/2;
endRange = kernelWidth/2 + 1;
}
for (int x = 0; x < image->height; x++)
{
for (int y = 0; y < image->width; y++)
{
for (int xk = startRange; xk < endRange; xk++)
{
for (int yk = startRange; yk < endRange; yk++)
{
int xkabs = xk + kernelWidth/2;
int ykabs = yk + kernelWidth/2;
unsigned int imgindex = image->width*(x + xk) + (y + yk);
if (imgindex >= (unsigned int)(image->width*image->height))
{
temp[kernelWidth*xkabs + ykabs].x = 0.0f;
temp[kernelWidth*xkabs + ykabs].y = 0.0f;
temp[kernelWidth*xkabs + ykabs].z = 0.0f;
temp[kernelWidth*xkabs + ykabs].w = 0.0f;
}
else
{
temp[kernelWidth*xkabs + ykabs].x = ((float)pixels[imgindex].r)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].y = ((float)pixels[imgindex].g)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].z = ((float)pixels[imgindex].b)/255.0f*kernel[kernelWidth*xkabs + ykabs];
temp[kernelWidth*xkabs + ykabs].w = ((float)pixels[imgindex].a)/255.0f*kernel[kernelWidth*xkabs + ykabs];
}
}
}
for (int i = 0; i < kernelSize; i++)
{
rRes += temp[i].x;
gRes += temp[i].y;
bRes += temp[i].z;
aRes += temp[i].w;
}
if (rRes < 0.0f) rRes = 0.0f;
if (gRes < 0.0f) gRes = 0.0f;
if (bRes < 0.0f) bRes = 0.0f;
if (rRes > 1.0f) rRes = 1.0f;
if (gRes > 1.0f) gRes = 1.0f;
if (bRes > 1.0f) bRes = 1.0f;
imageCopy2[image->width*x + y].x = rRes;
imageCopy2[image->width*x + y].y = gRes;
imageCopy2[image->width*x + y].z = bRes;
imageCopy2[image->width*x + y].w = aRes;
rRes = 0.0f;
gRes = 0.0f;
bRes = 0.0f;
aRes = 0.0f;
for (int i = 0; i < kernelSize; i++)
{
temp[i].x = 0.0f;
temp[i].y = 0.0f;
temp[i].z = 0.0f;
temp[i].w = 0.0f;
}
}
}
for (int i = 0; i < (image->width*image->height); i++)
{
float alpha = (float)imageCopy2[i].w;
pixels[i].r = (unsigned char)((imageCopy2[i].x)*255.0f);
pixels[i].g = (unsigned char)((imageCopy2[i].y)*255.0f);
pixels[i].b = (unsigned char)((imageCopy2[i].z)*255.0f);
pixels[i].a = (unsigned char)((alpha)*255.0f);
}
int format = image->format;
RL_FREE(image->data);
RL_FREE(imageCopy2);
RL_FREE(temp);
image->data = pixels;
image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
ImageFormat(image, format);
}
// Generate all mipmap levels for a provided image
// NOTE 1: Supports POT and NPOT images
// NOTE 2: image.data is scaled to include mipmap levels
@ -2469,21 +2672,17 @@ void ImageColorTint(Image *image, Color color)
float cB = (float)color.b/255;
float cA = (float)color.a/255;
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
int index = y*image->width + x;
unsigned char r = (unsigned char)(((float)pixels[index].r/255*cR)*255.0f);
unsigned char g = (unsigned char)(((float)pixels[index].g/255*cG)*255.0f);
unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
unsigned char r = (unsigned char)(((float)pixels[i].r/255*cR)*255.0f);
unsigned char g = (unsigned char)(((float)pixels[i].g/255*cG)*255.0f);
unsigned char b = (unsigned char)(((float)pixels[i].b/255*cB)*255.0f);
unsigned char a = (unsigned char)(((float)pixels[i].a/255*cA)*255.0f);
pixels[index].r = r;
pixels[index].g = g;
pixels[index].b = b;
pixels[index].a = a;
}
pixels[i].r = r;
pixels[i].g = g;
pixels[i].b = b;
pixels[i].a = a;
}
int format = image->format;
@ -2503,14 +2702,11 @@ void ImageColorInvert(Image *image)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
pixels[y*image->width + x].r = 255 - pixels[y*image->width + x].r;
pixels[y*image->width + x].g = 255 - pixels[y*image->width + x].g;
pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
}
pixels[i].r = 255 - pixels[i].r;
pixels[i].g = 255 - pixels[i].g;
pixels[i].b = 255 - pixels[i].b;
}
int format = image->format;
@ -2543,38 +2739,35 @@ void ImageColorContrast(Image *image, float contrast)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
float pR = (float)pixels[y*image->width + x].r/255.0f;
pR -= 0.5f;
pR *= contrast;
pR += 0.5f;
pR *= 255;
if (pR < 0) pR = 0;
if (pR > 255) pR = 255;
float pR = (float)pixels[i].r/255.0f;
pR -= 0.5f;
pR *= contrast;
pR += 0.5f;
pR *= 255;
if (pR < 0) pR = 0;
if (pR > 255) pR = 255;
float pG = (float)pixels[y*image->width + x].g/255.0f;
pG -= 0.5f;
pG *= contrast;
pG += 0.5f;
pG *= 255;
if (pG < 0) pG = 0;
if (pG > 255) pG = 255;
float pG = (float)pixels[i].g/255.0f;
pG -= 0.5f;
pG *= contrast;
pG += 0.5f;
pG *= 255;
if (pG < 0) pG = 0;
if (pG > 255) pG = 255;
float pB = (float)pixels[y*image->width + x].b/255.0f;
pB -= 0.5f;
pB *= contrast;
pB += 0.5f;
pB *= 255;
if (pB < 0) pB = 0;
if (pB > 255) pB = 255;
float pB = (float)pixels[i].b/255.0f;
pB -= 0.5f;
pB *= contrast;
pB += 0.5f;
pB *= 255;
if (pB < 0) pB = 0;
if (pB > 255) pB = 255;
pixels[y*image->width + x].r = (unsigned char)pR;
pixels[y*image->width + x].g = (unsigned char)pG;
pixels[y*image->width + x].b = (unsigned char)pB;
}
pixels[i].r = (unsigned char)pR;
pixels[i].g = (unsigned char)pG;
pixels[i].b = (unsigned char)pB;
}
int format = image->format;
@ -2598,27 +2791,24 @@ void ImageColorBrightness(Image *image, int brightness)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
{
int cR = pixels[y*image->width + x].r + brightness;
int cG = pixels[y*image->width + x].g + brightness;
int cB = pixels[y*image->width + x].b + brightness;
int cR = pixels[i].r + brightness;
int cG = pixels[i].g + brightness;
int cB = pixels[i].b + brightness;
if (cR < 0) cR = 1;
if (cR > 255) cR = 255;
if (cR < 0) cR = 1;
if (cR > 255) cR = 255;
if (cG < 0) cG = 1;
if (cG > 255) cG = 255;
if (cG < 0) cG = 1;
if (cG > 255) cG = 255;
if (cB < 0) cB = 1;
if (cB > 255) cB = 255;
if (cB < 0) cB = 1;
if (cB > 255) cB = 255;
pixels[y*image->width + x].r = (unsigned char)cR;
pixels[y*image->width + x].g = (unsigned char)cG;
pixels[y*image->width + x].b = (unsigned char)cB;
}
pixels[i].r = (unsigned char)cR;
pixels[i].g = (unsigned char)cG;
pixels[i].b = (unsigned char)cB;
}
int format = image->format;
@ -2638,20 +2828,17 @@ void ImageColorReplace(Image *image, Color color, Color replace)
Color *pixels = LoadImageColors(*image);
for (int y = 0; y < image->height; y++)
for (int i = 0; i < image->width*image->height; i++)
{
for (int x = 0; x < image->width; x++)
if ((pixels[i].r == color.r) &&
(pixels[i].g == color.g) &&
(pixels[i].b == color.b) &&
(pixels[i].a == color.a))
{
if ((pixels[y*image->width + x].r == color.r) &&
(pixels[y*image->width + x].g == color.g) &&
(pixels[y*image->width + x].b == color.b) &&
(pixels[y*image->width + x].a == color.a))
{
pixels[y*image->width + x].r = replace.r;
pixels[y*image->width + x].g = replace.g;
pixels[y*image->width + x].b = replace.b;
pixels[y*image->width + x].a = replace.a;
}
pixels[i].r = replace.r;
pixels[i].g = replace.g;
pixels[i].b = replace.b;
pixels[i].a = replace.a;
}
}
@ -3398,8 +3585,8 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
// Security check to avoid drawing out of bounds in case of bad user data
if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; }
if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; }
if (rec.x < 0) { rec.width += rec.x; rec.x = 0; }
if (rec.y < 0) { rec.height += rec.y; rec.y = 0; }
if (rec.width < 0) rec.width = 0;
if (rec.height < 0) rec.height = 0;
@ -3408,8 +3595,8 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y;
// Check if the rect is even inside the image
if ((rec.x > dst->width) || (rec.y > dst->height)) return;
if (((rec.x + rec.width) < 0) || (rec.y + rec.height < 0)) return;
if ((rec.x >= dst->width) || (rec.y >= dst->height)) return;
if (((rec.x + rec.width) <= 0) || (rec.y + rec.height <= 0)) return;
int sy = (int)rec.y;
int sx = (int)rec.x;
@ -3429,7 +3616,7 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
}
// Repeat the first row data for all other rows
int bytesPerRow = bytesPerPixel * (int)rec.width;
int bytesPerRow = bytesPerPixel*(int)rec.width;
for (int y = 1; y < (int)rec.height; y++)
{
memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow);
@ -3564,7 +3751,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color
// Draw text (default font) within an image (destination)
void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
{
#if defined(SUPPORT_MODULE_RTEXT)
#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
// Make sure default font is loaded to be used on image text drawing
if (GetFontDefault().texture.id == 0) LoadFontDefault();
@ -3646,7 +3833,9 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
}
} else {
}
else
{
if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) cubemap.width = image.height/6;
if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) cubemap.width = image.width/6;
if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR) cubemap.width = image.width/3;
@ -3663,6 +3852,7 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
Image faces = { 0 }; // Vertical column image
Rectangle faceRecs[6] = { 0 }; // Face source rectangles
for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
@ -3708,7 +3898,13 @@ TextureCubemap LoadTextureCubemap(Image image, int layout)
// NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
// one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
if (cubemap.id != 0)
{
cubemap.format = faces.format;
cubemap.mipmaps = 1;
}
else TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
UnloadImage(faces);
}
@ -3723,7 +3919,7 @@ RenderTexture2D LoadRenderTexture(int width, int height)
{
RenderTexture2D target = { 0 };
target.id = rlLoadFramebuffer(width, height); // Load an empty framebuffer
target.id = rlLoadFramebuffer(); // Load an empty framebuffer
if (target.id > 0)
{
@ -3760,13 +3956,17 @@ RenderTexture2D LoadRenderTexture(int width, int height)
// Check if a texture is ready
bool IsTextureReady(Texture2D texture)
{
bool result = false;
// TODO: Validate maximum texture size supported by GPU?
return ((texture.id > 0) && // Validate OpenGL id
(texture.width > 0) &&
(texture.height > 0) && // Validate texture size
(texture.format > 0) && // Validate texture pixel format
(texture.mipmaps > 0)); // Validate texture mipmaps (at least 1 for basic mipmap level)
if ((texture.id > 0) && // Validate OpenGL id
(texture.width > 0) &&
(texture.height > 0) && // Validate texture size
(texture.format > 0) && // Validate texture pixel format
(texture.mipmaps > 0)) result = true; // Validate texture mipmaps (at least 1 for basic mipmap level)
return result;
}
// Unload texture from GPU memory (VRAM)
@ -3783,9 +3983,13 @@ void UnloadTexture(Texture2D texture)
// Check if a render texture is ready
bool IsRenderTextureReady(RenderTexture2D target)
{
return ((target.id > 0) && // Validate OpenGL id
IsTextureReady(target.depth) && // Validate FBO depth texture/renderbuffer
IsTextureReady(target.texture)); // Validate FBO texture
bool result = false;
if ((target.id > 0) && // Validate OpenGL id
IsTextureReady(target.depth) && // Validate FBO depth texture/renderbuffer
IsTextureReady(target.texture)) result = true; // Validate FBO texture
return result;
}
// Unload render texture from GPU memory (VRAM)
@ -4043,7 +4247,7 @@ void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2
// NOTE: Vertex position can be transformed using matrices
// but the process is way more costly than just calculating
// the vertex positions manually, like done above.
// the vertex positions manually, like done above
// I leave here the old implementation for educational purposes,
// just in case someone wants to do some performance test
/*
@ -4280,19 +4484,35 @@ void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest,
}
}
// Check if two colors are equal
bool ColorIsEqual(Color col1, Color col2)
{
bool result = false;
if ((col1.r == col2.r) && (col1.g == col2.g) && (col1.b == col2.b) && (col1.a == col2.a)) result = true;
return result;
}
// Get color with alpha applied, alpha goes from 0.0f to 1.0f
Color Fade(Color color, float alpha)
{
Color result = color;
if (alpha < 0.0f) alpha = 0.0f;
else if (alpha > 1.0f) alpha = 1.0f;
return (Color){ color.r, color.g, color.b, (unsigned char)(255.0f*alpha) };
result.a = (unsigned char)(255.0f*alpha);
return result;
}
// Get hexadecimal value for a Color
int ColorToInt(Color color)
{
return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
int result = (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
return result;
}
// Get color normalized as float [0..1]
@ -4511,10 +4731,14 @@ Color ColorContrast(Color color, float contrast)
// Get color with alpha applied, alpha goes from 0.0f to 1.0f
Color ColorAlpha(Color color, float alpha)
{
Color result = color;
if (alpha < 0.0f) alpha = 0.0f;
else if (alpha > 1.0f) alpha = 1.0f;
return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
result.a = (unsigned char)(255.0f*alpha);
return result;
}
// Get src alpha-blended into dst color with tint
@ -4813,22 +5037,35 @@ int GetPixelDataSize(int width, int height, int format)
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
// From https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
// Convert half-float (stored as unsigned short) to float
// REF: https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
static float HalfToFloat(unsigned short x)
{
float result = 0.0f;
static float HalfToFloat(unsigned short x) {
const unsigned int e = (x&0x7C00)>>10; // exponent
const unsigned int m = (x&0x03FF)<<13; // mantissa
const unsigned int e = (x & 0x7C00) >> 10; // Exponent
const unsigned int m = (x & 0x03FF) << 13; // Mantissa
const float fm = (float)m;
const unsigned int v = (*(unsigned int*)&fm)>>23; // evil log2 bit hack to count leading zeros in denormalized format
const unsigned int r = (x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000)); // sign : normalized : denormalized
return *(float*)&r;
const unsigned int v = (*(unsigned int*)&fm) >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
const unsigned int r = (x & 0x8000) << 16 | (e != 0)*((e + 112) << 23 | m) | ((e == 0)&(m != 0))*((v - 37) << 23 | ((m << (150 - v)) & 0x007FE000)); // sign : normalized : denormalized
result = *(float *)&r;
return result;
}
static unsigned short FloatToHalf(float x) {
const unsigned int b = (*(unsigned int*)&x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
const unsigned int e = (b&0x7F800000)>>23; // exponent
const unsigned int m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate
// Convert float to half-float (stored as unsigned short)
static unsigned short FloatToHalf(float x)
{
unsigned short result = 0;
const unsigned int b = (*(unsigned int*) & x) + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
const unsigned int e = (b & 0x7F800000) >> 23; // Exponent
const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
result = (b & 0x80000000) >> 16 | (e > 112)*((((e - 112) << 10) & 0x7C00) | m >> 13) | ((e < 113) & (e > 101))*((((0x007FF000 + m) >> (125 - e)) + 1) >> 1) | (e > 143)*0x7FFF; // sign : normalized : denormalized : saturate
return result;
}
// Get pixel data from image as Vector4 array (float normalized)