minesweeper

A minewseeper implementation to play around with Hare and Raylib
git clone https://git.tronto.net/minesweeper
Download | Log | Files | Refs | README | LICENSE

rtextures.c (225677B)


      1 /**********************************************************************************************
      2 *
      3 *   rtextures - Basic functions to load and draw textures
      4 *
      5 *   CONFIGURATION:
      6 *       #define SUPPORT_MODULE_RTEXTURES
      7 *           rtextures module is included in the build
      8 *
      9 *       #define SUPPORT_FILEFORMAT_BMP
     10 *       #define SUPPORT_FILEFORMAT_PNG
     11 *       #define SUPPORT_FILEFORMAT_TGA
     12 *       #define SUPPORT_FILEFORMAT_JPG
     13 *       #define SUPPORT_FILEFORMAT_GIF
     14 *       #define SUPPORT_FILEFORMAT_QOI
     15 *       #define SUPPORT_FILEFORMAT_PSD
     16 *       #define SUPPORT_FILEFORMAT_HDR
     17 *       #define SUPPORT_FILEFORMAT_PIC
     18 *       #define SUPPORT_FILEFORMAT_PNM
     19 *       #define SUPPORT_FILEFORMAT_DDS
     20 *       #define SUPPORT_FILEFORMAT_PKM
     21 *       #define SUPPORT_FILEFORMAT_KTX
     22 *       #define SUPPORT_FILEFORMAT_PVR
     23 *       #define SUPPORT_FILEFORMAT_ASTC
     24 *           Select desired fileformats to be supported for image data loading. Some of those formats are
     25 *           supported by default, to remove support, just comment unrequired #define in this module
     26 *
     27 *       #define SUPPORT_IMAGE_EXPORT
     28 *           Support image export in multiple file formats
     29 *
     30 *       #define SUPPORT_IMAGE_MANIPULATION
     31 *           Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop...
     32 *           If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*()
     33 *
     34 *       #define SUPPORT_IMAGE_GENERATION
     35 *           Support procedural image generation functionality (gradient, spot, perlin-noise, cellular)
     36 *
     37 *   DEPENDENCIES:
     38 *       stb_image        - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
     39 *                          NOTE: stb_image has been slightly modified to support Android platform.
     40 *       stb_image_resize - Multiple image resize algorithms
     41 *
     42 *
     43 *   LICENSE: zlib/libpng
     44 *
     45 *   Copyright (c) 2013-2024 Ramon Santamaria (@raysan5)
     46 *
     47 *   This software is provided "as-is", without any express or implied warranty. In no event
     48 *   will the authors be held liable for any damages arising from the use of this software.
     49 *
     50 *   Permission is granted to anyone to use this software for any purpose, including commercial
     51 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
     52 *
     53 *     1. The origin of this software must not be misrepresented; you must not claim that you
     54 *     wrote the original software. If you use this software in a product, an acknowledgment
     55 *     in the product documentation would be appreciated but is not required.
     56 *
     57 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
     58 *     as being the original software.
     59 *
     60 *     3. This notice may not be removed or altered from any source distribution.
     61 *
     62 **********************************************************************************************/
     63 
     64 #include "raylib.h"             // Declares module functions
     65 
     66 // Check if config flags have been externally provided on compilation line
     67 #if !defined(EXTERNAL_CONFIG_FLAGS)
     68     #include "config.h"         // Defines module configuration flags
     69 #endif
     70 
     71 #if defined(SUPPORT_MODULE_RTEXTURES)
     72 
     73 #include "utils.h"              // Required for: TRACELOG()
     74 #include "rlgl.h"               // OpenGL abstraction layer to multiple versions
     75 
     76 #include <stdlib.h>             // Required for: malloc(), calloc(), free()
     77 #include <string.h>             // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()/LoadImageAnimFromMemory()/ExportImageToMemory()]
     78 #include <math.h>               // Required for: fabsf() [Used in DrawTextureRec()]
     79 #include <stdio.h>              // Required for: sprintf() [Used in ExportImageAsCode()]
     80 
     81 // Support only desired texture formats on stb_image
     82 #if !defined(SUPPORT_FILEFORMAT_BMP)
     83     #define STBI_NO_BMP
     84 #endif
     85 #if !defined(SUPPORT_FILEFORMAT_PNG)
     86     #define STBI_NO_PNG
     87 #endif
     88 #if !defined(SUPPORT_FILEFORMAT_TGA)
     89     #define STBI_NO_TGA
     90 #endif
     91 #if !defined(SUPPORT_FILEFORMAT_JPG)
     92     #define STBI_NO_JPEG        // Image format .jpg and .jpeg
     93 #endif
     94 #if !defined(SUPPORT_FILEFORMAT_PSD)
     95     #define STBI_NO_PSD
     96 #endif
     97 #if !defined(SUPPORT_FILEFORMAT_GIF)
     98     #define STBI_NO_GIF
     99 #endif
    100 #if !defined(SUPPORT_FILEFORMAT_PIC)
    101     #define STBI_NO_PIC
    102 #endif
    103 #if !defined(SUPPORT_FILEFORMAT_HDR)
    104     #define STBI_NO_HDR
    105 #endif
    106 #if !defined(SUPPORT_FILEFORMAT_PNM)
    107     #define STBI_NO_PNM
    108 #endif
    109 
    110 #if defined(SUPPORT_FILEFORMAT_DDS)
    111     #define RL_GPUTEX_SUPPORT_DDS
    112 #endif
    113 #if defined(SUPPORT_FILEFORMAT_PKM)
    114     #define RL_GPUTEX_SUPPORT_PKM
    115 #endif
    116 #if defined(SUPPORT_FILEFORMAT_KTX)
    117     #define RL_GPUTEX_SUPPORT_KTX
    118 #endif
    119 #if defined(SUPPORT_FILEFORMAT_PVR)
    120     #define RL_GPUTEX_SUPPORT_PVR
    121 #endif
    122 #if defined(SUPPORT_FILEFORMAT_ASTC)
    123     #define RL_GPUTEX_SUPPORT_ASTC
    124 #endif
    125 
    126 // Image fileformats not supported by default
    127 #if defined(__TINYC__)
    128     #define STBI_NO_SIMD
    129 #endif
    130 
    131 #if (defined(SUPPORT_FILEFORMAT_BMP) || \
    132      defined(SUPPORT_FILEFORMAT_PNG) || \
    133      defined(SUPPORT_FILEFORMAT_TGA) || \
    134      defined(SUPPORT_FILEFORMAT_JPG) || \
    135      defined(SUPPORT_FILEFORMAT_PSD) || \
    136      defined(SUPPORT_FILEFORMAT_GIF) || \
    137      defined(SUPPORT_FILEFORMAT_HDR) || \
    138      defined(SUPPORT_FILEFORMAT_PIC) || \
    139      defined(SUPPORT_FILEFORMAT_PNM))
    140 
    141     #if defined(__GNUC__) // GCC and Clang
    142         #pragma GCC diagnostic push
    143         #pragma GCC diagnostic ignored "-Wunused-function"
    144     #endif
    145 
    146     #define STBI_MALLOC RL_MALLOC
    147     #define STBI_FREE RL_FREE
    148     #define STBI_REALLOC RL_REALLOC
    149 
    150     #define STBI_NO_THREAD_LOCALS
    151 
    152     #define STB_IMAGE_IMPLEMENTATION
    153     #include "external/stb_image.h"         // Required for: stbi_load_from_file()
    154                                             // NOTE: Used to read image data (multiple formats support)
    155 
    156     #if defined(__GNUC__) // GCC and Clang
    157         #pragma GCC diagnostic pop
    158     #endif
    159 #endif
    160 
    161 #if (defined(SUPPORT_FILEFORMAT_DDS) || \
    162      defined(SUPPORT_FILEFORMAT_PKM) || \
    163      defined(SUPPORT_FILEFORMAT_KTX) || \
    164      defined(SUPPORT_FILEFORMAT_PVR) || \
    165      defined(SUPPORT_FILEFORMAT_ASTC))
    166 
    167     #if defined(__GNUC__) // GCC and Clang
    168         #pragma GCC diagnostic push
    169         #pragma GCC diagnostic ignored "-Wunused-function"
    170     #endif
    171 
    172     #define RL_GPUTEX_IMPLEMENTATION
    173     #include "external/rl_gputex.h"         // Required for: rl_load_xxx_from_memory()
    174                                             // NOTE: Used to read compressed textures data (multiple formats support)
    175 
    176     #if defined(__GNUC__) // GCC and Clang
    177         #pragma GCC diagnostic pop
    178     #endif
    179 #endif
    180 
    181 #if defined(SUPPORT_FILEFORMAT_QOI)
    182     #define QOI_MALLOC RL_MALLOC
    183     #define QOI_FREE RL_FREE
    184 
    185     #if defined(_MSC_VER)               // Disable some MSVC warning
    186         #pragma warning(push)
    187         #pragma warning(disable : 4267)
    188     #endif
    189 
    190     #define QOI_IMPLEMENTATION
    191     #include "external/qoi.h"
    192 
    193     #if defined(_MSC_VER)
    194         #pragma warning(pop)            // Disable MSVC warning suppression
    195     #endif
    196 
    197 #endif
    198 
    199 #if defined(SUPPORT_IMAGE_EXPORT)
    200     #define STBIW_MALLOC RL_MALLOC
    201     #define STBIW_FREE RL_FREE
    202     #define STBIW_REALLOC RL_REALLOC
    203 
    204     #define STB_IMAGE_WRITE_IMPLEMENTATION
    205     #include "external/stb_image_write.h"   // Required for: stbi_write_*()
    206 #endif
    207 
    208 #if defined(SUPPORT_IMAGE_GENERATION)
    209     #define STB_PERLIN_IMPLEMENTATION
    210     #include "external/stb_perlin.h"        // Required for: stb_perlin_fbm_noise3
    211 #endif
    212 
    213 #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
    214 #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
    215 
    216 #if defined(__GNUC__) // GCC and Clang
    217     #pragma GCC diagnostic push
    218     #pragma GCC diagnostic ignored "-Wunused-function"
    219 #endif
    220 
    221 #define STB_IMAGE_RESIZE_IMPLEMENTATION
    222 #include "external/stb_image_resize2.h"     // Required for: stbir_resize_uint8_linear() [ImageResize()]
    223 
    224 #if defined(__GNUC__) // GCC and Clang
    225     #pragma GCC diagnostic pop
    226 #endif
    227 
    228 //----------------------------------------------------------------------------------
    229 // Defines and Macros
    230 //----------------------------------------------------------------------------------
    231 #ifndef PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD
    232     #define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD  50    // Threshold over 255 to set alpha as 0
    233 #endif
    234 
    235 #ifndef GAUSSIAN_BLUR_ITERATIONS
    236     #define GAUSSIAN_BLUR_ITERATIONS  4    // Number of box blur iterations to approximate gaussian blur
    237 #endif
    238 
    239 //----------------------------------------------------------------------------------
    240 // Types and Structures Definition
    241 //----------------------------------------------------------------------------------
    242 // ...
    243 
    244 //----------------------------------------------------------------------------------
    245 // Global Variables Definition
    246 //----------------------------------------------------------------------------------
    247 // It's lonely here...
    248 
    249 //----------------------------------------------------------------------------------
    250 // Other Modules Functions Declaration (required by text)
    251 //----------------------------------------------------------------------------------
    252 extern void LoadFontDefault(void);          // [Module: text] Loads default font, required by ImageDrawText()
    253 
    254 //----------------------------------------------------------------------------------
    255 // Module specific Functions Declaration
    256 //----------------------------------------------------------------------------------
    257 static float HalfToFloat(unsigned short x);
    258 static unsigned short FloatToHalf(float x);
    259 static Vector4 *LoadImageDataNormalized(Image image);       // Load pixel data from image as Vector4 array (float normalized)
    260 
    261 //----------------------------------------------------------------------------------
    262 // Module Functions Definition
    263 //----------------------------------------------------------------------------------
    264 
    265 // Load image from file into CPU memory (RAM)
    266 Image LoadImage(const char *fileName)
    267 {
    268     Image image = { 0 };
    269 
    270 #if defined(SUPPORT_FILEFORMAT_PNG) || \
    271     defined(SUPPORT_FILEFORMAT_BMP) || \
    272     defined(SUPPORT_FILEFORMAT_TGA) || \
    273     defined(SUPPORT_FILEFORMAT_JPG) || \
    274     defined(SUPPORT_FILEFORMAT_GIF) || \
    275     defined(SUPPORT_FILEFORMAT_PIC) || \
    276     defined(SUPPORT_FILEFORMAT_HDR) || \
    277     defined(SUPPORT_FILEFORMAT_PNM) || \
    278     defined(SUPPORT_FILEFORMAT_PSD)
    279 
    280     #define STBI_REQUIRED
    281 #endif
    282 
    283     // Loading file to memory
    284     int dataSize = 0;
    285     unsigned char *fileData = LoadFileData(fileName, &dataSize);
    286 
    287     // Loading image from memory data
    288     if (fileData != NULL)
    289     {
    290         image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize);
    291 
    292         UnloadFileData(fileData);
    293     }
    294 
    295     return image;
    296 }
    297 
    298 // Load an image from RAW file data
    299 Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize)
    300 {
    301     Image image = { 0 };
    302 
    303     int dataSize = 0;
    304     unsigned char *fileData = LoadFileData(fileName, &dataSize);
    305 
    306     if (fileData != NULL)
    307     {
    308         unsigned char *dataPtr = fileData;
    309         int size = GetPixelDataSize(width, height, format);
    310 
    311         if (size <= dataSize)   // Security check
    312         {
    313             // Offset file data to expected raw image by header size
    314             if ((headerSize > 0) && ((headerSize + size) <= dataSize)) dataPtr += headerSize;
    315 
    316             image.data = RL_MALLOC(size);      // Allocate required memory in bytes
    317             memcpy(image.data, dataPtr, size); // Copy required data to image
    318             image.width = width;
    319             image.height = height;
    320             image.mipmaps = 1;
    321             image.format = format;
    322         }
    323 
    324         UnloadFileData(fileData);
    325     }
    326 
    327     return image;
    328 }
    329 
    330 // Load animated image data
    331 //  - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
    332 //  - Number of frames is returned through 'frames' parameter
    333 //  - All frames are returned in RGBA format
    334 //  - Frames delay data is discarded
    335 Image LoadImageAnim(const char *fileName, int *frames)
    336 {
    337     Image image = { 0 };
    338     int frameCount = 0;
    339 
    340 #if defined(SUPPORT_FILEFORMAT_GIF)
    341     if (IsFileExtension(fileName, ".gif"))
    342     {
    343         int dataSize = 0;
    344         unsigned char *fileData = LoadFileData(fileName, &dataSize);
    345 
    346         if (fileData != NULL)
    347         {
    348             int comp = 0;
    349             int *delays = NULL;
    350             image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
    351 
    352             image.mipmaps = 1;
    353             image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    354 
    355             UnloadFileData(fileData);
    356             RL_FREE(delays);        // NOTE: Frames delays are discarded
    357         }
    358     }
    359 #else
    360     if (false) { }
    361 #endif
    362     else
    363     {
    364         image = LoadImage(fileName);
    365         frameCount = 1;
    366     }
    367 
    368     *frames = frameCount;
    369     return image;
    370 }
    371 
    372 // Load animated image data
    373 //  - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
    374 //  - Number of frames is returned through 'frames' parameter
    375 //  - All frames are returned in RGBA format
    376 //  - Frames delay data is discarded
    377 Image LoadImageAnimFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int *frames)
    378 {
    379     Image image = { 0 };
    380     int frameCount = 0;
    381 
    382     // Security check for input data
    383     if ((fileType == NULL) || (fileData == NULL) || (dataSize == 0)) return image;
    384 
    385 #if defined(SUPPORT_FILEFORMAT_GIF)
    386     if ((strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0))
    387     {
    388         if (fileData != NULL)
    389         {
    390             int comp = 0;
    391             int *delays = NULL;
    392             image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
    393 
    394             image.mipmaps = 1;
    395             image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    396 
    397             RL_FREE(delays);        // NOTE: Frames delays are discarded
    398         }
    399     }
    400 #else
    401     if (false) { }
    402 #endif
    403     else
    404     {
    405         image = LoadImageFromMemory(fileType, fileData, dataSize);
    406         frameCount = 1;
    407     }
    408 
    409     *frames = frameCount;
    410     return image;
    411 }
    412 
    413 // Load image from memory buffer, fileType refers to extension: i.e. ".png"
    414 // WARNING: File extension must be provided in lower-case
    415 Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
    416 {
    417     Image image = { 0 };
    418 
    419     // Security check for input data
    420     if ((fileData == NULL) || (dataSize == 0))
    421     {
    422         TRACELOG(LOG_WARNING, "IMAGE: Invalid file data");
    423         return image;
    424     }
    425     if (fileType == NULL)
    426     {
    427         TRACELOG(LOG_WARNING, "IMAGE: Missing file extension");
    428         return image;
    429     }
    430 
    431     if ((false)
    432 #if defined(SUPPORT_FILEFORMAT_PNG)
    433         || (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)
    434 #endif
    435 #if defined(SUPPORT_FILEFORMAT_BMP)
    436         || (strcmp(fileType, ".bmp") == 0) || (strcmp(fileType, ".BMP") == 0)
    437 #endif
    438 #if defined(SUPPORT_FILEFORMAT_TGA)
    439         || (strcmp(fileType, ".tga") == 0) || (strcmp(fileType, ".TGA") == 0)
    440 #endif
    441 #if defined(SUPPORT_FILEFORMAT_JPG)
    442         || (strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0)
    443         || (strcmp(fileType, ".JPG") == 0) || (strcmp(fileType, ".JPEG") == 0)
    444 #endif
    445 #if defined(SUPPORT_FILEFORMAT_GIF)
    446         || (strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0)
    447 #endif
    448 #if defined(SUPPORT_FILEFORMAT_PIC)
    449         || (strcmp(fileType, ".pic") == 0) || (strcmp(fileType, ".PIC") == 0)
    450 #endif
    451 #if defined(SUPPORT_FILEFORMAT_PNM)
    452         || (strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0)
    453         || (strcmp(fileType, ".PPM") == 0) || (strcmp(fileType, ".PGM") == 0)
    454 #endif
    455 #if defined(SUPPORT_FILEFORMAT_PSD)
    456         || (strcmp(fileType, ".psd") == 0) || (strcmp(fileType, ".PSD") == 0)
    457 #endif
    458         )
    459     {
    460 #if defined(STBI_REQUIRED)
    461         // NOTE: Using stb_image to load images (Supports multiple image formats)
    462 
    463         if (fileData != NULL)
    464         {
    465             int comp = 0;
    466             image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
    467 
    468             if (image.data != NULL)
    469             {
    470                 image.mipmaps = 1;
    471 
    472                 if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
    473                 else if (comp == 2) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
    474                 else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
    475                 else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    476             }
    477         }
    478 #endif
    479     }
    480 #if defined(SUPPORT_FILEFORMAT_HDR)
    481     else if ((strcmp(fileType, ".hdr") == 0) || (strcmp(fileType, ".HDR") == 0))
    482     {
    483 #if defined(STBI_REQUIRED)
    484         if (fileData != NULL)
    485         {
    486             int comp = 0;
    487             image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
    488 
    489             image.mipmaps = 1;
    490 
    491             if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_R32;
    492             else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
    493             else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
    494             else
    495             {
    496                 TRACELOG(LOG_WARNING, "IMAGE: HDR file format not supported");
    497                 UnloadImage(image);
    498             }
    499         }
    500 #endif
    501     }
    502 #endif
    503 #if defined(SUPPORT_FILEFORMAT_QOI)
    504     else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0))
    505     {
    506         if (fileData != NULL)
    507         {
    508             qoi_desc desc = { 0 };
    509             image.data = qoi_decode(fileData, dataSize, &desc, (int) fileData[12]);
    510             image.width = desc.width;
    511             image.height = desc.height;
    512             image.format = desc.channels == 4 ? PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 : PIXELFORMAT_UNCOMPRESSED_R8G8B8;
    513             image.mipmaps = 1;
    514         }
    515     }
    516 #endif
    517 #if defined(SUPPORT_FILEFORMAT_DDS)
    518     else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0))
    519     {
    520         image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
    521     }
    522 #endif
    523 #if defined(SUPPORT_FILEFORMAT_PKM)
    524     else if ((strcmp(fileType, ".pkm") == 0) || (strcmp(fileType, ".PKM") == 0))
    525     {
    526         image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
    527     }
    528 #endif
    529 #if defined(SUPPORT_FILEFORMAT_KTX)
    530     else if ((strcmp(fileType, ".ktx") == 0) || (strcmp(fileType, ".KTX") == 0))
    531     {
    532         image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
    533     }
    534 #endif
    535 #if defined(SUPPORT_FILEFORMAT_PVR)
    536     else if ((strcmp(fileType, ".pvr") == 0) || (strcmp(fileType, ".PVR") == 0))
    537     {
    538         image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
    539     }
    540 #endif
    541 #if defined(SUPPORT_FILEFORMAT_ASTC)
    542     else if ((strcmp(fileType, ".astc") == 0) || (strcmp(fileType, ".ASTC") == 0))
    543     {
    544         image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
    545     }
    546 #endif
    547     else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported");
    548 
    549     if (image.data != NULL) TRACELOG(LOG_INFO, "IMAGE: Data loaded successfully (%ix%i | %s | %i mipmaps)", image.width, image.height, rlGetPixelFormatName(image.format), image.mipmaps);
    550     else TRACELOG(LOG_WARNING, "IMAGE: Failed to load image data");
    551 
    552     return image;
    553 }
    554 
    555 // Load image from GPU texture data
    556 // NOTE: Compressed texture formats not supported
    557 Image LoadImageFromTexture(Texture2D texture)
    558 {
    559     Image image = { 0 };
    560 
    561     if (texture.format < PIXELFORMAT_COMPRESSED_DXT1_RGB)
    562     {
    563         image.data = rlReadTexturePixels(texture.id, texture.width, texture.height, texture.format);
    564 
    565         if (image.data != NULL)
    566         {
    567             image.width = texture.width;
    568             image.height = texture.height;
    569             image.format = texture.format;
    570             image.mipmaps = 1;
    571 
    572 #if defined(GRAPHICS_API_OPENGL_ES2)
    573             // NOTE: Data retrieved on OpenGL ES 2.0 should be RGBA,
    574             // coming from FBO color buffer attachment, but it seems
    575             // original texture format is retrieved on RPI...
    576             image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    577 #endif
    578             TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Pixel data retrieved successfully", texture.id);
    579         }
    580         else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve pixel data", texture.id);
    581     }
    582     else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve compressed pixel data", texture.id);
    583 
    584     return image;
    585 }
    586 
    587 // Load image from screen buffer and (screenshot)
    588 Image LoadImageFromScreen(void)
    589 {
    590     Vector2 scale = GetWindowScaleDPI();
    591     Image image = { 0 };
    592 
    593     image.width = (int)(GetScreenWidth()*scale.x);
    594     image.height = (int)(GetScreenHeight()*scale.y);
    595     image.mipmaps = 1;
    596     image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    597     image.data = rlReadScreenPixels(image.width, image.height);
    598 
    599     return image;
    600 }
    601 
    602 // Check if an image is ready
    603 bool IsImageValid(Image image)
    604 {
    605     bool result = false;
    606 
    607     if ((image.data != NULL) &&     // Validate pixel data available
    608         (image.width > 0) &&        // Validate image width
    609         (image.height > 0) &&       // Validate image height
    610         (image.format > 0) &&       // Validate image format
    611         (image.mipmaps > 0)) result = true; // Validate image mipmaps (at least 1 for basic mipmap level)
    612 
    613     return result;
    614 }
    615 
    616 // Unload image from CPU memory (RAM)
    617 void UnloadImage(Image image)
    618 {
    619     RL_FREE(image.data);
    620 }
    621 
    622 // Export image data to file
    623 // NOTE: File format depends on fileName extension
    624 bool ExportImage(Image image, const char *fileName)
    625 {
    626     int result = 0;
    627 
    628     // Security check for input data
    629     if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result;
    630 
    631 #if defined(SUPPORT_IMAGE_EXPORT)
    632     int channels = 4;
    633     bool allocatedData = false;
    634     unsigned char *imgData = (unsigned char *)image.data;
    635 
    636     if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
    637     else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2;
    638     else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
    639     else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
    640     else
    641     {
    642         // NOTE: Getting Color array as RGBA unsigned char values
    643         imgData = (unsigned char *)LoadImageColors(image);
    644         allocatedData = true;
    645     }
    646 
    647 #if defined(SUPPORT_FILEFORMAT_PNG)
    648     if (IsFileExtension(fileName, ".png"))
    649     {
    650         int dataSize = 0;
    651         unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize);
    652         result = SaveFileData(fileName, fileData, dataSize);
    653         RL_FREE(fileData);
    654     }
    655 #else
    656     if (false) { }
    657 #endif
    658 #if defined(SUPPORT_FILEFORMAT_BMP)
    659     else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
    660 #endif
    661 #if defined(SUPPORT_FILEFORMAT_TGA)
    662     else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
    663 #endif
    664 #if defined(SUPPORT_FILEFORMAT_JPG)
    665     else if (IsFileExtension(fileName, ".jpg") ||
    666              IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90);  // JPG quality: between 1 and 100
    667 #endif
    668 #if defined(SUPPORT_FILEFORMAT_QOI)
    669     else if (IsFileExtension(fileName, ".qoi"))
    670     {
    671         channels = 0;
    672         if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
    673         else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
    674         else TRACELOG(LOG_WARNING, "IMAGE: Image pixel format must be R8G8B8 or R8G8B8A8");
    675 
    676         if ((channels == 3) || (channels == 4))
    677         {
    678             qoi_desc desc = { 0 };
    679             desc.width = image.width;
    680             desc.height = image.height;
    681             desc.channels = channels;
    682             desc.colorspace = QOI_SRGB;
    683 
    684             result = qoi_write(fileName, imgData, &desc);
    685         }
    686     }
    687 #endif
    688 #if defined(SUPPORT_FILEFORMAT_KTX)
    689     else if (IsFileExtension(fileName, ".ktx"))
    690     {
    691         result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps);
    692     }
    693 #endif
    694     else if (IsFileExtension(fileName, ".raw"))
    695     {
    696         // Export raw pixel data (without header)
    697         // NOTE: It's up to the user to track image parameters
    698         result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
    699     }
    700 
    701     if (allocatedData) RL_FREE(imgData);
    702 #endif      // SUPPORT_IMAGE_EXPORT
    703 
    704     if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
    705     else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
    706 
    707     return result;
    708 }
    709 
    710 // Export image to memory buffer
    711 unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataSize)
    712 {
    713     unsigned char *fileData = NULL;
    714     *dataSize = 0;
    715 
    716     // Security check for input data
    717     if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL;
    718 
    719 #if defined(SUPPORT_IMAGE_EXPORT)
    720     int channels = 4;
    721 
    722     if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
    723     else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2;
    724     else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
    725     else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
    726 
    727 #if defined(SUPPORT_FILEFORMAT_PNG)
    728     if ((strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0))
    729     {
    730         fileData = stbi_write_png_to_mem((const unsigned char *)image.data, image.width*channels, image.width, image.height, channels, dataSize);
    731     }
    732 #endif
    733 
    734 #endif
    735 
    736     return fileData;
    737 }
    738 
    739 // Export image as code file (.h) defining an array of bytes
    740 bool ExportImageAsCode(Image image, const char *fileName)
    741 {
    742     bool success = false;
    743 
    744 #if defined(SUPPORT_IMAGE_EXPORT)
    745 
    746 #ifndef TEXT_BYTES_PER_LINE
    747     #define TEXT_BYTES_PER_LINE     20
    748 #endif
    749 
    750     int dataSize = GetPixelDataSize(image.width, image.height, image.format);
    751 
    752     // NOTE: Text data buffer size is estimated considering image data size in bytes
    753     // and requiring 6 char bytes for every byte: "0x00, "
    754     char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
    755 
    756     int byteCount = 0;
    757     byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
    758     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
    759     byteCount += sprintf(txtData + byteCount, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes         //\n");
    760     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
    761     byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
    762     byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
    763     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
    764     byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2024 Ramon Santamaria (@raysan5)                                //\n");
    765     byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
    766     byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
    767 
    768     // Get file name from path and convert variable name to uppercase
    769     char varFileName[256] = { 0 };
    770     strcpy(varFileName, GetFileNameWithoutExt(fileName));
    771     for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
    772 
    773     // Add image information
    774     byteCount += sprintf(txtData + byteCount, "// Image data information\n");
    775     byteCount += sprintf(txtData + byteCount, "#define %s_WIDTH    %i\n", varFileName, image.width);
    776     byteCount += sprintf(txtData + byteCount, "#define %s_HEIGHT   %i\n", varFileName, image.height);
    777     byteCount += sprintf(txtData + byteCount, "#define %s_FORMAT   %i          // raylib internal pixel format\n\n", varFileName, image.format);
    778 
    779     byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
    780     for (int i = 0; i < dataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]);
    781     byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
    782 
    783     // NOTE: Text data size exported is determined by '\0' (NULL) character
    784     success = SaveFileText(fileName, txtData);
    785 
    786     RL_FREE(txtData);
    787 
    788 #endif      // SUPPORT_IMAGE_EXPORT
    789 
    790     if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
    791     else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
    792 
    793     return success;
    794 }
    795 
    796 //------------------------------------------------------------------------------------
    797 // Image generation functions
    798 //------------------------------------------------------------------------------------
    799 // Generate image: plain color
    800 Image GenImageColor(int width, int height, Color color)
    801 {
    802     Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color));
    803 
    804     for (int i = 0; i < width*height; i++) pixels[i] = color;
    805 
    806     Image image = {
    807         .data = pixels,
    808         .width = width,
    809         .height = height,
    810         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    811         .mipmaps = 1
    812     };
    813 
    814     return image;
    815 }
    816 
    817 #if defined(SUPPORT_IMAGE_GENERATION)
    818 // Generate image: linear gradient
    819 // The direction value specifies the direction of the gradient (in degrees)
    820 // with 0 being vertical (from top to bottom), 90 being horizontal (from left to right)
    821 // The gradient effectively rotates counter-clockwise by the specified amount
    822 Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end)
    823 {
    824     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
    825 
    826     float radianDirection = (float)(90 - direction)/180.f*3.14159f;
    827     float cosDir = cosf(radianDirection);
    828     float sinDir = sinf(radianDirection);
    829 
    830     // Calculate how far the top-left pixel is along the gradient direction from the center of said gradient
    831     float startingPos = 0.5f - (cosDir*width/2) - (sinDir*height/2);
    832     // With directions that lie in the first or third quadrant (i.e. from top-left to 
    833     // bottom-right or vice-versa), pixel (0, 0) is the farthest point on the gradient
    834     // (i.e. the pixel which should become one of the gradient's ends color); while for
    835     // directions that lie in the second or fourth quadrant, that point is pixel (width, 0).
    836     float maxPosValue = 
    837             ((signbit(sinDir) != 0) == (signbit(cosDir) != 0))
    838             ? fabsf(startingPos)
    839             : fabsf(startingPos+width*cosDir);
    840     for (int i = 0; i < width; i++)
    841     {
    842         for (int j = 0; j < height; j++)
    843         {
    844             // Calculate the relative position of the pixel along the gradient direction
    845             float pos = (startingPos + (i*cosDir + j*sinDir)) / maxPosValue;
    846 
    847             float factor = pos;
    848             factor = (factor > 1.0f)? 1.0f : factor;  // Clamp to [-1,1]
    849             factor = (factor < -1.0f)? -1.0f : factor;  // Clamp to [-1,1]
    850             factor = factor / 2 + 0.5f;
    851 
    852             // Generate the color for this pixel
    853             pixels[j*width + i].r = (int)((float)end.r*factor + (float)start.r*(1.0f - factor));
    854             pixels[j*width + i].g = (int)((float)end.g*factor + (float)start.g*(1.0f - factor));
    855             pixels[j*width + i].b = (int)((float)end.b*factor + (float)start.b*(1.0f - factor));
    856             pixels[j*width + i].a = (int)((float)end.a*factor + (float)start.a*(1.0f - factor));
    857         }
    858     }
    859 
    860     Image image = {
    861         .data = pixels,
    862         .width = width,
    863         .height = height,
    864         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    865         .mipmaps = 1
    866     };
    867 
    868     return image;
    869 }
    870 
    871 // Generate image: radial gradient
    872 Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
    873 {
    874     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
    875     float radius = (width < height)? (float)width/2.0f : (float)height/2.0f;
    876 
    877     float centerX = (float)width/2.0f;
    878     float centerY = (float)height/2.0f;
    879 
    880     for (int y = 0; y < height; y++)
    881     {
    882         for (int x = 0; x < width; x++)
    883         {
    884             float dist = hypotf((float)x - centerX, (float)y - centerY);
    885             float factor = (dist - radius*density)/(radius*(1.0f - density));
    886 
    887             factor = (float)fmax(factor, 0.0f);
    888             factor = (float)fmin(factor, 1.f); // dist can be bigger than radius, so we have to check
    889 
    890             pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
    891             pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
    892             pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
    893             pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor));
    894         }
    895     }
    896 
    897     Image image = {
    898         .data = pixels,
    899         .width = width,
    900         .height = height,
    901         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    902         .mipmaps = 1
    903     };
    904 
    905     return image;
    906 }
    907 
    908 // Generate image: square gradient
    909 Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer)
    910 {
    911     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
    912 
    913     float centerX = (float)width/2.0f;
    914     float centerY = (float)height/2.0f;
    915 
    916     for (int y = 0; y < height; y++)
    917     {
    918         for (int x = 0; x < width; x++)
    919         {
    920             // Calculate the Manhattan distance from the center
    921             float distX = fabsf(x - centerX);
    922             float distY = fabsf(y - centerY);
    923 
    924             // Normalize the distances by the dimensions of the gradient rectangle
    925             float normalizedDistX = distX/centerX;
    926             float normalizedDistY = distY/centerY;
    927 
    928             // Calculate the total normalized Manhattan distance
    929             float manhattanDist = fmaxf(normalizedDistX, normalizedDistY);
    930 
    931             // Subtract the density from the manhattanDist, then divide by (1 - density)
    932             // This makes the gradient start from the center when density is 0, and from the edge when density is 1
    933             float factor = (manhattanDist - density)/(1.0f - density);
    934 
    935             // Clamp the factor between 0 and 1
    936             factor = fminf(fmaxf(factor, 0.0f), 1.0f);
    937 
    938             // Blend the colors based on the calculated factor
    939             pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
    940             pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
    941             pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
    942             pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor));
    943         }
    944     }
    945 
    946     Image image = {
    947         .data = pixels,
    948         .width = width,
    949         .height = height,
    950         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    951         .mipmaps = 1
    952     };
    953 
    954     return image;
    955 }
    956 
    957 // Generate image: checked
    958 Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2)
    959 {
    960     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
    961 
    962     for (int y = 0; y < height; y++)
    963     {
    964         for (int x = 0; x < width; x++)
    965         {
    966             if ((x/checksX + y/checksY)%2 == 0) pixels[y*width + x] = col1;
    967             else pixels[y*width + x] = col2;
    968         }
    969     }
    970 
    971     Image image = {
    972         .data = pixels,
    973         .width = width,
    974         .height = height,
    975         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    976         .mipmaps = 1
    977     };
    978 
    979     return image;
    980 }
    981 
    982 // Generate image: white noise
    983 // NOTE: It requires GetRandomValue(), defined in [rcore]
    984 Image GenImageWhiteNoise(int width, int height, float factor)
    985 {
    986     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
    987 
    988     for (int i = 0; i < width*height; i++)
    989     {
    990         if (GetRandomValue(0, 99) < (int)(factor*100.0f)) pixels[i] = WHITE;
    991         else pixels[i] = BLACK;
    992     }
    993 
    994     Image image = {
    995         .data = pixels,
    996         .width = width,
    997         .height = height,
    998         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
    999         .mipmaps = 1
   1000     };
   1001 
   1002     return image;
   1003 }
   1004 
   1005 // Generate image: perlin noise
   1006 Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale)
   1007 {
   1008     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   1009 
   1010     float aspectRatio = (float)width / (float)height;
   1011     for (int y = 0; y < height; y++)
   1012     {
   1013         for (int x = 0; x < width; x++)
   1014         {
   1015             float nx = (float)(x + offsetX)*(scale/(float)width);
   1016             float ny = (float)(y + offsetY)*(scale/(float)height);
   1017 
   1018             // Apply aspect ratio compensation to wider side
   1019             if (width > height) nx *= aspectRatio;
   1020             else ny /= aspectRatio;
   1021 
   1022             // Basic perlin noise implementation (not used)
   1023             //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0);
   1024 
   1025             // Calculate a better perlin noise using fbm (fractal brownian motion)
   1026             // Typical values to start playing with:
   1027             //   lacunarity = ~2.0   -- spacing between successive octaves (use exactly 2.0 for wrapping output)
   1028             //   gain       =  0.5   -- relative weighting applied to each successive octave
   1029             //   octaves    =  6     -- number of "octaves" of noise3() to sum
   1030             float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6);
   1031 
   1032             // Clamp between -1.0f and 1.0f
   1033             if (p < -1.0f) p = -1.0f;
   1034             if (p > 1.0f) p = 1.0f;
   1035 
   1036             // We need to normalize the data from [-1..1] to [0..1]
   1037             float np = (p + 1.0f)/2.0f;
   1038 
   1039             int intensity = (int)(np*255.0f);
   1040             pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
   1041         }
   1042     }
   1043 
   1044     Image image = {
   1045         .data = pixels,
   1046         .width = width,
   1047         .height = height,
   1048         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   1049         .mipmaps = 1
   1050     };
   1051 
   1052     return image;
   1053 }
   1054 
   1055 // Generate image: cellular algorithm. Bigger tileSize means bigger cells
   1056 Image GenImageCellular(int width, int height, int tileSize)
   1057 {
   1058     Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   1059 
   1060     int seedsPerRow = width/tileSize;
   1061     int seedsPerCol = height/tileSize;
   1062     int seedCount = seedsPerRow*seedsPerCol;
   1063 
   1064     Vector2 *seeds = (Vector2 *)RL_MALLOC(seedCount*sizeof(Vector2));
   1065 
   1066     for (int i = 0; i < seedCount; i++)
   1067     {
   1068         int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
   1069         int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
   1070         seeds[i] = (Vector2){ (float)x, (float)y };
   1071     }
   1072 
   1073     for (int y = 0; y < height; y++)
   1074     {
   1075         int tileY = y/tileSize;
   1076 
   1077         for (int x = 0; x < width; x++)
   1078         {
   1079             int tileX = x/tileSize;
   1080 
   1081             float minDistance = 65536.0f; //(float)strtod("Inf", NULL);
   1082 
   1083             // Check all adjacent tiles
   1084             for (int i = -1; i < 2; i++)
   1085             {
   1086                 if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
   1087 
   1088                 for (int j = -1; j < 2; j++)
   1089                 {
   1090                     if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
   1091 
   1092                     Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
   1093 
   1094                     float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
   1095                     minDistance = (float)fmin(minDistance, dist);
   1096                 }
   1097             }
   1098 
   1099             // I made this up, but it seems to give good results at all tile sizes
   1100             int intensity = (int)(minDistance*256.0f/tileSize);
   1101             if (intensity > 255) intensity = 255;
   1102 
   1103             pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
   1104         }
   1105     }
   1106 
   1107     RL_FREE(seeds);
   1108 
   1109     Image image = {
   1110         .data = pixels,
   1111         .width = width,
   1112         .height = height,
   1113         .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   1114         .mipmaps = 1
   1115     };
   1116 
   1117     return image;
   1118 }
   1119 
   1120 // Generate image: grayscale image from text data
   1121 Image GenImageText(int width, int height, const char *text)
   1122 {
   1123     Image image = { 0 };
   1124 
   1125     int textLength = (int)strlen(text);
   1126     int imageViewSize = width*height;
   1127 
   1128     image.width = width;
   1129     image.height = height;
   1130     image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
   1131     image.data = RL_CALLOC(imageViewSize, 1);
   1132     image.mipmaps = 1;
   1133 
   1134     memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
   1135 
   1136     return image;
   1137 }
   1138 #endif      // SUPPORT_IMAGE_GENERATION
   1139 
   1140 //------------------------------------------------------------------------------------
   1141 // Image manipulation functions
   1142 //------------------------------------------------------------------------------------
   1143 // Copy an image to a new image
   1144 Image ImageCopy(Image image)
   1145 {
   1146     Image newImage = { 0 };
   1147 
   1148     int width = image.width;
   1149     int height = image.height;
   1150     int size = 0;
   1151 
   1152     for (int i = 0; i < image.mipmaps; i++)
   1153     {
   1154         size += GetPixelDataSize(width, height, image.format);
   1155 
   1156         width /= 2;
   1157         height /= 2;
   1158 
   1159         // Security check for NPOT textures
   1160         if (width < 1) width = 1;
   1161         if (height < 1) height = 1;
   1162     }
   1163 
   1164     newImage.data = RL_CALLOC(size, 1);
   1165 
   1166     if (newImage.data != NULL)
   1167     {
   1168         // NOTE: Size must be provided in bytes
   1169         memcpy(newImage.data, image.data, size);
   1170 
   1171         newImage.width = image.width;
   1172         newImage.height = image.height;
   1173         newImage.mipmaps = image.mipmaps;
   1174         newImage.format = image.format;
   1175     }
   1176 
   1177     return newImage;
   1178 }
   1179 
   1180 // Create an image from another image piece
   1181 Image ImageFromImage(Image image, Rectangle rec)
   1182 {
   1183     Image result = { 0 };
   1184 
   1185     int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
   1186 
   1187     result.width = (int)rec.width;
   1188     result.height = (int)rec.height;
   1189     result.data = RL_CALLOC((int)rec.width*(int)rec.height*bytesPerPixel, 1);
   1190     result.format = image.format;
   1191     result.mipmaps = 1;
   1192 
   1193     for (int y = 0; y < (int)rec.height; y++)
   1194     {
   1195         memcpy(((unsigned char *)result.data) + y*(int)rec.width*bytesPerPixel, ((unsigned char *)image.data) + ((y + (int)rec.y)*image.width + (int)rec.x)*bytesPerPixel, (int)rec.width*bytesPerPixel);
   1196     }
   1197 
   1198     return result;
   1199 }
   1200 
   1201 // Crop an image to area defined by a rectangle
   1202 // NOTE: Security checks are performed in case rectangle goes out of bounds
   1203 void ImageCrop(Image *image, Rectangle crop)
   1204 {
   1205     // Security check to avoid program crash
   1206     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1207 
   1208     // Security checks to validate crop rectangle
   1209     if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
   1210     if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
   1211     if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
   1212     if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
   1213     if ((crop.x > image->width) || (crop.y > image->height))
   1214     {
   1215         TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
   1216         return;
   1217     }
   1218 
   1219     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   1220     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   1221     else
   1222     {
   1223         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   1224 
   1225         unsigned char *croppedData = (unsigned char *)RL_MALLOC((int)(crop.width*crop.height)*bytesPerPixel);
   1226 
   1227         // OPTION 1: Move cropped data line-by-line
   1228         for (int y = (int)crop.y, offsetSize = 0; y < (int)(crop.y + crop.height); y++)
   1229         {
   1230             memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + (int)crop.x)*bytesPerPixel, (int)crop.width*bytesPerPixel);
   1231             offsetSize += ((int)crop.width*bytesPerPixel);
   1232         }
   1233 
   1234         /*
   1235         // OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte
   1236         for (int y = (int)crop.y; y < (int)(crop.y + crop.height); y++)
   1237         {
   1238             for (int x = (int)crop.x; x < (int)(crop.x + crop.width); x++)
   1239             {
   1240                 //memcpy(croppedData + ((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
   1241                 for (int i = 0; i < bytesPerPixel; i++) croppedData[((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i];
   1242             }
   1243         }
   1244         */
   1245 
   1246         RL_FREE(image->data);
   1247         image->data = croppedData;
   1248         image->width = (int)crop.width;
   1249         image->height = (int)crop.height;
   1250     }
   1251 }
   1252 
   1253 // Convert image data to desired format
   1254 void ImageFormat(Image *image, int newFormat)
   1255 {
   1256     // Security check to avoid program crash
   1257     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1258 
   1259     if ((newFormat != 0) && (image->format != newFormat))
   1260     {
   1261         if ((image->format < PIXELFORMAT_COMPRESSED_DXT1_RGB) && (newFormat < PIXELFORMAT_COMPRESSED_DXT1_RGB))
   1262         {
   1263             Vector4 *pixels = LoadImageDataNormalized(*image);     // Supports 8 to 32 bit per channel
   1264 
   1265             RL_FREE(image->data);      // WARNING! We loose mipmaps data --> Regenerated at the end...
   1266             image->data = NULL;
   1267             image->format = newFormat;
   1268 
   1269             switch (image->format)
   1270             {
   1271                 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   1272                 {
   1273                     image->data = (unsigned char *)RL_MALLOC(image->width*image->height*sizeof(unsigned char));
   1274 
   1275                     for (int i = 0; i < image->width*image->height; i++)
   1276                     {
   1277                         ((unsigned char *)image->data)[i] = (unsigned char)((pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)*255.0f);
   1278                     }
   1279 
   1280                 } break;
   1281                 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   1282                 {
   1283                     image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char));
   1284 
   1285                     for (int i = 0, k = 0; i < image->width*image->height*2; i += 2, k++)
   1286                     {
   1287                         ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f);
   1288                         ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f);
   1289                     }
   1290 
   1291                 } break;
   1292                 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   1293                 {
   1294                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
   1295 
   1296                     unsigned char r = 0;
   1297                     unsigned char g = 0;
   1298                     unsigned char b = 0;
   1299 
   1300                     for (int i = 0; i < image->width*image->height; i++)
   1301                     {
   1302                         r = (unsigned char)(round(pixels[i].x*31.0f));
   1303                         g = (unsigned char)(round(pixels[i].y*63.0f));
   1304                         b = (unsigned char)(round(pixels[i].z*31.0f));
   1305 
   1306                         ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
   1307                     }
   1308 
   1309                 } break;
   1310                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   1311                 {
   1312                     image->data = (unsigned char *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned char));
   1313 
   1314                     for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
   1315                     {
   1316                         ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
   1317                         ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
   1318                         ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
   1319                     }
   1320                 } break;
   1321                 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   1322                 {
   1323                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
   1324 
   1325                     unsigned char r = 0;
   1326                     unsigned char g = 0;
   1327                     unsigned char b = 0;
   1328                     unsigned char a = 0;
   1329 
   1330                     for (int i = 0; i < image->width*image->height; i++)
   1331                     {
   1332                         r = (unsigned char)(round(pixels[i].x*31.0f));
   1333                         g = (unsigned char)(round(pixels[i].y*31.0f));
   1334                         b = (unsigned char)(round(pixels[i].z*31.0f));
   1335                         a = (pixels[i].w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
   1336 
   1337                         ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
   1338                     }
   1339 
   1340                 } break;
   1341                 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   1342                 {
   1343                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
   1344 
   1345                     unsigned char r = 0;
   1346                     unsigned char g = 0;
   1347                     unsigned char b = 0;
   1348                     unsigned char a = 0;
   1349 
   1350                     for (int i = 0; i < image->width*image->height; i++)
   1351                     {
   1352                         r = (unsigned char)(round(pixels[i].x*15.0f));
   1353                         g = (unsigned char)(round(pixels[i].y*15.0f));
   1354                         b = (unsigned char)(round(pixels[i].z*15.0f));
   1355                         a = (unsigned char)(round(pixels[i].w*15.0f));
   1356 
   1357                         ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
   1358                     }
   1359 
   1360                 } break;
   1361                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   1362                 {
   1363                     image->data = (unsigned char *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned char));
   1364 
   1365                     for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
   1366                     {
   1367                         ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
   1368                         ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
   1369                         ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
   1370                         ((unsigned char *)image->data)[i + 3] = (unsigned char)(pixels[k].w*255.0f);
   1371                     }
   1372                 } break;
   1373                 case PIXELFORMAT_UNCOMPRESSED_R32:
   1374                 {
   1375                     // WARNING: Image is converted to GRAYSCALE equivalent 32bit
   1376 
   1377                     image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
   1378 
   1379                     for (int i = 0; i < image->width*image->height; i++)
   1380                     {
   1381                         ((float *)image->data)[i] = (float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f);
   1382                     }
   1383                 } break;
   1384                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   1385                 {
   1386                     image->data = (float *)RL_MALLOC(image->width*image->height*3*sizeof(float));
   1387 
   1388                     for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
   1389                     {
   1390                         ((float *)image->data)[i] = pixels[k].x;
   1391                         ((float *)image->data)[i + 1] = pixels[k].y;
   1392                         ((float *)image->data)[i + 2] = pixels[k].z;
   1393                     }
   1394                 } break;
   1395                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   1396                 {
   1397                     image->data = (float *)RL_MALLOC(image->width*image->height*4*sizeof(float));
   1398 
   1399                     for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
   1400                     {
   1401                         ((float *)image->data)[i] = pixels[k].x;
   1402                         ((float *)image->data)[i + 1] = pixels[k].y;
   1403                         ((float *)image->data)[i + 2] = pixels[k].z;
   1404                         ((float *)image->data)[i + 3] = pixels[k].w;
   1405                     }
   1406                 } break;
   1407                 case PIXELFORMAT_UNCOMPRESSED_R16:
   1408                 {
   1409                     // WARNING: Image is converted to GRAYSCALE equivalent 16bit
   1410 
   1411                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
   1412 
   1413                     for (int i = 0; i < image->width*image->height; i++)
   1414                     {
   1415                         ((unsigned short *)image->data)[i] = FloatToHalf((float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f));
   1416                     }
   1417                 } break;
   1418                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   1419                 {
   1420                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned short));
   1421 
   1422                     for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
   1423                     {
   1424                         ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x);
   1425                         ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y);
   1426                         ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z);
   1427                     }
   1428                 } break;
   1429                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   1430                 {
   1431                     image->data = (unsigned short *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned short));
   1432 
   1433                     for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
   1434                     {
   1435                         ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x);
   1436                         ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y);
   1437                         ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z);
   1438                         ((unsigned short *)image->data)[i + 3] = FloatToHalf(pixels[k].w);
   1439                     }
   1440                 } break;
   1441                 default: break;
   1442             }
   1443 
   1444             RL_FREE(pixels);
   1445             pixels = NULL;
   1446 
   1447             // In case original image had mipmaps, generate mipmaps for formatted image
   1448             // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
   1449             if (image->mipmaps > 1)
   1450             {
   1451                 image->mipmaps = 1;
   1452             #if defined(SUPPORT_IMAGE_MANIPULATION)
   1453                 if (image->data != NULL) ImageMipmaps(image);
   1454             #endif
   1455             }
   1456         }
   1457         else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
   1458     }
   1459 }
   1460 
   1461 // Create an image from text (default font)
   1462 Image ImageText(const char *text, int fontSize, Color color)
   1463 {
   1464     Image imText = { 0 };
   1465 #if defined(SUPPORT_MODULE_RTEXT)
   1466     int defaultFontSize = 10;   // Default Font chars height in pixel
   1467     if (fontSize < defaultFontSize) fontSize = defaultFontSize;
   1468     int spacing = fontSize/defaultFontSize;
   1469     imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color);   // WARNING: Module required: rtext
   1470 #else
   1471     imText = GenImageColor(200, 60, BLACK);     // Generating placeholder black image rectangle
   1472     TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
   1473 #endif
   1474     return imText;
   1475 }
   1476 
   1477 // Create an image from text (custom sprite font)
   1478 // WARNING: Module required: rtext
   1479 Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
   1480 {
   1481     Image imText = { 0 };
   1482 #if defined(SUPPORT_MODULE_RTEXT)
   1483     int size = (int)strlen(text);   // Get size in bytes of text
   1484 
   1485     int textOffsetX = 0;            // Image drawing position X
   1486     int textOffsetY = 0;            // Offset between lines (on linebreak '\n')
   1487 
   1488     // NOTE: Text image is generated at font base size, later scaled to desired font size
   1489     Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);  // WARNING: Module required: rtext
   1490     Vector2 textSize = MeasureTextEx(font, text, fontSize, spacing);
   1491 
   1492     // Create image to store text
   1493     imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
   1494 
   1495     for (int i = 0; i < size;)
   1496     {
   1497         // Get next codepoint from byte string and glyph index in font
   1498         int codepointByteCount = 0;
   1499         int codepoint = GetCodepointNext(&text[i], &codepointByteCount);    // WARNING: Module required: rtext
   1500         int index = GetGlyphIndex(font, codepoint);                         // WARNING: Module required: rtext
   1501 
   1502         if (codepoint == '\n')
   1503         {
   1504             // NOTE: Fixed line spacing of 1.5 line-height
   1505             // TODO: Support custom line spacing defined by user
   1506             textOffsetY += (font.baseSize + font.baseSize/2);
   1507             textOffsetX = 0;
   1508         }
   1509         else
   1510         {
   1511             if ((codepoint != ' ') && (codepoint != '\t'))
   1512             {
   1513                 Rectangle rec = { (float)(textOffsetX + font.glyphs[index].offsetX), (float)(textOffsetY + font.glyphs[index].offsetY), (float)font.recs[index].width, (float)font.recs[index].height };
   1514                 ImageDraw(&imText, font.glyphs[index].image, (Rectangle){ 0, 0, (float)font.glyphs[index].image.width, (float)font.glyphs[index].image.height }, rec, tint);
   1515             }
   1516 
   1517             if (font.glyphs[index].advanceX == 0) textOffsetX += (int)(font.recs[index].width + spacing);
   1518             else textOffsetX += font.glyphs[index].advanceX + (int)spacing;
   1519         }
   1520 
   1521         i += codepointByteCount;   // Move text bytes counter to next codepoint
   1522     }
   1523 
   1524     // Scale image depending on text size
   1525     if (textSize.y != imSize.y)
   1526     {
   1527         float scaleFactor = textSize.y/imSize.y;
   1528         TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
   1529 
   1530         // Using nearest-neighbor scaling algorithm for default font
   1531         // TODO: Allow defining the preferred scaling mechanism externally
   1532         if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
   1533         else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
   1534     }
   1535 #else
   1536     imText = GenImageColor(200, 60, BLACK);     // Generating placeholder black image rectangle
   1537     TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
   1538 #endif
   1539     return imText;
   1540 }
   1541 
   1542 // Create an image from a selected channel of another image
   1543 Image ImageFromChannel(Image image, int selectedChannel)
   1544 {
   1545     Image result = { 0 };
   1546 
   1547     // Security check to avoid program crash
   1548     if ((image.data == NULL) || (image.width == 0) || (image.height == 0)) return result;
   1549 
   1550     // Check selected channel is valid
   1551     if (selectedChannel < 0)
   1552     {
   1553         TRACELOG(LOG_WARNING, "Channel cannot be negative. Setting channel to 0.");
   1554         selectedChannel = 0;
   1555     }
   1556 
   1557     if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE ||
   1558         image.format == PIXELFORMAT_UNCOMPRESSED_R32 ||
   1559         image.format == PIXELFORMAT_UNCOMPRESSED_R16)
   1560     {
   1561         if (selectedChannel > 0)
   1562         {
   1563             TRACELOG(LOG_WARNING, "This image has only 1 channel. Setting channel to it.");
   1564             selectedChannel = 0;
   1565         }
   1566     }
   1567     else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA)
   1568     {
   1569         if (selectedChannel > 1)
   1570         {
   1571             TRACELOG(LOG_WARNING, "This image has only 2 channels. Setting channel to alpha.");
   1572             selectedChannel = 1;
   1573         }
   1574     }
   1575     else if (image.format == PIXELFORMAT_UNCOMPRESSED_R5G6B5 ||
   1576              image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8 ||
   1577              image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32 ||
   1578              image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16)
   1579     {
   1580         if (selectedChannel > 2)
   1581         {
   1582             TRACELOG(LOG_WARNING, "This image has only 3 channels. Setting channel to red.");
   1583             selectedChannel = 0;
   1584         }
   1585     }
   1586 
   1587     // Check for RGBA formats
   1588     if (selectedChannel > 3)
   1589     {
   1590         TRACELOG(LOG_WARNING, "ImageFromChannel supports channels 0 to 3 (rgba). Setting channel to alpha.");
   1591         selectedChannel = 3;
   1592     }
   1593 
   1594     // TODO: Consider other one-channel formats: R16, R32
   1595     result.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
   1596     result.height = image.height;
   1597     result.width = image.width;
   1598     result.mipmaps = 1;
   1599 
   1600     unsigned char *pixels = (unsigned char *)RL_CALLOC(image.width*image.height, sizeof(unsigned char)); // Values from 0 to 255
   1601 
   1602     if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
   1603     else
   1604     {
   1605         for (int i = 0, k = 0; i < image.width*image.height; i++)
   1606         {
   1607             float pixelValue = -1;
   1608             switch (image.format)
   1609             {
   1610                 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   1611                 {
   1612                     pixelValue = (float)((unsigned char *)image.data)[i + selectedChannel]/255.0f;
   1613 
   1614                 } break;
   1615                 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   1616                 {
   1617                     pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;
   1618                     k += 2;
   1619 
   1620                 } break;
   1621                 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   1622                 {
   1623                     unsigned short pixel = ((unsigned short *)image.data)[i];
   1624 
   1625                     if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
   1626                     else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
   1627                     else if (selectedChannel == 2) pixelValue = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
   1628                     else if (selectedChannel == 3) pixelValue = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
   1629 
   1630                 } break;
   1631                 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   1632                 {
   1633                     unsigned short pixel = ((unsigned short *)image.data)[i];
   1634 
   1635                     if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
   1636                     else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
   1637                     else if (selectedChannel == 2) pixelValue = (float)(pixel & 0b0000000000011111)*(1.0f/31);
   1638 
   1639                 } break;
   1640                 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   1641                 {
   1642                     unsigned short pixel = ((unsigned short *)image.data)[i];
   1643 
   1644                     if (selectedChannel == 0) pixelValue = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
   1645                     else if (selectedChannel == 1) pixelValue = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
   1646                     else if (selectedChannel == 2) pixelValue = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
   1647                     else if (selectedChannel == 3) pixelValue = (float)(pixel & 0b0000000000001111)*(1.0f/15);
   1648 
   1649                 } break;
   1650                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   1651                 {
   1652                     pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;
   1653                     k += 4;
   1654 
   1655                 } break;
   1656                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   1657                 {
   1658                     pixelValue = (float)((unsigned char *)image.data)[k + selectedChannel]/255.0f;
   1659                     k += 3;
   1660 
   1661                 } break;
   1662                 case PIXELFORMAT_UNCOMPRESSED_R32:
   1663                 {
   1664                     pixelValue = ((float *)image.data)[k];
   1665                     k += 1;
   1666 
   1667                 } break;
   1668                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   1669                 {
   1670                     pixelValue = ((float *)image.data)[k + selectedChannel];
   1671                     k += 3;
   1672 
   1673                 } break;
   1674                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   1675                 {
   1676                     pixelValue = ((float *)image.data)[k + selectedChannel];
   1677                     k += 4;
   1678 
   1679                 } break;
   1680                 case PIXELFORMAT_UNCOMPRESSED_R16:
   1681                 {
   1682                     pixelValue = HalfToFloat(((unsigned short *)image.data)[k]);
   1683                     k += 1;
   1684 
   1685                 } break;
   1686                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   1687                 {
   1688                     pixelValue = HalfToFloat(((unsigned short *)image.data)[k+selectedChannel]);
   1689                     k += 3;
   1690 
   1691                 } break;
   1692                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   1693                 {
   1694                     pixelValue = HalfToFloat(((unsigned short *)image.data)[k + selectedChannel]);
   1695                     k += 4;
   1696 
   1697                 } break;
   1698                 default: break;
   1699             }
   1700 
   1701             pixels[i] = (unsigned char)(pixelValue*255);
   1702         }
   1703     }
   1704 
   1705     result.data = pixels;
   1706 
   1707     return result;
   1708 }
   1709 
   1710 // Resize and image to new size using Nearest-Neighbor scaling algorithm
   1711 void ImageResizeNN(Image *image,int newWidth,int newHeight)
   1712 {
   1713     // Security check to avoid program crash
   1714     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1715 
   1716     Color *pixels = LoadImageColors(*image);
   1717     Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
   1718 
   1719     // EDIT: added +1 to account for an early rounding problem
   1720     int xRatio = (int)((image->width << 16)/newWidth) + 1;
   1721     int yRatio = (int)((image->height << 16)/newHeight) + 1;
   1722 
   1723     int x2, y2;
   1724     for (int y = 0; y < newHeight; y++)
   1725     {
   1726         for (int x = 0; x < newWidth; x++)
   1727         {
   1728             x2 = ((x*xRatio) >> 16);
   1729             y2 = ((y*yRatio) >> 16);
   1730 
   1731             output[(y*newWidth) + x] = pixels[(y2*image->width) + x2] ;
   1732         }
   1733     }
   1734 
   1735     int format = image->format;
   1736 
   1737     RL_FREE(image->data);
   1738 
   1739     image->data = output;
   1740     image->width = newWidth;
   1741     image->height = newHeight;
   1742     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   1743 
   1744     ImageFormat(image, format);  // Reformat 32bit RGBA image to original format
   1745 
   1746     UnloadImageColors(pixels);
   1747 }
   1748 
   1749 // Resize and image to new size
   1750 // NOTE: Uses stb default scaling filters (both bicubic):
   1751 // STBIR_DEFAULT_FILTER_UPSAMPLE    STBIR_FILTER_CATMULLROM
   1752 // STBIR_DEFAULT_FILTER_DOWNSAMPLE  STBIR_FILTER_MITCHELL   (high-quality Catmull-Rom)
   1753 void ImageResize(Image *image, int newWidth, int newHeight)
   1754 {
   1755     // Security check to avoid program crash
   1756     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1757 
   1758     // Check if we can use a fast path on image scaling
   1759     // It can be for 8 bit per channel images with 1 to 4 channels per pixel
   1760     if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
   1761         (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) ||
   1762         (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
   1763         (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
   1764     {
   1765         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   1766         unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel);
   1767 
   1768         switch (image->format)
   1769         {
   1770             case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)1); break;
   1771             case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)2); break;
   1772             case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)3); break;
   1773             case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)4); break;
   1774             default: break;
   1775         }
   1776 
   1777         RL_FREE(image->data);
   1778         image->data = output;
   1779         image->width = newWidth;
   1780         image->height = newHeight;
   1781     }
   1782     else
   1783     {
   1784         // Get data as Color pixels array to work with it
   1785         Color *pixels = LoadImageColors(*image);
   1786         Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
   1787 
   1788         // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem...
   1789         stbir_resize_uint8_linear((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, (stbir_pixel_layout)4);
   1790 
   1791         int format = image->format;
   1792 
   1793         UnloadImageColors(pixels);
   1794         RL_FREE(image->data);
   1795 
   1796         image->data = output;
   1797         image->width = newWidth;
   1798         image->height = newHeight;
   1799         image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   1800 
   1801         ImageFormat(image, format);  // Reformat 32bit RGBA image to original format
   1802     }
   1803 }
   1804 
   1805 // Resize canvas and fill with color
   1806 // NOTE: Resize offset is relative to the top-left corner of the original image
   1807 void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill)
   1808 {
   1809     // Security check to avoid program crash
   1810     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1811 
   1812     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   1813     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   1814     else if ((newWidth != image->width) || (newHeight != image->height))
   1815     {
   1816         Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height };
   1817         Vector2 dstPos = { (float)offsetX, (float)offsetY };
   1818 
   1819         if (offsetX < 0)
   1820         {
   1821             srcRec.x = (float)-offsetX;
   1822             srcRec.width += (float)offsetX;
   1823             dstPos.x = 0;
   1824         }
   1825         else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX);
   1826 
   1827         if (offsetY < 0)
   1828         {
   1829             srcRec.y = (float)-offsetY;
   1830             srcRec.height += (float)offsetY;
   1831             dstPos.y = 0;
   1832         }
   1833         else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY);
   1834 
   1835         if (newWidth < srcRec.width) srcRec.width = (float)newWidth;
   1836         if (newHeight < srcRec.height) srcRec.height = (float)newHeight;
   1837 
   1838         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   1839         unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
   1840 
   1841         // Fill resized canvas with fill color
   1842         // Set first pixel with image->format
   1843         SetPixelColor(resizedData, fill, image->format);
   1844 
   1845         // Fill remaining bytes of first row
   1846         for (int x = 1; x < newWidth; x++)
   1847         {
   1848             memcpy(resizedData + x*bytesPerPixel, resizedData, bytesPerPixel);
   1849         }
   1850         // Copy the first row into the other rows
   1851         for (int y = 1; y < newHeight; y++)
   1852         {
   1853             memcpy(resizedData + y*newWidth*bytesPerPixel, resizedData, newWidth*bytesPerPixel);
   1854         }
   1855 
   1856         // Copy old image to resized canvas
   1857         int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
   1858 
   1859         for (int y = 0; y < (int)srcRec.height; y++)
   1860         {
   1861             memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel);
   1862             dstOffsetSize += (newWidth*bytesPerPixel);
   1863         }
   1864 
   1865         RL_FREE(image->data);
   1866         image->data = resizedData;
   1867         image->width = newWidth;
   1868         image->height = newHeight;
   1869     }
   1870 }
   1871 
   1872 #if defined(SUPPORT_IMAGE_MANIPULATION)
   1873 // Convert image to POT (power-of-two)
   1874 // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
   1875 void ImageToPOT(Image *image, Color fill)
   1876 {
   1877     // Security check to avoid program crash
   1878     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1879 
   1880     // Calculate next power-of-two values
   1881     // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
   1882     int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
   1883     int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
   1884 
   1885     // Check if POT texture generation is required (if texture is not already POT)
   1886     if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill);
   1887 }
   1888 
   1889 // Crop image depending on alpha value
   1890 // NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f
   1891 void ImageAlphaCrop(Image *image, float threshold)
   1892 {
   1893     // Security check to avoid program crash
   1894     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1895 
   1896     Rectangle crop = GetImageAlphaBorder(*image, threshold);
   1897 
   1898     // Crop if rectangle is valid
   1899     if (((int)crop.width != 0) && ((int)crop.height != 0)) ImageCrop(image, crop);
   1900 }
   1901 
   1902 // Clear alpha channel to desired color
   1903 // NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
   1904 void ImageAlphaClear(Image *image, Color color, float threshold)
   1905 {
   1906     // Security check to avoid program crash
   1907     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   1908 
   1909     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   1910     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   1911     else
   1912     {
   1913         switch (image->format)
   1914         {
   1915             case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   1916             {
   1917                 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
   1918                 for (int i = 1; i < image->width*image->height*2; i += 2)
   1919                 {
   1920                     if (((unsigned char *)image->data)[i] <= thresholdValue)
   1921                     {
   1922                         ((unsigned char *)image->data)[i - 1] = color.r;
   1923                         ((unsigned char *)image->data)[i] = color.a;
   1924                     }
   1925                 }
   1926             } break;
   1927             case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   1928             {
   1929                 unsigned char thresholdValue = ((threshold < 0.5f)? 0 : 1);
   1930 
   1931                 unsigned char r = (unsigned char)(round((float)color.r*31.0f));
   1932                 unsigned char g = (unsigned char)(round((float)color.g*31.0f));
   1933                 unsigned char b = (unsigned char)(round((float)color.b*31.0f));
   1934                 unsigned char a = (color.a < 128)? 0 : 1;
   1935 
   1936                 for (int i = 0; i < image->width*image->height; i++)
   1937                 {
   1938                     if ((((unsigned short *)image->data)[i] & 0b0000000000000001) <= thresholdValue)
   1939                     {
   1940                         ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
   1941                     }
   1942                 }
   1943             } break;
   1944             case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   1945             {
   1946                 unsigned char thresholdValue = (unsigned char)(threshold*15.0f);
   1947 
   1948                 unsigned char r = (unsigned char)(round((float)color.r*15.0f));
   1949                 unsigned char g = (unsigned char)(round((float)color.g*15.0f));
   1950                 unsigned char b = (unsigned char)(round((float)color.b*15.0f));
   1951                 unsigned char a = (unsigned char)(round((float)color.a*15.0f));
   1952 
   1953                 for (int i = 0; i < image->width*image->height; i++)
   1954                 {
   1955                     if ((((unsigned short *)image->data)[i] & 0x000f) <= thresholdValue)
   1956                     {
   1957                         ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
   1958                     }
   1959                 }
   1960             } break;
   1961             case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   1962             {
   1963                 unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
   1964                 for (int i = 3; i < image->width*image->height*4; i += 4)
   1965                 {
   1966                     if (((unsigned char *)image->data)[i] <= thresholdValue)
   1967                     {
   1968                         ((unsigned char *)image->data)[i - 3] = color.r;
   1969                         ((unsigned char *)image->data)[i - 2] = color.g;
   1970                         ((unsigned char *)image->data)[i - 1] = color.b;
   1971                         ((unsigned char *)image->data)[i] = color.a;
   1972                     }
   1973                 }
   1974             } break;
   1975             case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   1976             {
   1977                 for (int i = 3; i < image->width*image->height*4; i += 4)
   1978                 {
   1979                     if (((float *)image->data)[i] <= threshold)
   1980                     {
   1981                         ((float *)image->data)[i - 3] = (float)color.r/255.0f;
   1982                         ((float *)image->data)[i - 2] = (float)color.g/255.0f;
   1983                         ((float *)image->data)[i - 1] = (float)color.b/255.0f;
   1984                         ((float *)image->data)[i] = (float)color.a/255.0f;
   1985                     }
   1986                 }
   1987             } break;
   1988             case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   1989             {
   1990                 for (int i = 3; i < image->width*image->height*4; i += 4)
   1991                 {
   1992                     if (HalfToFloat(((unsigned short *)image->data)[i]) <= threshold)
   1993                     {
   1994                         ((unsigned short *)image->data)[i - 3] = FloatToHalf((float)color.r/255.0f);
   1995                         ((unsigned short *)image->data)[i - 2] = FloatToHalf((float)color.g/255.0f);
   1996                         ((unsigned short *)image->data)[i - 1] = FloatToHalf((float)color.b/255.0f);
   1997                         ((unsigned short *)image->data)[i] = FloatToHalf((float)color.a/255.0f);
   1998                     }
   1999                 }
   2000             } break;
   2001             default: break;
   2002         }
   2003     }
   2004 }
   2005 
   2006 // Apply alpha mask to image
   2007 // NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit)
   2008 // NOTE 2: alphaMask should be same size as image
   2009 void ImageAlphaMask(Image *image, Image alphaMask)
   2010 {
   2011     if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
   2012     {
   2013         TRACELOG(LOG_WARNING, "IMAGE: Alpha mask must be same size as image");
   2014     }
   2015     else if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
   2016     {
   2017         TRACELOG(LOG_WARNING, "IMAGE: Alpha mask can not be applied to compressed data formats");
   2018     }
   2019     else
   2020     {
   2021         // Force mask to be Grayscale
   2022         Image mask = ImageCopy(alphaMask);
   2023         if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
   2024 
   2025         // In case image is only grayscale, we just add alpha channel
   2026         if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
   2027         {
   2028             unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
   2029 
   2030             // Apply alpha mask to alpha channel
   2031             for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
   2032             {
   2033                 data[k] = ((unsigned char *)image->data)[i];
   2034                 data[k + 1] = ((unsigned char *)mask.data)[i];
   2035             }
   2036 
   2037             RL_FREE(image->data);
   2038             image->data = data;
   2039             image->format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
   2040         }
   2041         else
   2042         {
   2043             // Convert image to RGBA
   2044             if (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
   2045 
   2046             // Apply alpha mask to alpha channel
   2047             for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
   2048             {
   2049                 ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
   2050             }
   2051         }
   2052 
   2053         UnloadImage(mask);
   2054     }
   2055 }
   2056 
   2057 // Premultiply alpha channel
   2058 void ImageAlphaPremultiply(Image *image)
   2059 {
   2060     // Security check to avoid program crash
   2061     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2062 
   2063     float alpha = 0.0f;
   2064     Color *pixels = LoadImageColors(*image);
   2065 
   2066     for (int i = 0; i < image->width*image->height; i++)
   2067     {
   2068         if (pixels[i].a == 0)
   2069         {
   2070             pixels[i].r = 0;
   2071             pixels[i].g = 0;
   2072             pixels[i].b = 0;
   2073         }
   2074         else if (pixels[i].a < 255)
   2075         {
   2076             alpha = (float)pixels[i].a/255.0f;
   2077             pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
   2078             pixels[i].g = (unsigned char)((float)pixels[i].g*alpha);
   2079             pixels[i].b = (unsigned char)((float)pixels[i].b*alpha);
   2080         }
   2081     }
   2082 
   2083     RL_FREE(image->data);
   2084 
   2085     int format = image->format;
   2086     image->data = pixels;
   2087     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2088 
   2089     ImageFormat(image, format);
   2090 }
   2091 
   2092 // Apply box blur to image
   2093 void ImageBlurGaussian(Image *image, int blurSize)
   2094 {
   2095     // Security check to avoid program crash
   2096     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2097 
   2098     ImageAlphaPremultiply(image);
   2099 
   2100     Color *pixels = LoadImageColors(*image);
   2101 
   2102     // Loop switches between pixelsCopy1 and pixelsCopy2
   2103     Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
   2104     Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
   2105 
   2106     for (int i = 0; i < (image->height*image->width); i++)
   2107     {
   2108         pixelsCopy1[i].x = pixels[i].r;
   2109         pixelsCopy1[i].y = pixels[i].g;
   2110         pixelsCopy1[i].z = pixels[i].b;
   2111         pixelsCopy1[i].w = pixels[i].a;
   2112     }
   2113 
   2114     // Repeated convolution of rectangular window signal by itself converges to a gaussian distribution
   2115     for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++)
   2116     {
   2117         // Horizontal motion blur
   2118         for (int row = 0; row < image->height; row++)
   2119         {
   2120             float avgR = 0.0f;
   2121             float avgG = 0.0f;
   2122             float avgB = 0.0f;
   2123             float avgAlpha = 0.0f;
   2124             int convolutionSize = blurSize;
   2125 
   2126             for (int i = 0; i < blurSize; i++)
   2127             {
   2128                 avgR += pixelsCopy1[row*image->width + i].x;
   2129                 avgG += pixelsCopy1[row*image->width + i].y;
   2130                 avgB += pixelsCopy1[row*image->width + i].z;
   2131                 avgAlpha += pixelsCopy1[row*image->width + i].w;
   2132             }
   2133 
   2134             for (int x = 0; x < image->width; x++)
   2135             {
   2136                 if (x-blurSize-1 >= 0)
   2137                 {
   2138                     avgR -= pixelsCopy1[row*image->width + x-blurSize-1].x;
   2139                     avgG -= pixelsCopy1[row*image->width + x-blurSize-1].y;
   2140                     avgB -= pixelsCopy1[row*image->width + x-blurSize-1].z;
   2141                     avgAlpha -= pixelsCopy1[row*image->width + x-blurSize-1].w;
   2142                     convolutionSize--;
   2143                 }
   2144 
   2145                 if (x+blurSize < image->width)
   2146                 {
   2147                     avgR += pixelsCopy1[row*image->width + x+blurSize].x;
   2148                     avgG += pixelsCopy1[row*image->width + x+blurSize].y;
   2149                     avgB += pixelsCopy1[row*image->width + x+blurSize].z;
   2150                     avgAlpha += pixelsCopy1[row*image->width + x+blurSize].w;
   2151                     convolutionSize++;
   2152                 }
   2153 
   2154                 pixelsCopy2[row*image->width + x].x = avgR/convolutionSize;
   2155                 pixelsCopy2[row*image->width + x].y = avgG/convolutionSize;
   2156                 pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
   2157                 pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
   2158             }
   2159         }
   2160 
   2161         // Vertical motion blur
   2162         for (int col = 0; col < image->width; col++)
   2163         {
   2164             float avgR = 0.0f;
   2165             float avgG = 0.0f;
   2166             float avgB = 0.0f;
   2167             float avgAlpha = 0.0f;
   2168             int convolutionSize = blurSize;
   2169 
   2170             for (int i = 0; i < blurSize; i++)
   2171             {
   2172                 avgR += pixelsCopy2[i*image->width + col].x;
   2173                 avgG += pixelsCopy2[i*image->width + col].y;
   2174                 avgB += pixelsCopy2[i*image->width + col].z;
   2175                 avgAlpha += pixelsCopy2[i*image->width + col].w;
   2176             }
   2177 
   2178             for (int y = 0; y < image->height; y++)
   2179             {
   2180                 if (y-blurSize-1 >= 0)
   2181                 {
   2182                     avgR -= pixelsCopy2[(y-blurSize-1)*image->width + col].x;
   2183                     avgG -= pixelsCopy2[(y-blurSize-1)*image->width + col].y;
   2184                     avgB -= pixelsCopy2[(y-blurSize-1)*image->width + col].z;
   2185                     avgAlpha -= pixelsCopy2[(y-blurSize-1)*image->width + col].w;
   2186                     convolutionSize--;
   2187                 }
   2188                 if (y+blurSize < image->height)
   2189                 {
   2190                     avgR += pixelsCopy2[(y+blurSize)*image->width + col].x;
   2191                     avgG += pixelsCopy2[(y+blurSize)*image->width + col].y;
   2192                     avgB += pixelsCopy2[(y+blurSize)*image->width + col].z;
   2193                     avgAlpha += pixelsCopy2[(y+blurSize)*image->width + col].w;
   2194                     convolutionSize++;
   2195                 }
   2196 
   2197                 pixelsCopy1[y*image->width + col].x = (unsigned char) (avgR/convolutionSize);
   2198                 pixelsCopy1[y*image->width + col].y = (unsigned char) (avgG/convolutionSize);
   2199                 pixelsCopy1[y*image->width + col].z = (unsigned char) (avgB/convolutionSize);
   2200                 pixelsCopy1[y*image->width + col].w = (unsigned char) (avgAlpha/convolutionSize);
   2201             }
   2202         }
   2203     }
   2204 
   2205     // Reverse premultiply
   2206     for (int i = 0; i < (image->width)*(image->height); i++)
   2207     {
   2208         if (pixelsCopy1[i].w == 0.0f)
   2209         {
   2210             pixels[i].r = 0;
   2211             pixels[i].g = 0;
   2212             pixels[i].b = 0;
   2213             pixels[i].a = 0;
   2214         }
   2215         else if (pixelsCopy1[i].w <= 255.0f)
   2216         {
   2217             float alpha = (float)pixelsCopy1[i].w/255.0f;
   2218             pixels[i].r = (unsigned char)((float)pixelsCopy1[i].x/alpha);
   2219             pixels[i].g = (unsigned char)((float)pixelsCopy1[i].y/alpha);
   2220             pixels[i].b = (unsigned char)((float)pixelsCopy1[i].z/alpha);
   2221             pixels[i].a = (unsigned char) pixelsCopy1[i].w;
   2222         }
   2223     }
   2224 
   2225     int format = image->format;
   2226     RL_FREE(image->data);
   2227     RL_FREE(pixelsCopy1);
   2228     RL_FREE(pixelsCopy2);
   2229 
   2230     image->data = pixels;
   2231     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2232 
   2233     ImageFormat(image, format);
   2234 }
   2235 
   2236 // Apply custom square convolution kernel to image
   2237 // NOTE: The convolution kernel matrix is expected to be square
   2238 void ImageKernelConvolution(Image *image, const float *kernel, int kernelSize)
   2239 {
   2240     if ((image->data == NULL) || (image->width == 0) || (image->height == 0) || kernel == NULL) return;
   2241 
   2242     int kernelWidth = (int)sqrtf((float)kernelSize);
   2243 
   2244     if (kernelWidth*kernelWidth != kernelSize)
   2245     {
   2246         TRACELOG(LOG_WARNING, "IMAGE: Convolution kernel must be square to be applied");
   2247         return;
   2248     }
   2249 
   2250     Color *pixels = LoadImageColors(*image);
   2251 
   2252     Vector4 *imageCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
   2253     Vector4 *temp = RL_MALLOC(kernelSize*sizeof(Vector4));
   2254 
   2255     for (int i = 0; i < kernelSize; i++)
   2256     {
   2257         temp[i].x = 0.0f;
   2258         temp[i].y = 0.0f;
   2259         temp[i].z = 0.0f;
   2260         temp[i].w = 0.0f;
   2261     }
   2262 
   2263     float rRes = 0.0f;
   2264     float gRes = 0.0f;
   2265     float bRes = 0.0f;
   2266     float aRes = 0.0f;
   2267 
   2268     int startRange = 0, endRange = 0;
   2269 
   2270     if (kernelWidth%2 == 0)
   2271     {
   2272         startRange = -kernelWidth/2;
   2273         endRange = kernelWidth/2;
   2274     }
   2275     else
   2276     {
   2277         startRange = -kernelWidth/2;
   2278         endRange = kernelWidth/2 + 1;
   2279     }
   2280 
   2281     for (int x = 0; x < image->height; x++)
   2282     {
   2283         for (int y = 0; y < image->width; y++)
   2284         {
   2285             for (int xk = startRange; xk < endRange; xk++)
   2286             {
   2287                 for (int yk = startRange; yk < endRange; yk++)
   2288                 {
   2289                     int xkabs = xk + kernelWidth/2;
   2290                     int ykabs = yk + kernelWidth/2;
   2291                     unsigned int imgindex = image->width*(x + xk) + (y + yk);
   2292 
   2293                     if (imgindex >= (unsigned int)(image->width*image->height))
   2294                     {
   2295                         temp[kernelWidth*xkabs + ykabs].x = 0.0f;
   2296                         temp[kernelWidth*xkabs + ykabs].y = 0.0f;
   2297                         temp[kernelWidth*xkabs + ykabs].z = 0.0f;
   2298                         temp[kernelWidth*xkabs + ykabs].w = 0.0f;
   2299                     }
   2300                     else
   2301                     {
   2302                         temp[kernelWidth*xkabs + ykabs].x = ((float)pixels[imgindex].r)/255.0f*kernel[kernelWidth*xkabs + ykabs];
   2303                         temp[kernelWidth*xkabs + ykabs].y = ((float)pixels[imgindex].g)/255.0f*kernel[kernelWidth*xkabs + ykabs];
   2304                         temp[kernelWidth*xkabs + ykabs].z = ((float)pixels[imgindex].b)/255.0f*kernel[kernelWidth*xkabs + ykabs];
   2305                         temp[kernelWidth*xkabs + ykabs].w = ((float)pixels[imgindex].a)/255.0f*kernel[kernelWidth*xkabs + ykabs];
   2306                     }
   2307                 }
   2308             }
   2309 
   2310             for (int i = 0; i < kernelSize; i++)
   2311             {
   2312                 rRes += temp[i].x;
   2313                 gRes += temp[i].y;
   2314                 bRes += temp[i].z;
   2315                 aRes += temp[i].w;
   2316             }
   2317 
   2318             if (rRes < 0.0f) rRes = 0.0f;
   2319             if (gRes < 0.0f) gRes = 0.0f;
   2320             if (bRes < 0.0f) bRes = 0.0f;
   2321 
   2322             if (rRes > 1.0f) rRes = 1.0f;
   2323             if (gRes > 1.0f) gRes = 1.0f;
   2324             if (bRes > 1.0f) bRes = 1.0f;
   2325 
   2326             imageCopy2[image->width*x + y].x = rRes;
   2327             imageCopy2[image->width*x + y].y = gRes;
   2328             imageCopy2[image->width*x + y].z = bRes;
   2329             imageCopy2[image->width*x + y].w = aRes;
   2330 
   2331             rRes = 0.0f;
   2332             gRes = 0.0f;
   2333             bRes = 0.0f;
   2334             aRes = 0.0f;
   2335 
   2336             for (int i = 0; i < kernelSize; i++)
   2337             {
   2338                 temp[i].x = 0.0f;
   2339                 temp[i].y = 0.0f;
   2340                 temp[i].z = 0.0f;
   2341                 temp[i].w = 0.0f;
   2342             }
   2343         }
   2344     }
   2345 
   2346     for (int i = 0; i < (image->width*image->height); i++)
   2347     {
   2348         float alpha = (float)imageCopy2[i].w;
   2349 
   2350         pixels[i].r = (unsigned char)((imageCopy2[i].x)*255.0f);
   2351         pixels[i].g = (unsigned char)((imageCopy2[i].y)*255.0f);
   2352         pixels[i].b = (unsigned char)((imageCopy2[i].z)*255.0f);
   2353         pixels[i].a = (unsigned char)((alpha)*255.0f);
   2354     }
   2355 
   2356     int format = image->format;
   2357     RL_FREE(image->data);
   2358     RL_FREE(imageCopy2);
   2359     RL_FREE(temp);
   2360 
   2361     image->data = pixels;
   2362     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2363     ImageFormat(image, format);
   2364 }
   2365 
   2366 // Generate all mipmap levels for a provided image
   2367 // NOTE 1: Supports POT and NPOT images
   2368 // NOTE 2: image.data is scaled to include mipmap levels
   2369 // NOTE 3: Mipmaps format is the same as base image
   2370 void ImageMipmaps(Image *image)
   2371 {
   2372     // Security check to avoid program crash
   2373     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2374 
   2375     int mipCount = 1;                   // Required mipmap levels count (including base level)
   2376     int mipWidth = image->width;        // Base image width
   2377     int mipHeight = image->height;      // Base image height
   2378     int mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);  // Image data size (in bytes)
   2379 
   2380     // Count mipmap levels required
   2381     while ((mipWidth != 1) || (mipHeight != 1))
   2382     {
   2383         if (mipWidth != 1) mipWidth /= 2;
   2384         if (mipHeight != 1) mipHeight /= 2;
   2385 
   2386         // Security check for NPOT textures
   2387         if (mipWidth < 1) mipWidth = 1;
   2388         if (mipHeight < 1) mipHeight = 1;
   2389 
   2390         TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
   2391 
   2392         mipCount++;
   2393         mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format);       // Add mipmap size (in bytes)
   2394     }
   2395 
   2396     if (image->mipmaps < mipCount)
   2397     {
   2398         void *temp = RL_REALLOC(image->data, mipSize);
   2399 
   2400         if (temp != NULL) image->data = temp;      // Assign new pointer (new size) to store mipmaps data
   2401         else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
   2402 
   2403         // Pointer to allocated memory point where store next mipmap level data
   2404         unsigned char *nextmip = image->data;
   2405 
   2406         mipWidth = image->width;
   2407         mipHeight = image->height;
   2408         mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
   2409         Image imCopy = ImageCopy(*image);
   2410 
   2411         for (int i = 1; i < mipCount; i++)
   2412         {
   2413             nextmip += mipSize;
   2414 
   2415             mipWidth /= 2;
   2416             mipHeight /= 2;
   2417 
   2418             // Security check for NPOT textures
   2419             if (mipWidth < 1) mipWidth = 1;
   2420             if (mipHeight < 1) mipHeight = 1;
   2421 
   2422             mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
   2423 
   2424             if (i < image->mipmaps) continue;
   2425 
   2426             TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
   2427 
   2428             ImageResize(&imCopy, mipWidth, mipHeight); // Uses internally Mitchell cubic downscale filter
   2429 
   2430             memcpy(nextmip, imCopy.data, mipSize);
   2431         }
   2432 
   2433         UnloadImage(imCopy);
   2434 
   2435         image->mipmaps = mipCount;
   2436     }
   2437     else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
   2438 }
   2439 
   2440 // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
   2441 // NOTE: In case selected bpp do not represent a known 16bit format,
   2442 // dithered data is stored in the LSB part of the unsigned short
   2443 void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
   2444 {
   2445     // Security check to avoid program crash
   2446     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2447 
   2448     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
   2449     {
   2450         TRACELOG(LOG_WARNING, "IMAGE: Compressed data formats can not be dithered");
   2451         return;
   2452     }
   2453 
   2454     if ((rBpp + gBpp + bBpp + aBpp) > 16)
   2455     {
   2456         TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
   2457     }
   2458     else
   2459     {
   2460         Color *pixels = LoadImageColors(*image);
   2461 
   2462         RL_FREE(image->data);      // free old image data
   2463 
   2464         if ((image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
   2465         {
   2466             TRACELOG(LOG_WARNING, "IMAGE: Format is already 16bpp or lower, dithering could have no effect");
   2467         }
   2468 
   2469         // Define new image format, check if desired bpp match internal known format
   2470         if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
   2471         else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
   2472         else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
   2473         else
   2474         {
   2475             image->format = 0;
   2476             TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
   2477         }
   2478 
   2479         // NOTE: We will store the dithered data as unsigned short (16bpp)
   2480         image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
   2481 
   2482         Color oldPixel = WHITE;
   2483         Color newPixel = WHITE;
   2484 
   2485         int rError, gError, bError;
   2486         unsigned short rPixel, gPixel, bPixel, aPixel;   // Used for 16bit pixel composition
   2487 
   2488         #define MIN(a,b) (((a)<(b))?(a):(b))
   2489 
   2490         for (int y = 0; y < image->height; y++)
   2491         {
   2492             for (int x = 0; x < image->width; x++)
   2493             {
   2494                 oldPixel = pixels[y*image->width + x];
   2495 
   2496                 // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
   2497                 newPixel.r = oldPixel.r >> (8 - rBpp);     // R bits
   2498                 newPixel.g = oldPixel.g >> (8 - gBpp);     // G bits
   2499                 newPixel.b = oldPixel.b >> (8 - bBpp);     // B bits
   2500                 newPixel.a = oldPixel.a >> (8 - aBpp);     // A bits (not used on dithering)
   2501 
   2502                 // NOTE: Error must be computed between new and old pixel but using same number of bits!
   2503                 // We want to know how much color precision we have lost...
   2504                 rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
   2505                 gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
   2506                 bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
   2507 
   2508                 pixels[y*image->width + x] = newPixel;
   2509 
   2510                 // NOTE: Some cases are out of the array and should be ignored
   2511                 if (x < (image->width - 1))
   2512                 {
   2513                     pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
   2514                     pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
   2515                     pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
   2516                 }
   2517 
   2518                 if ((x > 0) && (y < (image->height - 1)))
   2519                 {
   2520                     pixels[(y+1)*image->width + x-1].r = MIN((int)pixels[(y+1)*image->width + x-1].r + (int)((float)rError*3.0f/16), 0xff);
   2521                     pixels[(y+1)*image->width + x-1].g = MIN((int)pixels[(y+1)*image->width + x-1].g + (int)((float)gError*3.0f/16), 0xff);
   2522                     pixels[(y+1)*image->width + x-1].b = MIN((int)pixels[(y+1)*image->width + x-1].b + (int)((float)bError*3.0f/16), 0xff);
   2523                 }
   2524 
   2525                 if (y < (image->height - 1))
   2526                 {
   2527                     pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
   2528                     pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
   2529                     pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
   2530                 }
   2531 
   2532                 if ((x < (image->width - 1)) && (y < (image->height - 1)))
   2533                 {
   2534                     pixels[(y+1)*image->width + x+1].r = MIN((int)pixels[(y+1)*image->width + x+1].r + (int)((float)rError*1.0f/16), 0xff);
   2535                     pixels[(y+1)*image->width + x+1].g = MIN((int)pixels[(y+1)*image->width + x+1].g + (int)((float)gError*1.0f/16), 0xff);
   2536                     pixels[(y+1)*image->width + x+1].b = MIN((int)pixels[(y+1)*image->width + x+1].b + (int)((float)bError*1.0f/16), 0xff);
   2537                 }
   2538 
   2539                 rPixel = (unsigned short)newPixel.r;
   2540                 gPixel = (unsigned short)newPixel.g;
   2541                 bPixel = (unsigned short)newPixel.b;
   2542                 aPixel = (unsigned short)newPixel.a;
   2543 
   2544                 ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
   2545             }
   2546         }
   2547 
   2548         UnloadImageColors(pixels);
   2549     }
   2550 }
   2551 
   2552 // Flip image vertically
   2553 void ImageFlipVertical(Image *image)
   2554 {
   2555     // Security check to avoid program crash
   2556     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2557 
   2558     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   2559     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   2560     else
   2561     {
   2562         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   2563         unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
   2564 
   2565         for (int i = (image->height - 1), offsetSize = 0; i >= 0; i--)
   2566         {
   2567             memcpy(flippedData + offsetSize, ((unsigned char *)image->data) + i*image->width*bytesPerPixel, image->width*bytesPerPixel);
   2568             offsetSize += image->width*bytesPerPixel;
   2569         }
   2570 
   2571         RL_FREE(image->data);
   2572         image->data = flippedData;
   2573     }
   2574 }
   2575 
   2576 // Flip image horizontally
   2577 void ImageFlipHorizontal(Image *image)
   2578 {
   2579     // Security check to avoid program crash
   2580     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2581 
   2582     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   2583     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   2584     else
   2585     {
   2586         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   2587         unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
   2588 
   2589         for (int y = 0; y < image->height; y++)
   2590         {
   2591             for (int x = 0; x < image->width; x++)
   2592             {
   2593                 // OPTION 1: Move pixels with memcpy()
   2594                 //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel);
   2595 
   2596                 // OPTION 2: Just copy data pixel by pixel
   2597                 for (int i = 0; i < bytesPerPixel; i++) flippedData[(y*image->width + x)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - 1 - x))*bytesPerPixel + i];
   2598             }
   2599         }
   2600 
   2601         RL_FREE(image->data);
   2602         image->data = flippedData;
   2603 
   2604         /*
   2605         // OPTION 3: Faster implementation (specific for 32bit pixels)
   2606         // NOTE: It does not require additional allocations
   2607         uint32_t *ptr = (uint32_t *)image->data;
   2608         for (int y = 0; y < image->height; y++)
   2609         {
   2610             for (int x = 0; x < image->width/2; x++)
   2611             {
   2612                 uint32_t backup = ptr[y*image->width + x];
   2613                 ptr[y*image->width + x] = ptr[y*image->width + (image->width - 1 - x)];
   2614                 ptr[y*image->width + (image->width - 1 - x)] = backup;
   2615             }
   2616         }
   2617         */
   2618     }
   2619 }
   2620 
   2621 // Rotate image in degrees
   2622 void ImageRotate(Image *image, int degrees)
   2623 {
   2624     // Security check to avoid program crash
   2625     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2626 
   2627     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   2628     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   2629     else
   2630     {
   2631         float rad = degrees*PI/180.0f;
   2632         float sinRadius = sinf(rad);
   2633         float cosRadius = cosf(rad);
   2634 
   2635         int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius));
   2636         int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius));
   2637 
   2638         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   2639         unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel);
   2640 
   2641         for (int y = 0; y < height; y++)
   2642         {
   2643             for (int x = 0; x < width; x++)
   2644             {
   2645                 float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f;
   2646                 float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f;
   2647 
   2648                 if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height))
   2649                 {
   2650                     int x1 = (int)floorf(oldX);
   2651                     int y1 = (int)floorf(oldY);
   2652                     int x2 = MIN(x1 + 1, image->width - 1);
   2653                     int y2 = MIN(y1 + 1, image->height - 1);
   2654 
   2655                     float px = oldX - x1;
   2656                     float py = oldY - y1;
   2657 
   2658                     for (int i = 0; i < bytesPerPixel; i++)
   2659                     {
   2660                         float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i];
   2661                         float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i];
   2662                         float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i];
   2663                         float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i];
   2664 
   2665                         float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py;
   2666 
   2667                         rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val;
   2668                     }
   2669                 }
   2670             }
   2671         }
   2672 
   2673         RL_FREE(image->data);
   2674         image->data = rotatedData;
   2675         image->width = width;
   2676         image->height = height;
   2677     }
   2678 }
   2679 
   2680 // Rotate image clockwise 90deg
   2681 void ImageRotateCW(Image *image)
   2682 {
   2683     // Security check to avoid program crash
   2684     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2685 
   2686     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   2687     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   2688     else
   2689     {
   2690         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   2691         unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
   2692 
   2693         for (int y = 0; y < image->height; y++)
   2694         {
   2695             for (int x = 0; x < image->width; x++)
   2696             {
   2697                 //memcpy(rotatedData + (x*image->height + (image->height - y - 1))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
   2698                 for (int i = 0; i < bytesPerPixel; i++) rotatedData[(x*image->height + (image->height - y - 1))*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + x)*bytesPerPixel + i];
   2699             }
   2700         }
   2701 
   2702         RL_FREE(image->data);
   2703         image->data = rotatedData;
   2704         int width = image->width;
   2705         int height = image-> height;
   2706 
   2707         image->width = height;
   2708         image->height = width;
   2709     }
   2710 }
   2711 
   2712 // Rotate image counter-clockwise 90deg
   2713 void ImageRotateCCW(Image *image)
   2714 {
   2715     // Security check to avoid program crash
   2716     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2717 
   2718     if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
   2719     if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
   2720     else
   2721     {
   2722         int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
   2723         unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
   2724 
   2725         for (int y = 0; y < image->height; y++)
   2726         {
   2727             for (int x = 0; x < image->width; x++)
   2728             {
   2729                 //memcpy(rotatedData + (x*image->height + y))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - x - 1))*bytesPerPixel, bytesPerPixel);
   2730                 for (int i = 0; i < bytesPerPixel; i++) rotatedData[(x*image->height + y)*bytesPerPixel + i] = ((unsigned char *)image->data)[(y*image->width + (image->width - x - 1))*bytesPerPixel + i];
   2731             }
   2732         }
   2733 
   2734         RL_FREE(image->data);
   2735         image->data = rotatedData;
   2736         int width = image->width;
   2737         int height = image-> height;
   2738 
   2739         image->width = height;
   2740         image->height = width;
   2741     }
   2742 }
   2743 
   2744 // Modify image color: tint
   2745 void ImageColorTint(Image *image, Color color)
   2746 {
   2747     // Security check to avoid program crash
   2748     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2749 
   2750     Color *pixels = LoadImageColors(*image);
   2751 
   2752     for (int i = 0; i < image->width*image->height; i++)
   2753     {
   2754         unsigned char r = (unsigned char)(((int)pixels[i].r*(int)color.r)/255);
   2755         unsigned char g = (unsigned char)(((int)pixels[i].g*(int)color.g)/255);
   2756         unsigned char b = (unsigned char)(((int)pixels[i].b*(int)color.b)/255);
   2757         unsigned char a = (unsigned char)(((int)pixels[i].a*(int)color.a)/255);
   2758 
   2759         pixels[i].r = r;
   2760         pixels[i].g = g;
   2761         pixels[i].b = b;
   2762         pixels[i].a = a;
   2763     }
   2764 
   2765     int format = image->format;
   2766     RL_FREE(image->data);
   2767 
   2768     image->data = pixels;
   2769     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2770 
   2771     ImageFormat(image, format);
   2772 }
   2773 
   2774 // Modify image color: invert
   2775 void ImageColorInvert(Image *image)
   2776 {
   2777     // Security check to avoid program crash
   2778     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2779 
   2780     Color *pixels = LoadImageColors(*image);
   2781 
   2782     for (int i = 0; i < image->width*image->height; i++)
   2783     {
   2784         pixels[i].r = 255 - pixels[i].r;
   2785         pixels[i].g = 255 - pixels[i].g;
   2786         pixels[i].b = 255 - pixels[i].b;
   2787     }
   2788 
   2789     int format = image->format;
   2790     RL_FREE(image->data);
   2791 
   2792     image->data = pixels;
   2793     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2794 
   2795     ImageFormat(image, format);
   2796 }
   2797 
   2798 // Modify image color: grayscale
   2799 void ImageColorGrayscale(Image *image)
   2800 {
   2801     ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
   2802 }
   2803 
   2804 // Modify image color: contrast
   2805 // NOTE: Contrast values between -100 and 100
   2806 void ImageColorContrast(Image *image, float contrast)
   2807 {
   2808     // Security check to avoid program crash
   2809     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2810 
   2811     if (contrast < -100) contrast = -100;
   2812     if (contrast > 100) contrast = 100;
   2813 
   2814     contrast = (100.0f + contrast)/100.0f;
   2815     contrast *= contrast;
   2816 
   2817     Color *pixels = LoadImageColors(*image);
   2818 
   2819     for (int i = 0; i < image->width*image->height; i++)
   2820     {
   2821         float pR = (float)pixels[i].r/255.0f;
   2822         pR -= 0.5f;
   2823         pR *= contrast;
   2824         pR += 0.5f;
   2825         pR *= 255;
   2826         if (pR < 0) pR = 0;
   2827         if (pR > 255) pR = 255;
   2828 
   2829         float pG = (float)pixels[i].g/255.0f;
   2830         pG -= 0.5f;
   2831         pG *= contrast;
   2832         pG += 0.5f;
   2833         pG *= 255;
   2834         if (pG < 0) pG = 0;
   2835         if (pG > 255) pG = 255;
   2836 
   2837         float pB = (float)pixels[i].b/255.0f;
   2838         pB -= 0.5f;
   2839         pB *= contrast;
   2840         pB += 0.5f;
   2841         pB *= 255;
   2842         if (pB < 0) pB = 0;
   2843         if (pB > 255) pB = 255;
   2844 
   2845         pixels[i].r = (unsigned char)pR;
   2846         pixels[i].g = (unsigned char)pG;
   2847         pixels[i].b = (unsigned char)pB;
   2848     }
   2849 
   2850     int format = image->format;
   2851     RL_FREE(image->data);
   2852 
   2853     image->data = pixels;
   2854     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2855 
   2856     ImageFormat(image, format);
   2857 }
   2858 
   2859 // Modify image color: brightness
   2860 // NOTE: Brightness values between -255 and 255
   2861 void ImageColorBrightness(Image *image, int brightness)
   2862 {
   2863     // Security check to avoid program crash
   2864     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2865 
   2866     if (brightness < -255) brightness = -255;
   2867     if (brightness > 255) brightness = 255;
   2868 
   2869     Color *pixels = LoadImageColors(*image);
   2870 
   2871     for (int i = 0; i < image->width*image->height; i++)
   2872     {
   2873         int cR = pixels[i].r + brightness;
   2874         int cG = pixels[i].g + brightness;
   2875         int cB = pixels[i].b + brightness;
   2876 
   2877         if (cR < 0) cR = 1;
   2878         if (cR > 255) cR = 255;
   2879 
   2880         if (cG < 0) cG = 1;
   2881         if (cG > 255) cG = 255;
   2882 
   2883         if (cB < 0) cB = 1;
   2884         if (cB > 255) cB = 255;
   2885 
   2886         pixels[i].r = (unsigned char)cR;
   2887         pixels[i].g = (unsigned char)cG;
   2888         pixels[i].b = (unsigned char)cB;
   2889     }
   2890 
   2891     int format = image->format;
   2892     RL_FREE(image->data);
   2893 
   2894     image->data = pixels;
   2895     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2896 
   2897     ImageFormat(image, format);
   2898 }
   2899 
   2900 // Modify image color: replace color
   2901 void ImageColorReplace(Image *image, Color color, Color replace)
   2902 {
   2903     // Security check to avoid program crash
   2904     if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   2905 
   2906     Color *pixels = LoadImageColors(*image);
   2907 
   2908     for (int i = 0; i < image->width*image->height; i++)
   2909     {
   2910         if ((pixels[i].r == color.r) &&
   2911             (pixels[i].g == color.g) &&
   2912             (pixels[i].b == color.b) &&
   2913             (pixels[i].a == color.a))
   2914         {
   2915             pixels[i].r = replace.r;
   2916             pixels[i].g = replace.g;
   2917             pixels[i].b = replace.b;
   2918             pixels[i].a = replace.a;
   2919         }
   2920     }
   2921 
   2922     int format = image->format;
   2923     RL_FREE(image->data);
   2924 
   2925     image->data = pixels;
   2926     image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   2927 
   2928     ImageFormat(image, format);
   2929 }
   2930 #endif      // SUPPORT_IMAGE_MANIPULATION
   2931 
   2932 // Load color data from image as a Color array (RGBA - 32bit)
   2933 // NOTE: Memory allocated should be freed using UnloadImageColors();
   2934 Color *LoadImageColors(Image image)
   2935 {
   2936     if ((image.width == 0) || (image.height == 0)) return NULL;
   2937 
   2938     Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
   2939 
   2940     if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
   2941     else
   2942     {
   2943         if ((image.format == PIXELFORMAT_UNCOMPRESSED_R32) ||
   2944             (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
   2945             (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel");
   2946 
   2947         if ((image.format == PIXELFORMAT_UNCOMPRESSED_R16) ||
   2948             (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) ||
   2949             (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 16bit to 8bit per channel");
   2950 
   2951         for (int i = 0, k = 0; i < image.width*image.height; i++)
   2952         {
   2953             switch (image.format)
   2954             {
   2955                 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   2956                 {
   2957                     pixels[i].r = ((unsigned char *)image.data)[i];
   2958                     pixels[i].g = ((unsigned char *)image.data)[i];
   2959                     pixels[i].b = ((unsigned char *)image.data)[i];
   2960                     pixels[i].a = 255;
   2961 
   2962                 } break;
   2963                 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   2964                 {
   2965                     pixels[i].r = ((unsigned char *)image.data)[k];
   2966                     pixels[i].g = ((unsigned char *)image.data)[k];
   2967                     pixels[i].b = ((unsigned char *)image.data)[k];
   2968                     pixels[i].a = ((unsigned char *)image.data)[k + 1];
   2969 
   2970                     k += 2;
   2971                 } break;
   2972                 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   2973                 {
   2974                     unsigned short pixel = ((unsigned short *)image.data)[i];
   2975 
   2976                     pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
   2977                     pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
   2978                     pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
   2979                     pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
   2980 
   2981                 } break;
   2982                 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   2983                 {
   2984                     unsigned short pixel = ((unsigned short *)image.data)[i];
   2985 
   2986                     pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
   2987                     pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
   2988                     pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
   2989                     pixels[i].a = 255;
   2990 
   2991                 } break;
   2992                 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   2993                 {
   2994                     unsigned short pixel = ((unsigned short *)image.data)[i];
   2995 
   2996                     pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
   2997                     pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
   2998                     pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
   2999                     pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
   3000 
   3001                 } break;
   3002                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   3003                 {
   3004                     pixels[i].r = ((unsigned char *)image.data)[k];
   3005                     pixels[i].g = ((unsigned char *)image.data)[k + 1];
   3006                     pixels[i].b = ((unsigned char *)image.data)[k + 2];
   3007                     pixels[i].a = ((unsigned char *)image.data)[k + 3];
   3008 
   3009                     k += 4;
   3010                 } break;
   3011                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   3012                 {
   3013                     pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
   3014                     pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
   3015                     pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
   3016                     pixels[i].a = 255;
   3017 
   3018                     k += 3;
   3019                 } break;
   3020                 case PIXELFORMAT_UNCOMPRESSED_R32:
   3021                 {
   3022                     pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
   3023                     pixels[i].g = 0;
   3024                     pixels[i].b = 0;
   3025                     pixels[i].a = 255;
   3026 
   3027                     k += 1;
   3028                 } break;
   3029                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   3030                 {
   3031                     pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
   3032                     pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
   3033                     pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
   3034                     pixels[i].a = 255;
   3035 
   3036                     k += 3;
   3037                 } break;
   3038                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   3039                 {
   3040                     pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
   3041                     pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
   3042                     pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
   3043                     pixels[i].a = (unsigned char)(((float *)image.data)[k + 3]*255.0f);
   3044 
   3045                     k += 4;
   3046                 } break;
   3047                 case PIXELFORMAT_UNCOMPRESSED_R16:
   3048                 {
   3049                     pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f);
   3050                     pixels[i].g = 0;
   3051                     pixels[i].b = 0;
   3052                     pixels[i].a = 255;
   3053 
   3054                     k += 1;
   3055                 } break;
   3056                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   3057                 {
   3058                     pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f);
   3059                     pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 1])*255.0f);
   3060                     pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 2])*255.0f);
   3061                     pixels[i].a = 255;
   3062 
   3063                     k += 3;
   3064                 } break;
   3065                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   3066                 {
   3067                     pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f);
   3068                     pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 1])*255.0f);
   3069                     pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 2])*255.0f);
   3070                     pixels[i].a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 3])*255.0f);
   3071 
   3072                     k += 4;
   3073                 } break;
   3074                 default: break;
   3075             }
   3076         }
   3077     }
   3078 
   3079     return pixels;
   3080 }
   3081 
   3082 // Load colors palette from image as a Color array (RGBA - 32bit)
   3083 // NOTE: Memory allocated should be freed using UnloadImagePalette()
   3084 Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount)
   3085 {
   3086     #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
   3087 
   3088     int palCount = 0;
   3089     Color *palette = NULL;
   3090     Color *pixels = LoadImageColors(image);
   3091 
   3092     if (pixels != NULL)
   3093     {
   3094         palette = (Color *)RL_MALLOC(maxPaletteSize*sizeof(Color));
   3095 
   3096         for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK;   // Set all colors to BLANK
   3097 
   3098         for (int i = 0; i < image.width*image.height; i++)
   3099         {
   3100             if (pixels[i].a > 0)
   3101             {
   3102                 bool colorInPalette = false;
   3103 
   3104                 // Check if the color is already on palette
   3105                 for (int j = 0; j < maxPaletteSize; j++)
   3106                 {
   3107                     if (COLOR_EQUAL(pixels[i], palette[j]))
   3108                     {
   3109                         colorInPalette = true;
   3110                         break;
   3111                     }
   3112                 }
   3113 
   3114                 // Store color if not on the palette
   3115                 if (!colorInPalette)
   3116                 {
   3117                     palette[palCount] = pixels[i];      // Add pixels[i] to palette
   3118                     palCount++;
   3119 
   3120                     // We reached the limit of colors supported by palette
   3121                     if (palCount >= maxPaletteSize)
   3122                     {
   3123                         i = image.width*image.height;   // Finish palette get
   3124                         TRACELOG(LOG_WARNING, "IMAGE: Palette is greater than %i colors", maxPaletteSize);
   3125                     }
   3126                 }
   3127             }
   3128         }
   3129 
   3130         UnloadImageColors(pixels);
   3131     }
   3132 
   3133     *colorCount = palCount;
   3134 
   3135     return palette;
   3136 }
   3137 
   3138 // Unload color data loaded with LoadImageColors()
   3139 void UnloadImageColors(Color *colors)
   3140 {
   3141     RL_FREE(colors);
   3142 }
   3143 
   3144 // Unload colors palette loaded with LoadImagePalette()
   3145 void UnloadImagePalette(Color *colors)
   3146 {
   3147     RL_FREE(colors);
   3148 }
   3149 
   3150 // Get image alpha border rectangle
   3151 // NOTE: Threshold is defined as a percentage: 0.0f -> 1.0f
   3152 Rectangle GetImageAlphaBorder(Image image, float threshold)
   3153 {
   3154     Rectangle crop = { 0 };
   3155 
   3156     Color *pixels = LoadImageColors(image);
   3157 
   3158     if (pixels != NULL)
   3159     {
   3160         int xMin = 65536;   // Define a big enough number
   3161         int xMax = 0;
   3162         int yMin = 65536;
   3163         int yMax = 0;
   3164 
   3165         for (int y = 0; y < image.height; y++)
   3166         {
   3167             for (int x = 0; x < image.width; x++)
   3168             {
   3169                 if (pixels[y*image.width + x].a > (unsigned char)(threshold*255.0f))
   3170                 {
   3171                     if (x < xMin) xMin = x;
   3172                     if (x > xMax) xMax = x;
   3173                     if (y < yMin) yMin = y;
   3174                     if (y > yMax) yMax = y;
   3175                 }
   3176             }
   3177         }
   3178 
   3179         // Check for empty blank image
   3180         if ((xMin != 65536) && (xMax != 65536))
   3181         {
   3182             crop = (Rectangle){ (float)xMin, (float)yMin, (float)((xMax + 1) - xMin), (float)((yMax + 1) - yMin) };
   3183         }
   3184 
   3185         UnloadImageColors(pixels);
   3186     }
   3187 
   3188     return crop;
   3189 }
   3190 
   3191 // Get image pixel color at (x, y) position
   3192 Color GetImageColor(Image image, int x, int y)
   3193 {
   3194     Color color = { 0 };
   3195 
   3196     if ((x >=0) && (x < image.width) && (y >= 0) && (y < image.height))
   3197     {
   3198         switch (image.format)
   3199         {
   3200             case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   3201             {
   3202                 color.r = ((unsigned char *)image.data)[y*image.width + x];
   3203                 color.g = ((unsigned char *)image.data)[y*image.width + x];
   3204                 color.b = ((unsigned char *)image.data)[y*image.width + x];
   3205                 color.a = 255;
   3206 
   3207             } break;
   3208             case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   3209             {
   3210                 color.r = ((unsigned char *)image.data)[(y*image.width + x)*2];
   3211                 color.g = ((unsigned char *)image.data)[(y*image.width + x)*2];
   3212                 color.b = ((unsigned char *)image.data)[(y*image.width + x)*2];
   3213                 color.a = ((unsigned char *)image.data)[(y*image.width + x)*2 + 1];
   3214 
   3215             } break;
   3216             case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   3217             {
   3218                 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
   3219 
   3220                 color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
   3221                 color.g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
   3222                 color.b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
   3223                 color.a = (unsigned char)((pixel & 0b0000000000000001)*255);
   3224 
   3225             } break;
   3226             case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   3227             {
   3228                 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
   3229 
   3230                 color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
   3231                 color.g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
   3232                 color.b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
   3233                 color.a = 255;
   3234 
   3235             } break;
   3236             case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   3237             {
   3238                 unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
   3239 
   3240                 color.r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
   3241                 color.g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
   3242                 color.b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
   3243                 color.a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
   3244 
   3245             } break;
   3246             case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   3247             {
   3248                 color.r = ((unsigned char *)image.data)[(y*image.width + x)*4];
   3249                 color.g = ((unsigned char *)image.data)[(y*image.width + x)*4 + 1];
   3250                 color.b = ((unsigned char *)image.data)[(y*image.width + x)*4 + 2];
   3251                 color.a = ((unsigned char *)image.data)[(y*image.width + x)*4 + 3];
   3252 
   3253             } break;
   3254             case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   3255             {
   3256                 color.r = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3];
   3257                 color.g = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 1];
   3258                 color.b = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 2];
   3259                 color.a = 255;
   3260 
   3261             } break;
   3262             case PIXELFORMAT_UNCOMPRESSED_R32:
   3263             {
   3264                 color.r = (unsigned char)(((float *)image.data)[y*image.width + x]*255.0f);
   3265                 color.g = 0;
   3266                 color.b = 0;
   3267                 color.a = 255;
   3268 
   3269             } break;
   3270             case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   3271             {
   3272                 color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*3]*255.0f);
   3273                 color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 1]*255.0f);
   3274                 color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 2]*255.0f);
   3275                 color.a = 255;
   3276 
   3277             } break;
   3278             case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   3279             {
   3280                 color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
   3281                 color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
   3282                 color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
   3283                 color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
   3284 
   3285             } break;
   3286             case PIXELFORMAT_UNCOMPRESSED_R16:
   3287             {
   3288                 color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[y*image.width + x])*255.0f);
   3289                 color.g = 0;
   3290                 color.b = 0;
   3291                 color.a = 255;
   3292 
   3293             } break;
   3294             case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   3295             {
   3296                 color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3])*255.0f);
   3297                 color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 1])*255.0f);
   3298                 color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 2])*255.0f);
   3299                 color.a = 255;
   3300 
   3301             } break;
   3302             case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   3303             {
   3304                 color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f);
   3305                 color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f);
   3306                 color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f);
   3307                 color.a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f);
   3308 
   3309             } break;
   3310             default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break;
   3311         }
   3312     }
   3313     else TRACELOG(LOG_WARNING, "Requested image pixel (%i, %i) out of bounds", x, y);
   3314 
   3315     return color;
   3316 }
   3317 
   3318 //------------------------------------------------------------------------------------
   3319 // Image drawing functions
   3320 //------------------------------------------------------------------------------------
   3321 // Clear image background with given color
   3322 void ImageClearBackground(Image *dst, Color color)
   3323 {
   3324     // Security check to avoid program crash
   3325     if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
   3326 
   3327     // Fill in first pixel based on image format
   3328     ImageDrawPixel(dst, 0, 0, color);
   3329 
   3330     unsigned char *pSrcPixel = (unsigned char *)dst->data;
   3331     int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
   3332 
   3333     // Repeat the first pixel data throughout the image
   3334     for (int i = 1; i < dst->width*dst->height; i++)
   3335     {
   3336         memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, bytesPerPixel);
   3337     }
   3338 }
   3339 
   3340 // Draw pixel within an image
   3341 // NOTE: Compressed image formats not supported
   3342 void ImageDrawPixel(Image *dst, int x, int y, Color color)
   3343 {
   3344     // Security check to avoid program crash
   3345     if ((dst->data == NULL) || (x < 0) || (x >= dst->width) || (y < 0) || (y >= dst->height)) return;
   3346 
   3347     switch (dst->format)
   3348     {
   3349         case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   3350         {
   3351             // NOTE: Calculate grayscale equivalent color
   3352             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3353             unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
   3354 
   3355             ((unsigned char *)dst->data)[y*dst->width + x] = gray;
   3356 
   3357         } break;
   3358         case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   3359         {
   3360             // NOTE: Calculate grayscale equivalent color
   3361             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3362             unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
   3363 
   3364             ((unsigned char *)dst->data)[(y*dst->width + x)*2] = gray;
   3365             ((unsigned char *)dst->data)[(y*dst->width + x)*2 + 1] = color.a;
   3366 
   3367         } break;
   3368         case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   3369         {
   3370             // NOTE: Calculate R5G6B5 equivalent color
   3371             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3372 
   3373             unsigned char r = (unsigned char)(round(coln.x*31.0f));
   3374             unsigned char g = (unsigned char)(round(coln.y*63.0f));
   3375             unsigned char b = (unsigned char)(round(coln.z*31.0f));
   3376 
   3377             ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
   3378 
   3379         } break;
   3380         case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   3381         {
   3382             // NOTE: Calculate R5G5B5A1 equivalent color
   3383             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   3384 
   3385             unsigned char r = (unsigned char)(round(coln.x*31.0f));
   3386             unsigned char g = (unsigned char)(round(coln.y*31.0f));
   3387             unsigned char b = (unsigned char)(round(coln.z*31.0f));
   3388             unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
   3389 
   3390             ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
   3391 
   3392         } break;
   3393         case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   3394         {
   3395             // NOTE: Calculate R5G5B5A1 equivalent color
   3396             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   3397 
   3398             unsigned char r = (unsigned char)(round(coln.x*15.0f));
   3399             unsigned char g = (unsigned char)(round(coln.y*15.0f));
   3400             unsigned char b = (unsigned char)(round(coln.z*15.0f));
   3401             unsigned char a = (unsigned char)(round(coln.w*15.0f));
   3402 
   3403             ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
   3404 
   3405         } break;
   3406         case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   3407         {
   3408             ((unsigned char *)dst->data)[(y*dst->width + x)*3] = color.r;
   3409             ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 1] = color.g;
   3410             ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 2] = color.b;
   3411 
   3412         } break;
   3413         case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   3414         {
   3415             ((unsigned char *)dst->data)[(y*dst->width + x)*4] = color.r;
   3416             ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 1] = color.g;
   3417             ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 2] = color.b;
   3418             ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 3] = color.a;
   3419 
   3420         } break;
   3421         case PIXELFORMAT_UNCOMPRESSED_R32:
   3422         {
   3423             // NOTE: Calculate grayscale equivalent color (normalized to 32bit)
   3424             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3425 
   3426             ((float *)dst->data)[y*dst->width + x] = coln.x*0.299f + coln.y*0.587f + coln.z*0.114f;
   3427 
   3428         } break;
   3429         case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   3430         {
   3431             // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit)
   3432             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3433 
   3434             ((float *)dst->data)[(y*dst->width + x)*3] = coln.x;
   3435             ((float *)dst->data)[(y*dst->width + x)*3 + 1] = coln.y;
   3436             ((float *)dst->data)[(y*dst->width + x)*3 + 2] = coln.z;
   3437         } break;
   3438         case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   3439         {
   3440             // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit)
   3441             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   3442 
   3443             ((float *)dst->data)[(y*dst->width + x)*4] = coln.x;
   3444             ((float *)dst->data)[(y*dst->width + x)*4 + 1] = coln.y;
   3445             ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z;
   3446             ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w;
   3447 
   3448         } break;
   3449         case PIXELFORMAT_UNCOMPRESSED_R16:
   3450         {
   3451             // NOTE: Calculate grayscale equivalent color (normalized to 32bit)
   3452             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3453 
   3454             ((unsigned short*)dst->data)[y*dst->width + x] = FloatToHalf(coln.x*0.299f + coln.y*0.587f + coln.z*0.114f);
   3455 
   3456         } break;
   3457         case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   3458         {
   3459             // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit)
   3460             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   3461 
   3462             ((unsigned short *)dst->data)[(y*dst->width + x)*3] = FloatToHalf(coln.x);
   3463             ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 1] = FloatToHalf(coln.y);
   3464             ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 2] = FloatToHalf(coln.z);
   3465         } break;
   3466         case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   3467         {
   3468             // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit)
   3469             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   3470 
   3471             ((unsigned short *)dst->data)[(y*dst->width + x)*4] = FloatToHalf(coln.x);
   3472             ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 1] = FloatToHalf(coln.y);
   3473             ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 2] = FloatToHalf(coln.z);
   3474             ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 3] = FloatToHalf(coln.w);
   3475 
   3476         } break;
   3477         default: break;
   3478     }
   3479 }
   3480 
   3481 // Draw pixel within an image (Vector version)
   3482 void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
   3483 {
   3484     ImageDrawPixel(dst, (int)position.x, (int)position.y, color);
   3485 }
   3486 
   3487 // Draw line within an image
   3488 void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
   3489 {
   3490     // Calculate differences in coordinates
   3491     int shortLen = endPosY - startPosY;
   3492     int longLen = endPosX - startPosX;
   3493     bool yLonger = false;
   3494 
   3495     // Determine if the line is more vertical than horizontal
   3496     if (abs(shortLen) > abs(longLen))
   3497     {
   3498         // Swap the lengths if the line is more vertical
   3499         int temp = shortLen;
   3500         shortLen = longLen;
   3501         longLen = temp;
   3502         yLonger = true;
   3503     }
   3504 
   3505     // Initialize variables for drawing loop
   3506     int endVal = longLen;
   3507     int sgnInc = 1;
   3508 
   3509     // Adjust direction increment based on longLen sign
   3510     if (longLen < 0)
   3511     {
   3512         longLen = -longLen;
   3513         sgnInc = -1;
   3514     }
   3515 
   3516     // Calculate fixed-point increment for shorter length
   3517     int decInc = (longLen == 0)? 0 : (shortLen << 16)/longLen;
   3518 
   3519     // Draw the line pixel by pixel
   3520     if (yLonger)
   3521     {
   3522         // If line is more vertical, iterate over y-axis
   3523         for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc)
   3524         {
   3525             // Calculate pixel position and draw it
   3526             ImageDrawPixel(dst, startPosX + (j >> 16), startPosY + i, color);
   3527         }
   3528     }
   3529     else
   3530     {
   3531         // If line is more horizontal, iterate over x-axis
   3532         for (int i = 0, j = 0; i != endVal; i += sgnInc, j += decInc)
   3533         {
   3534             // Calculate pixel position and draw it
   3535             ImageDrawPixel(dst, startPosX + i, startPosY + (j >> 16), color);
   3536         }
   3537     }
   3538 }
   3539 
   3540 // Draw line within an image (Vector version)
   3541 void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
   3542 {
   3543     // Round start and end positions to nearest integer coordinates
   3544     int x1 = (int)(start.x + 0.5f);
   3545     int y1 = (int)(start.y + 0.5f);
   3546     int x2 = (int)(end.x + 0.5f);
   3547     int y2 = (int)(end.y + 0.5f);
   3548 
   3549     // Draw a vertical line using ImageDrawLine function
   3550     ImageDrawLine(dst, x1, y1, x2, y2, color);
   3551 }
   3552 
   3553 // Draw a line defining thickness within an image
   3554 void ImageDrawLineEx(Image *dst, Vector2 start, Vector2 end, int thick, Color color)
   3555 {
   3556     // Round start and end positions to nearest integer coordinates
   3557     int x1 = (int)(start.x + 0.5f);
   3558     int y1 = (int)(start.y + 0.5f);
   3559     int x2 = (int)(end.x + 0.5f);
   3560     int y2 = (int)(end.y + 0.5f);
   3561 
   3562     // Calculate differences in x and y coordinates
   3563     int dx = x2 - x1;
   3564     int dy = y2 - y1;
   3565 
   3566     // Draw the main line between (x1, y1) and (x2, y2)
   3567     ImageDrawLine(dst, x1, y1, x2, y2, color);
   3568 
   3569     // Determine if the line is more horizontal or vertical
   3570     if (dx != 0 && abs(dy/dx) < 1)
   3571     {
   3572         // Line is more horizontal
   3573         // Calculate half the width of the line
   3574         int wy = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dx));
   3575 
   3576         // Draw additional lines above and below the main line
   3577         for (int i = 1; i <= wy; i++)
   3578         {
   3579             ImageDrawLine(dst, x1, y1 - i, x2, y2 - i, color); // Draw above the main line
   3580             ImageDrawLine(dst, x1, y1 + i, x2, y2 + i, color); // Draw below the main line
   3581         }
   3582     }
   3583     else if (dy != 0)
   3584     {
   3585         // Line is more vertical or perfectly horizontal
   3586         // Calculate half the width of the line
   3587         int wx = (thick - 1)*(int)sqrtf((float)(dx*dx + dy*dy))/(2*abs(dy));
   3588 
   3589         // Draw additional lines to the left and right of the main line
   3590         for (int i = 1; i <= wx; i++)
   3591         {
   3592             ImageDrawLine(dst, x1 - i, y1, x2 - i, y2, color); // Draw left of the main line
   3593             ImageDrawLine(dst, x1 + i, y1, x2 + i, y2, color); // Draw right of the main line
   3594         }
   3595     }
   3596 }
   3597 
   3598 // Draw circle within an image
   3599 void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color)
   3600 {
   3601     int x = 0;
   3602     int y = radius;
   3603     int decesionParameter = 3 - 2*radius;
   3604 
   3605     while (y >= x)
   3606     {
   3607         ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color);
   3608         ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color);
   3609         ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color);
   3610         ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color);
   3611         x++;
   3612 
   3613         if (decesionParameter > 0)
   3614         {
   3615             y--;
   3616             decesionParameter = decesionParameter + 4*(x - y) + 10;
   3617         }
   3618         else decesionParameter = decesionParameter + 4*x + 6;
   3619     }
   3620 }
   3621 
   3622 // Draw circle within an image (Vector version)
   3623 void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color)
   3624 {
   3625     ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color);
   3626 }
   3627 
   3628 // Draw circle outline within an image
   3629 void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color)
   3630 {
   3631     int x = 0;
   3632     int y = radius;
   3633     int decesionParameter = 3 - 2*radius;
   3634 
   3635     while (y >= x)
   3636     {
   3637         ImageDrawPixel(dst, centerX + x, centerY + y, color);
   3638         ImageDrawPixel(dst, centerX - x, centerY + y, color);
   3639         ImageDrawPixel(dst, centerX + x, centerY - y, color);
   3640         ImageDrawPixel(dst, centerX - x, centerY - y, color);
   3641         ImageDrawPixel(dst, centerX + y, centerY + x, color);
   3642         ImageDrawPixel(dst, centerX - y, centerY + x, color);
   3643         ImageDrawPixel(dst, centerX + y, centerY - x, color);
   3644         ImageDrawPixel(dst, centerX - y, centerY - x, color);
   3645         x++;
   3646 
   3647         if (decesionParameter > 0)
   3648         {
   3649             y--;
   3650             decesionParameter = decesionParameter + 4*(x - y) + 10;
   3651         }
   3652         else decesionParameter = decesionParameter + 4*x + 6;
   3653     }
   3654 }
   3655 
   3656 // Draw circle outline within an image (Vector version)
   3657 void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color)
   3658 {
   3659     ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, color);
   3660 }
   3661 
   3662 // Draw rectangle within an image
   3663 void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
   3664 {
   3665     ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color);
   3666 }
   3667 
   3668 // Draw rectangle within an image (Vector version)
   3669 void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
   3670 {
   3671     ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color);
   3672 }
   3673 
   3674 // Draw rectangle within an image
   3675 void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
   3676 {
   3677     // Security check to avoid program crash
   3678     if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
   3679 
   3680     // Security check to avoid drawing out of bounds in case of bad user data
   3681     if (rec.x < 0) { rec.width += rec.x; rec.x = 0; }
   3682     if (rec.y < 0) { rec.height += rec.y; rec.y = 0; }
   3683     if (rec.width < 0) rec.width = 0;
   3684     if (rec.height < 0) rec.height = 0;
   3685 
   3686     // Clamp the size the the image bounds
   3687     if ((rec.x + rec.width) >= dst->width) rec.width = dst->width - rec.x;
   3688     if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y;
   3689 
   3690     // Check if the rect is even inside the image
   3691     if ((rec.x >= dst->width) || (rec.y >= dst->height)) return;
   3692     if (((rec.x + rec.width) <= 0) || (rec.y + rec.height <= 0)) return;
   3693 
   3694     int sy = (int)rec.y;
   3695     int sx = (int)rec.x;
   3696 
   3697     int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
   3698 
   3699     // Fill in the first pixel of the first row based on image format
   3700     ImageDrawPixel(dst, sx, sy, color);
   3701 
   3702     int bytesOffset = ((sy*dst->width) + sx)*bytesPerPixel;
   3703     unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
   3704 
   3705     // Repeat the first pixel data throughout the row
   3706     for (int x = 1; x < (int)rec.width; x++)
   3707     {
   3708         memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel);
   3709     }
   3710 
   3711     // Repeat the first row data for all other rows
   3712     int bytesPerRow = bytesPerPixel*(int)rec.width;
   3713     for (int y = 1; y < (int)rec.height; y++)
   3714     {
   3715         memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow);
   3716     }
   3717 }
   3718 
   3719 // Draw rectangle lines within an image
   3720 void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
   3721 {
   3722     ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color);
   3723     ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
   3724     ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
   3725     ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color);
   3726 }
   3727 
   3728 // Draw triangle within an image
   3729 void ImageDrawTriangle(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color)
   3730 {
   3731     // Calculate the 2D bounding box of the triangle
   3732     // Determine the minimum and maximum x and y coordinates of the triangle vertices
   3733     int xMin = (int)((v1.x < v2.x)? ((v1.x < v3.x)? v1.x : v3.x) : ((v2.x < v3.x)? v2.x : v3.x));
   3734     int yMin = (int)((v1.y < v2.y)? ((v1.y < v3.y)? v1.y : v3.y) : ((v2.y < v3.y)? v2.y : v3.y));
   3735     int xMax = (int)((v1.x > v2.x)? ((v1.x > v3.x)? v1.x : v3.x) : ((v2.x > v3.x)? v2.x : v3.x));
   3736     int yMax = (int)((v1.y > v2.y)? ((v1.y > v3.y)? v1.y : v3.y) : ((v2.y > v3.y)? v2.y : v3.y));
   3737 
   3738     // Clamp the bounding box to the image dimensions
   3739     if (xMin < 0) xMin = 0;
   3740     if (yMin < 0) yMin = 0;
   3741     if (xMax > dst->width) xMax = dst->width;
   3742     if (yMax > dst->height) yMax = dst->height;
   3743 
   3744     // Check the order of the vertices to determine if it's a front or back face
   3745     // NOTE: if signedArea is equal to 0, the face is degenerate
   3746     float signedArea = (v2.x - v1.x)*(v3.y - v1.y) - (v3.x - v1.x)*(v2.y - v1.y);
   3747     bool isBackFace = (signedArea > 0);
   3748 
   3749     // Barycentric interpolation setup
   3750     // Calculate the step increments for the barycentric coordinates
   3751     int w1XStep = (int)(v3.y - v2.y), w1YStep = (int)(v2.x - v3.x);
   3752     int w2XStep = (int)(v1.y - v3.y), w2YStep = (int)(v3.x - v1.x);
   3753     int w3XStep = (int)(v2.y - v1.y), w3YStep = (int)(v1.x - v2.x);
   3754 
   3755     // If the triangle is a back face, invert the steps
   3756     if (isBackFace)
   3757     {
   3758         w1XStep = -w1XStep, w1YStep = -w1YStep;
   3759         w2XStep = -w2XStep, w2YStep = -w2YStep;
   3760         w3XStep = -w3XStep, w3YStep = -w3YStep;
   3761     }
   3762 
   3763     // Calculate the initial barycentric coordinates for the top-left point of the bounding box
   3764     int w1Row = (int)((xMin - v2.x)*w1XStep + w1YStep*(yMin - v2.y));
   3765     int w2Row = (int)((xMin - v3.x)*w2XStep + w2YStep*(yMin - v3.y));
   3766     int w3Row = (int)((xMin - v1.x)*w3XStep + w3YStep*(yMin - v1.y));
   3767 
   3768     // Rasterization loop
   3769     // Iterate through each pixel in the bounding box
   3770     for (int y = yMin; y <= yMax; y++)
   3771     {
   3772         int w1 = w1Row;
   3773         int w2 = w2Row;
   3774         int w3 = w3Row;
   3775 
   3776         for (int x = xMin; x <= xMax; x++)
   3777         {
   3778             // Check if the pixel is inside the triangle using barycentric coordinates
   3779             // If it is then we can draw the pixel with the given color
   3780             if ((w1 | w2 | w3) >= 0) ImageDrawPixel(dst, x, y, color);
   3781 
   3782             // Increment the barycentric coordinates for the next pixel
   3783             w1 += w1XStep;
   3784             w2 += w2XStep;
   3785             w3 += w3XStep;
   3786         }
   3787 
   3788         // Move to the next row in the bounding box
   3789         w1Row += w1YStep;
   3790         w2Row += w2YStep;
   3791         w3Row += w3YStep;
   3792     }
   3793 }
   3794 
   3795 // Draw triangle with interpolated colors within an image
   3796 void ImageDrawTriangleEx(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color c1, Color c2, Color c3)
   3797 {
   3798     // Calculate the 2D bounding box of the triangle
   3799     // Determine the minimum and maximum x and y coordinates of the triangle vertices
   3800     int xMin = (int)((v1.x < v2.x)? ((v1.x < v3.x)? v1.x : v3.x) : ((v2.x < v3.x)? v2.x : v3.x));
   3801     int yMin = (int)((v1.y < v2.y)? ((v1.y < v3.y)? v1.y : v3.y) : ((v2.y < v3.y)? v2.y : v3.y));
   3802     int xMax = (int)((v1.x > v2.x)? ((v1.x > v3.x)? v1.x : v3.x) : ((v2.x > v3.x)? v2.x : v3.x));
   3803     int yMax = (int)((v1.y > v2.y)? ((v1.y > v3.y)? v1.y : v3.y) : ((v2.y > v3.y)? v2.y : v3.y));
   3804 
   3805     // Clamp the bounding box to the image dimensions
   3806     if (xMin < 0) xMin = 0;
   3807     if (yMin < 0) yMin = 0;
   3808     if (xMax > dst->width) xMax = dst->width;
   3809     if (yMax > dst->height) yMax = dst->height;
   3810 
   3811     // Check the order of the vertices to determine if it's a front or back face
   3812     // NOTE: if signedArea is equal to 0, the face is degenerate
   3813     float signedArea = (v2.x - v1.x)*(v3.y - v1.y) - (v3.x - v1.x)*(v2.y - v1.y);
   3814     bool isBackFace = (signedArea > 0);
   3815 
   3816     // Barycentric interpolation setup
   3817     // Calculate the step increments for the barycentric coordinates
   3818     int w1XStep = (int)(v3.y - v2.y), w1YStep = (int)(v2.x - v3.x);
   3819     int w2XStep = (int)(v1.y - v3.y), w2YStep = (int)(v3.x - v1.x);
   3820     int w3XStep = (int)(v2.y - v1.y), w3YStep = (int)(v1.x - v2.x);
   3821 
   3822     // If the triangle is a back face, invert the steps
   3823     if (isBackFace)
   3824     {
   3825         w1XStep = -w1XStep, w1YStep = -w1YStep;
   3826         w2XStep = -w2XStep, w2YStep = -w2YStep;
   3827         w3XStep = -w3XStep, w3YStep = -w3YStep;
   3828     }
   3829 
   3830     // Calculate the initial barycentric coordinates for the top-left point of the bounding box
   3831     int w1Row = (int)((xMin - v2.x)*w1XStep + w1YStep*(yMin - v2.y));
   3832     int w2Row = (int)((xMin - v3.x)*w2XStep + w2YStep*(yMin - v3.y));
   3833     int w3Row = (int)((xMin - v1.x)*w3XStep + w3YStep*(yMin - v1.y));
   3834 
   3835     // Calculate the inverse of the sum of the barycentric coordinates for normalization
   3836     // NOTE 1: Here, we act as if we multiply by 255 the reciprocal, which avoids additional
   3837     //         calculations in the loop. This is acceptable because we are only interpolating colors.
   3838     // NOTE 2: This sum remains constant throughout the triangle
   3839     float wInvSum = 255.0f/(w1Row + w2Row + w3Row);
   3840 
   3841     // Rasterization loop
   3842     // Iterate through each pixel in the bounding box
   3843     for (int y = yMin; y <= yMax; y++)
   3844     {
   3845         int w1 = w1Row;
   3846         int w2 = w2Row;
   3847         int w3 = w3Row;
   3848 
   3849         for (int x = xMin; x <= xMax; x++)
   3850         {
   3851             // Check if the pixel is inside the triangle using barycentric coordinates
   3852             if ((w1 | w2 | w3) >= 0)
   3853             {
   3854                 // Compute the normalized barycentric coordinates
   3855                 unsigned char aW1 = (unsigned char)((float)w1*wInvSum);
   3856                 unsigned char aW2 = (unsigned char)((float)w2*wInvSum);
   3857                 unsigned char aW3 = (unsigned char)((float)w3*wInvSum);
   3858 
   3859                 // Interpolate the color using the barycentric coordinates
   3860                 Color finalColor = { 0 };
   3861                 finalColor.r = (c1.r*aW1 + c2.r*aW2 + c3.r*aW3)/255;
   3862                 finalColor.g = (c1.g*aW1 + c2.g*aW2 + c3.g*aW3)/255;
   3863                 finalColor.b = (c1.b*aW1 + c2.b*aW2 + c3.b*aW3)/255;
   3864                 finalColor.a = (c1.a*aW1 + c2.a*aW2 + c3.a*aW3)/255;
   3865 
   3866                 // Draw the pixel with the interpolated color
   3867                 ImageDrawPixel(dst, x, y, finalColor);
   3868             }
   3869 
   3870             // Increment the barycentric coordinates for the next pixel
   3871             w1 += w1XStep;
   3872             w2 += w2XStep;
   3873             w3 += w3XStep;
   3874         }
   3875 
   3876         // Move to the next row in the bounding box
   3877         w1Row += w1YStep;
   3878         w2Row += w2YStep;
   3879         w3Row += w3YStep;
   3880     }
   3881 }
   3882 
   3883 // Draw triangle outline within an image
   3884 void ImageDrawTriangleLines(Image *dst, Vector2 v1, Vector2 v2, Vector2 v3, Color color)
   3885 {
   3886     ImageDrawLine(dst, (int)v1.x, (int)v1.y, (int)v2.x, (int)v2.y, color);
   3887     ImageDrawLine(dst, (int)v2.x, (int)v2.y, (int)v3.x, (int)v3.y, color);
   3888     ImageDrawLine(dst, (int)v3.x, (int)v3.y, (int)v1.x, (int)v1.y, color);
   3889 }
   3890 
   3891 // Draw a triangle fan defined by points within an image (first vertex is the center)
   3892 void ImageDrawTriangleFan(Image *dst, Vector2 *points, int pointCount, Color color)
   3893 {
   3894     if (pointCount >= 3)
   3895     {
   3896         for (int i = 1; i < pointCount - 1; i++)
   3897         {
   3898             ImageDrawTriangle(dst, points[0], points[i], points[i + 1], color);
   3899         }
   3900     }
   3901 }
   3902 
   3903 // Draw a triangle strip defined by points within an image
   3904 void ImageDrawTriangleStrip(Image *dst, Vector2 *points, int pointCount, Color color)
   3905 {
   3906     if (pointCount >= 3)
   3907     {
   3908         for (int i = 2; i < pointCount; i++)
   3909         {
   3910             if ((i%2) == 0) ImageDrawTriangle(dst, points[i], points[i - 2], points[i - 1], color);
   3911             else ImageDrawTriangle(dst, points[i], points[i - 1], points[i - 2], color);
   3912         }
   3913     }
   3914 }
   3915 
   3916 // Draw an image (source) within an image (destination)
   3917 // NOTE: Color tint is applied to source image
   3918 void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)
   3919 {
   3920     // Security check to avoid program crash
   3921     if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
   3922         (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
   3923 
   3924     if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
   3925     else
   3926     {
   3927         Image srcMod = { 0 };       // Source copy (in case it was required)
   3928         Image *srcPtr = &src;       // Pointer to source image
   3929         bool useSrcMod = false;     // Track source copy required
   3930 
   3931         // Source rectangle out-of-bounds security checks
   3932         if (srcRec.x < 0) { srcRec.width += srcRec.x; srcRec.x = 0; }
   3933         if (srcRec.y < 0) { srcRec.height += srcRec.y; srcRec.y = 0; }
   3934         if ((srcRec.x + srcRec.width) > src.width) srcRec.width = src.width - srcRec.x;
   3935         if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
   3936 
   3937         // Check if source rectangle needs to be resized to destination rectangle
   3938         // In that case, we make a copy of source, and we apply all required transform
   3939         if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
   3940         {
   3941             srcMod = ImageFromImage(src, srcRec);   // Create image from another image
   3942             ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height);   // Resize to destination rectangle
   3943             srcRec = (Rectangle){ 0, 0, (float)srcMod.width, (float)srcMod.height };
   3944 
   3945             srcPtr = &srcMod;
   3946             useSrcMod = true;
   3947         }
   3948 
   3949         // Destination rectangle out-of-bounds security checks
   3950         if (dstRec.x < 0)
   3951         {
   3952             srcRec.x -= dstRec.x;
   3953             srcRec.width += dstRec.x;
   3954             dstRec.x = 0;
   3955         }
   3956         else if ((dstRec.x + srcRec.width) > dst->width) srcRec.width = dst->width - dstRec.x;
   3957 
   3958         if (dstRec.y < 0)
   3959         {
   3960             srcRec.y -= dstRec.y;
   3961             srcRec.height += dstRec.y;
   3962             dstRec.y = 0;
   3963         }
   3964         else if ((dstRec.y + srcRec.height) > dst->height) srcRec.height = dst->height - dstRec.y;
   3965 
   3966         if (dst->width < srcRec.width) srcRec.width = (float)dst->width;
   3967         if (dst->height < srcRec.height) srcRec.height = (float)dst->height;
   3968 
   3969         // This blitting method is quite fast! The process followed is:
   3970         // for every pixel -> [get_src_format/get_dst_format -> blend -> format_to_dst]
   3971         // Some optimization ideas:
   3972         //    [x] Avoid creating source copy if not required (no resize required)
   3973         //    [x] Optimize ImageResize() for pixel format (alternative: ImageResizeNN())
   3974         //    [x] Optimize ColorAlphaBlend() to avoid processing (alpha = 0) and (alpha = 1)
   3975         //    [x] Optimize ColorAlphaBlend() for faster operations (maybe avoiding divs?)
   3976         //    [x] Consider fast path: no alpha blending required cases (src has no alpha)
   3977         //    [x] Consider fast path: same src/dst format with no alpha -> direct line copy
   3978         //    [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
   3979         //    [ ] Support f32bit channels drawing
   3980 
   3981         // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 and PIXELFORMAT_UNCOMPRESSED_R1616B16A16
   3982 
   3983         Color colSrc, colDst, blend;
   3984         bool blendRequired = true;
   3985 
   3986         // Fast path: Avoid blend if source has no alpha to blend
   3987         if ((tint.a == 255) &&
   3988             ((srcPtr->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
   3989             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R5G6B5) ||
   3990             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
   3991             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R32) ||
   3992             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
   3993             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R16) ||
   3994             (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R16G16B16)))
   3995             blendRequired = false;
   3996 
   3997         int strideDst = GetPixelDataSize(dst->width, 1, dst->format);
   3998         int bytesPerPixelDst = strideDst/(dst->width);
   3999 
   4000         int strideSrc = GetPixelDataSize(srcPtr->width, 1, srcPtr->format);
   4001         int bytesPerPixelSrc = strideSrc/(srcPtr->width);
   4002 
   4003         unsigned char *pSrcBase = (unsigned char *)srcPtr->data + ((int)srcRec.y*srcPtr->width + (int)srcRec.x)*bytesPerPixelSrc;
   4004         unsigned char *pDstBase = (unsigned char *)dst->data + ((int)dstRec.y*dst->width + (int)dstRec.x)*bytesPerPixelDst;
   4005 
   4006         for (int y = 0; y < (int)srcRec.height; y++)
   4007         {
   4008             unsigned char *pSrc = pSrcBase;
   4009             unsigned char *pDst = pDstBase;
   4010 
   4011             // Fast path: Avoid moving pixel by pixel if no blend required and same format
   4012             if (!blendRequired && (srcPtr->format == dst->format)) memcpy(pDst, pSrc, (int)(srcRec.width)*bytesPerPixelSrc);
   4013             else
   4014             {
   4015                 for (int x = 0; x < (int)srcRec.width; x++)
   4016                 {
   4017                     colSrc = GetPixelColor(pSrc, srcPtr->format);
   4018                     colDst = GetPixelColor(pDst, dst->format);
   4019 
   4020                     // Fast path: Avoid blend if source has no alpha to blend
   4021                     if (blendRequired) blend = ColorAlphaBlend(colDst, colSrc, tint);
   4022                     else blend = colSrc;
   4023 
   4024                     SetPixelColor(pDst, blend, dst->format);
   4025 
   4026                     pDst += bytesPerPixelDst;
   4027                     pSrc += bytesPerPixelSrc;
   4028                 }
   4029             }
   4030 
   4031             pSrcBase += strideSrc;
   4032             pDstBase += strideDst;
   4033         }
   4034 
   4035         if (useSrcMod) UnloadImage(srcMod);     // Unload source modified image
   4036 
   4037         if ((dst->mipmaps > 1) && (src.mipmaps > 1))
   4038         {
   4039             Image mipmapDst = *dst;
   4040             mipmapDst.data = (char *)mipmapDst.data + GetPixelDataSize(mipmapDst.width, mipmapDst.height, mipmapDst.format);
   4041             mipmapDst.width /= 2;
   4042             mipmapDst.height /= 2;
   4043             mipmapDst.mipmaps--;
   4044 
   4045             Image mipmapSrc = src;
   4046             mipmapSrc.data = (char *)mipmapSrc.data + GetPixelDataSize(mipmapSrc.width, mipmapSrc.height, mipmapSrc.format);
   4047             mipmapSrc.width /= 2;
   4048             mipmapSrc.height /= 2;
   4049             mipmapSrc.mipmaps--;
   4050 
   4051             Rectangle mipmapSrcRec = srcRec;
   4052             mipmapSrcRec.width /= 2;
   4053             mipmapSrcRec.height /= 2;
   4054             mipmapSrcRec.x /= 2;
   4055             mipmapSrcRec.y /= 2;
   4056 
   4057             Rectangle mipmapDstRec = dstRec;
   4058             mipmapDstRec.width /= 2;
   4059             mipmapDstRec.height /= 2;
   4060             mipmapDstRec.x /= 2;
   4061             mipmapDstRec.y /= 2;
   4062 
   4063             ImageDraw(&mipmapDst, mipmapSrc, mipmapSrcRec, mipmapDstRec, tint);
   4064         }
   4065     }
   4066 }
   4067 
   4068 // Draw text (default font) within an image (destination)
   4069 void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
   4070 {
   4071 #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT)
   4072     // Make sure default font is loaded to be used on image text drawing
   4073     if (GetFontDefault().texture.id == 0) LoadFontDefault();
   4074 
   4075     Vector2 position = { (float)posX, (float)posY };
   4076     ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, 1.0f, color);   // WARNING: Module required: rtext
   4077 #else
   4078     TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext");
   4079 #endif
   4080 }
   4081 
   4082 // Draw text (custom sprite font) within an image (destination)
   4083 void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
   4084 {
   4085     Image imText = ImageTextEx(font, text, fontSize, spacing, tint);
   4086 
   4087     Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height };
   4088     Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height };
   4089 
   4090     ImageDraw(dst, imText, srcRec, dstRec, WHITE);
   4091 
   4092     UnloadImage(imText);
   4093 }
   4094 
   4095 //------------------------------------------------------------------------------------
   4096 // Texture loading functions
   4097 //------------------------------------------------------------------------------------
   4098 // Load texture from file into GPU memory (VRAM)
   4099 Texture2D LoadTexture(const char *fileName)
   4100 {
   4101     Texture2D texture = { 0 };
   4102 
   4103     Image image = LoadImage(fileName);
   4104 
   4105     if (image.data != NULL)
   4106     {
   4107         texture = LoadTextureFromImage(image);
   4108         UnloadImage(image);
   4109     }
   4110 
   4111     return texture;
   4112 }
   4113 
   4114 // Load a texture from image data
   4115 // NOTE: image is not unloaded, it must be done manually
   4116 Texture2D LoadTextureFromImage(Image image)
   4117 {
   4118     Texture2D texture = { 0 };
   4119 
   4120     if ((image.width != 0) && (image.height != 0))
   4121     {
   4122         texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
   4123     }
   4124     else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture");
   4125 
   4126     texture.width = image.width;
   4127     texture.height = image.height;
   4128     texture.mipmaps = image.mipmaps;
   4129     texture.format = image.format;
   4130 
   4131     return texture;
   4132 }
   4133 
   4134 // Load cubemap from image, multiple image cubemap layouts supported
   4135 TextureCubemap LoadTextureCubemap(Image image, int layout)
   4136 {
   4137     TextureCubemap cubemap = { 0 };
   4138 
   4139     if (layout == CUBEMAP_LAYOUT_AUTO_DETECT)      // Try to automatically guess layout type
   4140     {
   4141         // Check image width/height to determine the type of cubemap provided
   4142         if (image.width > image.height)
   4143         {
   4144             if ((image.width/6) == image.height) { layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL; cubemap.width = image.width/6; }
   4145             else if ((image.width/4) == (image.height/3)) { layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
   4146         }
   4147         else if (image.height > image.width)
   4148         {
   4149             if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
   4150             else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
   4151         }
   4152     }
   4153     else
   4154     {
   4155         if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) cubemap.width = image.height/6;
   4156         if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) cubemap.width = image.width/6;
   4157         if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR) cubemap.width = image.width/3;
   4158         if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE) cubemap.width = image.width/4;
   4159     }
   4160 
   4161     cubemap.height = cubemap.width;
   4162 
   4163     // Layout provided or already auto-detected
   4164     if (layout != CUBEMAP_LAYOUT_AUTO_DETECT)
   4165     {
   4166         int size = cubemap.width;
   4167 
   4168         Image faces = { 0 };                // Vertical column image
   4169         Rectangle faceRecs[6] = { 0 };      // Face source rectangles
   4170 
   4171         for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
   4172 
   4173         if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
   4174         {
   4175             faces = ImageCopy(image);       // Image data already follows expected convention
   4176         }
   4177         /*else if (layout == CUBEMAP_LAYOUT_PANORAMA)
   4178         {
   4179             // TODO: implement panorama by converting image to square faces...
   4180             // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
   4181         } */
   4182         else
   4183         {
   4184             if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = (float)size*i;
   4185             else if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR)
   4186             {
   4187                 faceRecs[0].x = (float)size; faceRecs[0].y = (float)size;
   4188                 faceRecs[1].x = (float)size; faceRecs[1].y = (float)size*3;
   4189                 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
   4190                 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
   4191                 faceRecs[4].x = 0; faceRecs[4].y = (float)size;
   4192                 faceRecs[5].x = (float)size*2; faceRecs[5].y = (float)size;
   4193             }
   4194             else if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE)
   4195             {
   4196                 faceRecs[0].x = (float)size*2; faceRecs[0].y = (float)size;
   4197                 faceRecs[1].x = 0; faceRecs[1].y = (float)size;
   4198                 faceRecs[2].x = (float)size; faceRecs[2].y = 0;
   4199                 faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
   4200                 faceRecs[4].x = (float)size; faceRecs[4].y = (float)size;
   4201                 faceRecs[5].x = (float)size*3; faceRecs[5].y = (float)size;
   4202             }
   4203 
   4204             // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
   4205             faces = GenImageColor(size, size*6, MAGENTA);
   4206             ImageFormat(&faces, image.format);
   4207 
   4208             Image mipmapped = ImageCopy(image);
   4209             ImageMipmaps(&mipmapped);
   4210             ImageMipmaps(&faces);
   4211 
   4212             // NOTE: Image formatting does not work with compressed textures
   4213 
   4214             for (int i = 0; i < 6; i++) ImageDraw(&faces, mipmapped, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
   4215 
   4216             UnloadImage(mipmapped);
   4217         }
   4218 
   4219         // NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
   4220         // one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
   4221         cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format, faces.mipmaps);
   4222 
   4223         if (cubemap.id != 0)
   4224         {
   4225             cubemap.format = faces.format;
   4226             cubemap.mipmaps = 1;
   4227         }
   4228         else TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
   4229 
   4230         UnloadImage(faces);
   4231     }
   4232     else TRACELOG(LOG_WARNING, "IMAGE: Failed to detect cubemap image layout");
   4233 
   4234     return cubemap;
   4235 }
   4236 
   4237 // Load texture for rendering (framebuffer)
   4238 // NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
   4239 RenderTexture2D LoadRenderTexture(int width, int height)
   4240 {
   4241     RenderTexture2D target = { 0 };
   4242 
   4243     target.id = rlLoadFramebuffer(); // Load an empty framebuffer
   4244 
   4245     if (target.id > 0)
   4246     {
   4247         rlEnableFramebuffer(target.id);
   4248 
   4249         // Create color texture (default to RGBA)
   4250         target.texture.id = rlLoadTexture(NULL, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
   4251         target.texture.width = width;
   4252         target.texture.height = height;
   4253         target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   4254         target.texture.mipmaps = 1;
   4255 
   4256         // Create depth renderbuffer/texture
   4257         target.depth.id = rlLoadTextureDepth(width, height, true);
   4258         target.depth.width = width;
   4259         target.depth.height = height;
   4260         target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
   4261         target.depth.mipmaps = 1;
   4262 
   4263         // Attach color texture and depth renderbuffer/texture to FBO
   4264         rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
   4265         rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
   4266 
   4267         // Check if fbo is complete with attachments (valid)
   4268         if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
   4269 
   4270         rlDisableFramebuffer();
   4271     }
   4272     else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
   4273 
   4274     return target;
   4275 }
   4276 
   4277 // Check if a texture is valid (loaded in GPU)
   4278 bool IsTextureValid(Texture2D texture)
   4279 {
   4280     bool result = false;
   4281 
   4282     // TODO: Validate maximum texture size supported by GPU
   4283 
   4284     if ((texture.id > 0) &&         // Validate OpenGL id (texture uplaoded to GPU)
   4285         (texture.width > 0) &&      // Validate texture width
   4286         (texture.height > 0) &&     // Validate texture height
   4287         (texture.format > 0) &&     // Validate texture pixel format
   4288         (texture.mipmaps > 0)) result = true;     // Validate texture mipmaps (at least 1 for basic mipmap level)
   4289 
   4290     return result;
   4291 }
   4292 
   4293 // Unload texture from GPU memory (VRAM)
   4294 void UnloadTexture(Texture2D texture)
   4295 {
   4296     if (texture.id > 0)
   4297     {
   4298         rlUnloadTexture(texture.id);
   4299 
   4300         TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
   4301     }
   4302 }
   4303 
   4304 // Check if a render texture is valid (loaded in GPU)
   4305 bool IsRenderTextureValid(RenderTexture2D target)
   4306 {
   4307     bool result = false;
   4308 
   4309     if ((target.id > 0) &&                  // Validate OpenGL id (loaded on GPU)
   4310         IsTextureValid(target.depth) &&     // Validate FBO depth texture/renderbuffer attachment
   4311         IsTextureValid(target.texture)) result = true; // Validate FBO texture attachment
   4312 
   4313     return result;
   4314 }
   4315 
   4316 // Unload render texture from GPU memory (VRAM)
   4317 void UnloadRenderTexture(RenderTexture2D target)
   4318 {
   4319     if (target.id > 0)
   4320     {
   4321         if (target.texture.id > 0)
   4322         {
   4323             // Color texture attached to FBO is deleted
   4324             rlUnloadTexture(target.texture.id);
   4325         }
   4326 
   4327         // NOTE: Depth texture/renderbuffer is automatically
   4328         // queried and deleted before deleting framebuffer
   4329         rlUnloadFramebuffer(target.id);
   4330     }
   4331 }
   4332 
   4333 // Update GPU texture with new data
   4334 // NOTE: pixels data must match texture.format
   4335 void UpdateTexture(Texture2D texture, const void *pixels)
   4336 {
   4337     rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
   4338 }
   4339 
   4340 // Update GPU texture rectangle with new data
   4341 // NOTE: pixels data must match texture.format
   4342 void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
   4343 {
   4344     rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
   4345 }
   4346 
   4347 //------------------------------------------------------------------------------------
   4348 // Texture configuration functions
   4349 //------------------------------------------------------------------------------------
   4350 // Generate GPU mipmaps for a texture
   4351 void GenTextureMipmaps(Texture2D *texture)
   4352 {
   4353     // NOTE: NPOT textures support check inside function
   4354     // On WebGL (OpenGL ES 2.0) NPOT textures support is limited
   4355     rlGenTextureMipmaps(texture->id, texture->width, texture->height, texture->format, &texture->mipmaps);
   4356 }
   4357 
   4358 // Set texture scaling filter mode
   4359 void SetTextureFilter(Texture2D texture, int filter)
   4360 {
   4361     switch (filter)
   4362     {
   4363         case TEXTURE_FILTER_POINT:
   4364         {
   4365             if (texture.mipmaps > 1)
   4366             {
   4367                 // RL_TEXTURE_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps)
   4368                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_NEAREST);
   4369 
   4370                 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
   4371                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
   4372             }
   4373             else
   4374             {
   4375                 // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
   4376                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_NEAREST);
   4377                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
   4378             }
   4379         } break;
   4380         case TEXTURE_FILTER_BILINEAR:
   4381         {
   4382             if (texture.mipmaps > 1)
   4383             {
   4384                 // RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps)
   4385                 // Alternative: RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps)
   4386                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST);
   4387 
   4388                 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
   4389                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4390             }
   4391             else
   4392             {
   4393                 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
   4394                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4395                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4396             }
   4397         } break;
   4398         case TEXTURE_FILTER_TRILINEAR:
   4399         {
   4400             if (texture.mipmaps > 1)
   4401             {
   4402                 // RL_TEXTURE_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps)
   4403                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR);
   4404 
   4405                 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
   4406                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4407             }
   4408             else
   4409             {
   4410                 TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
   4411 
   4412                 // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
   4413                 rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4414                 rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
   4415             }
   4416         } break;
   4417         case TEXTURE_FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 4); break;
   4418         case TEXTURE_FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 8); break;
   4419         case TEXTURE_FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 16); break;
   4420         default: break;
   4421     }
   4422 }
   4423 
   4424 // Set texture wrapping mode
   4425 void SetTextureWrap(Texture2D texture, int wrap)
   4426 {
   4427     switch (wrap)
   4428     {
   4429         case TEXTURE_WRAP_REPEAT:
   4430         {
   4431             // NOTE: It only works if NPOT textures are supported, i.e. OpenGL ES 2.0 could not support it
   4432             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_REPEAT);
   4433             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_REPEAT);
   4434         } break;
   4435         case TEXTURE_WRAP_CLAMP:
   4436         {
   4437             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_CLAMP);
   4438             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_CLAMP);
   4439         } break;
   4440         case TEXTURE_WRAP_MIRROR_REPEAT:
   4441         {
   4442             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_REPEAT);
   4443             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_REPEAT);
   4444         } break;
   4445         case TEXTURE_WRAP_MIRROR_CLAMP:
   4446         {
   4447             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_CLAMP);
   4448             rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_CLAMP);
   4449         } break;
   4450         default: break;
   4451     }
   4452 }
   4453 
   4454 //------------------------------------------------------------------------------------
   4455 // Texture drawing functions
   4456 //------------------------------------------------------------------------------------
   4457 // Draw a texture
   4458 void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
   4459 {
   4460     DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint);
   4461 }
   4462 
   4463 // Draw a texture with position defined as Vector2
   4464 void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
   4465 {
   4466     DrawTextureEx(texture, position, 0, 1.0f, tint);
   4467 }
   4468 
   4469 // Draw a texture with extended parameters
   4470 void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
   4471 {
   4472     Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
   4473     Rectangle dest = { position.x, position.y, (float)texture.width*scale, (float)texture.height*scale };
   4474     Vector2 origin = { 0.0f, 0.0f };
   4475 
   4476     DrawTexturePro(texture, source, dest, origin, rotation, tint);
   4477 }
   4478 
   4479 // Draw a part of a texture (defined by a rectangle)
   4480 void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint)
   4481 {
   4482     Rectangle dest = { position.x, position.y, fabsf(source.width), fabsf(source.height) };
   4483     Vector2 origin = { 0.0f, 0.0f };
   4484 
   4485     DrawTexturePro(texture, source, dest, origin, 0.0f, tint);
   4486 }
   4487 
   4488 // Draw a part of a texture (defined by a rectangle) with 'pro' parameters
   4489 // NOTE: origin is relative to destination rectangle size
   4490 void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint)
   4491 {
   4492     // Check if texture is valid
   4493     if (texture.id > 0)
   4494     {
   4495         float width = (float)texture.width;
   4496         float height = (float)texture.height;
   4497 
   4498         bool flipX = false;
   4499 
   4500         if (source.width < 0) { flipX = true; source.width *= -1; }
   4501         if (source.height < 0) source.y -= source.height;
   4502 
   4503         if (dest.width < 0) dest.width *= -1;
   4504         if (dest.height < 0) dest.height *= -1;
   4505 
   4506         Vector2 topLeft = { 0 };
   4507         Vector2 topRight = { 0 };
   4508         Vector2 bottomLeft = { 0 };
   4509         Vector2 bottomRight = { 0 };
   4510 
   4511         // Only calculate rotation if needed
   4512         if (rotation == 0.0f)
   4513         {
   4514             float x = dest.x - origin.x;
   4515             float y = dest.y - origin.y;
   4516             topLeft = (Vector2){ x, y };
   4517             topRight = (Vector2){ x + dest.width, y };
   4518             bottomLeft = (Vector2){ x, y + dest.height };
   4519             bottomRight = (Vector2){ x + dest.width, y + dest.height };
   4520         }
   4521         else
   4522         {
   4523             float sinRotation = sinf(rotation*DEG2RAD);
   4524             float cosRotation = cosf(rotation*DEG2RAD);
   4525             float x = dest.x;
   4526             float y = dest.y;
   4527             float dx = -origin.x;
   4528             float dy = -origin.y;
   4529 
   4530             topLeft.x = x + dx*cosRotation - dy*sinRotation;
   4531             topLeft.y = y + dx*sinRotation + dy*cosRotation;
   4532 
   4533             topRight.x = x + (dx + dest.width)*cosRotation - dy*sinRotation;
   4534             topRight.y = y + (dx + dest.width)*sinRotation + dy*cosRotation;
   4535 
   4536             bottomLeft.x = x + dx*cosRotation - (dy + dest.height)*sinRotation;
   4537             bottomLeft.y = y + dx*sinRotation + (dy + dest.height)*cosRotation;
   4538 
   4539             bottomRight.x = x + (dx + dest.width)*cosRotation - (dy + dest.height)*sinRotation;
   4540             bottomRight.y = y + (dx + dest.width)*sinRotation + (dy + dest.height)*cosRotation;
   4541         }
   4542 
   4543         rlSetTexture(texture.id);
   4544         rlBegin(RL_QUADS);
   4545 
   4546             rlColor4ub(tint.r, tint.g, tint.b, tint.a);
   4547             rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
   4548 
   4549             // Top-left corner for texture and quad
   4550             if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
   4551             else rlTexCoord2f(source.x/width, source.y/height);
   4552             rlVertex2f(topLeft.x, topLeft.y);
   4553 
   4554             // Bottom-left corner for texture and quad
   4555             if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
   4556             else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
   4557             rlVertex2f(bottomLeft.x, bottomLeft.y);
   4558 
   4559             // Bottom-right corner for texture and quad
   4560             if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
   4561             else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
   4562             rlVertex2f(bottomRight.x, bottomRight.y);
   4563 
   4564             // Top-right corner for texture and quad
   4565             if (flipX) rlTexCoord2f(source.x/width, source.y/height);
   4566             else rlTexCoord2f((source.x + source.width)/width, source.y/height);
   4567             rlVertex2f(topRight.x, topRight.y);
   4568 
   4569         rlEnd();
   4570         rlSetTexture(0);
   4571 
   4572         // NOTE: Vertex position can be transformed using matrices
   4573         // but the process is way more costly than just calculating
   4574         // the vertex positions manually, like done above
   4575         // I leave here the old implementation for educational purposes,
   4576         // just in case someone wants to do some performance test
   4577         /*
   4578         rlSetTexture(texture.id);
   4579         rlPushMatrix();
   4580             rlTranslatef(dest.x, dest.y, 0.0f);
   4581             if (rotation != 0.0f) rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
   4582             rlTranslatef(-origin.x, -origin.y, 0.0f);
   4583 
   4584             rlBegin(RL_QUADS);
   4585                 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
   4586                 rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
   4587 
   4588                 // Bottom-left corner for texture and quad
   4589                 if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
   4590                 else rlTexCoord2f(source.x/width, source.y/height);
   4591                 rlVertex2f(0.0f, 0.0f);
   4592 
   4593                 // Bottom-right corner for texture and quad
   4594                 if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
   4595                 else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
   4596                 rlVertex2f(0.0f, dest.height);
   4597 
   4598                 // Top-right corner for texture and quad
   4599                 if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
   4600                 else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
   4601                 rlVertex2f(dest.width, dest.height);
   4602 
   4603                 // Top-left corner for texture and quad
   4604                 if (flipX) rlTexCoord2f(source.x/width, source.y/height);
   4605                 else rlTexCoord2f((source.x + source.width)/width, source.y/height);
   4606                 rlVertex2f(dest.width, 0.0f);
   4607             rlEnd();
   4608         rlPopMatrix();
   4609         rlSetTexture(0);
   4610         */
   4611     }
   4612 }
   4613 
   4614 // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info
   4615 void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint)
   4616 {
   4617     if (texture.id > 0)
   4618     {
   4619         float width = (float)texture.width;
   4620         float height = (float)texture.height;
   4621 
   4622         float patchWidth = ((int)dest.width <= 0)? 0.0f : dest.width;
   4623         float patchHeight = ((int)dest.height <= 0)? 0.0f : dest.height;
   4624 
   4625         if (nPatchInfo.source.width < 0) nPatchInfo.source.x -= nPatchInfo.source.width;
   4626         if (nPatchInfo.source.height < 0) nPatchInfo.source.y -= nPatchInfo.source.height;
   4627         if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL) patchHeight = nPatchInfo.source.height;
   4628         if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL) patchWidth = nPatchInfo.source.width;
   4629 
   4630         bool drawCenter = true;
   4631         bool drawMiddle = true;
   4632         float leftBorder = (float)nPatchInfo.left;
   4633         float topBorder = (float)nPatchInfo.top;
   4634         float rightBorder = (float)nPatchInfo.right;
   4635         float bottomBorder = (float)nPatchInfo.bottom;
   4636 
   4637         // Adjust the lateral (left and right) border widths in case patchWidth < texture.width
   4638         if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_VERTICAL)
   4639         {
   4640             drawCenter = false;
   4641             leftBorder = (leftBorder/(leftBorder + rightBorder))*patchWidth;
   4642             rightBorder = patchWidth - leftBorder;
   4643         }
   4644 
   4645         // Adjust the lateral (top and bottom) border heights in case patchHeight < texture.height
   4646         if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_HORIZONTAL)
   4647         {
   4648             drawMiddle = false;
   4649             topBorder = (topBorder/(topBorder + bottomBorder))*patchHeight;
   4650             bottomBorder = patchHeight - topBorder;
   4651         }
   4652 
   4653         Vector2 vertA, vertB, vertC, vertD;
   4654         vertA.x = 0.0f;                             // outer left
   4655         vertA.y = 0.0f;                             // outer top
   4656         vertB.x = leftBorder;                       // inner left
   4657         vertB.y = topBorder;                        // inner top
   4658         vertC.x = patchWidth  - rightBorder;        // inner right
   4659         vertC.y = patchHeight - bottomBorder;       // inner bottom
   4660         vertD.x = patchWidth;                       // outer right
   4661         vertD.y = patchHeight;                      // outer bottom
   4662 
   4663         Vector2 coordA, coordB, coordC, coordD;
   4664         coordA.x = nPatchInfo.source.x/width;
   4665         coordA.y = nPatchInfo.source.y/height;
   4666         coordB.x = (nPatchInfo.source.x + leftBorder)/width;
   4667         coordB.y = (nPatchInfo.source.y + topBorder)/height;
   4668         coordC.x = (nPatchInfo.source.x + nPatchInfo.source.width  - rightBorder)/width;
   4669         coordC.y = (nPatchInfo.source.y + nPatchInfo.source.height - bottomBorder)/height;
   4670         coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width;
   4671         coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height;
   4672 
   4673         rlSetTexture(texture.id);
   4674 
   4675         rlPushMatrix();
   4676             rlTranslatef(dest.x, dest.y, 0.0f);
   4677             rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
   4678             rlTranslatef(-origin.x, -origin.y, 0.0f);
   4679 
   4680             rlBegin(RL_QUADS);
   4681                 rlColor4ub(tint.r, tint.g, tint.b, tint.a);
   4682                 rlNormal3f(0.0f, 0.0f, 1.0f);               // Normal vector pointing towards viewer
   4683 
   4684                 if (nPatchInfo.layout == NPATCH_NINE_PATCH)
   4685                 {
   4686                     // ------------------------------------------------------------
   4687                     // TOP-LEFT QUAD
   4688                     rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Bottom-left corner for texture and quad
   4689                     rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Bottom-right corner for texture and quad
   4690                     rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-right corner for texture and quad
   4691                     rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
   4692                     if (drawCenter)
   4693                     {
   4694                         // TOP-CENTER QUAD
   4695                         rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Bottom-left corner for texture and quad
   4696                         rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Bottom-right corner for texture and quad
   4697                         rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-right corner for texture and quad
   4698                         rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-left corner for texture and quad
   4699                     }
   4700                     // TOP-RIGHT QUAD
   4701                     rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Bottom-left corner for texture and quad
   4702                     rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Bottom-right corner for texture and quad
   4703                     rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
   4704                     rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-left corner for texture and quad
   4705                     if (drawMiddle)
   4706                     {
   4707                         // ------------------------------------------------------------
   4708                         // MIDDLE-LEFT QUAD
   4709                         rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Bottom-left corner for texture and quad
   4710                         rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Bottom-right corner for texture and quad
   4711                         rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Top-right corner for texture and quad
   4712                         rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Top-left corner for texture and quad
   4713                         if (drawCenter)
   4714                         {
   4715                             // MIDDLE-CENTER QUAD
   4716                             rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Bottom-left corner for texture and quad
   4717                             rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Bottom-right corner for texture and quad
   4718                             rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Top-right corner for texture and quad
   4719                             rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Top-left corner for texture and quad
   4720                         }
   4721 
   4722                         // MIDDLE-RIGHT QUAD
   4723                         rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Bottom-left corner for texture and quad
   4724                         rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Bottom-right corner for texture and quad
   4725                         rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Top-right corner for texture and quad
   4726                         rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Top-left corner for texture and quad
   4727                     }
   4728 
   4729                     // ------------------------------------------------------------
   4730                     // BOTTOM-LEFT QUAD
   4731                     rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
   4732                     rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-right corner for texture and quad
   4733                     rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Top-right corner for texture and quad
   4734                     rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Top-left corner for texture and quad
   4735                     if (drawCenter)
   4736                     {
   4737                         // BOTTOM-CENTER QUAD
   4738                         rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-left corner for texture and quad
   4739                         rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-right corner for texture and quad
   4740                         rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Top-right corner for texture and quad
   4741                         rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Top-left corner for texture and quad
   4742                     }
   4743 
   4744                     // BOTTOM-RIGHT QUAD
   4745                     rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-left corner for texture and quad
   4746                     rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
   4747                     rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Top-right corner for texture and quad
   4748                     rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Top-left corner for texture and quad
   4749                 }
   4750                 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL)
   4751                 {
   4752                     // TOP QUAD
   4753                     // -----------------------------------------------------------
   4754                     // Texture coords                 Vertices
   4755                     rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Bottom-left corner for texture and quad
   4756                     rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Bottom-right corner for texture and quad
   4757                     rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
   4758                     rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
   4759                     if (drawCenter)
   4760                     {
   4761                         // MIDDLE QUAD
   4762                         // -----------------------------------------------------------
   4763                         // Texture coords                 Vertices
   4764                         rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Bottom-left corner for texture and quad
   4765                         rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Bottom-right corner for texture and quad
   4766                         rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Top-right corner for texture and quad
   4767                         rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Top-left corner for texture and quad
   4768                     }
   4769                     // BOTTOM QUAD
   4770                     // -----------------------------------------------------------
   4771                     // Texture coords                 Vertices
   4772                     rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
   4773                     rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
   4774                     rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Top-right corner for texture and quad
   4775                     rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Top-left corner for texture and quad
   4776                 }
   4777                 else if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL)
   4778                 {
   4779                     // LEFT QUAD
   4780                     // -----------------------------------------------------------
   4781                     // Texture coords                 Vertices
   4782                     rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
   4783                     rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-right corner for texture and quad
   4784                     rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-right corner for texture and quad
   4785                     rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
   4786                     if (drawCenter)
   4787                     {
   4788                         // CENTER QUAD
   4789                         // -----------------------------------------------------------
   4790                         // Texture coords                 Vertices
   4791                         rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-left corner for texture and quad
   4792                         rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-right corner for texture and quad
   4793                         rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-right corner for texture and quad
   4794                         rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-left corner for texture and quad
   4795                     }
   4796                     // RIGHT QUAD
   4797                     // -----------------------------------------------------------
   4798                     // Texture coords                 Vertices
   4799                     rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-left corner for texture and quad
   4800                     rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
   4801                     rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
   4802                     rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-left corner for texture and quad
   4803                 }
   4804             rlEnd();
   4805         rlPopMatrix();
   4806 
   4807         rlSetTexture(0);
   4808     }
   4809 }
   4810 
   4811 // Check if two colors are equal
   4812 bool ColorIsEqual(Color col1, Color col2)
   4813 {
   4814     bool result = false;
   4815 
   4816     if ((col1.r == col2.r) && (col1.g == col2.g) && (col1.b == col2.b) && (col1.a == col2.a)) result = true;
   4817 
   4818     return result;
   4819 }
   4820 
   4821 // Get color with alpha applied, alpha goes from 0.0f to 1.0f
   4822 Color Fade(Color color, float alpha)
   4823 {
   4824     Color result = color;
   4825 
   4826     if (alpha < 0.0f) alpha = 0.0f;
   4827     else if (alpha > 1.0f) alpha = 1.0f;
   4828 
   4829     result.a = (unsigned char)(255.0f*alpha);
   4830 
   4831     return result;
   4832 }
   4833 
   4834 // Get hexadecimal value for a Color
   4835 int ColorToInt(Color color)
   4836 {
   4837     int result = 0;
   4838 
   4839     result = (int)(((unsigned int)color.r << 24) |
   4840                    ((unsigned int)color.g << 16) |
   4841                    ((unsigned int)color.b << 8) |
   4842                     (unsigned int)color.a);
   4843 
   4844     return result;
   4845 }
   4846 
   4847 // Get color normalized as float [0..1]
   4848 Vector4 ColorNormalize(Color color)
   4849 {
   4850     Vector4 result;
   4851 
   4852     result.x = (float)color.r/255.0f;
   4853     result.y = (float)color.g/255.0f;
   4854     result.z = (float)color.b/255.0f;
   4855     result.w = (float)color.a/255.0f;
   4856 
   4857     return result;
   4858 }
   4859 
   4860 // Get color from normalized values [0..1]
   4861 Color ColorFromNormalized(Vector4 normalized)
   4862 {
   4863     Color result;
   4864 
   4865     result.r = (unsigned char)(normalized.x*255.0f);
   4866     result.g = (unsigned char)(normalized.y*255.0f);
   4867     result.b = (unsigned char)(normalized.z*255.0f);
   4868     result.a = (unsigned char)(normalized.w*255.0f);
   4869 
   4870     return result;
   4871 }
   4872 
   4873 // Get HSV values for a Color
   4874 // NOTE: Hue is returned as degrees [0..360]
   4875 Vector3 ColorToHSV(Color color)
   4876 {
   4877     Vector3 hsv = { 0 };
   4878     Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   4879     float min, max, delta;
   4880 
   4881     min = rgb.x < rgb.y? rgb.x : rgb.y;
   4882     min = min  < rgb.z? min  : rgb.z;
   4883 
   4884     max = rgb.x > rgb.y? rgb.x : rgb.y;
   4885     max = max  > rgb.z? max  : rgb.z;
   4886 
   4887     hsv.z = max;            // Value
   4888     delta = max - min;
   4889 
   4890     if (delta < 0.00001f)
   4891     {
   4892         hsv.y = 0.0f;
   4893         hsv.x = 0.0f;       // Undefined, maybe NAN?
   4894         return hsv;
   4895     }
   4896 
   4897     if (max > 0.0f)
   4898     {
   4899         // NOTE: If max is 0, this divide would cause a crash
   4900         hsv.y = (delta/max);    // Saturation
   4901     }
   4902     else
   4903     {
   4904         // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
   4905         hsv.y = 0.0f;
   4906         hsv.x = NAN;        // Undefined
   4907         return hsv;
   4908     }
   4909 
   4910     // NOTE: Comparing float values could not work properly
   4911     if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta;    // Between yellow & magenta
   4912     else
   4913     {
   4914         if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta;  // Between cyan & yellow
   4915         else hsv.x = 4.0f + (rgb.x - rgb.y)/delta;      // Between magenta & cyan
   4916     }
   4917 
   4918     hsv.x *= 60.0f;     // Convert to degrees
   4919 
   4920     if (hsv.x < 0.0f) hsv.x += 360.0f;
   4921 
   4922     return hsv;
   4923 }
   4924 
   4925 // Get a Color from HSV values
   4926 // Implementation reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSV_conversion
   4927 // NOTE: Color->HSV->Color conversion will not yield exactly the same color due to rounding errors
   4928 // Hue is provided in degrees: [0..360]
   4929 // Saturation/Value are provided normalized: [0.0f..1.0f]
   4930 Color ColorFromHSV(float hue, float saturation, float value)
   4931 {
   4932     Color color = { 0, 0, 0, 255 };
   4933 
   4934     // Red channel
   4935     float k = fmodf((5.0f + hue/60.0f), 6);
   4936     float t = 4.0f - k;
   4937     k = (t < k)? t : k;
   4938     k = (k < 1)? k : 1;
   4939     k = (k > 0)? k : 0;
   4940     color.r = (unsigned char)((value - value*saturation*k)*255.0f);
   4941 
   4942     // Green channel
   4943     k = fmodf((3.0f + hue/60.0f), 6);
   4944     t = 4.0f - k;
   4945     k = (t < k)? t : k;
   4946     k = (k < 1)? k : 1;
   4947     k = (k > 0)? k : 0;
   4948     color.g = (unsigned char)((value - value*saturation*k)*255.0f);
   4949 
   4950     // Blue channel
   4951     k = fmodf((1.0f + hue/60.0f), 6);
   4952     t = 4.0f - k;
   4953     k = (t < k)? t : k;
   4954     k = (k < 1)? k : 1;
   4955     k = (k > 0)? k : 0;
   4956     color.b = (unsigned char)((value - value*saturation*k)*255.0f);
   4957 
   4958     return color;
   4959 }
   4960 
   4961 // Get color multiplied with another color
   4962 Color ColorTint(Color color, Color tint)
   4963 {
   4964     Color result = color;
   4965 
   4966     unsigned char r = (unsigned char)(((int)color.r*(int)tint.r)/255);
   4967     unsigned char g = (unsigned char)(((int)color.g*(int)tint.g)/255);
   4968     unsigned char b = (unsigned char)(((int)color.b*(int)tint.b)/255);
   4969     unsigned char a = (unsigned char)(((int)color.a*(int)tint.a)/255);
   4970 
   4971     result.r = r;
   4972     result.g = g;
   4973     result.b = b;
   4974     result.a = a;
   4975 
   4976     return result;
   4977 }
   4978 
   4979 // Get color with brightness correction, brightness factor goes from -1.0f to 1.0f
   4980 Color ColorBrightness(Color color, float factor)
   4981 {
   4982     Color result = color;
   4983 
   4984     if (factor > 1.0f) factor = 1.0f;
   4985     else if (factor < -1.0f) factor = -1.0f;
   4986 
   4987     float red = (float)color.r;
   4988     float green = (float)color.g;
   4989     float blue = (float)color.b;
   4990 
   4991     if (factor < 0.0f)
   4992     {
   4993         factor = 1.0f + factor;
   4994         red *= factor;
   4995         green *= factor;
   4996         blue *= factor;
   4997     }
   4998     else
   4999     {
   5000         red = (255 - red)*factor + red;
   5001         green = (255 - green)*factor + green;
   5002         blue = (255 - blue)*factor + blue;
   5003     }
   5004 
   5005     result.r = (unsigned char)red;
   5006     result.g = (unsigned char)green;
   5007     result.b = (unsigned char)blue;
   5008 
   5009     return result;
   5010 }
   5011 
   5012 // Get color with contrast correction
   5013 // NOTE: Contrast values between -1.0f and 1.0f
   5014 Color ColorContrast(Color color, float contrast)
   5015 {
   5016     Color result = color;
   5017 
   5018     if (contrast < -1.0f) contrast = -1.0f;
   5019     else if (contrast > 1.0f) contrast = 1.0f;
   5020 
   5021     contrast = (1.0f + contrast);
   5022     contrast *= contrast;
   5023 
   5024     float pR = (float)color.r/255.0f;
   5025     pR -= 0.5f;
   5026     pR *= contrast;
   5027     pR += 0.5f;
   5028     pR *= 255;
   5029     if (pR < 0) pR = 0;
   5030     else if (pR > 255) pR = 255;
   5031 
   5032     float pG = (float)color.g/255.0f;
   5033     pG -= 0.5f;
   5034     pG *= contrast;
   5035     pG += 0.5f;
   5036     pG *= 255;
   5037     if (pG < 0) pG = 0;
   5038     else if (pG > 255) pG = 255;
   5039 
   5040     float pB = (float)color.b/255.0f;
   5041     pB -= 0.5f;
   5042     pB *= contrast;
   5043     pB += 0.5f;
   5044     pB *= 255;
   5045     if (pB < 0) pB = 0;
   5046     else if (pB > 255) pB = 255;
   5047 
   5048     result.r = (unsigned char)pR;
   5049     result.g = (unsigned char)pG;
   5050     result.b = (unsigned char)pB;
   5051 
   5052     return result;
   5053 }
   5054 
   5055 // Get color with alpha applied, alpha goes from 0.0f to 1.0f
   5056 Color ColorAlpha(Color color, float alpha)
   5057 {
   5058     Color result = color;
   5059 
   5060     if (alpha < 0.0f) alpha = 0.0f;
   5061     else if (alpha > 1.0f) alpha = 1.0f;
   5062 
   5063     result.a = (unsigned char)(255.0f*alpha);
   5064 
   5065     return result;
   5066 }
   5067 
   5068 // Get src alpha-blended into dst color with tint
   5069 Color ColorAlphaBlend(Color dst, Color src, Color tint)
   5070 {
   5071     Color out = WHITE;
   5072 
   5073     // Apply color tint to source color
   5074     src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8);
   5075     src.g = (unsigned char)(((unsigned int)src.g*((unsigned int)tint.g+1)) >> 8);
   5076     src.b = (unsigned char)(((unsigned int)src.b*((unsigned int)tint.b+1)) >> 8);
   5077     src.a = (unsigned char)(((unsigned int)src.a*((unsigned int)tint.a+1)) >> 8);
   5078 
   5079 //#define COLORALPHABLEND_FLOAT
   5080 #define COLORALPHABLEND_INTEGERS
   5081 #if defined(COLORALPHABLEND_INTEGERS)
   5082     if (src.a == 0) out = dst;
   5083     else if (src.a == 255) out = src;
   5084     else
   5085     {
   5086         unsigned int alpha = (unsigned int)src.a + 1;     // We are shifting by 8 (dividing by 256), so we need to take that excess into account
   5087         out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8);
   5088 
   5089         if (out.a > 0)
   5090         {
   5091             out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
   5092             out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
   5093             out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
   5094         }
   5095     }
   5096 #endif
   5097 #if defined(COLORALPHABLEND_FLOAT)
   5098     if (src.a == 0) out = dst;
   5099     else if (src.a == 255) out = src;
   5100     else
   5101     {
   5102         Vector4 fdst = ColorNormalize(dst);
   5103         Vector4 fsrc = ColorNormalize(src);
   5104         Vector4 ftint = ColorNormalize(tint);
   5105         Vector4 fout = { 0 };
   5106 
   5107         fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
   5108 
   5109         if (fout.w > 0.0f)
   5110         {
   5111             fout.x = (fsrc.x*fsrc.w + fdst.x*fdst.w*(1 - fsrc.w))/fout.w;
   5112             fout.y = (fsrc.y*fsrc.w + fdst.y*fdst.w*(1 - fsrc.w))/fout.w;
   5113             fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
   5114         }
   5115 
   5116         out = (Color){ (unsigned char)(fout.x*255.0f), (unsigned char)(fout.y*255.0f), (unsigned char)(fout.z*255.0f), (unsigned char)(fout.w*255.0f) };
   5117     }
   5118 #endif
   5119 
   5120     return out;
   5121 }
   5122 
   5123 // Get color lerp interpolation between two colors, factor [0.0f..1.0f]
   5124 Color ColorLerp(Color color1, Color color2, float factor)
   5125 {
   5126     Color color = { 0 };
   5127 
   5128     if (factor < 0.0f) factor = 0.0f;
   5129     else if (factor > 1.0f) factor = 1.0f;
   5130 
   5131     color.r = (unsigned char)((1.0f - factor)*color1.r + factor*color2.r);
   5132     color.g = (unsigned char)((1.0f - factor)*color1.g + factor*color2.g);
   5133     color.b = (unsigned char)((1.0f - factor)*color1.b + factor*color2.b);
   5134     color.a = (unsigned char)((1.0f - factor)*color1.a + factor*color2.a);
   5135 
   5136     return color;
   5137 }
   5138 
   5139 // Get a Color struct from hexadecimal value
   5140 Color GetColor(unsigned int hexValue)
   5141 {
   5142     Color color;
   5143 
   5144     color.r = (unsigned char)(hexValue >> 24) & 0xFF;
   5145     color.g = (unsigned char)(hexValue >> 16) & 0xFF;
   5146     color.b = (unsigned char)(hexValue >> 8) & 0xFF;
   5147     color.a = (unsigned char)hexValue & 0xFF;
   5148 
   5149     return color;
   5150 }
   5151 
   5152 // Get color from a pixel from certain format
   5153 Color GetPixelColor(void *srcPtr, int format)
   5154 {
   5155     Color color = { 0 };
   5156 
   5157     switch (format)
   5158     {
   5159         case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], 255 }; break;
   5160         case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1] }; break;
   5161         case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   5162         {
   5163             color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
   5164             color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 5) & 0b0000000000111111)*255/63);
   5165             color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
   5166             color.a = 255;
   5167 
   5168         } break;
   5169         case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   5170         {
   5171             color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
   5172             color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 6) & 0b0000000000011111)*255/31);
   5173             color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
   5174             color.a = (((unsigned short *)srcPtr)[0] & 0b0000000000000001)? 255 : 0;
   5175 
   5176         } break;
   5177         case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   5178         {
   5179             color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 12)*255/15);
   5180             color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 8) & 0b0000000000001111)*255/15);
   5181             color.b = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 4) & 0b0000000000001111)*255/15);
   5182             color.a = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000001111)*255/15);
   5183 
   5184         } break;
   5185         case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], ((unsigned char *)srcPtr)[3] }; break;
   5186         case PIXELFORMAT_UNCOMPRESSED_R8G8B8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], 255 }; break;
   5187         case PIXELFORMAT_UNCOMPRESSED_R32:
   5188         {
   5189             // NOTE: Pixel normalized float value is converted to [0..255]
   5190             color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
   5191             color.g = (unsigned char)(((float *)srcPtr)[0]*255.0f);
   5192             color.b = (unsigned char)(((float *)srcPtr)[0]*255.0f);
   5193             color.a = 255;
   5194 
   5195         } break;
   5196         case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   5197         {
   5198             // NOTE: Pixel normalized float value is converted to [0..255]
   5199             color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
   5200             color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
   5201             color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
   5202             color.a = 255;
   5203 
   5204         } break;
   5205         case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   5206         {
   5207             // NOTE: Pixel normalized float value is converted to [0..255]
   5208             color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
   5209             color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
   5210             color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
   5211             color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f);
   5212 
   5213         } break;
   5214         case PIXELFORMAT_UNCOMPRESSED_R16:
   5215         {
   5216             // NOTE: Pixel normalized float value is converted to [0..255]
   5217             color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f);
   5218             color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f);
   5219             color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f);
   5220             color.a = 255;
   5221 
   5222         } break;
   5223         case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   5224         {
   5225             // NOTE: Pixel normalized float value is converted to [0..255]
   5226             color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f);
   5227             color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f);
   5228             color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f);
   5229             color.a = 255;
   5230 
   5231         } break;
   5232         case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   5233         {
   5234             // NOTE: Pixel normalized float value is converted to [0..255]
   5235             color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f);
   5236             color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f);
   5237             color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f);
   5238             color.a = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[3])*255.0f);
   5239 
   5240         } break;
   5241         default: break;
   5242     }
   5243 
   5244     return color;
   5245 }
   5246 
   5247 // Set pixel color formatted into destination pointer
   5248 void SetPixelColor(void *dstPtr, Color color, int format)
   5249 {
   5250     switch (format)
   5251     {
   5252         case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   5253         {
   5254             // NOTE: Calculate grayscale equivalent color
   5255             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   5256             unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
   5257 
   5258             ((unsigned char *)dstPtr)[0] = gray;
   5259 
   5260         } break;
   5261         case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   5262         {
   5263             // NOTE: Calculate grayscale equivalent color
   5264             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   5265             unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
   5266 
   5267             ((unsigned char *)dstPtr)[0] = gray;
   5268             ((unsigned char *)dstPtr)[1] = color.a;
   5269 
   5270         } break;
   5271         case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   5272         {
   5273             // NOTE: Calculate R5G6B5 equivalent color
   5274             Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
   5275 
   5276             unsigned char r = (unsigned char)(round(coln.x*31.0f));
   5277             unsigned char g = (unsigned char)(round(coln.y*63.0f));
   5278             unsigned char b = (unsigned char)(round(coln.z*31.0f));
   5279 
   5280             ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
   5281 
   5282         } break;
   5283         case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   5284         {
   5285             // NOTE: Calculate R5G5B5A1 equivalent color
   5286             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   5287 
   5288             unsigned char r = (unsigned char)(round(coln.x*31.0f));
   5289             unsigned char g = (unsigned char)(round(coln.y*31.0f));
   5290             unsigned char b = (unsigned char)(round(coln.z*31.0f));
   5291             unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
   5292 
   5293             ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
   5294 
   5295         } break;
   5296         case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   5297         {
   5298             // NOTE: Calculate R5G5B5A1 equivalent color
   5299             Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
   5300 
   5301             unsigned char r = (unsigned char)(round(coln.x*15.0f));
   5302             unsigned char g = (unsigned char)(round(coln.y*15.0f));
   5303             unsigned char b = (unsigned char)(round(coln.z*15.0f));
   5304             unsigned char a = (unsigned char)(round(coln.w*15.0f));
   5305 
   5306             ((unsigned short *)dstPtr)[0] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
   5307 
   5308         } break;
   5309         case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   5310         {
   5311             ((unsigned char *)dstPtr)[0] = color.r;
   5312             ((unsigned char *)dstPtr)[1] = color.g;
   5313             ((unsigned char *)dstPtr)[2] = color.b;
   5314 
   5315         } break;
   5316         case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   5317         {
   5318             ((unsigned char *)dstPtr)[0] = color.r;
   5319             ((unsigned char *)dstPtr)[1] = color.g;
   5320             ((unsigned char *)dstPtr)[2] = color.b;
   5321             ((unsigned char *)dstPtr)[3] = color.a;
   5322 
   5323         } break;
   5324         default: break;
   5325     }
   5326 }
   5327 
   5328 // Get pixel data size in bytes for certain format
   5329 // NOTE: Size can be requested for Image or Texture data
   5330 int GetPixelDataSize(int width, int height, int format)
   5331 {
   5332     int dataSize = 0;       // Size in bytes
   5333     int bpp = 0;            // Bits per pixel
   5334 
   5335     switch (format)
   5336     {
   5337         case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
   5338         case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   5339         case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   5340         case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   5341         case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
   5342         case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
   5343         case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
   5344         case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
   5345         case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
   5346         case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
   5347         case PIXELFORMAT_UNCOMPRESSED_R16: bpp = 16; break;
   5348         case PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break;
   5349         case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break;
   5350         case PIXELFORMAT_COMPRESSED_DXT1_RGB:
   5351         case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
   5352         case PIXELFORMAT_COMPRESSED_ETC1_RGB:
   5353         case PIXELFORMAT_COMPRESSED_ETC2_RGB:
   5354         case PIXELFORMAT_COMPRESSED_PVRT_RGB:
   5355         case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
   5356         case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
   5357         case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
   5358         case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
   5359         case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
   5360         case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
   5361         default: break;
   5362     }
   5363 
   5364     double bytesPerPixel = (double)bpp/8.0;
   5365     dataSize = (int)(bytesPerPixel*width*height); // Total data size in bytes
   5366 
   5367     // Most compressed formats works on 4x4 blocks,
   5368     // if texture is smaller, minimum dataSize is 8 or 16
   5369     if ((width < 4) && (height < 4))
   5370     {
   5371         if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
   5372         else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
   5373     }
   5374 
   5375     return dataSize;
   5376 }
   5377 
   5378 //----------------------------------------------------------------------------------
   5379 // Module specific Functions Definition
   5380 //----------------------------------------------------------------------------------
   5381 // Convert half-float (stored as unsigned short) to float
   5382 // REF: https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308
   5383 static float HalfToFloat(unsigned short x)
   5384 {
   5385     float result = 0.0f;
   5386 
   5387     const unsigned int e = (x & 0x7C00) >> 10; // Exponent
   5388     const unsigned int m = (x & 0x03FF) << 13; // Mantissa
   5389     const float fm = (float)m;
   5390     const unsigned int v = (*(unsigned int*)&fm) >> 23; // Evil log2 bit hack to count leading zeros in denormalized format
   5391     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
   5392 
   5393     result = *(float *)&r;
   5394 
   5395     return result;
   5396 }
   5397 
   5398 // Convert float to half-float (stored as unsigned short)
   5399 static unsigned short FloatToHalf(float x)
   5400 {
   5401     unsigned short result = 0;
   5402 
   5403     const unsigned int b = (*(unsigned int*) & x) + 0x00001000; // Round-to-nearest-even: add last bit after truncated mantissa
   5404     const unsigned int e = (b & 0x7F800000) >> 23; // Exponent
   5405     const unsigned int m = b & 0x007FFFFF; // Mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
   5406 
   5407     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
   5408 
   5409     return result;
   5410 }
   5411 
   5412 // Get pixel data from image as Vector4 array (float normalized)
   5413 static Vector4 *LoadImageDataNormalized(Image image)
   5414 {
   5415     Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4));
   5416 
   5417     if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
   5418     else
   5419     {
   5420         for (int i = 0, k = 0; i < image.width*image.height; i++)
   5421         {
   5422             switch (image.format)
   5423             {
   5424                 case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
   5425                 {
   5426                     pixels[i].x = (float)((unsigned char *)image.data)[i]/255.0f;
   5427                     pixels[i].y = (float)((unsigned char *)image.data)[i]/255.0f;
   5428                     pixels[i].z = (float)((unsigned char *)image.data)[i]/255.0f;
   5429                     pixels[i].w = 1.0f;
   5430 
   5431                 } break;
   5432                 case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   5433                 {
   5434                     pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
   5435                     pixels[i].y = (float)((unsigned char *)image.data)[k]/255.0f;
   5436                     pixels[i].z = (float)((unsigned char *)image.data)[k]/255.0f;
   5437                     pixels[i].w = (float)((unsigned char *)image.data)[k + 1]/255.0f;
   5438 
   5439                     k += 2;
   5440                 } break;
   5441                 case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   5442                 {
   5443                     unsigned short pixel = ((unsigned short *)image.data)[i];
   5444 
   5445                     pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
   5446                     pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
   5447                     pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
   5448                     pixels[i].w = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
   5449 
   5450                 } break;
   5451                 case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   5452                 {
   5453                     unsigned short pixel = ((unsigned short *)image.data)[i];
   5454 
   5455                     pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
   5456                     pixels[i].y = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
   5457                     pixels[i].z = (float)(pixel & 0b0000000000011111)*(1.0f/31);
   5458                     pixels[i].w = 1.0f;
   5459 
   5460                 } break;
   5461                 case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
   5462                 {
   5463                     unsigned short pixel = ((unsigned short *)image.data)[i];
   5464 
   5465                     pixels[i].x = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
   5466                     pixels[i].y = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
   5467                     pixels[i].z = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
   5468                     pixels[i].w = (float)(pixel & 0b0000000000001111)*(1.0f/15);
   5469 
   5470                 } break;
   5471                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
   5472                 {
   5473                     pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
   5474                     pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
   5475                     pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
   5476                     pixels[i].w = (float)((unsigned char *)image.data)[k + 3]/255.0f;
   5477 
   5478                     k += 4;
   5479                 } break;
   5480                 case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
   5481                 {
   5482                     pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
   5483                     pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
   5484                     pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
   5485                     pixels[i].w = 1.0f;
   5486 
   5487                     k += 3;
   5488                 } break;
   5489                 case PIXELFORMAT_UNCOMPRESSED_R32:
   5490                 {
   5491                     pixels[i].x = ((float *)image.data)[k];
   5492                     pixels[i].y = 0.0f;
   5493                     pixels[i].z = 0.0f;
   5494                     pixels[i].w = 1.0f;
   5495 
   5496                 } break;
   5497                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
   5498                 {
   5499                     pixels[i].x = ((float *)image.data)[k];
   5500                     pixels[i].y = ((float *)image.data)[k + 1];
   5501                     pixels[i].z = ((float *)image.data)[k + 2];
   5502                     pixels[i].w = 1.0f;
   5503 
   5504                     k += 3;
   5505                 } break;
   5506                 case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
   5507                 {
   5508                     pixels[i].x = ((float *)image.data)[k];
   5509                     pixels[i].y = ((float *)image.data)[k + 1];
   5510                     pixels[i].z = ((float *)image.data)[k + 2];
   5511                     pixels[i].w = ((float *)image.data)[k + 3];
   5512 
   5513                     k += 4;
   5514                 } break;
   5515                 case PIXELFORMAT_UNCOMPRESSED_R16:
   5516                 {
   5517                     pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]);
   5518                     pixels[i].y = 0.0f;
   5519                     pixels[i].z = 0.0f;
   5520                     pixels[i].w = 1.0f;
   5521                 } break;
   5522                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16:
   5523                 {
   5524                     pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]);
   5525                     pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]);
   5526                     pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]);
   5527                     pixels[i].w = 1.0f;
   5528 
   5529                     k += 3;
   5530                 } break;
   5531                 case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16:
   5532                 {
   5533                     pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]);
   5534                     pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]);
   5535                     pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]);
   5536                     pixels[i].w = HalfToFloat(((unsigned short *)image.data)[k + 3]);
   5537 
   5538                     k += 4;
   5539                 } break;
   5540                 default: break;
   5541             }
   5542         }
   5543     }
   5544 
   5545     return pixels;
   5546 }
   5547 
   5548 #endif      // SUPPORT_MODULE_RTEXTURES