github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/rtextures.c (about)

     1  /**********************************************************************************************
     2  *
     3  *   rtextures - Basic functions to load and draw textures
     4  *
     5  *   CONFIGURATION:
     6  *
     7  *   #define SUPPORT_MODULE_RTEXTURES
     8  *       rtextures module is included in the build
     9  *
    10  *   #define SUPPORT_FILEFORMAT_BMP
    11  *   #define SUPPORT_FILEFORMAT_PNG
    12  *   #define SUPPORT_FILEFORMAT_TGA
    13  *   #define SUPPORT_FILEFORMAT_JPG
    14  *   #define SUPPORT_FILEFORMAT_GIF
    15  *   #define SUPPORT_FILEFORMAT_QOI
    16  *   #define SUPPORT_FILEFORMAT_PSD
    17  *   #define SUPPORT_FILEFORMAT_PIC
    18  *   #define SUPPORT_FILEFORMAT_HDR
    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 three image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageToPOT()
    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-2022 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 OpenGL 1.1, 3.3 or ES2
    75  
    76  #include <stdlib.h>             // Required for: malloc(), free()
    77  #include <string.h>             // Required for: strlen() [Used in ImageTextEx()], strcmp() [Used in LoadImageFromMemory()]
    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  
   107  #if defined(SUPPORT_FILEFORMAT_DDS)
   108      #define RL_GPUTEX_SUPPORT_DDS
   109  #endif
   110  #if defined(SUPPORT_FILEFORMAT_PKM)
   111      #define RL_GPUTEX_SUPPORT_PKM
   112  #endif
   113  #if defined(SUPPORT_FILEFORMAT_KTX)
   114      #define RL_GPUTEX_SUPPORT_KTX
   115  #endif
   116  #if defined(SUPPORT_FILEFORMAT_PVR)
   117      #define RL_GPUTEX_SUPPORT_PVR
   118  #endif
   119  #if defined(SUPPORT_FILEFORMAT_ASTC)
   120      #define RL_GPUTEX_SUPPORT_ASTC
   121  #endif
   122  
   123  // Image fileformats not supported by default
   124  #define STBI_NO_PIC
   125  #define STBI_NO_PNM             // Image format .ppm and .pgm
   126  
   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_PIC) || \
   138       defined(SUPPORT_FILEFORMAT_HDR))
   139  
   140      #define STBI_MALLOC RL_MALLOC
   141      #define STBI_FREE RL_FREE
   142      #define STBI_REALLOC RL_REALLOC
   143  
   144      #define STB_IMAGE_IMPLEMENTATION
   145      #include "external/stb_image.h"         // Required for: stbi_load_from_file()
   146                                              // NOTE: Used to read image data (multiple formats support)
   147  #endif
   148  
   149  #if (defined(SUPPORT_FILEFORMAT_DDS) || \
   150       defined(SUPPORT_FILEFORMAT_PKM) || \
   151       defined(SUPPORT_FILEFORMAT_KTX) || \
   152       defined(SUPPORT_FILEFORMAT_PVR) || \
   153       defined(SUPPORT_FILEFORMAT_ASTC))
   154  
   155      #define RL_GPUTEX_IMPLEMENTATION
   156      #include "external/rl_gputex.h"         // Required for: rl_load_xxx_from_memory()
   157                                              // NOTE: Used to read compressed textures data (multiple formats support)
   158  #endif
   159  
   160  #if defined(SUPPORT_FILEFORMAT_QOI)
   161      #define QOI_MALLOC RL_MALLOC
   162      #define QOI_FREE RL_FREE
   163  
   164      #define QOI_IMPLEMENTATION
   165      #include "external/qoi.h"
   166  #endif
   167  
   168  #if defined(SUPPORT_IMAGE_EXPORT)
   169      #define STBIW_MALLOC RL_MALLOC
   170      #define STBIW_FREE RL_FREE
   171      #define STBIW_REALLOC RL_REALLOC
   172  
   173      #define STB_IMAGE_WRITE_IMPLEMENTATION
   174      #include "external/stb_image_write.h"   // Required for: stbi_write_*()
   175  #endif
   176  
   177  #if defined(SUPPORT_IMAGE_GENERATION)
   178      #define STB_PERLIN_IMPLEMENTATION
   179      #include "external/stb_perlin.h"        // Required for: stb_perlin_fbm_noise3
   180  #endif
   181  
   182  #if defined(SUPPORT_IMAGE_MANIPULATION)
   183      #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size))
   184      #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr))
   185  
   186      #define STB_IMAGE_RESIZE_IMPLEMENTATION
   187      #include "external/stb_image_resize.h"  // Required for: stbir_resize_uint8() [ImageResize()]
   188  #endif
   189  
   190  //----------------------------------------------------------------------------------
   191  // Defines and Macros
   192  //----------------------------------------------------------------------------------
   193  #ifndef PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD
   194      #define PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD  50    // Threshold over 255 to set alpha as 0
   195  #endif
   196  
   197  #ifndef GAUSSIAN_BLUR_ITERATIONS
   198      #define GAUSSIAN_BLUR_ITERATIONS  4    // Number of box blur iterations to approximate gaussian blur
   199  #endif
   200  
   201  //----------------------------------------------------------------------------------
   202  // Types and Structures Definition
   203  //----------------------------------------------------------------------------------
   204  // ...
   205  
   206  //----------------------------------------------------------------------------------
   207  // Global Variables Definition
   208  //----------------------------------------------------------------------------------
   209  // It's lonely here...
   210  
   211  //----------------------------------------------------------------------------------
   212  // Other Modules Functions Declaration (required by text)
   213  //----------------------------------------------------------------------------------
   214  // ...
   215  
   216  //----------------------------------------------------------------------------------
   217  // Module specific Functions Declaration
   218  //----------------------------------------------------------------------------------
   219  static Vector4 *LoadImageDataNormalized(Image image);       // Load pixel data from image as Vector4 array (float normalized)
   220  
   221  //----------------------------------------------------------------------------------
   222  // Module Functions Definition
   223  //----------------------------------------------------------------------------------
   224  
   225  // Load image from file into CPU memory (RAM)
   226  Image LoadImage(const char *fileName)
   227  {
   228      Image image = { 0 };
   229  
   230  #if defined(SUPPORT_FILEFORMAT_PNG) || \
   231      defined(SUPPORT_FILEFORMAT_BMP) || \
   232      defined(SUPPORT_FILEFORMAT_TGA) || \
   233      defined(SUPPORT_FILEFORMAT_JPG) || \
   234      defined(SUPPORT_FILEFORMAT_GIF) || \
   235      defined(SUPPORT_FILEFORMAT_PIC) || \
   236      defined(SUPPORT_FILEFORMAT_HDR) || \
   237      defined(SUPPORT_FILEFORMAT_PSD)
   238  
   239      #define STBI_REQUIRED
   240  #endif
   241  
   242      // Loading file to memory
   243      unsigned int fileSize = 0;
   244      unsigned char *fileData = LoadFileData(fileName, &fileSize);
   245  
   246      // Loading image from memory data
   247      if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, fileSize);
   248  
   249      RL_FREE(fileData);
   250  
   251      return image;
   252  }
   253  
   254  // Load an image from RAW file data
   255  Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize)
   256  {
   257      Image image = { 0 };
   258  
   259      unsigned int dataSize = 0;
   260      unsigned char *fileData = LoadFileData(fileName, &dataSize);
   261  
   262      if (fileData != NULL)
   263      {
   264          unsigned char *dataPtr = fileData;
   265          unsigned int size = GetPixelDataSize(width, height, format);
   266  
   267          if (headerSize > 0) dataPtr += headerSize;
   268  
   269          image.data = RL_MALLOC(size);      // Allocate required memory in bytes
   270          memcpy(image.data, dataPtr, size); // Copy required data to image
   271          image.width = width;
   272          image.height = height;
   273          image.mipmaps = 1;
   274          image.format = format;
   275  
   276          RL_FREE(fileData);
   277      }
   278  
   279      return image;
   280  }
   281  
   282  // Load animated image data
   283  //  - Image.data buffer includes all frames: [image#0][image#1][image#2][...]
   284  //  - Number of frames is returned through 'frames' parameter
   285  //  - All frames are returned in RGBA format
   286  //  - Frames delay data is discarded
   287  Image LoadImageAnim(const char *fileName, int *frames)
   288  {
   289      Image image = { 0 };
   290      int frameCount = 1;
   291  
   292  #if defined(SUPPORT_FILEFORMAT_GIF)
   293      if (IsFileExtension(fileName, ".gif"))
   294      {
   295          unsigned int dataSize = 0;
   296          unsigned char *fileData = LoadFileData(fileName, &dataSize);
   297  
   298          if (fileData != NULL)
   299          {
   300              int comp = 0;
   301              int *delays = NULL;
   302              image.data = stbi_load_gif_from_memory(fileData, dataSize, &delays, &image.width, &image.height, &frameCount, &comp, 4);
   303  
   304              image.mipmaps = 1;
   305              image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   306  
   307              RL_FREE(fileData);
   308              RL_FREE(delays);        // NOTE: Frames delays are discarded
   309          }
   310      }
   311  #else
   312      if (false) { }
   313  #endif
   314      else image = LoadImage(fileName);
   315  
   316      // TODO: Support APNG animated images
   317  
   318      *frames = frameCount;
   319      return image;
   320  }
   321  
   322  // Load image from memory buffer, fileType refers to extension: i.e. ".png"
   323  // WARNING: File extension must be provided in lower-case
   324  Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize)
   325  {
   326      Image image = { 0 };
   327  
   328      if ((false)
   329  #if defined(SUPPORT_FILEFORMAT_PNG)
   330          || (strcmp(fileType, ".png") == 0)
   331  #endif
   332  #if defined(SUPPORT_FILEFORMAT_BMP)
   333          || (strcmp(fileType, ".bmp") == 0)
   334  #endif
   335  #if defined(SUPPORT_FILEFORMAT_TGA)
   336          || (strcmp(fileType, ".tga") == 0)
   337  #endif
   338  #if defined(SUPPORT_FILEFORMAT_JPG)
   339          || ((strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0))
   340  #endif
   341  #if defined(SUPPORT_FILEFORMAT_GIF)
   342          || (strcmp(fileType, ".gif") == 0)
   343  #endif
   344  #if defined(SUPPORT_FILEFORMAT_PIC)
   345          || (strcmp(fileType, ".pic") == 0)
   346  #endif
   347  #if defined(SUPPORT_FILEFORMAT_PSD)
   348          || (strcmp(fileType, ".psd") == 0)
   349  #endif
   350          )
   351      {
   352  #if defined(STBI_REQUIRED)
   353          // NOTE: Using stb_image to load images (Supports multiple image formats)
   354  
   355          if (fileData != NULL)
   356          {
   357              int comp = 0;
   358              image.data = stbi_load_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
   359  
   360              if (image.data != NULL)
   361              {
   362                  image.mipmaps = 1;
   363  
   364                  if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
   365                  else if (comp == 2) image.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
   366                  else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
   367                  else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   368              }
   369          }
   370  #endif
   371      }
   372  #if defined(SUPPORT_FILEFORMAT_HDR)
   373      else if (strcmp(fileType, ".hdr") == 0)
   374      {
   375  #if defined(STBI_REQUIRED)
   376          if (fileData != NULL)
   377          {
   378              int comp = 0;
   379              image.data = stbi_loadf_from_memory(fileData, dataSize, &image.width, &image.height, &comp, 0);
   380  
   381              image.mipmaps = 1;
   382  
   383              if (comp == 1) image.format = PIXELFORMAT_UNCOMPRESSED_R32;
   384              else if (comp == 3) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32;
   385              else if (comp == 4) image.format = PIXELFORMAT_UNCOMPRESSED_R32G32B32A32;
   386              else
   387              {
   388                  TRACELOG(LOG_WARNING, "IMAGE: HDR file format not supported");
   389                  UnloadImage(image);
   390              }
   391          }
   392  #endif
   393      }
   394  #endif
   395  #if defined(SUPPORT_FILEFORMAT_QOI)
   396      else if (strcmp(fileType, ".qoi") == 0)
   397      {
   398          qoi_desc desc = { 0 };
   399          image.data = qoi_decode(fileData, dataSize, &desc, 4);
   400          image.width = desc.width;
   401          image.height = desc.height;
   402          image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   403          image.mipmaps = 1;
   404      }
   405  #endif
   406  #if defined(SUPPORT_FILEFORMAT_DDS)
   407      else if (strcmp(fileType, ".dds") == 0)
   408      {
   409          image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
   410      }
   411  #endif
   412  #if defined(SUPPORT_FILEFORMAT_PKM)
   413      else if (strcmp(fileType, ".pkm") == 0)
   414      {
   415          image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
   416      }
   417  #endif
   418  #if defined(SUPPORT_FILEFORMAT_KTX)
   419      else if (strcmp(fileType, ".ktx") == 0)
   420      {
   421          image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
   422      }
   423  #endif
   424  #if defined(SUPPORT_FILEFORMAT_PVR)
   425      else if (strcmp(fileType, ".pvr") == 0)
   426      {
   427          image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
   428      }
   429  #endif
   430  #if defined(SUPPORT_FILEFORMAT_ASTC)
   431      else if (strcmp(fileType, ".astc") == 0)
   432      {
   433          image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps);
   434      }
   435  #endif
   436      else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported");
   437  
   438      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);
   439      else TRACELOG(LOG_WARNING, "IMAGE: Failed to load image data");
   440  
   441      return image;
   442  }
   443  
   444  // Load image from GPU texture data
   445  // NOTE: Compressed texture formats not supported
   446  Image LoadImageFromTexture(Texture2D texture)
   447  {
   448      Image image = { 0 };
   449  
   450      if (texture.format < PIXELFORMAT_COMPRESSED_DXT1_RGB)
   451      {
   452          image.data = rlReadTexturePixels(texture.id, texture.width, texture.height, texture.format);
   453  
   454          if (image.data != NULL)
   455          {
   456              image.width = texture.width;
   457              image.height = texture.height;
   458              image.format = texture.format;
   459              image.mipmaps = 1;
   460  
   461  #if defined(GRAPHICS_API_OPENGL_ES2)
   462              // NOTE: Data retrieved on OpenGL ES 2.0 should be RGBA,
   463              // coming from FBO color buffer attachment, but it seems
   464              // original texture format is retrieved on RPI...
   465              image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   466  #endif
   467              TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Pixel data retrieved successfully", texture.id);
   468          }
   469          else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve pixel data", texture.id);
   470      }
   471      else TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] Failed to retrieve compressed pixel data", texture.id);
   472  
   473      return image;
   474  }
   475  
   476  // Load image from screen buffer and (screenshot)
   477  Image LoadImageFromScreen(void)
   478  {
   479      Image image = { 0 };
   480  
   481      image.width = GetScreenWidth();
   482      image.height = GetScreenHeight();
   483      image.mipmaps = 1;
   484      image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   485      image.data = rlReadScreenPixels(image.width, image.height);
   486  
   487      return image;
   488  }
   489  
   490  // Unload image from CPU memory (RAM)
   491  void UnloadImage(Image image)
   492  {
   493      RL_FREE(image.data);
   494  }
   495  
   496  // Export image data to file
   497  // NOTE: File format depends on fileName extension
   498  bool ExportImage(Image image, const char *fileName)
   499  {
   500      int success = 0;
   501  
   502  #if defined(SUPPORT_IMAGE_EXPORT)
   503      int channels = 4;
   504      bool allocatedData = false;
   505      unsigned char *imgData = (unsigned char *)image.data;
   506  
   507      if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1;
   508      else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2;
   509      else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
   510      else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
   511      else
   512      {
   513          // NOTE: Getting Color array as RGBA unsigned char values
   514          imgData = (unsigned char *)LoadImageColors(image);
   515          allocatedData = true;
   516      }
   517  
   518  #if defined(SUPPORT_FILEFORMAT_PNG)
   519      if (IsFileExtension(fileName, ".png"))
   520      {
   521          int dataSize = 0;
   522          unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize);
   523          success = SaveFileData(fileName, fileData, dataSize);
   524          RL_FREE(fileData);
   525      }
   526  #else
   527      if (false) { }
   528  #endif
   529  #if defined(SUPPORT_FILEFORMAT_BMP)
   530      else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData);
   531  #endif
   532  #if defined(SUPPORT_FILEFORMAT_TGA)
   533      else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData);
   534  #endif
   535  #if defined(SUPPORT_FILEFORMAT_JPG)
   536      else if (IsFileExtension(fileName, ".jpg") ||
   537               IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90);  // JPG quality: between 1 and 100
   538  #endif
   539  #if defined(SUPPORT_FILEFORMAT_QOI)
   540      else if (IsFileExtension(fileName, ".qoi"))
   541      {
   542          channels = 0;
   543          if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3;
   544          else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4;
   545          else TRACELOG(LOG_WARNING, "IMAGE: Image pixel format must be R8G8B8 or R8G8B8A8");
   546  
   547          if ((channels == 3) || (channels == 4))
   548          {
   549              qoi_desc desc = { 0 };
   550              desc.width = image.width;
   551              desc.height = image.height;
   552              desc.channels = channels;
   553              desc.colorspace = QOI_SRGB;
   554  
   555              success = qoi_write(fileName, imgData, &desc);
   556          }
   557      }
   558  #endif
   559  #if defined(SUPPORT_FILEFORMAT_KTX)
   560      else if (IsFileExtension(fileName, ".ktx"))
   561      {
   562          success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps);
   563      }
   564  #endif
   565      else if (IsFileExtension(fileName, ".raw"))
   566      {
   567          // Export raw pixel data (without header)
   568          // NOTE: It's up to the user to track image parameters
   569          success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format));
   570      }
   571  
   572      if (allocatedData) RL_FREE(imgData);
   573  #endif      // SUPPORT_IMAGE_EXPORT
   574  
   575      if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName);
   576      else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName);
   577  
   578      return success;
   579  }
   580  
   581  // Export image as code file (.h) defining an array of bytes
   582  bool ExportImageAsCode(Image image, const char *fileName)
   583  {
   584      bool success = false;
   585  
   586  #if defined(SUPPORT_IMAGE_EXPORT)
   587  
   588  #ifndef TEXT_BYTES_PER_LINE
   589      #define TEXT_BYTES_PER_LINE     20
   590  #endif
   591  
   592      int dataSize = GetPixelDataSize(image.width, image.height, image.format);
   593  
   594      // NOTE: Text data buffer size is estimated considering image data size in bytes
   595      // and requiring 6 char bytes for every byte: "0x00, "
   596      char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
   597  
   598      int byteCount = 0;
   599      byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
   600      byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
   601      byteCount += sprintf(txtData + byteCount, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes         //\n");
   602      byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
   603      byteCount += sprintf(txtData + byteCount, "// more info and bugs-report:  github.com/raysan5/raylib                              //\n");
   604      byteCount += sprintf(txtData + byteCount, "// feedback and support:       ray[at]raylib.com                                      //\n");
   605      byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
   606      byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2018-2022 Ramon Santamaria (@raysan5)                                //\n");
   607      byteCount += sprintf(txtData + byteCount, "//                                                                                    //\n");
   608      byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
   609  
   610      // Get file name from path and convert variable name to uppercase
   611      char varFileName[256] = { 0 };
   612      strcpy(varFileName, GetFileNameWithoutExt(fileName));
   613      for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
   614  
   615      // Add image information
   616      byteCount += sprintf(txtData + byteCount, "// Image data information\n");
   617      byteCount += sprintf(txtData + byteCount, "#define %s_WIDTH    %i\n", varFileName, image.width);
   618      byteCount += sprintf(txtData + byteCount, "#define %s_HEIGHT   %i\n", varFileName, image.height);
   619      byteCount += sprintf(txtData + byteCount, "#define %s_FORMAT   %i          // raylib internal pixel format\n\n", varFileName, image.format);
   620  
   621      byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize);
   622      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]);
   623      byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]);
   624  
   625      // NOTE: Text data size exported is determined by '\0' (NULL) character
   626      success = SaveFileText(fileName, txtData);
   627  
   628      RL_FREE(txtData);
   629  
   630  #endif      // SUPPORT_IMAGE_EXPORT
   631  
   632      if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image as code exported successfully", fileName);
   633      else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image as code", fileName);
   634  
   635      return success;
   636  }
   637  
   638  //------------------------------------------------------------------------------------
   639  // Image generation functions
   640  //------------------------------------------------------------------------------------
   641  // Generate image: plain color
   642  Image GenImageColor(int width, int height, Color color)
   643  {
   644      Color *pixels = (Color *)RL_CALLOC(width*height, sizeof(Color));
   645  
   646      for (int i = 0; i < width*height; i++) pixels[i] = color;
   647  
   648      Image image = {
   649          .data = pixels,
   650          .width = width,
   651          .height = height,
   652          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   653          .mipmaps = 1
   654      };
   655  
   656      return image;
   657  }
   658  
   659  #if defined(SUPPORT_IMAGE_GENERATION)
   660  // Generate image: vertical gradient
   661  Image GenImageGradientV(int width, int height, Color top, Color bottom)
   662  {
   663      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   664  
   665      for (int j = 0; j < height; j++)
   666      {
   667          float factor = (float)j/(float)height;
   668          for (int i = 0; i < width; i++)
   669          {
   670              pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor));
   671              pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor));
   672              pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor));
   673              pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor));
   674          }
   675      }
   676  
   677      Image image = {
   678          .data = pixels,
   679          .width = width,
   680          .height = height,
   681          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   682          .mipmaps = 1
   683      };
   684  
   685      return image;
   686  }
   687  
   688  // Generate image: horizontal gradient
   689  Image GenImageGradientH(int width, int height, Color left, Color right)
   690  {
   691      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   692  
   693      for (int i = 0; i < width; i++)
   694      {
   695          float factor = (float)i/(float)width;
   696          for (int j = 0; j < height; j++)
   697          {
   698              pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor));
   699              pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor));
   700              pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor));
   701              pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor));
   702          }
   703      }
   704  
   705      Image image = {
   706          .data = pixels,
   707          .width = width,
   708          .height = height,
   709          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   710          .mipmaps = 1
   711      };
   712  
   713      return image;
   714  }
   715  
   716  // Generate image: radial gradient
   717  Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer)
   718  {
   719      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   720      float radius = (width < height)? (float)width/2.0f : (float)height/2.0f;
   721  
   722      float centerX = (float)width/2.0f;
   723      float centerY = (float)height/2.0f;
   724  
   725      for (int y = 0; y < height; y++)
   726      {
   727          for (int x = 0; x < width; x++)
   728          {
   729              float dist = hypotf((float)x - centerX, (float)y - centerY);
   730              float factor = (dist - radius*density)/(radius*(1.0f - density));
   731  
   732              factor = (float)fmax(factor, 0.0f);
   733              factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
   734  
   735              pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
   736              pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
   737              pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor));
   738              pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor));
   739          }
   740      }
   741  
   742      Image image = {
   743          .data = pixels,
   744          .width = width,
   745          .height = height,
   746          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   747          .mipmaps = 1
   748      };
   749  
   750      return image;
   751  }
   752  
   753  // Generate image: checked
   754  Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2)
   755  {
   756      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   757  
   758      for (int y = 0; y < height; y++)
   759      {
   760          for (int x = 0; x < width; x++)
   761          {
   762              if ((x/checksX + y/checksY)%2 == 0) pixels[y*width + x] = col1;
   763              else pixels[y*width + x] = col2;
   764          }
   765      }
   766  
   767      Image image = {
   768          .data = pixels,
   769          .width = width,
   770          .height = height,
   771          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   772          .mipmaps = 1
   773      };
   774  
   775      return image;
   776  }
   777  
   778  // Generate image: white noise
   779  Image GenImageWhiteNoise(int width, int height, float factor)
   780  {
   781      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   782  
   783      for (int i = 0; i < width*height; i++)
   784      {
   785          if (GetRandomValue(0, 99) < (int)(factor*100.0f)) pixels[i] = WHITE;
   786          else pixels[i] = BLACK;
   787      }
   788  
   789      Image image = {
   790          .data = pixels,
   791          .width = width,
   792          .height = height,
   793          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   794          .mipmaps = 1
   795      };
   796  
   797      return image;
   798  }
   799  
   800  // Generate image: perlin noise
   801  Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale)
   802  {
   803      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   804  
   805      for (int y = 0; y < height; y++)
   806      {
   807          for (int x = 0; x < width; x++)
   808          {
   809              float nx = (float)(x + offsetX)*scale/(float)width;
   810              float ny = (float)(y + offsetY)*scale/(float)height;
   811  
   812              // Typical values to start playing with:
   813              //   lacunarity = ~2.0   -- spacing between successive octaves (use exactly 2.0 for wrapping output)
   814              //   gain       =  0.5   -- relative weighting applied to each successive octave
   815              //   octaves    =  6     -- number of "octaves" of noise3() to sum
   816  
   817              // NOTE: We need to translate the data from [-1..1] to [0..1]
   818              float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f;
   819  
   820              int intensity = (int)(p*255.0f);
   821              pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
   822          }
   823      }
   824  
   825      Image image = {
   826          .data = pixels,
   827          .width = width,
   828          .height = height,
   829          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   830          .mipmaps = 1
   831      };
   832  
   833      return image;
   834  }
   835  
   836  // Generate image: cellular algorithm. Bigger tileSize means bigger cells
   837  Image GenImageCellular(int width, int height, int tileSize)
   838  {
   839      Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color));
   840  
   841      int seedsPerRow = width/tileSize;
   842      int seedsPerCol = height/tileSize;
   843      int seedCount = seedsPerRow*seedsPerCol;
   844  
   845      Vector2 *seeds = (Vector2 *)RL_MALLOC(seedCount*sizeof(Vector2));
   846  
   847      for (int i = 0; i < seedCount; i++)
   848      {
   849          int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
   850          int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
   851          seeds[i] = (Vector2){ (float)x, (float)y };
   852      }
   853  
   854      for (int y = 0; y < height; y++)
   855      {
   856          int tileY = y/tileSize;
   857  
   858          for (int x = 0; x < width; x++)
   859          {
   860              int tileX = x/tileSize;
   861  
   862              float minDistance = 65536.0f; //(float)strtod("Inf", NULL);
   863  
   864              // Check all adjacent tiles
   865              for (int i = -1; i < 2; i++)
   866              {
   867                  if ((tileX + i < 0) || (tileX + i >= seedsPerRow)) continue;
   868  
   869                  for (int j = -1; j < 2; j++)
   870                  {
   871                      if ((tileY + j < 0) || (tileY + j >= seedsPerCol)) continue;
   872  
   873                      Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
   874  
   875                      float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
   876                      minDistance = (float)fmin(minDistance, dist);
   877                  }
   878              }
   879  
   880              // I made this up but it seems to give good results at all tile sizes
   881              int intensity = (int)(minDistance*256.0f/tileSize);
   882              if (intensity > 255) intensity = 255;
   883  
   884              pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 };
   885          }
   886      }
   887  
   888      RL_FREE(seeds);
   889  
   890      Image image = {
   891          .data = pixels,
   892          .width = width,
   893          .height = height,
   894          .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
   895          .mipmaps = 1
   896      };
   897  
   898      return image;
   899  }
   900  
   901  // Generate image: grayscale image from text data
   902  Image GenImageText(int width, int height, const char *text)
   903  {
   904      Image image = { 0 };
   905      
   906      int textLength = TextLength(text);
   907      int imageViewSize = width*height;
   908  
   909      image.width = width;
   910      image.height = height;
   911      image.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
   912      image.data = RL_CALLOC(imageViewSize, 1);
   913      image.mipmaps = 1;
   914  
   915      memcpy(image.data, text, (textLength > imageViewSize)? imageViewSize : textLength);
   916  
   917      return image;
   918  }
   919  #endif      // SUPPORT_IMAGE_GENERATION
   920  
   921  //------------------------------------------------------------------------------------
   922  // Image manipulation functions
   923  //------------------------------------------------------------------------------------
   924  // Copy an image to a new image
   925  Image ImageCopy(Image image)
   926  {
   927      Image newImage = { 0 };
   928  
   929      int width = image.width;
   930      int height = image.height;
   931      int size = 0;
   932  
   933      for (int i = 0; i < image.mipmaps; i++)
   934      {
   935          size += GetPixelDataSize(width, height, image.format);
   936  
   937          width /= 2;
   938          height /= 2;
   939  
   940          // Security check for NPOT textures
   941          if (width < 1) width = 1;
   942          if (height < 1) height = 1;
   943      }
   944  
   945      newImage.data = RL_MALLOC(size);
   946  
   947      if (newImage.data != NULL)
   948      {
   949          // NOTE: Size must be provided in bytes
   950          memcpy(newImage.data, image.data, size);
   951  
   952          newImage.width = image.width;
   953          newImage.height = image.height;
   954          newImage.mipmaps = image.mipmaps;
   955          newImage.format = image.format;
   956      }
   957  
   958      return newImage;
   959  }
   960  
   961  // Create an image from another image piece
   962  Image ImageFromImage(Image image, Rectangle rec)
   963  {
   964      Image result = { 0 };
   965  
   966      int bytesPerPixel = GetPixelDataSize(1, 1, image.format);
   967  
   968      result.width = (int)rec.width;
   969      result.height = (int)rec.height;
   970      result.data = RL_CALLOC((int)rec.width*(int)rec.height*bytesPerPixel, 1);
   971      result.format = image.format;
   972      result.mipmaps = 1;
   973  
   974      for (int y = 0; y < (int)rec.height; y++)
   975      {
   976          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);
   977      }
   978  
   979      return result;
   980  }
   981  
   982  // Crop an image to area defined by a rectangle
   983  // NOTE: Security checks are performed in case rectangle goes out of bounds
   984  void ImageCrop(Image *image, Rectangle crop)
   985  {
   986      // Security check to avoid program crash
   987      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
   988  
   989      // Security checks to validate crop rectangle
   990      if (crop.x < 0) { crop.width += crop.x; crop.x = 0; }
   991      if (crop.y < 0) { crop.height += crop.y; crop.y = 0; }
   992      if ((crop.x + crop.width) > image->width) crop.width = image->width - crop.x;
   993      if ((crop.y + crop.height) > image->height) crop.height = image->height - crop.y;
   994      if ((crop.x > image->width) || (crop.y > image->height))
   995      {
   996          TRACELOG(LOG_WARNING, "IMAGE: Failed to crop, rectangle out of bounds");
   997          return;
   998      }
   999  
  1000      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  1001      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  1002      else
  1003      {
  1004          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  1005  
  1006          unsigned char *croppedData = (unsigned char *)RL_MALLOC((int)(crop.width*crop.height)*bytesPerPixel);
  1007  
  1008          // OPTION 1: Move cropped data line-by-line
  1009          for (int y = (int)crop.y, offsetSize = 0; y < (int)(crop.y + crop.height); y++)
  1010          {
  1011              memcpy(croppedData + offsetSize, ((unsigned char *)image->data) + (y*image->width + (int)crop.x)*bytesPerPixel, (int)crop.width*bytesPerPixel);
  1012              offsetSize += ((int)crop.width*bytesPerPixel);
  1013          }
  1014  
  1015          /*
  1016          // OPTION 2: Move cropped data pixel-by-pixel or byte-by-byte
  1017          for (int y = (int)crop.y; y < (int)(crop.y + crop.height); y++)
  1018          {
  1019              for (int x = (int)crop.x; x < (int)(crop.x + crop.width); x++)
  1020              {
  1021                  //memcpy(croppedData + ((y - (int)crop.y)*(int)crop.width + (x - (int)crop.x))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
  1022                  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];
  1023              }
  1024          }
  1025          */
  1026  
  1027          RL_FREE(image->data);
  1028          image->data = croppedData;
  1029          image->width = (int)crop.width;
  1030          image->height = (int)crop.height;
  1031      }
  1032  }
  1033  
  1034  // Convert image data to desired format
  1035  void ImageFormat(Image *image, int newFormat)
  1036  {
  1037      // Security check to avoid program crash
  1038      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1039  
  1040      if ((newFormat != 0) && (image->format != newFormat))
  1041      {
  1042          if ((image->format < PIXELFORMAT_COMPRESSED_DXT1_RGB) && (newFormat < PIXELFORMAT_COMPRESSED_DXT1_RGB))
  1043          {
  1044              Vector4 *pixels = LoadImageDataNormalized(*image);     // Supports 8 to 32 bit per channel
  1045  
  1046              RL_FREE(image->data);      // WARNING! We loose mipmaps data --> Regenerated at the end...
  1047              image->data = NULL;
  1048              image->format = newFormat;
  1049  
  1050              int k = 0;
  1051  
  1052              switch (image->format)
  1053              {
  1054                  case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  1055                  {
  1056                      image->data = (unsigned char *)RL_MALLOC(image->width*image->height*sizeof(unsigned char));
  1057  
  1058                      for (int i = 0; i < image->width*image->height; i++)
  1059                      {
  1060                          ((unsigned char *)image->data)[i] = (unsigned char)((pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)*255.0f);
  1061                      }
  1062  
  1063                  } break;
  1064                  case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  1065                  {
  1066                      image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char));
  1067  
  1068                      for (int i = 0; i < image->width*image->height*2; i += 2, k++)
  1069                      {
  1070                          ((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);
  1071                          ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f);
  1072                      }
  1073  
  1074                  } break;
  1075                  case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  1076                  {
  1077                      image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
  1078  
  1079                      unsigned char r = 0;
  1080                      unsigned char g = 0;
  1081                      unsigned char b = 0;
  1082  
  1083                      for (int i = 0; i < image->width*image->height; i++)
  1084                      {
  1085                          r = (unsigned char)(round(pixels[i].x*31.0f));
  1086                          g = (unsigned char)(round(pixels[i].y*63.0f));
  1087                          b = (unsigned char)(round(pixels[i].z*31.0f));
  1088  
  1089                          ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
  1090                      }
  1091  
  1092                  } break;
  1093                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  1094                  {
  1095                      image->data = (unsigned char *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned char));
  1096  
  1097                      for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
  1098                      {
  1099                          ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
  1100                          ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
  1101                          ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
  1102                      }
  1103                  } break;
  1104                  case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  1105                  {
  1106                      image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
  1107  
  1108                      unsigned char r = 0;
  1109                      unsigned char g = 0;
  1110                      unsigned char b = 0;
  1111                      unsigned char a = 0;
  1112  
  1113                      for (int i = 0; i < image->width*image->height; i++)
  1114                      {
  1115                          r = (unsigned char)(round(pixels[i].x*31.0f));
  1116                          g = (unsigned char)(round(pixels[i].y*31.0f));
  1117                          b = (unsigned char)(round(pixels[i].z*31.0f));
  1118                          a = (pixels[i].w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
  1119  
  1120                          ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
  1121                      }
  1122  
  1123                  } break;
  1124                  case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  1125                  {
  1126                      image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
  1127  
  1128                      unsigned char r = 0;
  1129                      unsigned char g = 0;
  1130                      unsigned char b = 0;
  1131                      unsigned char a = 0;
  1132  
  1133                      for (int i = 0; i < image->width*image->height; i++)
  1134                      {
  1135                          r = (unsigned char)(round(pixels[i].x*15.0f));
  1136                          g = (unsigned char)(round(pixels[i].y*15.0f));
  1137                          b = (unsigned char)(round(pixels[i].z*15.0f));
  1138                          a = (unsigned char)(round(pixels[i].w*15.0f));
  1139  
  1140                          ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
  1141                      }
  1142  
  1143                  } break;
  1144                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  1145                  {
  1146                      image->data = (unsigned char *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned char));
  1147  
  1148                      for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
  1149                      {
  1150                          ((unsigned char *)image->data)[i] = (unsigned char)(pixels[k].x*255.0f);
  1151                          ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].y*255.0f);
  1152                          ((unsigned char *)image->data)[i + 2] = (unsigned char)(pixels[k].z*255.0f);
  1153                          ((unsigned char *)image->data)[i + 3] = (unsigned char)(pixels[k].w*255.0f);
  1154                      }
  1155                  } break;
  1156                  case PIXELFORMAT_UNCOMPRESSED_R32:
  1157                  {
  1158                      // WARNING: Image is converted to GRAYSCALE eqeuivalent 32bit
  1159  
  1160                      image->data = (float *)RL_MALLOC(image->width*image->height*sizeof(float));
  1161  
  1162                      for (int i = 0; i < image->width*image->height; i++)
  1163                      {
  1164                          ((float *)image->data)[i] = (float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f);
  1165                      }
  1166                  } break;
  1167                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  1168                  {
  1169                      image->data = (float *)RL_MALLOC(image->width*image->height*3*sizeof(float));
  1170  
  1171                      for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++)
  1172                      {
  1173                          ((float *)image->data)[i] = pixels[k].x;
  1174                          ((float *)image->data)[i + 1] = pixels[k].y;
  1175                          ((float *)image->data)[i + 2] = pixels[k].z;
  1176                      }
  1177                  } break;
  1178                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  1179                  {
  1180                      image->data = (float *)RL_MALLOC(image->width*image->height*4*sizeof(float));
  1181  
  1182                      for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++)
  1183                      {
  1184                          ((float *)image->data)[i] = pixels[k].x;
  1185                          ((float *)image->data)[i + 1] = pixels[k].y;
  1186                          ((float *)image->data)[i + 2] = pixels[k].z;
  1187                          ((float *)image->data)[i + 3] = pixels[k].w;
  1188                      }
  1189                  } break;
  1190                  default: break;
  1191              }
  1192  
  1193              RL_FREE(pixels);
  1194              pixels = NULL;
  1195  
  1196              // In case original image had mipmaps, generate mipmaps for formated image
  1197              // NOTE: Original mipmaps are replaced by new ones, if custom mipmaps were used, they are lost
  1198              if (image->mipmaps > 1)
  1199              {
  1200                  image->mipmaps = 1;
  1201              #if defined(SUPPORT_IMAGE_MANIPULATION)
  1202                  if (image->data != NULL) ImageMipmaps(image);
  1203              #endif
  1204              }
  1205          }
  1206          else TRACELOG(LOG_WARNING, "IMAGE: Data format is compressed, can not be converted");
  1207      }
  1208  }
  1209  
  1210  // Convert image to POT (power-of-two)
  1211  // NOTE: It could be useful on OpenGL ES 2.0 (RPI, HTML5)
  1212  void ImageToPOT(Image *image, Color fill)
  1213  {
  1214      // Security check to avoid program crash
  1215      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1216  
  1217      // Calculate next power-of-two values
  1218      // NOTE: Just add the required amount of pixels at the right and bottom sides of image...
  1219      int potWidth = (int)powf(2, ceilf(logf((float)image->width)/logf(2)));
  1220      int potHeight = (int)powf(2, ceilf(logf((float)image->height)/logf(2)));
  1221  
  1222      // Check if POT texture generation is required (if texture is not already POT)
  1223      if ((potWidth != image->width) || (potHeight != image->height)) ImageResizeCanvas(image, potWidth, potHeight, 0, 0, fill);
  1224  }
  1225  
  1226  #if defined(SUPPORT_IMAGE_MANIPULATION)
  1227  // Create an image from text (default font)
  1228  Image ImageText(const char *text, int fontSize, Color color)
  1229  {
  1230      Image imText = { 0 };
  1231  #if defined(SUPPORT_MODULE_RTEXT)
  1232      int defaultFontSize = 10;   // Default Font chars height in pixel
  1233      if (fontSize < defaultFontSize) fontSize = defaultFontSize;
  1234      int spacing = fontSize/defaultFontSize;
  1235      imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color);   // WARNING: Module required: rtext
  1236  #else
  1237      imText = GenImageColor(200, 60, BLACK);     // Generating placeholder black image rectangle
  1238      TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
  1239  #endif
  1240      return imText;
  1241  }
  1242  
  1243  // Create an image from text (custom sprite font)
  1244  Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint)
  1245  {
  1246      Image imText = { 0 };
  1247  #if defined(SUPPORT_MODULE_RTEXT)
  1248      int size = (int)strlen(text);   // Get size in bytes of text
  1249  
  1250      int textOffsetX = 0;            // Image drawing position X
  1251      int textOffsetY = 0;            // Offset between lines (on line break '\n')
  1252  
  1253      // NOTE: Text image is generated at font base size, later scaled to desired font size
  1254      Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);  // WARNING: Module required: rtext
  1255      Vector2 textSize = MeasureTextEx(font, text, fontSize, spacing);
  1256  
  1257      // Create image to store text
  1258      imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK);
  1259  
  1260      for (int i = 0; i < size; i++)
  1261      {
  1262          // Get next codepoint from byte string and glyph index in font
  1263          int codepointByteCount = 0;
  1264          int codepoint = GetCodepointNext(&text[i], &codepointByteCount);    // WARNING: Module required: rtext
  1265          int index = GetGlyphIndex(font, codepoint);                         // WARNING: Module required: rtext
  1266  
  1267          // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f)
  1268          // but we need to draw all of the bad bytes using the '?' symbol moving one byte
  1269          if (codepoint == 0x3f) codepointByteCount = 1;
  1270  
  1271          if (codepoint == '\n')
  1272          {
  1273              // NOTE: Fixed line spacing of 1.5 line-height
  1274              // TODO: Support custom line spacing defined by user
  1275              textOffsetY += (font.baseSize + font.baseSize/2);
  1276              textOffsetX = 0;
  1277          }
  1278          else
  1279          {
  1280              if ((codepoint != ' ') && (codepoint != '\t'))
  1281              {
  1282                  Rectangle rec = { (float)(textOffsetX + font.glyphs[index].offsetX), (float)(textOffsetY + font.glyphs[index].offsetY), (float)font.recs[index].width, (float)font.recs[index].height };
  1283                  ImageDraw(&imText, font.glyphs[index].image, (Rectangle){ 0, 0, (float)font.glyphs[index].image.width, (float)font.glyphs[index].image.height }, rec, tint);
  1284              }
  1285  
  1286              if (font.glyphs[index].advanceX == 0) textOffsetX += (int)(font.recs[index].width + spacing);
  1287              else textOffsetX += font.glyphs[index].advanceX + (int)spacing;
  1288          }
  1289  
  1290          i += (codepointByteCount - 1);   // Move text bytes counter to next codepoint
  1291      }
  1292  
  1293      // Scale image depending on text size
  1294      if (textSize.y != imSize.y)
  1295      {
  1296          float scaleFactor = textSize.y / imSize.y;
  1297          TRACELOG(LOG_INFO, "IMAGE: Text scaled by factor: %f", scaleFactor);
  1298  
  1299          // Using nearest-neighbor scaling algorithm for default font
  1300          // TODO: Allow defining the preferred scaling mechanism externally
  1301          // WARNING: Module required: rtext
  1302          if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
  1303          else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor));
  1304      }
  1305  #else
  1306      imText = GenImageColor(200, 60, BLACK);     // Generating placeholder black image rectangle
  1307      TRACELOG(LOG_WARNING, "IMAGE: ImageTextEx() requires module: rtext");
  1308  #endif
  1309      return imText;
  1310  }
  1311  
  1312  // Crop image depending on alpha value
  1313  // NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
  1314  void ImageAlphaCrop(Image *image, float threshold)
  1315  {
  1316      // Security check to avoid program crash
  1317      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1318  
  1319      Rectangle crop = GetImageAlphaBorder(*image, threshold);
  1320  
  1321      // Crop if rectangle is valid
  1322      if (((int)crop.width != 0) && ((int)crop.height != 0)) ImageCrop(image, crop);
  1323  }
  1324  
  1325  // Clear alpha channel to desired color
  1326  // NOTE: Threshold defines the alpha limit, 0.0f to 1.0f
  1327  void ImageAlphaClear(Image *image, Color color, float threshold)
  1328  {
  1329      // Security check to avoid program crash
  1330      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1331  
  1332      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  1333      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  1334      else
  1335      {
  1336          switch (image->format)
  1337          {
  1338              case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  1339              {
  1340                  unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
  1341                  for (int i = 1; i < image->width*image->height*2; i += 2)
  1342                  {
  1343                      if (((unsigned char *)image->data)[i] <= thresholdValue)
  1344                      {
  1345                          ((unsigned char *)image->data)[i - 1] = color.r;
  1346                          ((unsigned char *)image->data)[i] = color.a;
  1347                      }
  1348                  }
  1349              } break;
  1350              case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  1351              {
  1352                  unsigned char thresholdValue = ((threshold < 0.5f)? 0 : 1);
  1353  
  1354                  unsigned char r = (unsigned char)(round((float)color.r*31.0f));
  1355                  unsigned char g = (unsigned char)(round((float)color.g*31.0f));
  1356                  unsigned char b = (unsigned char)(round((float)color.b*31.0f));
  1357                  unsigned char a = (color.a < 128)? 0 : 1;
  1358  
  1359                  for (int i = 0; i < image->width*image->height; i++)
  1360                  {
  1361                      if ((((unsigned short *)image->data)[i] & 0b0000000000000001) <= thresholdValue)
  1362                      {
  1363                          ((unsigned short *)image->data)[i] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
  1364                      }
  1365                  }
  1366              } break;
  1367              case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  1368              {
  1369                  unsigned char thresholdValue = (unsigned char)(threshold*15.0f);
  1370  
  1371                  unsigned char r = (unsigned char)(round((float)color.r*15.0f));
  1372                  unsigned char g = (unsigned char)(round((float)color.g*15.0f));
  1373                  unsigned char b = (unsigned char)(round((float)color.b*15.0f));
  1374                  unsigned char a = (unsigned char)(round((float)color.a*15.0f));
  1375  
  1376                  for (int i = 0; i < image->width*image->height; i++)
  1377                  {
  1378                      if ((((unsigned short *)image->data)[i] & 0x000f) <= thresholdValue)
  1379                      {
  1380                          ((unsigned short *)image->data)[i] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
  1381                      }
  1382                  }
  1383              } break;
  1384              case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  1385              {
  1386                  unsigned char thresholdValue = (unsigned char)(threshold*255.0f);
  1387                  for (int i = 3; i < image->width*image->height*4; i += 4)
  1388                  {
  1389                      if (((unsigned char *)image->data)[i] <= thresholdValue)
  1390                      {
  1391                          ((unsigned char *)image->data)[i - 3] = color.r;
  1392                          ((unsigned char *)image->data)[i - 2] = color.g;
  1393                          ((unsigned char *)image->data)[i - 1] = color.b;
  1394                          ((unsigned char *)image->data)[i] = color.a;
  1395                      }
  1396                  }
  1397              } break;
  1398              case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  1399              {
  1400                  for (int i = 3; i < image->width*image->height*4; i += 4)
  1401                  {
  1402                      if (((float *)image->data)[i] <= threshold)
  1403                      {
  1404                          ((float *)image->data)[i - 3] = (float)color.r/255.0f;
  1405                          ((float *)image->data)[i - 2] = (float)color.g/255.0f;
  1406                          ((float *)image->data)[i - 1] = (float)color.b/255.0f;
  1407                          ((float *)image->data)[i] = (float)color.a/255.0f;
  1408                      }
  1409                  }
  1410              } break;
  1411              default: break;
  1412          }
  1413      }
  1414  }
  1415  
  1416  // Apply alpha mask to image
  1417  // NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit)
  1418  // NOTE 2: alphaMask should be same size as image
  1419  void ImageAlphaMask(Image *image, Image alphaMask)
  1420  {
  1421      if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
  1422      {
  1423          TRACELOG(LOG_WARNING, "IMAGE: Alpha mask must be same size as image");
  1424      }
  1425      else if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
  1426      {
  1427          TRACELOG(LOG_WARNING, "IMAGE: Alpha mask can not be applied to compressed data formats");
  1428      }
  1429      else
  1430      {
  1431          // Force mask to be Grayscale
  1432          Image mask = ImageCopy(alphaMask);
  1433          if (mask.format != PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
  1434  
  1435          // In case image is only grayscale, we just add alpha channel
  1436          if (image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE)
  1437          {
  1438              unsigned char *data = (unsigned char *)RL_MALLOC(image->width*image->height*2);
  1439  
  1440              // Apply alpha mask to alpha channel
  1441              for (int i = 0, k = 0; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
  1442              {
  1443                  data[k] = ((unsigned char *)image->data)[i];
  1444                  data[k + 1] = ((unsigned char *)mask.data)[i];
  1445              }
  1446  
  1447              RL_FREE(image->data);
  1448              image->data = data;
  1449              image->format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
  1450          }
  1451          else
  1452          {
  1453              // Convert image to RGBA
  1454              if (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
  1455  
  1456              // Apply alpha mask to alpha channel
  1457              for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
  1458              {
  1459                  ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
  1460              }
  1461          }
  1462  
  1463          UnloadImage(mask);
  1464      }
  1465  }
  1466  
  1467  // Premultiply alpha channel
  1468  void ImageAlphaPremultiply(Image *image)
  1469  {
  1470      // Security check to avoid program crash
  1471      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1472  
  1473      float alpha = 0.0f;
  1474      Color *pixels = LoadImageColors(*image);
  1475  
  1476      for (int i = 0; i < image->width*image->height; i++)
  1477      {
  1478          if (pixels[i].a == 0)
  1479          {
  1480              pixels[i].r = 0;
  1481              pixels[i].g = 0;
  1482              pixels[i].b = 0;
  1483          }
  1484          else if (pixels[i].a < 255)
  1485          {
  1486              alpha = (float)pixels[i].a/255.0f;
  1487              pixels[i].r = (unsigned char)((float)pixels[i].r*alpha);
  1488              pixels[i].g = (unsigned char)((float)pixels[i].g*alpha);
  1489              pixels[i].b = (unsigned char)((float)pixels[i].b*alpha);
  1490          }
  1491      }
  1492  
  1493      RL_FREE(image->data);
  1494  
  1495      int format = image->format;
  1496      image->data = pixels;
  1497      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  1498  
  1499      ImageFormat(image, format);
  1500  }
  1501  
  1502  // Apply box blur
  1503  void ImageBlurGaussian(Image *image, int blurSize) {
  1504      // Security check to avoid program crash
  1505      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1506  
  1507      ImageAlphaPremultiply(image);
  1508  
  1509      Color *pixels = LoadImageColors(*image);
  1510  
  1511      // Loop switches between pixelsCopy1 and pixelsCopy2
  1512      Vector4 *pixelsCopy1 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
  1513      Vector4 *pixelsCopy2 = RL_MALLOC((image->height)*(image->width)*sizeof(Vector4));
  1514  
  1515      for (int i = 0; i < (image->height)*(image->width); i++) {
  1516          pixelsCopy1[i].x = pixels[i].r;
  1517          pixelsCopy1[i].y = pixels[i].g;
  1518          pixelsCopy1[i].z = pixels[i].b;
  1519          pixelsCopy1[i].w = pixels[i].a;
  1520      }
  1521  
  1522      // Repeated convolution of rectangular window signal by itself converges to a gaussian distribution
  1523      for (int j = 0; j < GAUSSIAN_BLUR_ITERATIONS; j++) {
  1524          // Horizontal motion blur
  1525          for (int row = 0; row < image->height; row++)
  1526          {
  1527              float avgR = 0.0f;
  1528              float avgG = 0.0f;
  1529              float avgB = 0.0f;
  1530              float avgAlpha = 0.0f;
  1531              int convolutionSize = blurSize+1;
  1532  
  1533              for (int i = 0; i < blurSize+1; i++) 
  1534              {
  1535                  avgR += pixelsCopy1[row*image->width + i].x;
  1536                  avgG += pixelsCopy1[row*image->width + i].y;
  1537                  avgB += pixelsCopy1[row*image->width + i].z;
  1538                  avgAlpha += pixelsCopy1[row*image->width + i].w;
  1539              }
  1540  
  1541              pixelsCopy2[row*image->width].x = avgR/convolutionSize;
  1542              pixelsCopy2[row*image->width].y = avgG/convolutionSize;
  1543              pixelsCopy2[row*image->width].z = avgB/convolutionSize;
  1544              pixelsCopy2[row*image->width].w = avgAlpha/convolutionSize;
  1545  
  1546              for (int x = 1; x < image->width; x++)
  1547              {
  1548                  if (x-blurSize >= 0)
  1549                  {
  1550                      avgR -= pixelsCopy1[row*image->width + x-blurSize].x;
  1551                      avgG -= pixelsCopy1[row*image->width + x-blurSize].y;
  1552                      avgB -= pixelsCopy1[row*image->width + x-blurSize].z;
  1553                      avgAlpha -= pixelsCopy1[row*image->width + x-blurSize].w;
  1554                      convolutionSize--;
  1555                  }
  1556  
  1557                  if (x+blurSize < image->width)
  1558                  {
  1559                      avgR += pixelsCopy1[row*image->width + x+blurSize].x;
  1560                      avgG += pixelsCopy1[row*image->width + x+blurSize].y;
  1561                      avgB += pixelsCopy1[row*image->width + x+blurSize].z;
  1562                      avgAlpha += pixelsCopy1[row*image->width + x+blurSize].w;
  1563                      convolutionSize++;
  1564                  }
  1565  
  1566                  pixelsCopy2[row*image->width + x].x = avgR/convolutionSize;
  1567                  pixelsCopy2[row*image->width + x].y = avgG/convolutionSize;
  1568                  pixelsCopy2[row*image->width + x].z = avgB/convolutionSize;
  1569                  pixelsCopy2[row*image->width + x].w = avgAlpha/convolutionSize;
  1570              }
  1571                  }
  1572  
  1573          // Vertical motion blur
  1574          for (int col = 0; col < image->width; col++)
  1575          {
  1576              float avgR = 0.0f;
  1577              float avgG = 0.0f;
  1578              float avgB = 0.0f;
  1579              float avgAlpha = 0.0f;
  1580              int convolutionSize = blurSize+1;
  1581  
  1582              for (int i = 0; i < blurSize+1; i++) 
  1583              {
  1584                  avgR += pixelsCopy2[i*image->width + col].x;
  1585                  avgG += pixelsCopy2[i*image->width + col].y;
  1586                  avgB += pixelsCopy2[i*image->width + col].z;
  1587                  avgAlpha += pixelsCopy2[i*image->width + col].w;
  1588              }
  1589  
  1590              pixelsCopy1[col].x = (unsigned char) (avgR/convolutionSize);
  1591              pixelsCopy1[col].y = (unsigned char) (avgG/convolutionSize);
  1592              pixelsCopy1[col].z = (unsigned char) (avgB/convolutionSize);
  1593              pixelsCopy1[col].w = (unsigned char) (avgAlpha/convolutionSize);
  1594  
  1595              for (int y = 1; y < image->height; y++)
  1596              {
  1597                  if (y-blurSize >= 0)
  1598                  {
  1599                      avgR -= pixelsCopy2[(y-blurSize)*image->width + col].x;
  1600                      avgG -= pixelsCopy2[(y-blurSize)*image->width + col].y;
  1601                      avgB -= pixelsCopy2[(y-blurSize)*image->width + col].z;
  1602                      avgAlpha -= pixelsCopy2[(y-blurSize)*image->width + col].w;
  1603                      convolutionSize--;
  1604                  }
  1605                  if (y+blurSize < image->height)
  1606                  {
  1607                      avgR += pixelsCopy2[(y+blurSize)*image->width + col].x;
  1608                      avgG += pixelsCopy2[(y+blurSize)*image->width + col].y;
  1609                      avgB += pixelsCopy2[(y+blurSize)*image->width + col].z;
  1610                      avgAlpha += pixelsCopy2[(y+blurSize)*image->width + col].w;
  1611                      convolutionSize++;
  1612                  }
  1613  
  1614                  pixelsCopy1[y*image->width + col].x = (unsigned char) (avgR/convolutionSize);
  1615                  pixelsCopy1[y*image->width + col].y = (unsigned char) (avgG/convolutionSize);
  1616                  pixelsCopy1[y*image->width + col].z = (unsigned char) (avgB/convolutionSize);
  1617                  pixelsCopy1[y*image->width + col].w = (unsigned char) (avgAlpha/convolutionSize);
  1618              }
  1619          }
  1620      }
  1621  
  1622  
  1623      // Reverse premultiply
  1624      for (int i = 0; i < (image->width)*(image->height); i++)
  1625      {
  1626          if (pixelsCopy1[i].w == 0.0f)
  1627          {
  1628              pixels[i].r = 0;
  1629              pixels[i].g = 0;
  1630              pixels[i].b = 0;
  1631              pixels[i].a = 0;
  1632          }
  1633          else if (pixelsCopy1[i].w <= 255.0f)
  1634          {
  1635              float alpha = (float)pixelsCopy1[i].w/255.0f;
  1636              pixels[i].r = (unsigned char)((float)pixelsCopy1[i].x/alpha);
  1637              pixels[i].g = (unsigned char)((float)pixelsCopy1[i].y/alpha);
  1638              pixels[i].b = (unsigned char)((float)pixelsCopy1[i].z/alpha);
  1639              pixels[i].a = (unsigned char) pixelsCopy1[i].w;
  1640          }
  1641      }
  1642  
  1643      int format = image->format;
  1644      RL_FREE(image->data);
  1645      RL_FREE(pixelsCopy1);
  1646      RL_FREE(pixelsCopy2);
  1647  
  1648      image->data = pixels;
  1649      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  1650  
  1651      ImageFormat(image, format);
  1652  }
  1653  
  1654  // Resize and image to new size
  1655  // NOTE: Uses stb default scaling filters (both bicubic):
  1656  // STBIR_DEFAULT_FILTER_UPSAMPLE    STBIR_FILTER_CATMULLROM
  1657  // STBIR_DEFAULT_FILTER_DOWNSAMPLE  STBIR_FILTER_MITCHELL   (high-quality Catmull-Rom)
  1658  void ImageResize(Image *image, int newWidth, int newHeight)
  1659  {
  1660      // Security check to avoid program crash
  1661      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1662  
  1663      // Check if we can use a fast path on image scaling
  1664      // It can be for 8 bit per channel images with 1 to 4 channels per pixel
  1665      if ((image->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) ||
  1666          (image->format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) ||
  1667          (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) ||
  1668          (image->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
  1669      {
  1670          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  1671          unsigned char *output = (unsigned char *)RL_MALLOC(newWidth*newHeight*bytesPerPixel);
  1672  
  1673          switch (image->format)
  1674          {
  1675              case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break;
  1676              case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break;
  1677              case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break;
  1678              case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break;
  1679              default: break;
  1680          }
  1681  
  1682          RL_FREE(image->data);
  1683          image->data = output;
  1684          image->width = newWidth;
  1685          image->height = newHeight;
  1686      }
  1687      else
  1688      {
  1689          // Get data as Color pixels array to work with it
  1690          Color *pixels = LoadImageColors(*image);
  1691          Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color));
  1692  
  1693          // NOTE: Color data is casted to (unsigned char *), there shouldn't been any problem...
  1694          stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4);
  1695  
  1696          int format = image->format;
  1697  
  1698          UnloadImageColors(pixels);
  1699          RL_FREE(image->data);
  1700  
  1701          image->data = output;
  1702          image->width = newWidth;
  1703          image->height = newHeight;
  1704          image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  1705  
  1706          ImageFormat(image, format);  // Reformat 32bit RGBA image to original format
  1707      }
  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 canvas and fill with color
  1750  // NOTE: Resize offset is relative to the top-left corner of the original image
  1751  void ImageResizeCanvas(Image *image, int newWidth, int newHeight, int offsetX, int offsetY, Color fill)
  1752  {
  1753      // Security check to avoid program crash
  1754      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1755  
  1756      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  1757      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  1758      else if ((newWidth != image->width) || (newHeight != image->height))
  1759      {
  1760          Rectangle srcRec = { 0, 0, (float)image->width, (float)image->height };
  1761          Vector2 dstPos = { (float)offsetX, (float)offsetY };
  1762  
  1763          if (offsetX < 0)
  1764          {
  1765              srcRec.x = (float)-offsetX;
  1766              srcRec.width += (float)offsetX;
  1767              dstPos.x = 0;
  1768          }
  1769          else if ((offsetX + image->width) > newWidth) srcRec.width = (float)(newWidth - offsetX);
  1770  
  1771          if (offsetY < 0)
  1772          {
  1773              srcRec.y = (float)-offsetY;
  1774              srcRec.height += (float)offsetY;
  1775              dstPos.y = 0;
  1776          }
  1777          else if ((offsetY + image->height) > newHeight) srcRec.height = (float)(newHeight - offsetY);
  1778  
  1779          if (newWidth < srcRec.width) srcRec.width = (float)newWidth;
  1780          if (newHeight < srcRec.height) srcRec.height = (float)newHeight;
  1781  
  1782          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  1783          unsigned char *resizedData = (unsigned char *)RL_CALLOC(newWidth*newHeight*bytesPerPixel, 1);
  1784  
  1785          // TODO: Fill resized canvas with fill color (must be formatted to image->format)
  1786  
  1787          int dstOffsetSize = ((int)dstPos.y*newWidth + (int)dstPos.x)*bytesPerPixel;
  1788  
  1789          for (int y = 0; y < (int)srcRec.height; y++)
  1790          {
  1791              memcpy(resizedData + dstOffsetSize, ((unsigned char *)image->data) + ((y + (int)srcRec.y)*image->width + (int)srcRec.x)*bytesPerPixel, (int)srcRec.width*bytesPerPixel);
  1792              dstOffsetSize += (newWidth*bytesPerPixel);
  1793          }
  1794  
  1795          RL_FREE(image->data);
  1796          image->data = resizedData;
  1797          image->width = newWidth;
  1798          image->height = newHeight;
  1799      }
  1800  }
  1801  
  1802  // Generate all mipmap levels for a provided image
  1803  // NOTE 1: Supports POT and NPOT images
  1804  // NOTE 2: image.data is scaled to include mipmap levels
  1805  // NOTE 3: Mipmaps format is the same as base image
  1806  void ImageMipmaps(Image *image)
  1807  {
  1808      // Security check to avoid program crash
  1809      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1810  
  1811      int mipCount = 1;                   // Required mipmap levels count (including base level)
  1812      int mipWidth = image->width;        // Base image width
  1813      int mipHeight = image->height;      // Base image height
  1814      int mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);  // Image data size (in bytes)
  1815  
  1816      // Count mipmap levels required
  1817      while ((mipWidth != 1) || (mipHeight != 1))
  1818      {
  1819          if (mipWidth != 1) mipWidth /= 2;
  1820          if (mipHeight != 1) mipHeight /= 2;
  1821  
  1822          // Security check for NPOT textures
  1823          if (mipWidth < 1) mipWidth = 1;
  1824          if (mipHeight < 1) mipHeight = 1;
  1825  
  1826          TRACELOGD("IMAGE: Next mipmap level: %i x %i - current size %i", mipWidth, mipHeight, mipSize);
  1827  
  1828          mipCount++;
  1829          mipSize += GetPixelDataSize(mipWidth, mipHeight, image->format);       // Add mipmap size (in bytes)
  1830      }
  1831  
  1832      if (image->mipmaps < mipCount)
  1833      {
  1834          void *temp = RL_REALLOC(image->data, mipSize);
  1835  
  1836          if (temp != NULL) image->data = temp;      // Assign new pointer (new size) to store mipmaps data
  1837          else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps required memory could not be allocated");
  1838  
  1839          // Pointer to allocated memory point where store next mipmap level data
  1840          unsigned char *nextmip = (unsigned char *)image->data + GetPixelDataSize(image->width, image->height, image->format);
  1841  
  1842          mipWidth = image->width/2;
  1843          mipHeight = image->height/2;
  1844          mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
  1845          Image imCopy = ImageCopy(*image);
  1846  
  1847          for (int i = 1; i < mipCount; i++)
  1848          {
  1849              TRACELOGD("IMAGE: Generating mipmap level: %i (%i x %i) - size: %i - offset: 0x%x", i, mipWidth, mipHeight, mipSize, nextmip);
  1850  
  1851              ImageResize(&imCopy, mipWidth, mipHeight);  // Uses internally Mitchell cubic downscale filter
  1852  
  1853              memcpy(nextmip, imCopy.data, mipSize);
  1854              nextmip += mipSize;
  1855              image->mipmaps++;
  1856  
  1857              mipWidth /= 2;
  1858              mipHeight /= 2;
  1859  
  1860              // Security check for NPOT textures
  1861              if (mipWidth < 1) mipWidth = 1;
  1862              if (mipHeight < 1) mipHeight = 1;
  1863  
  1864              mipSize = GetPixelDataSize(mipWidth, mipHeight, image->format);
  1865          }
  1866  
  1867          UnloadImage(imCopy);
  1868      }
  1869      else TRACELOG(LOG_WARNING, "IMAGE: Mipmaps already available");
  1870  }
  1871  
  1872  // Dither image data to 16bpp or lower (Floyd-Steinberg dithering)
  1873  // NOTE: In case selected bpp do not represent an known 16bit format,
  1874  // dithered data is stored in the LSB part of the unsigned short
  1875  void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp)
  1876  {
  1877      // Security check to avoid program crash
  1878      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1879  
  1880      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB)
  1881      {
  1882          TRACELOG(LOG_WARNING, "IMAGE: Compressed data formats can not be dithered");
  1883          return;
  1884      }
  1885  
  1886      if ((rBpp + gBpp + bBpp + aBpp) > 16)
  1887      {
  1888          TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithering bpps (%ibpp), only 16bpp or lower modes supported", (rBpp+gBpp+bBpp+aBpp));
  1889      }
  1890      else
  1891      {
  1892          Color *pixels = LoadImageColors(*image);
  1893  
  1894          RL_FREE(image->data);      // free old image data
  1895  
  1896          if ((image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8) && (image->format != PIXELFORMAT_UNCOMPRESSED_R8G8B8A8))
  1897          {
  1898              TRACELOG(LOG_WARNING, "IMAGE: Format is already 16bpp or lower, dithering could have no effect");
  1899          }
  1900  
  1901          // Define new image format, check if desired bpp match internal known format
  1902          if ((rBpp == 5) && (gBpp == 6) && (bBpp == 5) && (aBpp == 0)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
  1903          else if ((rBpp == 5) && (gBpp == 5) && (bBpp == 5) && (aBpp == 1)) image->format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
  1904          else if ((rBpp == 4) && (gBpp == 4) && (bBpp == 4) && (aBpp == 4)) image->format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
  1905          else
  1906          {
  1907              image->format = 0;
  1908              TRACELOG(LOG_WARNING, "IMAGE: Unsupported dithered OpenGL internal format: %ibpp (R%iG%iB%iA%i)", (rBpp+gBpp+bBpp+aBpp), rBpp, gBpp, bBpp, aBpp);
  1909          }
  1910  
  1911          // NOTE: We will store the dithered data as unsigned short (16bpp)
  1912          image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short));
  1913  
  1914          Color oldPixel = WHITE;
  1915          Color newPixel = WHITE;
  1916  
  1917          int rError, gError, bError;
  1918          unsigned short rPixel, gPixel, bPixel, aPixel;   // Used for 16bit pixel composition
  1919  
  1920          #define MIN(a,b) (((a)<(b))?(a):(b))
  1921  
  1922          for (int y = 0; y < image->height; y++)
  1923          {
  1924              for (int x = 0; x < image->width; x++)
  1925              {
  1926                  oldPixel = pixels[y*image->width + x];
  1927  
  1928                  // NOTE: New pixel obtained by bits truncate, it would be better to round values (check ImageFormat())
  1929                  newPixel.r = oldPixel.r >> (8 - rBpp);     // R bits
  1930                  newPixel.g = oldPixel.g >> (8 - gBpp);     // G bits
  1931                  newPixel.b = oldPixel.b >> (8 - bBpp);     // B bits
  1932                  newPixel.a = oldPixel.a >> (8 - aBpp);     // A bits (not used on dithering)
  1933  
  1934                  // NOTE: Error must be computed between new and old pixel but using same number of bits!
  1935                  // We want to know how much color precision we have lost...
  1936                  rError = (int)oldPixel.r - (int)(newPixel.r << (8 - rBpp));
  1937                  gError = (int)oldPixel.g - (int)(newPixel.g << (8 - gBpp));
  1938                  bError = (int)oldPixel.b - (int)(newPixel.b << (8 - bBpp));
  1939  
  1940                  pixels[y*image->width + x] = newPixel;
  1941  
  1942                  // NOTE: Some cases are out of the array and should be ignored
  1943                  if (x < (image->width - 1))
  1944                  {
  1945                      pixels[y*image->width + x+1].r = MIN((int)pixels[y*image->width + x+1].r + (int)((float)rError*7.0f/16), 0xff);
  1946                      pixels[y*image->width + x+1].g = MIN((int)pixels[y*image->width + x+1].g + (int)((float)gError*7.0f/16), 0xff);
  1947                      pixels[y*image->width + x+1].b = MIN((int)pixels[y*image->width + x+1].b + (int)((float)bError*7.0f/16), 0xff);
  1948                  }
  1949  
  1950                  if ((x > 0) && (y < (image->height - 1)))
  1951                  {
  1952                      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);
  1953                      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);
  1954                      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);
  1955                  }
  1956  
  1957                  if (y < (image->height - 1))
  1958                  {
  1959                      pixels[(y+1)*image->width + x].r = MIN((int)pixels[(y+1)*image->width + x].r + (int)((float)rError*5.0f/16), 0xff);
  1960                      pixels[(y+1)*image->width + x].g = MIN((int)pixels[(y+1)*image->width + x].g + (int)((float)gError*5.0f/16), 0xff);
  1961                      pixels[(y+1)*image->width + x].b = MIN((int)pixels[(y+1)*image->width + x].b + (int)((float)bError*5.0f/16), 0xff);
  1962                  }
  1963  
  1964                  if ((x < (image->width - 1)) && (y < (image->height - 1)))
  1965                  {
  1966                      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);
  1967                      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);
  1968                      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);
  1969                  }
  1970  
  1971                  rPixel = (unsigned short)newPixel.r;
  1972                  gPixel = (unsigned short)newPixel.g;
  1973                  bPixel = (unsigned short)newPixel.b;
  1974                  aPixel = (unsigned short)newPixel.a;
  1975  
  1976                  ((unsigned short *)image->data)[y*image->width + x] = (rPixel << (gBpp + bBpp + aBpp)) | (gPixel << (bBpp + aBpp)) | (bPixel << aBpp) | aPixel;
  1977              }
  1978          }
  1979  
  1980          UnloadImageColors(pixels);
  1981      }
  1982  }
  1983  
  1984  // Flip image vertically
  1985  void ImageFlipVertical(Image *image)
  1986  {
  1987      // Security check to avoid program crash
  1988      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  1989  
  1990      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  1991      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  1992      else
  1993      {
  1994          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  1995          unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
  1996  
  1997          for (int i = (image->height - 1), offsetSize = 0; i >= 0; i--)
  1998          {
  1999              memcpy(flippedData + offsetSize, ((unsigned char *)image->data) + i*image->width*bytesPerPixel, image->width*bytesPerPixel);
  2000              offsetSize += image->width*bytesPerPixel;
  2001          }
  2002  
  2003          RL_FREE(image->data);
  2004          image->data = flippedData;
  2005      }
  2006  }
  2007  
  2008  // Flip image horizontally
  2009  void ImageFlipHorizontal(Image *image)
  2010  {
  2011      // Security check to avoid program crash
  2012      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2013  
  2014      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  2015      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  2016      else
  2017      {
  2018          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  2019          unsigned char *flippedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
  2020  
  2021          for (int y = 0; y < image->height; y++)
  2022          {
  2023              for (int x = 0; x < image->width; x++)
  2024              {
  2025                  // OPTION 1: Move pixels with memcopy()
  2026                  //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel);
  2027  
  2028                  // OPTION 2: Just copy data pixel by pixel
  2029                  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];
  2030              }
  2031          }
  2032  
  2033          RL_FREE(image->data);
  2034          image->data = flippedData;
  2035  
  2036          /*
  2037          // OPTION 3: Faster implementation (specific for 32bit pixels)
  2038          // NOTE: It does not require additional allocations
  2039          uint32_t *ptr = (uint32_t *)image->data;
  2040          for (int y = 0; y < image->height; y++)
  2041          {
  2042              for (int x = 0; x < image->width/2; x++)
  2043              {
  2044                  uint32_t backup = ptr[y*image->width + x];
  2045                  ptr[y*image->width + x] = ptr[y*image->width + (image->width - 1 - x)];
  2046                  ptr[y*image->width + (image->width - 1 - x)] = backup;
  2047              }
  2048          }
  2049          */
  2050      }
  2051  }
  2052  
  2053  // Rotate image clockwise 90deg
  2054  void ImageRotateCW(Image *image)
  2055  {
  2056      // Security check to avoid program crash
  2057      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2058  
  2059      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  2060      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  2061      else
  2062      {
  2063          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  2064          unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
  2065  
  2066          for (int y = 0; y < image->height; y++)
  2067          {
  2068              for (int x = 0; x < image->width; x++)
  2069              {
  2070                  //memcpy(rotatedData + (x*image->height + (image->height - y - 1))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + x)*bytesPerPixel, bytesPerPixel);
  2071                  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];
  2072              }
  2073          }
  2074  
  2075          RL_FREE(image->data);
  2076          image->data = rotatedData;
  2077          int width = image->width;
  2078          int height = image-> height;
  2079  
  2080          image->width = height;
  2081          image->height = width;
  2082      }
  2083  }
  2084  
  2085  // Rotate image counter-clockwise 90deg
  2086  void ImageRotateCCW(Image *image)
  2087  {
  2088      // Security check to avoid program crash
  2089      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2090  
  2091      if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level");
  2092      if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats");
  2093      else
  2094      {
  2095          int bytesPerPixel = GetPixelDataSize(1, 1, image->format);
  2096          unsigned char *rotatedData = (unsigned char *)RL_MALLOC(image->width*image->height*bytesPerPixel);
  2097  
  2098          for (int y = 0; y < image->height; y++)
  2099          {
  2100              for (int x = 0; x < image->width; x++)
  2101              {
  2102                  //memcpy(rotatedData + (x*image->height + y))*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - x - 1))*bytesPerPixel, bytesPerPixel);
  2103                  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];
  2104              }
  2105          }
  2106  
  2107          RL_FREE(image->data);
  2108          image->data = rotatedData;
  2109          int width = image->width;
  2110          int height = image-> height;
  2111  
  2112          image->width = height;
  2113          image->height = width;
  2114      }
  2115  }
  2116  
  2117  // Modify image color: tint
  2118  void ImageColorTint(Image *image, Color color)
  2119  {
  2120      // Security check to avoid program crash
  2121      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2122  
  2123      Color *pixels = LoadImageColors(*image);
  2124  
  2125      float cR = (float)color.r/255;
  2126      float cG = (float)color.g/255;
  2127      float cB = (float)color.b/255;
  2128      float cA = (float)color.a/255;
  2129  
  2130      for (int y = 0; y < image->height; y++)
  2131      {
  2132          for (int x = 0; x < image->width; x++)
  2133          {
  2134              int index = y*image->width + x;
  2135              unsigned char r = (unsigned char)(((float)pixels[index].r/255*cR)*255.0f);
  2136              unsigned char g = (unsigned char)(((float)pixels[index].g/255*cG)*255.0f);
  2137              unsigned char b = (unsigned char)(((float)pixels[index].b/255*cB)*255.0f);
  2138              unsigned char a = (unsigned char)(((float)pixels[index].a/255*cA)*255.0f);
  2139  
  2140              pixels[index].r = r;
  2141              pixels[index].g = g;
  2142              pixels[index].b = b;
  2143              pixels[index].a = a;
  2144          }
  2145      }
  2146  
  2147      int format = image->format;
  2148      RL_FREE(image->data);
  2149  
  2150      image->data = pixels;
  2151      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  2152  
  2153      ImageFormat(image, format);
  2154  }
  2155  
  2156  // Modify image color: invert
  2157  void ImageColorInvert(Image *image)
  2158  {
  2159      // Security check to avoid program crash
  2160      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2161  
  2162      Color *pixels = LoadImageColors(*image);
  2163  
  2164      for (int y = 0; y < image->height; y++)
  2165      {
  2166          for (int x = 0; x < image->width; x++)
  2167          {
  2168              pixels[y*image->width + x].r = 255 - pixels[y*image->width + x].r;
  2169              pixels[y*image->width + x].g = 255 - pixels[y*image->width + x].g;
  2170              pixels[y*image->width + x].b = 255 - pixels[y*image->width + x].b;
  2171          }
  2172      }
  2173  
  2174      int format = image->format;
  2175      RL_FREE(image->data);
  2176  
  2177      image->data = pixels;
  2178      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  2179  
  2180      ImageFormat(image, format);
  2181  }
  2182  
  2183  // Modify image color: grayscale
  2184  void ImageColorGrayscale(Image *image)
  2185  {
  2186      ImageFormat(image, PIXELFORMAT_UNCOMPRESSED_GRAYSCALE);
  2187  }
  2188  
  2189  // Modify image color: contrast
  2190  // NOTE: Contrast values between -100 and 100
  2191  void ImageColorContrast(Image *image, float contrast)
  2192  {
  2193      // Security check to avoid program crash
  2194      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2195  
  2196      if (contrast < -100) contrast = -100;
  2197      if (contrast > 100) contrast = 100;
  2198  
  2199      contrast = (100.0f + contrast)/100.0f;
  2200      contrast *= contrast;
  2201  
  2202      Color *pixels = LoadImageColors(*image);
  2203  
  2204      for (int y = 0; y < image->height; y++)
  2205      {
  2206          for (int x = 0; x < image->width; x++)
  2207          {
  2208              float pR = (float)pixels[y*image->width + x].r/255.0f;
  2209              pR -= 0.5f;
  2210              pR *= contrast;
  2211              pR += 0.5f;
  2212              pR *= 255;
  2213              if (pR < 0) pR = 0;
  2214              if (pR > 255) pR = 255;
  2215  
  2216              float pG = (float)pixels[y*image->width + x].g/255.0f;
  2217              pG -= 0.5f;
  2218              pG *= contrast;
  2219              pG += 0.5f;
  2220              pG *= 255;
  2221              if (pG < 0) pG = 0;
  2222              if (pG > 255) pG = 255;
  2223  
  2224              float pB = (float)pixels[y*image->width + x].b/255.0f;
  2225              pB -= 0.5f;
  2226              pB *= contrast;
  2227              pB += 0.5f;
  2228              pB *= 255;
  2229              if (pB < 0) pB = 0;
  2230              if (pB > 255) pB = 255;
  2231  
  2232              pixels[y*image->width + x].r = (unsigned char)pR;
  2233              pixels[y*image->width + x].g = (unsigned char)pG;
  2234              pixels[y*image->width + x].b = (unsigned char)pB;
  2235          }
  2236      }
  2237  
  2238      int format = image->format;
  2239      RL_FREE(image->data);
  2240  
  2241      image->data = pixels;
  2242      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  2243  
  2244      ImageFormat(image, format);
  2245  }
  2246  
  2247  // Modify image color: brightness
  2248  // NOTE: Brightness values between -255 and 255
  2249  void ImageColorBrightness(Image *image, int brightness)
  2250  {
  2251      // Security check to avoid program crash
  2252      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2253  
  2254      if (brightness < -255) brightness = -255;
  2255      if (brightness > 255) brightness = 255;
  2256  
  2257      Color *pixels = LoadImageColors(*image);
  2258  
  2259      for (int y = 0; y < image->height; y++)
  2260      {
  2261          for (int x = 0; x < image->width; x++)
  2262          {
  2263              int cR = pixels[y*image->width + x].r + brightness;
  2264              int cG = pixels[y*image->width + x].g + brightness;
  2265              int cB = pixels[y*image->width + x].b + brightness;
  2266  
  2267              if (cR < 0) cR = 1;
  2268              if (cR > 255) cR = 255;
  2269  
  2270              if (cG < 0) cG = 1;
  2271              if (cG > 255) cG = 255;
  2272  
  2273              if (cB < 0) cB = 1;
  2274              if (cB > 255) cB = 255;
  2275  
  2276              pixels[y*image->width + x].r = (unsigned char)cR;
  2277              pixels[y*image->width + x].g = (unsigned char)cG;
  2278              pixels[y*image->width + x].b = (unsigned char)cB;
  2279          }
  2280      }
  2281  
  2282      int format = image->format;
  2283      RL_FREE(image->data);
  2284  
  2285      image->data = pixels;
  2286      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  2287  
  2288      ImageFormat(image, format);
  2289  }
  2290  
  2291  // Modify image color: replace color
  2292  void ImageColorReplace(Image *image, Color color, Color replace)
  2293  {
  2294      // Security check to avoid program crash
  2295      if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return;
  2296  
  2297      Color *pixels = LoadImageColors(*image);
  2298  
  2299      for (int y = 0; y < image->height; y++)
  2300      {
  2301          for (int x = 0; x < image->width; x++)
  2302          {
  2303              if ((pixels[y*image->width + x].r == color.r) &&
  2304                  (pixels[y*image->width + x].g == color.g) &&
  2305                  (pixels[y*image->width + x].b == color.b) &&
  2306                  (pixels[y*image->width + x].a == color.a))
  2307              {
  2308                  pixels[y*image->width + x].r = replace.r;
  2309                  pixels[y*image->width + x].g = replace.g;
  2310                  pixels[y*image->width + x].b = replace.b;
  2311                  pixels[y*image->width + x].a = replace.a;
  2312              }
  2313          }
  2314      }
  2315  
  2316      int format = image->format;
  2317      RL_FREE(image->data);
  2318  
  2319      image->data = pixels;
  2320      image->format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  2321  
  2322      ImageFormat(image, format);
  2323  }
  2324  #endif      // SUPPORT_IMAGE_MANIPULATION
  2325  
  2326  // Load color data from image as a Color array (RGBA - 32bit)
  2327  // NOTE: Memory allocated should be freed using UnloadImageColors();
  2328  Color *LoadImageColors(Image image)
  2329  {
  2330      if ((image.width == 0) || (image.height == 0)) return NULL;
  2331  
  2332      Color *pixels = (Color *)RL_MALLOC(image.width*image.height*sizeof(Color));
  2333  
  2334      if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
  2335      else
  2336      {
  2337          if ((image.format == PIXELFORMAT_UNCOMPRESSED_R32) ||
  2338              (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) ||
  2339              (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel");
  2340  
  2341          for (int i = 0, k = 0; i < image.width*image.height; i++)
  2342          {
  2343              switch (image.format)
  2344              {
  2345                  case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  2346                  {
  2347                      pixels[i].r = ((unsigned char *)image.data)[i];
  2348                      pixels[i].g = ((unsigned char *)image.data)[i];
  2349                      pixels[i].b = ((unsigned char *)image.data)[i];
  2350                      pixels[i].a = 255;
  2351  
  2352                  } break;
  2353                  case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  2354                  {
  2355                      pixels[i].r = ((unsigned char *)image.data)[k];
  2356                      pixels[i].g = ((unsigned char *)image.data)[k];
  2357                      pixels[i].b = ((unsigned char *)image.data)[k];
  2358                      pixels[i].a = ((unsigned char *)image.data)[k + 1];
  2359  
  2360                      k += 2;
  2361                  } break;
  2362                  case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  2363                  {
  2364                      unsigned short pixel = ((unsigned short *)image.data)[i];
  2365  
  2366                      pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
  2367                      pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
  2368                      pixels[i].b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
  2369                      pixels[i].a = (unsigned char)((pixel & 0b0000000000000001)*255);
  2370  
  2371                  } break;
  2372                  case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  2373                  {
  2374                      unsigned short pixel = ((unsigned short *)image.data)[i];
  2375  
  2376                      pixels[i].r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
  2377                      pixels[i].g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
  2378                      pixels[i].b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
  2379                      pixels[i].a = 255;
  2380  
  2381                  } break;
  2382                  case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  2383                  {
  2384                      unsigned short pixel = ((unsigned short *)image.data)[i];
  2385  
  2386                      pixels[i].r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
  2387                      pixels[i].g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
  2388                      pixels[i].b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
  2389                      pixels[i].a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
  2390  
  2391                  } break;
  2392                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  2393                  {
  2394                      pixels[i].r = ((unsigned char *)image.data)[k];
  2395                      pixels[i].g = ((unsigned char *)image.data)[k + 1];
  2396                      pixels[i].b = ((unsigned char *)image.data)[k + 2];
  2397                      pixels[i].a = ((unsigned char *)image.data)[k + 3];
  2398  
  2399                      k += 4;
  2400                  } break;
  2401                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  2402                  {
  2403                      pixels[i].r = (unsigned char)((unsigned char *)image.data)[k];
  2404                      pixels[i].g = (unsigned char)((unsigned char *)image.data)[k + 1];
  2405                      pixels[i].b = (unsigned char)((unsigned char *)image.data)[k + 2];
  2406                      pixels[i].a = 255;
  2407  
  2408                      k += 3;
  2409                  } break;
  2410                  case PIXELFORMAT_UNCOMPRESSED_R32:
  2411                  {
  2412                      pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
  2413                      pixels[i].g = 0;
  2414                      pixels[i].b = 0;
  2415                      pixels[i].a = 255;
  2416  
  2417                  } break;
  2418                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  2419                  {
  2420                      pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
  2421                      pixels[i].g = (unsigned char)(((float *)image.data)[k + 1]*255.0f);
  2422                      pixels[i].b = (unsigned char)(((float *)image.data)[k + 2]*255.0f);
  2423                      pixels[i].a = 255;
  2424  
  2425                      k += 3;
  2426                  } break;
  2427                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  2428                  {
  2429                      pixels[i].r = (unsigned char)(((float *)image.data)[k]*255.0f);
  2430                      pixels[i].g = (unsigned char)(((float *)image.data)[k]*255.0f);
  2431                      pixels[i].b = (unsigned char)(((float *)image.data)[k]*255.0f);
  2432                      pixels[i].a = (unsigned char)(((float *)image.data)[k]*255.0f);
  2433  
  2434                      k += 4;
  2435                  } break;
  2436                  default: break;
  2437              }
  2438          }
  2439      }
  2440  
  2441      return pixels;
  2442  }
  2443  
  2444  // Load colors palette from image as a Color array (RGBA - 32bit)
  2445  // NOTE: Memory allocated should be freed using UnloadImagePalette()
  2446  Color *LoadImagePalette(Image image, int maxPaletteSize, int *colorCount)
  2447  {
  2448      #define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
  2449  
  2450      int palCount = 0;
  2451      Color *palette = NULL;
  2452      Color *pixels = LoadImageColors(image);
  2453  
  2454      if (pixels != NULL)
  2455      {
  2456          palette = (Color *)RL_MALLOC(maxPaletteSize*sizeof(Color));
  2457  
  2458          for (int i = 0; i < maxPaletteSize; i++) palette[i] = BLANK;   // Set all colors to BLANK
  2459  
  2460          for (int i = 0; i < image.width*image.height; i++)
  2461          {
  2462              if (pixels[i].a > 0)
  2463              {
  2464                  bool colorInPalette = false;
  2465  
  2466                  // Check if the color is already on palette
  2467                  for (int j = 0; j < maxPaletteSize; j++)
  2468                  {
  2469                      if (COLOR_EQUAL(pixels[i], palette[j]))
  2470                      {
  2471                          colorInPalette = true;
  2472                          break;
  2473                      }
  2474                  }
  2475  
  2476                  // Store color if not on the palette
  2477                  if (!colorInPalette)
  2478                  {
  2479                      palette[palCount] = pixels[i];      // Add pixels[i] to palette
  2480                      palCount++;
  2481  
  2482                      // We reached the limit of colors supported by palette
  2483                      if (palCount >= maxPaletteSize)
  2484                      {
  2485                          i = image.width*image.height;   // Finish palette get
  2486                          TRACELOG(LOG_WARNING, "IMAGE: Palette is greater than %i colors", maxPaletteSize);
  2487                      }
  2488                  }
  2489              }
  2490          }
  2491  
  2492          UnloadImageColors(pixels);
  2493      }
  2494  
  2495      *colorCount = palCount;
  2496  
  2497      return palette;
  2498  }
  2499  
  2500  // Unload color data loaded with LoadImageColors()
  2501  void UnloadImageColors(Color *colors)
  2502  {
  2503      RL_FREE(colors);
  2504  }
  2505  
  2506  // Unload colors palette loaded with LoadImagePalette()
  2507  void UnloadImagePalette(Color *colors)
  2508  {
  2509      RL_FREE(colors);
  2510  }
  2511  
  2512  // Get image alpha border rectangle
  2513  // NOTE: Threshold is defined as a percentatge: 0.0f -> 1.0f
  2514  Rectangle GetImageAlphaBorder(Image image, float threshold)
  2515  {
  2516      Rectangle crop = { 0 };
  2517  
  2518      Color *pixels = LoadImageColors(image);
  2519  
  2520      if (pixels != NULL)
  2521      {
  2522          int xMin = 65536;   // Define a big enough number
  2523          int xMax = 0;
  2524          int yMin = 65536;
  2525          int yMax = 0;
  2526  
  2527          for (int y = 0; y < image.height; y++)
  2528          {
  2529              for (int x = 0; x < image.width; x++)
  2530              {
  2531                  if (pixels[y*image.width + x].a > (unsigned char)(threshold*255.0f))
  2532                  {
  2533                      if (x < xMin) xMin = x;
  2534                      if (x > xMax) xMax = x;
  2535                      if (y < yMin) yMin = y;
  2536                      if (y > yMax) yMax = y;
  2537                  }
  2538              }
  2539          }
  2540  
  2541          // Check for empty blank image
  2542          if ((xMin != 65536) && (xMax != 65536))
  2543          {
  2544              crop = (Rectangle){ (float)xMin, (float)yMin, (float)((xMax + 1) - xMin), (float)((yMax + 1) - yMin) };
  2545          }
  2546  
  2547          UnloadImageColors(pixels);
  2548      }
  2549  
  2550      return crop;
  2551  }
  2552  
  2553  // Get image pixel color at (x, y) position
  2554  Color GetImageColor(Image image, int x, int y)
  2555  {
  2556      Color color = { 0 };
  2557  
  2558      if ((x >=0) && (x < image.width) && (y >= 0) && (y < image.height))
  2559      {
  2560          switch (image.format)
  2561          {
  2562              case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  2563              {
  2564                  color.r = ((unsigned char *)image.data)[y*image.width + x];
  2565                  color.g = ((unsigned char *)image.data)[y*image.width + x];
  2566                  color.b = ((unsigned char *)image.data)[y*image.width + x];
  2567                  color.a = 255;
  2568  
  2569              } break;
  2570              case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  2571              {
  2572                  color.r = ((unsigned char *)image.data)[(y*image.width + x)*2];
  2573                  color.g = ((unsigned char *)image.data)[(y*image.width + x)*2];
  2574                  color.b = ((unsigned char *)image.data)[(y*image.width + x)*2];
  2575                  color.a = ((unsigned char *)image.data)[(y*image.width + x)*2 + 1];
  2576  
  2577              } break;
  2578              case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  2579              {
  2580                  unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
  2581  
  2582                  color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
  2583                  color.g = (unsigned char)((float)((pixel & 0b0000011111000000) >> 6)*(255/31));
  2584                  color.b = (unsigned char)((float)((pixel & 0b0000000000111110) >> 1)*(255/31));
  2585                  color.a = (unsigned char)((pixel & 0b0000000000000001)*255);
  2586  
  2587              } break;
  2588              case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  2589              {
  2590                  unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
  2591  
  2592                  color.r = (unsigned char)((float)((pixel & 0b1111100000000000) >> 11)*(255/31));
  2593                  color.g = (unsigned char)((float)((pixel & 0b0000011111100000) >> 5)*(255/63));
  2594                  color.b = (unsigned char)((float)(pixel & 0b0000000000011111)*(255/31));
  2595                  color.a = 255;
  2596  
  2597              } break;
  2598              case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  2599              {
  2600                  unsigned short pixel = ((unsigned short *)image.data)[y*image.width + x];
  2601  
  2602                  color.r = (unsigned char)((float)((pixel & 0b1111000000000000) >> 12)*(255/15));
  2603                  color.g = (unsigned char)((float)((pixel & 0b0000111100000000) >> 8)*(255/15));
  2604                  color.b = (unsigned char)((float)((pixel & 0b0000000011110000) >> 4)*(255/15));
  2605                  color.a = (unsigned char)((float)(pixel & 0b0000000000001111)*(255/15));
  2606  
  2607              } break;
  2608              case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  2609              {
  2610                  color.r = ((unsigned char *)image.data)[(y*image.width + x)*4];
  2611                  color.g = ((unsigned char *)image.data)[(y*image.width + x)*4 + 1];
  2612                  color.b = ((unsigned char *)image.data)[(y*image.width + x)*4 + 2];
  2613                  color.a = ((unsigned char *)image.data)[(y*image.width + x)*4 + 3];
  2614  
  2615              } break;
  2616              case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  2617              {
  2618                  color.r = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3];
  2619                  color.g = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 1];
  2620                  color.b = (unsigned char)((unsigned char *)image.data)[(y*image.width + x)*3 + 2];
  2621                  color.a = 255;
  2622  
  2623              } break;
  2624              case PIXELFORMAT_UNCOMPRESSED_R32:
  2625              {
  2626                  color.r = (unsigned char)(((float *)image.data)[y*image.width + x]*255.0f);
  2627                  color.g = 0;
  2628                  color.b = 0;
  2629                  color.a = 255;
  2630  
  2631              } break;
  2632              case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  2633              {
  2634                  color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*3]*255.0f);
  2635                  color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 1]*255.0f);
  2636                  color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*3 + 2]*255.0f);
  2637                  color.a = 255;
  2638  
  2639              } break;
  2640              case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  2641              {
  2642                  color.r = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
  2643                  color.g = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
  2644                  color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
  2645                  color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f);
  2646  
  2647              } break;
  2648              default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break;
  2649          }
  2650      }
  2651      else TRACELOG(LOG_WARNING, "Requested image pixel (%i, %i) out of bounds", x, y);
  2652  
  2653      return color;
  2654  }
  2655  
  2656  //------------------------------------------------------------------------------------
  2657  // Image drawing functions
  2658  //------------------------------------------------------------------------------------
  2659  // Clear image background with given color
  2660  void ImageClearBackground(Image *dst, Color color)
  2661  {
  2662      // Security check to avoid program crash
  2663      if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
  2664  
  2665      // Fill in first pixel based on image format
  2666      ImageDrawPixel(dst, 0, 0, color);
  2667  
  2668      unsigned char *pSrcPixel = (unsigned char *)dst->data;
  2669      int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
  2670  
  2671      // Repeat the first pixel data throughout the image
  2672      for (int i = 1; i < dst->width * dst->height; i++)
  2673      {
  2674          memcpy(pSrcPixel + i * bytesPerPixel, pSrcPixel, bytesPerPixel);
  2675      }
  2676  }
  2677  
  2678  // Draw pixel within an image
  2679  // NOTE: Compressed image formats not supported
  2680  void ImageDrawPixel(Image *dst, int x, int y, Color color)
  2681  {
  2682      // Security check to avoid program crash
  2683      if ((dst->data == NULL) || (x < 0) || (x >= dst->width) || (y < 0) || (y >= dst->height)) return;
  2684  
  2685      switch (dst->format)
  2686      {
  2687          case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  2688          {
  2689              // NOTE: Calculate grayscale equivalent color
  2690              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  2691              unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
  2692  
  2693              ((unsigned char *)dst->data)[y*dst->width + x] = gray;
  2694  
  2695          } break;
  2696          case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  2697          {
  2698              // NOTE: Calculate grayscale equivalent color
  2699              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  2700              unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
  2701  
  2702              ((unsigned char *)dst->data)[(y*dst->width + x)*2] = gray;
  2703              ((unsigned char *)dst->data)[(y*dst->width + x)*2 + 1] = color.a;
  2704  
  2705          } break;
  2706          case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  2707          {
  2708              // NOTE: Calculate R5G6B5 equivalent color
  2709              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  2710  
  2711              unsigned char r = (unsigned char)(round(coln.x*31.0f));
  2712              unsigned char g = (unsigned char)(round(coln.y*63.0f));
  2713              unsigned char b = (unsigned char)(round(coln.z*31.0f));
  2714  
  2715              ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
  2716  
  2717          } break;
  2718          case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  2719          {
  2720              // NOTE: Calculate R5G5B5A1 equivalent color
  2721              Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
  2722  
  2723              unsigned char r = (unsigned char)(round(coln.x*31.0f));
  2724              unsigned char g = (unsigned char)(round(coln.y*31.0f));
  2725              unsigned char b = (unsigned char)(round(coln.z*31.0f));
  2726              unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
  2727  
  2728              ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
  2729  
  2730          } break;
  2731          case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  2732          {
  2733              // NOTE: Calculate R5G5B5A1 equivalent color
  2734              Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
  2735  
  2736              unsigned char r = (unsigned char)(round(coln.x*15.0f));
  2737              unsigned char g = (unsigned char)(round(coln.y*15.0f));
  2738              unsigned char b = (unsigned char)(round(coln.z*15.0f));
  2739              unsigned char a = (unsigned char)(round(coln.w*15.0f));
  2740  
  2741              ((unsigned short *)dst->data)[y*dst->width + x] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
  2742  
  2743          } break;
  2744          case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  2745          {
  2746              ((unsigned char *)dst->data)[(y*dst->width + x)*3] = color.r;
  2747              ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 1] = color.g;
  2748              ((unsigned char *)dst->data)[(y*dst->width + x)*3 + 2] = color.b;
  2749  
  2750          } break;
  2751          case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  2752          {
  2753              ((unsigned char *)dst->data)[(y*dst->width + x)*4] = color.r;
  2754              ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 1] = color.g;
  2755              ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 2] = color.b;
  2756              ((unsigned char *)dst->data)[(y*dst->width + x)*4 + 3] = color.a;
  2757  
  2758          } break;
  2759          case PIXELFORMAT_UNCOMPRESSED_R32:
  2760          {
  2761              // NOTE: Calculate grayscale equivalent color (normalized to 32bit)
  2762              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  2763  
  2764              ((float *)dst->data)[y*dst->width + x] = coln.x*0.299f + coln.y*0.587f + coln.z*0.114f;
  2765  
  2766          } break;
  2767          case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  2768          {
  2769              // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit)
  2770              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  2771  
  2772              ((float *)dst->data)[(y*dst->width + x)*3] = coln.x;
  2773              ((float *)dst->data)[(y*dst->width + x)*3 + 1] = coln.y;
  2774              ((float *)dst->data)[(y*dst->width + x)*3 + 2] = coln.z;
  2775          } break;
  2776          case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  2777          {
  2778              // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit)
  2779              Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
  2780  
  2781              ((float *)dst->data)[(y*dst->width + x)*4] = coln.x;
  2782              ((float *)dst->data)[(y*dst->width + x)*4 + 1] = coln.y;
  2783              ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z;
  2784              ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w;
  2785  
  2786          } break;
  2787          default: break;
  2788      }
  2789  }
  2790  
  2791  // Draw pixel within an image (Vector version)
  2792  void ImageDrawPixelV(Image *dst, Vector2 position, Color color)
  2793  {
  2794      ImageDrawPixel(dst, (int)position.x, (int)position.y, color);
  2795  }
  2796  
  2797  // Draw line within an image
  2798  void ImageDrawLine(Image *dst, int startPosX, int startPosY, int endPosX, int endPosY, Color color)
  2799  {
  2800      // Using Bresenham's algorithm as described in
  2801      // Drawing Lines with Pixels - Joshua Scott - March 2012
  2802      // https://classic.csunplugged.org/wp-content/uploads/2014/12/Lines.pdf
  2803  
  2804      int changeInX = (endPosX - startPosX);
  2805      int absChangeInX = (changeInX < 0)? -changeInX : changeInX;
  2806      int changeInY = (endPosY - startPosY);
  2807      int absChangeInY = (changeInY < 0)? -changeInY : changeInY;
  2808  
  2809      int startU, startV, endU, stepV; // Substitutions, either U = X, V = Y or vice versa. See loop at end of function
  2810      //int endV;     // Not needed but left for better understanding, check code below
  2811      int A, B, P;    // See linked paper above, explained down in the main loop
  2812      int reversedXY = (absChangeInY < absChangeInX);
  2813  
  2814      if (reversedXY)
  2815      {
  2816          A = 2*absChangeInY;
  2817          B = A - 2*absChangeInX;
  2818          P = A - absChangeInX;
  2819  
  2820          if (changeInX > 0)
  2821          {
  2822              startU = startPosX;
  2823              startV = startPosY;
  2824              endU = endPosX;
  2825              //endV = endPosY;
  2826          }
  2827          else
  2828          {
  2829              startU = endPosX;
  2830              startV = endPosY;
  2831              endU = startPosX;
  2832              //endV = startPosY;
  2833  
  2834              // Since start and end are reversed
  2835              changeInX = -changeInX;
  2836              changeInY = -changeInY;
  2837          }
  2838  
  2839          stepV = (changeInY < 0)? -1 : 1;
  2840  
  2841          ImageDrawPixel(dst, startU, startV, color);     // At this point they are correctly ordered...
  2842      }
  2843      else
  2844      {
  2845          A = 2*absChangeInX;
  2846          B = A - 2*absChangeInY;
  2847          P = A - absChangeInY;
  2848  
  2849          if (changeInY > 0)
  2850          {
  2851              startU = startPosY;
  2852              startV = startPosX;
  2853              endU = endPosY;
  2854              //endV = endPosX;
  2855          }
  2856          else
  2857          {
  2858              startU = endPosY;
  2859              startV = endPosX;
  2860              endU = startPosY;
  2861              //endV = startPosX;
  2862  
  2863              // Since start and end are reversed
  2864              changeInX = -changeInX;
  2865              changeInY = -changeInY;
  2866          }
  2867  
  2868          stepV = (changeInX < 0)? -1 : 1;
  2869  
  2870          ImageDrawPixel(dst, startV, startU, color);     // ... but need to be reversed here. Repeated in the main loop below
  2871      }
  2872  
  2873      // We already drew the start point. If we started at startU + 0, the line would be crooked and too short
  2874      for (int u = startU + 1, v = startV; u <= endU; u++)
  2875      {
  2876          if (P >= 0)
  2877          {
  2878              v += stepV;     // Adjusts whenever we stray too far from the direct line. Details in the linked paper above
  2879              P += B;         // Remembers that we corrected our path
  2880          }
  2881          else P += A;        // Remembers how far we are from the direct line
  2882  
  2883          if (reversedXY) ImageDrawPixel(dst, u, v, color);
  2884          else ImageDrawPixel(dst, v, u, color);
  2885      }
  2886  }
  2887  
  2888  // Draw line within an image (Vector version)
  2889  void ImageDrawLineV(Image *dst, Vector2 start, Vector2 end, Color color)
  2890  {
  2891      ImageDrawLine(dst, (int)start.x, (int)start.y, (int)end.x, (int)end.y, color);
  2892  }
  2893  
  2894  // Draw circle within an image
  2895  void ImageDrawCircle(Image* dst, int centerX, int centerY, int radius, Color color)
  2896  {
  2897      int x = 0;
  2898      int y = radius;
  2899      int decesionParameter = 3 - 2*radius;
  2900  
  2901      while (y >= x)
  2902      {
  2903          ImageDrawRectangle(dst, centerX - x, centerY + y, x*2, 1, color);
  2904          ImageDrawRectangle(dst, centerX - x, centerY - y, x*2, 1, color);
  2905          ImageDrawRectangle(dst, centerX - y, centerY + x, y*2, 1, color);
  2906          ImageDrawRectangle(dst, centerX - y, centerY - x, y*2, 1, color);
  2907          x++;
  2908  
  2909          if (decesionParameter > 0)
  2910          {
  2911              y--;
  2912              decesionParameter = decesionParameter + 4*(x - y) + 10;
  2913          } 
  2914          else decesionParameter = decesionParameter + 4*x + 6;
  2915      }
  2916  }
  2917  
  2918  // Draw circle within an image (Vector version)
  2919  void ImageDrawCircleV(Image* dst, Vector2 center, int radius, Color color)
  2920  {
  2921      ImageDrawCircle(dst, (int)center.x, (int)center.y, radius, color);
  2922  }
  2923  
  2924  // Draw circle outline within an image
  2925  void ImageDrawCircleLines(Image *dst, int centerX, int centerY, int radius, Color color)
  2926  {
  2927      int x = 0;
  2928      int y = radius;
  2929      int decesionParameter = 3 - 2*radius;
  2930  
  2931      while (y >= x)
  2932      {
  2933          ImageDrawPixel(dst, centerX + x, centerY + y, color);
  2934          ImageDrawPixel(dst, centerX - x, centerY + y, color);
  2935          ImageDrawPixel(dst, centerX + x, centerY - y, color);
  2936          ImageDrawPixel(dst, centerX - x, centerY - y, color);
  2937          ImageDrawPixel(dst, centerX + y, centerY + x, color);
  2938          ImageDrawPixel(dst, centerX - y, centerY + x, color);
  2939          ImageDrawPixel(dst, centerX + y, centerY - x, color);
  2940          ImageDrawPixel(dst, centerX - y, centerY - x, color);
  2941          x++;
  2942  
  2943          if (decesionParameter > 0)
  2944          {
  2945              y--;
  2946              decesionParameter = decesionParameter + 4*(x - y) + 10;
  2947          }
  2948          else decesionParameter = decesionParameter + 4*x + 6;
  2949      }
  2950  }
  2951  
  2952  // Draw circle outline within an image (Vector version)
  2953  void ImageDrawCircleLinesV(Image *dst, Vector2 center, int radius, Color color)
  2954  {
  2955      ImageDrawCircleLines(dst, (int)center.x, (int)center.y, radius, color);
  2956  }
  2957  
  2958  // Draw rectangle within an image
  2959  void ImageDrawRectangle(Image *dst, int posX, int posY, int width, int height, Color color)
  2960  {
  2961      ImageDrawRectangleRec(dst, (Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color);
  2962  }
  2963  
  2964  // Draw rectangle within an image (Vector version)
  2965  void ImageDrawRectangleV(Image *dst, Vector2 position, Vector2 size, Color color)
  2966  {
  2967      ImageDrawRectangle(dst, (int)position.x, (int)position.y, (int)size.x, (int)size.y, color);
  2968  }
  2969  
  2970  // Draw rectangle within an image
  2971  void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color)
  2972  {
  2973      // Security check to avoid program crash
  2974      if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return;
  2975  
  2976      int sy = (int)rec.y;
  2977      int ey = sy + (int)rec.height;
  2978  
  2979      int sx = (int)rec.x;
  2980  
  2981      int bytesPerPixel = GetPixelDataSize(1, 1, dst->format);
  2982  
  2983      for (int y = sy; y < ey; y++)
  2984      {
  2985          // Fill in the first pixel of the row based on image format
  2986          ImageDrawPixel(dst, sx, y, color);
  2987  
  2988          int bytesOffset = ((y * dst->width) + sx) * bytesPerPixel;
  2989          unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset;
  2990  
  2991          // Repeat the first pixel data throughout the row
  2992          for (int x = 1; x < (int)rec.width; x++)
  2993          {
  2994              memcpy(pSrcPixel + x * bytesPerPixel, pSrcPixel, bytesPerPixel);
  2995          }
  2996      }
  2997  }
  2998  
  2999  // Draw rectangle lines within an image
  3000  void ImageDrawRectangleLines(Image *dst, Rectangle rec, int thick, Color color)
  3001  {
  3002      ImageDrawRectangle(dst, (int)rec.x, (int)rec.y, (int)rec.width, thick, color);
  3003      ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
  3004      ImageDrawRectangle(dst, (int)(rec.x + rec.width - thick), (int)(rec.y + thick), thick, (int)(rec.height - thick*2), color);
  3005      ImageDrawRectangle(dst, (int)rec.x, (int)(rec.y + rec.height - thick), (int)rec.width, thick, color);
  3006  }
  3007  
  3008  // Draw an image (source) within an image (destination)
  3009  // NOTE: Color tint is applied to source image
  3010  void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color tint)
  3011  {
  3012      // Security check to avoid program crash
  3013      if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0) ||
  3014          (src.data == NULL) || (src.width == 0) || (src.height == 0)) return;
  3015  
  3016      if (dst->mipmaps > 1) TRACELOG(LOG_WARNING, "Image drawing only applied to base mipmap level");
  3017      if (dst->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image drawing not supported for compressed formats");
  3018      else
  3019      {
  3020          Image srcMod = { 0 };       // Source copy (in case it was required)
  3021          Image *srcPtr = &src;       // Pointer to source image
  3022          bool useSrcMod = false;     // Track source copy required
  3023  
  3024          // Source rectangle out-of-bounds security checks
  3025          if (srcRec.x < 0) { srcRec.width += srcRec.x; srcRec.x = 0; }
  3026          if (srcRec.y < 0) { srcRec.height += srcRec.y; srcRec.y = 0; }
  3027          if ((srcRec.x + srcRec.width) > src.width) srcRec.width = src.width - srcRec.x;
  3028          if ((srcRec.y + srcRec.height) > src.height) srcRec.height = src.height - srcRec.y;
  3029  
  3030          // Check if source rectangle needs to be resized to destination rectangle
  3031          // In that case, we make a copy of source and we apply all required transform
  3032          if (((int)srcRec.width != (int)dstRec.width) || ((int)srcRec.height != (int)dstRec.height))
  3033          {
  3034              srcMod = ImageFromImage(src, srcRec);   // Create image from another image
  3035              ImageResize(&srcMod, (int)dstRec.width, (int)dstRec.height);   // Resize to destination rectangle
  3036              srcRec = (Rectangle){ 0, 0, (float)srcMod.width, (float)srcMod.height };
  3037  
  3038              srcPtr = &srcMod;
  3039              useSrcMod = true;
  3040          }
  3041  
  3042          // Destination rectangle out-of-bounds security checks
  3043          if (dstRec.x < 0)
  3044          {
  3045              srcRec.x = -dstRec.x;
  3046              srcRec.width += dstRec.x;
  3047              dstRec.x = 0;
  3048          }
  3049          else if ((dstRec.x + srcRec.width) > dst->width) srcRec.width = dst->width - dstRec.x;
  3050  
  3051          if (dstRec.y < 0)
  3052          {
  3053              srcRec.y = -dstRec.y;
  3054              srcRec.height += dstRec.y;
  3055              dstRec.y = 0;
  3056          }
  3057          else if ((dstRec.y + srcRec.height) > dst->height) srcRec.height = dst->height - dstRec.y;
  3058  
  3059          if (dst->width < srcRec.width) srcRec.width = (float)dst->width;
  3060          if (dst->height < srcRec.height) srcRec.height = (float)dst->height;
  3061  
  3062          // This blitting method is quite fast! The process followed is:
  3063          // for every pixel -> [get_src_format/get_dst_format -> blend -> format_to_dst]
  3064          // Some optimization ideas:
  3065          //    [x] Avoid creating source copy if not required (no resize required)
  3066          //    [x] Optimize ImageResize() for pixel format (alternative: ImageResizeNN())
  3067          //    [x] Optimize ColorAlphaBlend() to avoid processing (alpha = 0) and (alpha = 1)
  3068          //    [x] Optimize ColorAlphaBlend() for faster operations (maybe avoiding divs?)
  3069          //    [x] Consider fast path: no alpha blending required cases (src has no alpha)
  3070          //    [x] Consider fast path: same src/dst format with no alpha -> direct line copy
  3071          //    [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend()
  3072          //    [ ] Support f32bit channels drawing
  3073  
  3074          // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32
  3075  
  3076          Color colSrc, colDst, blend;
  3077          bool blendRequired = true;
  3078  
  3079          // Fast path: Avoid blend if source has no alpha to blend
  3080          if ((tint.a == 255) && ((srcPtr->format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) || (srcPtr->format == PIXELFORMAT_UNCOMPRESSED_R5G6B5))) blendRequired = false;
  3081  
  3082          int strideDst = GetPixelDataSize(dst->width, 1, dst->format);
  3083          int bytesPerPixelDst = strideDst/(dst->width);
  3084  
  3085          int strideSrc = GetPixelDataSize(srcPtr->width, 1, srcPtr->format);
  3086          int bytesPerPixelSrc = strideSrc/(srcPtr->width);
  3087  
  3088          unsigned char *pSrcBase = (unsigned char *)srcPtr->data + ((int)srcRec.y*srcPtr->width + (int)srcRec.x)*bytesPerPixelSrc;
  3089          unsigned char *pDstBase = (unsigned char *)dst->data + ((int)dstRec.y*dst->width + (int)dstRec.x)*bytesPerPixelDst;
  3090  
  3091          for (int y = 0; y < (int)srcRec.height; y++)
  3092          {
  3093              unsigned char *pSrc = pSrcBase;
  3094              unsigned char *pDst = pDstBase;
  3095  
  3096              // Fast path: Avoid moving pixel by pixel if no blend required and same format
  3097              if (!blendRequired && (srcPtr->format == dst->format)) memcpy(pDst, pSrc, (int)(srcRec.width)*bytesPerPixelSrc);
  3098              else
  3099              {
  3100                  for (int x = 0; x < (int)srcRec.width; x++)
  3101                  {
  3102                      colSrc = GetPixelColor(pSrc, srcPtr->format);
  3103                      colDst = GetPixelColor(pDst, dst->format);
  3104  
  3105                      // Fast path: Avoid blend if source has no alpha to blend
  3106                      if (blendRequired) blend = ColorAlphaBlend(colDst, colSrc, tint);
  3107                      else blend = colSrc;
  3108  
  3109                      SetPixelColor(pDst, blend, dst->format);
  3110  
  3111                      pDst += bytesPerPixelDst;
  3112                      pSrc += bytesPerPixelSrc;
  3113                  }
  3114              }
  3115  
  3116              pSrcBase += strideSrc;
  3117              pDstBase += strideDst;
  3118          }
  3119  
  3120          if (useSrcMod) UnloadImage(srcMod);     // Unload source modified image
  3121      }
  3122  }
  3123  
  3124  // Draw text (default font) within an image (destination)
  3125  void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color)
  3126  {
  3127  #if defined(SUPPORT_MODULE_RTEXT)
  3128      Vector2 position = { (float)posX, (float)posY };
  3129      // NOTE: For default font, spacing is set to desired font size / default font size (10)
  3130      ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color);   // WARNING: Module required: rtext
  3131  #else
  3132      TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext");
  3133  #endif
  3134  }
  3135  
  3136  // Draw text (custom sprite font) within an image (destination)
  3137  void ImageDrawTextEx(Image *dst, Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint)
  3138  {
  3139      Image imText = ImageTextEx(font, text, fontSize, spacing, tint);
  3140  
  3141      Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height };
  3142      Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height };
  3143  
  3144      ImageDraw(dst, imText, srcRec, dstRec, WHITE);
  3145  
  3146      UnloadImage(imText);
  3147  }
  3148  
  3149  //------------------------------------------------------------------------------------
  3150  // Texture loading functions
  3151  //------------------------------------------------------------------------------------
  3152  // Load texture from file into GPU memory (VRAM)
  3153  Texture2D LoadTexture(const char *fileName)
  3154  {
  3155      Texture2D texture = { 0 };
  3156  
  3157      Image image = LoadImage(fileName);
  3158  
  3159      if (image.data != NULL)
  3160      {
  3161          texture = LoadTextureFromImage(image);
  3162          UnloadImage(image);
  3163      }
  3164  
  3165      return texture;
  3166  }
  3167  
  3168  // Load a texture from image data
  3169  // NOTE: image is not unloaded, it must be done manually
  3170  Texture2D LoadTextureFromImage(Image image)
  3171  {
  3172      Texture2D texture = { 0 };
  3173  
  3174      if ((image.width != 0) && (image.height != 0))
  3175      {
  3176          texture.id = rlLoadTexture(image.data, image.width, image.height, image.format, image.mipmaps);
  3177      }
  3178      else TRACELOG(LOG_WARNING, "IMAGE: Data is not valid to load texture");
  3179  
  3180      texture.width = image.width;
  3181      texture.height = image.height;
  3182      texture.mipmaps = image.mipmaps;
  3183      texture.format = image.format;
  3184  
  3185      return texture;
  3186  }
  3187  
  3188  // Load cubemap from image, multiple image cubemap layouts supported
  3189  TextureCubemap LoadTextureCubemap(Image image, int layout)
  3190  {
  3191      TextureCubemap cubemap = { 0 };
  3192  
  3193      if (layout == CUBEMAP_LAYOUT_AUTO_DETECT)      // Try to automatically guess layout type
  3194      {
  3195          // Check image width/height to determine the type of cubemap provided
  3196          if (image.width > image.height)
  3197          {
  3198              if ((image.width/6) == image.height) { layout = CUBEMAP_LAYOUT_LINE_HORIZONTAL; cubemap.width = image.width/6; }
  3199              else if ((image.width/4) == (image.height/3)) { layout = CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE; cubemap.width = image.width/4; }
  3200              else if (image.width >= (int)((float)image.height*1.85f)) { layout = CUBEMAP_LAYOUT_PANORAMA; cubemap.width = image.width/4; }
  3201          }
  3202          else if (image.height > image.width)
  3203          {
  3204              if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; }
  3205              else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; }
  3206          }
  3207  
  3208          cubemap.height = cubemap.width;
  3209      }
  3210  
  3211      // Layout provided or already auto-detected
  3212      if (layout != CUBEMAP_LAYOUT_AUTO_DETECT)
  3213      {
  3214          int size = cubemap.width;
  3215  
  3216          Image faces = { 0 };                // Vertical column image
  3217          Rectangle faceRecs[6] = { 0 };      // Face source rectangles
  3218          for (int i = 0; i < 6; i++) faceRecs[i] = (Rectangle){ 0, 0, (float)size, (float)size };
  3219  
  3220          if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL)
  3221          {
  3222              faces = ImageCopy(image);       // Image data already follows expected convention
  3223          }
  3224          else if (layout == CUBEMAP_LAYOUT_PANORAMA)
  3225          {
  3226              // TODO: Convert panorama image to square faces...
  3227              // Ref: https://github.com/denivip/panorama/blob/master/panorama.cpp
  3228          }
  3229          else
  3230          {
  3231              if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) for (int i = 0; i < 6; i++) faceRecs[i].x = (float)size*i;
  3232              else if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR)
  3233              {
  3234                  faceRecs[0].x = (float)size; faceRecs[0].y = (float)size;
  3235                  faceRecs[1].x = (float)size; faceRecs[1].y = (float)size*3;
  3236                  faceRecs[2].x = (float)size; faceRecs[2].y = 0;
  3237                  faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
  3238                  faceRecs[4].x = 0; faceRecs[4].y = (float)size;
  3239                  faceRecs[5].x = (float)size*2; faceRecs[5].y = (float)size;
  3240              }
  3241              else if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE)
  3242              {
  3243                  faceRecs[0].x = (float)size*2; faceRecs[0].y = (float)size;
  3244                  faceRecs[1].x = 0; faceRecs[1].y = (float)size;
  3245                  faceRecs[2].x = (float)size; faceRecs[2].y = 0;
  3246                  faceRecs[3].x = (float)size; faceRecs[3].y = (float)size*2;
  3247                  faceRecs[4].x = (float)size; faceRecs[4].y = (float)size;
  3248                  faceRecs[5].x = (float)size*3; faceRecs[5].y = (float)size;
  3249              }
  3250  
  3251              // Convert image data to 6 faces in a vertical column, that's the optimum layout for loading
  3252              faces = GenImageColor(size, size*6, MAGENTA);
  3253              ImageFormat(&faces, image.format);
  3254  
  3255              // NOTE: Image formating does not work with compressed textures
  3256  
  3257              for (int i = 0; i < 6; i++) ImageDraw(&faces, image, faceRecs[i], (Rectangle){ 0, (float)size*i, (float)size, (float)size }, WHITE);
  3258          }
  3259  
  3260          // NOTE: Cubemap data is expected to be provided as 6 images in a single data array,
  3261          // one after the other (that's a vertical image), following convention: +X, -X, +Y, -Y, +Z, -Z
  3262          cubemap.id = rlLoadTextureCubemap(faces.data, size, faces.format);
  3263          if (cubemap.id == 0) TRACELOG(LOG_WARNING, "IMAGE: Failed to load cubemap image");
  3264  
  3265          UnloadImage(faces);
  3266      }
  3267      else TRACELOG(LOG_WARNING, "IMAGE: Failed to detect cubemap image layout");
  3268  
  3269      return cubemap;
  3270  }
  3271  
  3272  // Load texture for rendering (framebuffer)
  3273  // NOTE: Render texture is loaded by default with RGBA color attachment and depth RenderBuffer
  3274  RenderTexture2D LoadRenderTexture(int width, int height)
  3275  {
  3276      RenderTexture2D target = { 0 };
  3277  
  3278      target.id = rlLoadFramebuffer(width, height);   // Load an empty framebuffer
  3279  
  3280      if (target.id > 0)
  3281      {
  3282          rlEnableFramebuffer(target.id);
  3283  
  3284          // Create color texture (default to RGBA)
  3285          target.texture.id = rlLoadTexture(NULL, width, height, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1);
  3286          target.texture.width = width;
  3287          target.texture.height = height;
  3288          target.texture.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
  3289          target.texture.mipmaps = 1;
  3290  
  3291          // Create depth renderbuffer/texture
  3292          target.depth.id = rlLoadTextureDepth(width, height, true);
  3293          target.depth.width = width;
  3294          target.depth.height = height;
  3295          target.depth.format = 19;       //DEPTH_COMPONENT_24BIT?
  3296          target.depth.mipmaps = 1;
  3297  
  3298          // Attach color texture and depth renderbuffer/texture to FBO
  3299          rlFramebufferAttach(target.id, target.texture.id, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0);
  3300          rlFramebufferAttach(target.id, target.depth.id, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0);
  3301  
  3302          // Check if fbo is complete with attachments (valid)
  3303          if (rlFramebufferComplete(target.id)) TRACELOG(LOG_INFO, "FBO: [ID %i] Framebuffer object created successfully", target.id);
  3304  
  3305          rlDisableFramebuffer();
  3306      }
  3307      else TRACELOG(LOG_WARNING, "FBO: Framebuffer object can not be created");
  3308  
  3309      return target;
  3310  }
  3311  
  3312  // Unload texture from GPU memory (VRAM)
  3313  void UnloadTexture(Texture2D texture)
  3314  {
  3315      if (texture.id > 0)
  3316      {
  3317          rlUnloadTexture(texture.id);
  3318  
  3319          TRACELOG(LOG_INFO, "TEXTURE: [ID %i] Unloaded texture data from VRAM (GPU)", texture.id);
  3320      }
  3321  }
  3322  
  3323  // Unload render texture from GPU memory (VRAM)
  3324  void UnloadRenderTexture(RenderTexture2D target)
  3325  {
  3326      if (target.id > 0)
  3327      {
  3328          // Color texture attached to FBO is deleted
  3329          rlUnloadTexture(target.texture.id);
  3330  
  3331          // NOTE: Depth texture/renderbuffer is automatically
  3332          // queried and deleted before deleting framebuffer
  3333          rlUnloadFramebuffer(target.id);
  3334      }
  3335  }
  3336  
  3337  // Update GPU texture with new data
  3338  // NOTE: pixels data must match texture.format
  3339  void UpdateTexture(Texture2D texture, const void *pixels)
  3340  {
  3341      rlUpdateTexture(texture.id, 0, 0, texture.width, texture.height, texture.format, pixels);
  3342  }
  3343  
  3344  // Update GPU texture rectangle with new data
  3345  // NOTE: pixels data must match texture.format
  3346  void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels)
  3347  {
  3348      rlUpdateTexture(texture.id, (int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, texture.format, pixels);
  3349  }
  3350  
  3351  //------------------------------------------------------------------------------------
  3352  // Texture configuration functions
  3353  //------------------------------------------------------------------------------------
  3354  // Generate GPU mipmaps for a texture
  3355  void GenTextureMipmaps(Texture2D *texture)
  3356  {
  3357      // NOTE: NPOT textures support check inside function
  3358      // On WebGL (OpenGL ES 2.0) NPOT textures support is limited
  3359      rlGenTextureMipmaps(texture->id, texture->width, texture->height, texture->format, &texture->mipmaps);
  3360  }
  3361  
  3362  // Set texture scaling filter mode
  3363  void SetTextureFilter(Texture2D texture, int filter)
  3364  {
  3365      switch (filter)
  3366      {
  3367          case TEXTURE_FILTER_POINT:
  3368          {
  3369              if (texture.mipmaps > 1)
  3370              {
  3371                  // RL_TEXTURE_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps)
  3372                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_NEAREST);
  3373  
  3374                  // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
  3375                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
  3376              }
  3377              else
  3378              {
  3379                  // RL_TEXTURE_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
  3380                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_NEAREST);
  3381                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_NEAREST);
  3382              }
  3383          } break;
  3384          case TEXTURE_FILTER_BILINEAR:
  3385          {
  3386              if (texture.mipmaps > 1)
  3387              {
  3388                  // RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps)
  3389                  // Alternative: RL_TEXTURE_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps)
  3390                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR_MIP_NEAREST);
  3391  
  3392                  // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
  3393                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3394              }
  3395              else
  3396              {
  3397                  // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
  3398                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3399                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3400              }
  3401          } break;
  3402          case TEXTURE_FILTER_TRILINEAR:
  3403          {
  3404              if (texture.mipmaps > 1)
  3405              {
  3406                  // RL_TEXTURE_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps)
  3407                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_MIP_LINEAR);
  3408  
  3409                  // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
  3410                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3411              }
  3412              else
  3413              {
  3414                  TRACELOG(LOG_WARNING, "TEXTURE: [ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
  3415  
  3416                  // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
  3417                  rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3418                  rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR);
  3419              }
  3420          } break;
  3421          case TEXTURE_FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 4); break;
  3422          case TEXTURE_FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 8); break;
  3423          case TEXTURE_FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_FILTER_ANISOTROPIC, 16); break;
  3424          default: break;
  3425      }
  3426  }
  3427  
  3428  // Set texture wrapping mode
  3429  void SetTextureWrap(Texture2D texture, int wrap)
  3430  {
  3431      switch (wrap)
  3432      {
  3433          case TEXTURE_WRAP_REPEAT:
  3434          {
  3435              // NOTE: It only works if NPOT textures are supported, i.e. OpenGL ES 2.0 could not support it
  3436              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_REPEAT);
  3437              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_REPEAT);
  3438          } break;
  3439          case TEXTURE_WRAP_CLAMP:
  3440          {
  3441              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_CLAMP);
  3442              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_CLAMP);
  3443          } break;
  3444          case TEXTURE_WRAP_MIRROR_REPEAT:
  3445          {
  3446              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_REPEAT);
  3447              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_REPEAT);
  3448          } break;
  3449          case TEXTURE_WRAP_MIRROR_CLAMP:
  3450          {
  3451              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_TEXTURE_WRAP_MIRROR_CLAMP);
  3452              rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_TEXTURE_WRAP_MIRROR_CLAMP);
  3453          } break;
  3454          default: break;
  3455      }
  3456  }
  3457  
  3458  //------------------------------------------------------------------------------------
  3459  // Texture drawing functions
  3460  //------------------------------------------------------------------------------------
  3461  // Draw a texture
  3462  void DrawTexture(Texture2D texture, int posX, int posY, Color tint)
  3463  {
  3464      DrawTextureEx(texture, (Vector2){ (float)posX, (float)posY }, 0.0f, 1.0f, tint);
  3465  }
  3466  
  3467  // Draw a texture with position defined as Vector2
  3468  void DrawTextureV(Texture2D texture, Vector2 position, Color tint)
  3469  {
  3470      DrawTextureEx(texture, position, 0, 1.0f, tint);
  3471  }
  3472  
  3473  // Draw a texture with extended parameters
  3474  void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float scale, Color tint)
  3475  {
  3476      Rectangle source = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
  3477      Rectangle dest = { position.x, position.y, (float)texture.width*scale, (float)texture.height*scale };
  3478      Vector2 origin = { 0.0f, 0.0f };
  3479  
  3480      DrawTexturePro(texture, source, dest, origin, rotation, tint);
  3481  }
  3482  
  3483  // Draw a part of a texture (defined by a rectangle)
  3484  void DrawTextureRec(Texture2D texture, Rectangle source, Vector2 position, Color tint)
  3485  {
  3486      Rectangle dest = { position.x, position.y, fabsf(source.width), fabsf(source.height) };
  3487      Vector2 origin = { 0.0f, 0.0f };
  3488  
  3489      DrawTexturePro(texture, source, dest, origin, 0.0f, tint);
  3490  }
  3491  
  3492  // Draw a part of a texture (defined by a rectangle) with 'pro' parameters
  3493  // NOTE: origin is relative to destination rectangle size
  3494  void DrawTexturePro(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, Color tint)
  3495  {
  3496      // Check if texture is valid
  3497      if (texture.id > 0)
  3498      {
  3499          float width = (float)texture.width;
  3500          float height = (float)texture.height;
  3501  
  3502          bool flipX = false;
  3503  
  3504          if (source.width < 0) { flipX = true; source.width *= -1; }
  3505          if (source.height < 0) source.y -= source.height;
  3506  
  3507          Vector2 topLeft = { 0 };
  3508          Vector2 topRight = { 0 };
  3509          Vector2 bottomLeft = { 0 };
  3510          Vector2 bottomRight = { 0 };
  3511  
  3512          // Only calculate rotation if needed
  3513          if (rotation == 0.0f)
  3514          {
  3515              float x = dest.x - origin.x;
  3516              float y = dest.y - origin.y;
  3517              topLeft = (Vector2){ x, y };
  3518              topRight = (Vector2){ x + dest.width, y };
  3519              bottomLeft = (Vector2){ x, y + dest.height };
  3520              bottomRight = (Vector2){ x + dest.width, y + dest.height };
  3521          }
  3522          else
  3523          {
  3524              float sinRotation = sinf(rotation*DEG2RAD);
  3525              float cosRotation = cosf(rotation*DEG2RAD);
  3526              float x = dest.x;
  3527              float y = dest.y;
  3528              float dx = -origin.x;
  3529              float dy = -origin.y;
  3530  
  3531              topLeft.x = x + dx*cosRotation - dy*sinRotation;
  3532              topLeft.y = y + dx*sinRotation + dy*cosRotation;
  3533  
  3534              topRight.x = x + (dx + dest.width)*cosRotation - dy*sinRotation;
  3535              topRight.y = y + (dx + dest.width)*sinRotation + dy*cosRotation;
  3536  
  3537              bottomLeft.x = x + dx*cosRotation - (dy + dest.height)*sinRotation;
  3538              bottomLeft.y = y + dx*sinRotation + (dy + dest.height)*cosRotation;
  3539  
  3540              bottomRight.x = x + (dx + dest.width)*cosRotation - (dy + dest.height)*sinRotation;
  3541              bottomRight.y = y + (dx + dest.width)*sinRotation + (dy + dest.height)*cosRotation;
  3542          }
  3543  
  3544          rlSetTexture(texture.id);
  3545          rlBegin(RL_QUADS);
  3546  
  3547              rlColor4ub(tint.r, tint.g, tint.b, tint.a);
  3548              rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
  3549  
  3550              // Top-left corner for texture and quad
  3551              if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
  3552              else rlTexCoord2f(source.x/width, source.y/height);
  3553              rlVertex2f(topLeft.x, topLeft.y);
  3554  
  3555              // Bottom-left corner for texture and quad
  3556              if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
  3557              else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
  3558              rlVertex2f(bottomLeft.x, bottomLeft.y);
  3559  
  3560              // Bottom-right corner for texture and quad
  3561              if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
  3562              else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
  3563              rlVertex2f(bottomRight.x, bottomRight.y);
  3564  
  3565              // Top-right corner for texture and quad
  3566              if (flipX) rlTexCoord2f(source.x/width, source.y/height);
  3567              else rlTexCoord2f((source.x + source.width)/width, source.y/height);
  3568              rlVertex2f(topRight.x, topRight.y);
  3569  
  3570          rlEnd();
  3571          rlSetTexture(0);
  3572  
  3573          // NOTE: Vertex position can be transformed using matrices
  3574          // but the process is way more costly than just calculating
  3575          // the vertex positions manually, like done above.
  3576          // I leave here the old implementation for educational pourposes,
  3577          // just in case someone wants to do some performance test
  3578          /*
  3579          rlSetTexture(texture.id);
  3580          rlPushMatrix();
  3581              rlTranslatef(dest.x, dest.y, 0.0f);
  3582              if (rotation != 0.0f) rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
  3583              rlTranslatef(-origin.x, -origin.y, 0.0f);
  3584  
  3585              rlBegin(RL_QUADS);
  3586                  rlColor4ub(tint.r, tint.g, tint.b, tint.a);
  3587                  rlNormal3f(0.0f, 0.0f, 1.0f);                          // Normal vector pointing towards viewer
  3588  
  3589                  // Bottom-left corner for texture and quad
  3590                  if (flipX) rlTexCoord2f((source.x + source.width)/width, source.y/height);
  3591                  else rlTexCoord2f(source.x/width, source.y/height);
  3592                  rlVertex2f(0.0f, 0.0f);
  3593  
  3594                  // Bottom-right corner for texture and quad
  3595                  if (flipX) rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
  3596                  else rlTexCoord2f(source.x/width, (source.y + source.height)/height);
  3597                  rlVertex2f(0.0f, dest.height);
  3598  
  3599                  // Top-right corner for texture and quad
  3600                  if (flipX) rlTexCoord2f(source.x/width, (source.y + source.height)/height);
  3601                  else rlTexCoord2f((source.x + source.width)/width, (source.y + source.height)/height);
  3602                  rlVertex2f(dest.width, dest.height);
  3603  
  3604                  // Top-left corner for texture and quad
  3605                  if (flipX) rlTexCoord2f(source.x/width, source.y/height);
  3606                  else rlTexCoord2f((source.x + source.width)/width, source.y/height);
  3607                  rlVertex2f(dest.width, 0.0f);
  3608              rlEnd();
  3609          rlPopMatrix();
  3610          rlSetTexture(0);
  3611          */
  3612      }
  3613  }
  3614  
  3615  // Draws a texture (or part of it) that stretches or shrinks nicely using n-patch info
  3616  void DrawTextureNPatch(Texture2D texture, NPatchInfo nPatchInfo, Rectangle dest, Vector2 origin, float rotation, Color tint)
  3617  {
  3618      if (texture.id > 0)
  3619      {
  3620          float width = (float)texture.width;
  3621          float height = (float)texture.height;
  3622  
  3623          float patchWidth = ((int)dest.width <= 0)? 0.0f : dest.width;
  3624          float patchHeight = ((int)dest.height <= 0)? 0.0f : dest.height;
  3625  
  3626          if (nPatchInfo.source.width < 0) nPatchInfo.source.x -= nPatchInfo.source.width;
  3627          if (nPatchInfo.source.height < 0) nPatchInfo.source.y -= nPatchInfo.source.height;
  3628          if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL) patchHeight = nPatchInfo.source.height;
  3629          if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL) patchWidth = nPatchInfo.source.width;
  3630  
  3631          bool drawCenter = true;
  3632          bool drawMiddle = true;
  3633          float leftBorder = (float)nPatchInfo.left;
  3634          float topBorder = (float)nPatchInfo.top;
  3635          float rightBorder = (float)nPatchInfo.right;
  3636          float bottomBorder = (float)nPatchInfo.bottom;
  3637  
  3638          // Adjust the lateral (left and right) border widths in case patchWidth < texture.width
  3639          if (patchWidth <= (leftBorder + rightBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_VERTICAL)
  3640          {
  3641              drawCenter = false;
  3642              leftBorder = (leftBorder/(leftBorder + rightBorder))*patchWidth;
  3643              rightBorder = patchWidth - leftBorder;
  3644          }
  3645  
  3646          // Adjust the lateral (top and bottom) border heights in case patchHeight < texture.height
  3647          if (patchHeight <= (topBorder + bottomBorder) && nPatchInfo.layout != NPATCH_THREE_PATCH_HORIZONTAL)
  3648          {
  3649              drawMiddle = false;
  3650              topBorder = (topBorder/(topBorder + bottomBorder))*patchHeight;
  3651              bottomBorder = patchHeight - topBorder;
  3652          }
  3653  
  3654          Vector2 vertA, vertB, vertC, vertD;
  3655          vertA.x = 0.0f;                             // outer left
  3656          vertA.y = 0.0f;                             // outer top
  3657          vertB.x = leftBorder;                       // inner left
  3658          vertB.y = topBorder;                        // inner top
  3659          vertC.x = patchWidth  - rightBorder;        // inner right
  3660          vertC.y = patchHeight - bottomBorder;       // inner bottom
  3661          vertD.x = patchWidth;                       // outer right
  3662          vertD.y = patchHeight;                      // outer bottom
  3663  
  3664          Vector2 coordA, coordB, coordC, coordD;
  3665          coordA.x = nPatchInfo.source.x/width;
  3666          coordA.y = nPatchInfo.source.y/height;
  3667          coordB.x = (nPatchInfo.source.x + leftBorder)/width;
  3668          coordB.y = (nPatchInfo.source.y + topBorder)/height;
  3669          coordC.x = (nPatchInfo.source.x + nPatchInfo.source.width  - rightBorder)/width;
  3670          coordC.y = (nPatchInfo.source.y + nPatchInfo.source.height - bottomBorder)/height;
  3671          coordD.x = (nPatchInfo.source.x + nPatchInfo.source.width)/width;
  3672          coordD.y = (nPatchInfo.source.y + nPatchInfo.source.height)/height;
  3673  
  3674          rlSetTexture(texture.id);
  3675  
  3676          rlPushMatrix();
  3677              rlTranslatef(dest.x, dest.y, 0.0f);
  3678              rlRotatef(rotation, 0.0f, 0.0f, 1.0f);
  3679              rlTranslatef(-origin.x, -origin.y, 0.0f);
  3680  
  3681              rlBegin(RL_QUADS);
  3682                  rlColor4ub(tint.r, tint.g, tint.b, tint.a);
  3683                  rlNormal3f(0.0f, 0.0f, 1.0f);               // Normal vector pointing towards viewer
  3684  
  3685                  if (nPatchInfo.layout == NPATCH_NINE_PATCH)
  3686                  {
  3687                      // ------------------------------------------------------------
  3688                      // TOP-LEFT QUAD
  3689                      rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Bottom-left corner for texture and quad
  3690                      rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Bottom-right corner for texture and quad
  3691                      rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-right corner for texture and quad
  3692                      rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
  3693                      if (drawCenter)
  3694                      {
  3695                          // TOP-CENTER QUAD
  3696                          rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Bottom-left corner for texture and quad
  3697                          rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Bottom-right corner for texture and quad
  3698                          rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-right corner for texture and quad
  3699                          rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-left corner for texture and quad
  3700                      }
  3701                      // TOP-RIGHT QUAD
  3702                      rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Bottom-left corner for texture and quad
  3703                      rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Bottom-right corner for texture and quad
  3704                      rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
  3705                      rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-left corner for texture and quad
  3706                      if (drawMiddle)
  3707                      {
  3708                          // ------------------------------------------------------------
  3709                          // MIDDLE-LEFT QUAD
  3710                          rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Bottom-left corner for texture and quad
  3711                          rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Bottom-right corner for texture and quad
  3712                          rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Top-right corner for texture and quad
  3713                          rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Top-left corner for texture and quad
  3714                          if (drawCenter)
  3715                          {
  3716                              // MIDDLE-CENTER QUAD
  3717                              rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Bottom-left corner for texture and quad
  3718                              rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Bottom-right corner for texture and quad
  3719                              rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Top-right corner for texture and quad
  3720                              rlTexCoord2f(coordB.x, coordB.y); rlVertex2f(vertB.x, vertB.y);  // Top-left corner for texture and quad
  3721                          }
  3722  
  3723                          // MIDDLE-RIGHT QUAD
  3724                          rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Bottom-left corner for texture and quad
  3725                          rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Bottom-right corner for texture and quad
  3726                          rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Top-right corner for texture and quad
  3727                          rlTexCoord2f(coordC.x, coordB.y); rlVertex2f(vertC.x, vertB.y);  // Top-left corner for texture and quad
  3728                      }
  3729  
  3730                      // ------------------------------------------------------------
  3731                      // BOTTOM-LEFT QUAD
  3732                      rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
  3733                      rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-right corner for texture and quad
  3734                      rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Top-right corner for texture and quad
  3735                      rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Top-left corner for texture and quad
  3736                      if (drawCenter)
  3737                      {
  3738                          // BOTTOM-CENTER QUAD
  3739                          rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-left corner for texture and quad
  3740                          rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-right corner for texture and quad
  3741                          rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Top-right corner for texture and quad
  3742                          rlTexCoord2f(coordB.x, coordC.y); rlVertex2f(vertB.x, vertC.y);  // Top-left corner for texture and quad
  3743                      }
  3744  
  3745                      // BOTTOM-RIGHT QUAD
  3746                      rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-left corner for texture and quad
  3747                      rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
  3748                      rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Top-right corner for texture and quad
  3749                      rlTexCoord2f(coordC.x, coordC.y); rlVertex2f(vertC.x, vertC.y);  // Top-left corner for texture and quad
  3750                  }
  3751                  else if (nPatchInfo.layout == NPATCH_THREE_PATCH_VERTICAL)
  3752                  {
  3753                      // TOP QUAD
  3754                      // -----------------------------------------------------------
  3755                      // Texture coords                 Vertices
  3756                      rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Bottom-left corner for texture and quad
  3757                      rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Bottom-right corner for texture and quad
  3758                      rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
  3759                      rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
  3760                      if (drawCenter)
  3761                      {
  3762                          // MIDDLE QUAD
  3763                          // -----------------------------------------------------------
  3764                          // Texture coords                 Vertices
  3765                          rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Bottom-left corner for texture and quad
  3766                          rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Bottom-right corner for texture and quad
  3767                          rlTexCoord2f(coordD.x, coordB.y); rlVertex2f(vertD.x, vertB.y);  // Top-right corner for texture and quad
  3768                          rlTexCoord2f(coordA.x, coordB.y); rlVertex2f(vertA.x, vertB.y);  // Top-left corner for texture and quad
  3769                      }
  3770                      // BOTTOM QUAD
  3771                      // -----------------------------------------------------------
  3772                      // Texture coords                 Vertices
  3773                      rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
  3774                      rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
  3775                      rlTexCoord2f(coordD.x, coordC.y); rlVertex2f(vertD.x, vertC.y);  // Top-right corner for texture and quad
  3776                      rlTexCoord2f(coordA.x, coordC.y); rlVertex2f(vertA.x, vertC.y);  // Top-left corner for texture and quad
  3777                  }
  3778                  else if (nPatchInfo.layout == NPATCH_THREE_PATCH_HORIZONTAL)
  3779                  {
  3780                      // LEFT QUAD
  3781                      // -----------------------------------------------------------
  3782                      // Texture coords                 Vertices
  3783                      rlTexCoord2f(coordA.x, coordD.y); rlVertex2f(vertA.x, vertD.y);  // Bottom-left corner for texture and quad
  3784                      rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-right corner for texture and quad
  3785                      rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-right corner for texture and quad
  3786                      rlTexCoord2f(coordA.x, coordA.y); rlVertex2f(vertA.x, vertA.y);  // Top-left corner for texture and quad
  3787                      if (drawCenter)
  3788                      {
  3789                          // CENTER QUAD
  3790                          // -----------------------------------------------------------
  3791                          // Texture coords                 Vertices
  3792                          rlTexCoord2f(coordB.x, coordD.y); rlVertex2f(vertB.x, vertD.y);  // Bottom-left corner for texture and quad
  3793                          rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-right corner for texture and quad
  3794                          rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-right corner for texture and quad
  3795                          rlTexCoord2f(coordB.x, coordA.y); rlVertex2f(vertB.x, vertA.y);  // Top-left corner for texture and quad
  3796                      }
  3797                      // RIGHT QUAD
  3798                      // -----------------------------------------------------------
  3799                      // Texture coords                 Vertices
  3800                      rlTexCoord2f(coordC.x, coordD.y); rlVertex2f(vertC.x, vertD.y);  // Bottom-left corner for texture and quad
  3801                      rlTexCoord2f(coordD.x, coordD.y); rlVertex2f(vertD.x, vertD.y);  // Bottom-right corner for texture and quad
  3802                      rlTexCoord2f(coordD.x, coordA.y); rlVertex2f(vertD.x, vertA.y);  // Top-right corner for texture and quad
  3803                      rlTexCoord2f(coordC.x, coordA.y); rlVertex2f(vertC.x, vertA.y);  // Top-left corner for texture and quad
  3804                  }
  3805              rlEnd();
  3806          rlPopMatrix();
  3807  
  3808          rlSetTexture(0);
  3809      }
  3810  }
  3811  
  3812  // Get color with alpha applied, alpha goes from 0.0f to 1.0f
  3813  Color Fade(Color color, float alpha)
  3814  {
  3815      if (alpha < 0.0f) alpha = 0.0f;
  3816      else if (alpha > 1.0f) alpha = 1.0f;
  3817  
  3818      return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
  3819  }
  3820  
  3821  // Get hexadecimal value for a Color
  3822  int ColorToInt(Color color)
  3823  {
  3824      return (((int)color.r << 24) | ((int)color.g << 16) | ((int)color.b << 8) | (int)color.a);
  3825  }
  3826  
  3827  // Get color normalized as float [0..1]
  3828  Vector4 ColorNormalize(Color color)
  3829  {
  3830      Vector4 result;
  3831  
  3832      result.x = (float)color.r/255.0f;
  3833      result.y = (float)color.g/255.0f;
  3834      result.z = (float)color.b/255.0f;
  3835      result.w = (float)color.a/255.0f;
  3836  
  3837      return result;
  3838  }
  3839  
  3840  // Get color from normalized values [0..1]
  3841  Color ColorFromNormalized(Vector4 normalized)
  3842  {
  3843      Color result;
  3844  
  3845      result.r = (unsigned char)(normalized.x*255.0f);
  3846      result.g = (unsigned char)(normalized.y*255.0f);
  3847      result.b = (unsigned char)(normalized.z*255.0f);
  3848      result.a = (unsigned char)(normalized.w*255.0f);
  3849  
  3850      return result;
  3851  }
  3852  
  3853  // Get HSV values for a Color
  3854  // NOTE: Hue is returned as degrees [0..360]
  3855  Vector3 ColorToHSV(Color color)
  3856  {
  3857      Vector3 hsv = { 0 };
  3858      Vector3 rgb = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  3859      float min, max, delta;
  3860  
  3861      min = rgb.x < rgb.y? rgb.x : rgb.y;
  3862      min = min  < rgb.z? min  : rgb.z;
  3863  
  3864      max = rgb.x > rgb.y? rgb.x : rgb.y;
  3865      max = max  > rgb.z? max  : rgb.z;
  3866  
  3867      hsv.z = max;            // Value
  3868      delta = max - min;
  3869  
  3870      if (delta < 0.00001f)
  3871      {
  3872          hsv.y = 0.0f;
  3873          hsv.x = 0.0f;       // Undefined, maybe NAN?
  3874          return hsv;
  3875      }
  3876  
  3877      if (max > 0.0f)
  3878      {
  3879          // NOTE: If max is 0, this divide would cause a crash
  3880          hsv.y = (delta/max);    // Saturation
  3881      }
  3882      else
  3883      {
  3884          // NOTE: If max is 0, then r = g = b = 0, s = 0, h is undefined
  3885          hsv.y = 0.0f;
  3886          hsv.x = NAN;        // Undefined
  3887          return hsv;
  3888      }
  3889  
  3890      // NOTE: Comparing float values could not work properly
  3891      if (rgb.x >= max) hsv.x = (rgb.y - rgb.z)/delta;    // Between yellow & magenta
  3892      else
  3893      {
  3894          if (rgb.y >= max) hsv.x = 2.0f + (rgb.z - rgb.x)/delta;  // Between cyan & yellow
  3895          else hsv.x = 4.0f + (rgb.x - rgb.y)/delta;      // Between magenta & cyan
  3896      }
  3897  
  3898      hsv.x *= 60.0f;     // Convert to degrees
  3899  
  3900      if (hsv.x < 0.0f) hsv.x += 360.0f;
  3901  
  3902      return hsv;
  3903  }
  3904  
  3905  // Get a Color from HSV values
  3906  // Implementation reference: https://en.wikipedia.org/wiki/HSL_and_HSV#Alternative_HSV_conversion
  3907  // NOTE: Color->HSV->Color conversion will not yield exactly the same color due to rounding errors
  3908  // Hue is provided in degrees: [0..360]
  3909  // Saturation/Value are provided normalized: [0.0f..1.0f]
  3910  Color ColorFromHSV(float hue, float saturation, float value)
  3911  {
  3912      Color color = { 0, 0, 0, 255 };
  3913  
  3914      // Red channel
  3915      float k = fmodf((5.0f + hue/60.0f), 6);
  3916      float t = 4.0f - k;
  3917      k = (t < k)? t : k;
  3918      k = (k < 1)? k : 1;
  3919      k = (k > 0)? k : 0;
  3920      color.r = (unsigned char)((value - value*saturation*k)*255.0f);
  3921  
  3922      // Green channel
  3923      k = fmodf((3.0f + hue/60.0f), 6);
  3924      t = 4.0f - k;
  3925      k = (t < k)? t : k;
  3926      k = (k < 1)? k : 1;
  3927      k = (k > 0)? k : 0;
  3928      color.g = (unsigned char)((value - value*saturation*k)*255.0f);
  3929  
  3930      // Blue channel
  3931      k = fmodf((1.0f + hue/60.0f), 6);
  3932      t = 4.0f - k;
  3933      k = (t < k)? t : k;
  3934      k = (k < 1)? k : 1;
  3935      k = (k > 0)? k : 0;
  3936      color.b = (unsigned char)((value - value*saturation*k)*255.0f);
  3937  
  3938      return color;
  3939  }
  3940  
  3941  // Get color with alpha applied, alpha goes from 0.0f to 1.0f
  3942  Color ColorAlpha(Color color, float alpha)
  3943  {
  3944      if (alpha < 0.0f) alpha = 0.0f;
  3945      else if (alpha > 1.0f) alpha = 1.0f;
  3946  
  3947      return (Color){color.r, color.g, color.b, (unsigned char)(255.0f*alpha)};
  3948  }
  3949  
  3950  // Get src alpha-blended into dst color with tint
  3951  Color ColorAlphaBlend(Color dst, Color src, Color tint)
  3952  {
  3953      Color out = WHITE;
  3954  
  3955      // Apply color tint to source color
  3956      src.r = (unsigned char)(((unsigned int)src.r*((unsigned int)tint.r+1)) >> 8);
  3957      src.g = (unsigned char)(((unsigned int)src.g*((unsigned int)tint.g+1)) >> 8);
  3958      src.b = (unsigned char)(((unsigned int)src.b*((unsigned int)tint.b+1)) >> 8);
  3959      src.a = (unsigned char)(((unsigned int)src.a*((unsigned int)tint.a+1)) >> 8);
  3960  
  3961  //#define COLORALPHABLEND_FLOAT
  3962  #define COLORALPHABLEND_INTEGERS
  3963  #if defined(COLORALPHABLEND_INTEGERS)
  3964      if (src.a == 0) out = dst;
  3965      else if (src.a == 255) out = src;
  3966      else
  3967      {
  3968          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
  3969          out.a = (unsigned char)(((unsigned int)alpha*256 + (unsigned int)dst.a*(256 - alpha)) >> 8);
  3970  
  3971          if (out.a > 0)
  3972          {
  3973              out.r = (unsigned char)((((unsigned int)src.r*alpha*256 + (unsigned int)dst.r*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
  3974              out.g = (unsigned char)((((unsigned int)src.g*alpha*256 + (unsigned int)dst.g*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
  3975              out.b = (unsigned char)((((unsigned int)src.b*alpha*256 + (unsigned int)dst.b*(unsigned int)dst.a*(256 - alpha))/out.a) >> 8);
  3976          }
  3977      }
  3978  #endif
  3979  #if defined(COLORALPHABLEND_FLOAT)
  3980      if (src.a == 0) out = dst;
  3981      else if (src.a == 255) out = src;
  3982      else
  3983      {
  3984          Vector4 fdst = ColorNormalize(dst);
  3985          Vector4 fsrc = ColorNormalize(src);
  3986          Vector4 ftint = ColorNormalize(tint);
  3987          Vector4 fout = { 0 };
  3988  
  3989          fout.w = fsrc.w + fdst.w*(1.0f - fsrc.w);
  3990  
  3991          if (fout.w > 0.0f)
  3992          {
  3993              fout.x = (fsrc.x*fsrc.w + fdst.x*fdst.w*(1 - fsrc.w))/fout.w;
  3994              fout.y = (fsrc.y*fsrc.w + fdst.y*fdst.w*(1 - fsrc.w))/fout.w;
  3995              fout.z = (fsrc.z*fsrc.w + fdst.z*fdst.w*(1 - fsrc.w))/fout.w;
  3996          }
  3997  
  3998          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) };
  3999      }
  4000  #endif
  4001  
  4002      return out;
  4003  }
  4004  
  4005  // Get a Color struct from hexadecimal value
  4006  Color GetColor(unsigned int hexValue)
  4007  {
  4008      Color color;
  4009  
  4010      color.r = (unsigned char)(hexValue >> 24) & 0xFF;
  4011      color.g = (unsigned char)(hexValue >> 16) & 0xFF;
  4012      color.b = (unsigned char)(hexValue >> 8) & 0xFF;
  4013      color.a = (unsigned char)hexValue & 0xFF;
  4014  
  4015      return color;
  4016  }
  4017  
  4018  // Get color from a pixel from certain format
  4019  Color GetPixelColor(void *srcPtr, int format)
  4020  {
  4021      Color color = { 0 };
  4022  
  4023      switch (format)
  4024      {
  4025          case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], 255 }; break;
  4026          case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1] }; break;
  4027          case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  4028          {
  4029              color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
  4030              color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 5) & 0b0000000000111111)*255/63);
  4031              color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
  4032              color.a = 255;
  4033  
  4034          } break;
  4035          case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  4036          {
  4037              color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 11)*255/31);
  4038              color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 6) & 0b0000000000011111)*255/31);
  4039              color.b = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000011111)*255/31);
  4040              color.a = (((unsigned short *)srcPtr)[0] & 0b0000000000000001)? 255 : 0;
  4041  
  4042          } break;
  4043          case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  4044          {
  4045              color.r = (unsigned char)((((unsigned short *)srcPtr)[0] >> 12)*255/15);
  4046              color.g = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 8) & 0b0000000000001111)*255/15);
  4047              color.b = (unsigned char)(((((unsigned short *)srcPtr)[0] >> 4) & 0b0000000000001111)*255/15);
  4048              color.a = (unsigned char)((((unsigned short *)srcPtr)[0] & 0b0000000000001111)*255/15);
  4049  
  4050          } break;
  4051          case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], ((unsigned char *)srcPtr)[3] }; break;
  4052          case PIXELFORMAT_UNCOMPRESSED_R8G8B8: color = (Color){ ((unsigned char *)srcPtr)[0], ((unsigned char *)srcPtr)[1], ((unsigned char *)srcPtr)[2], 255 }; break;
  4053          case PIXELFORMAT_UNCOMPRESSED_R32:
  4054          {
  4055              // NOTE: Pixel normalized float value is converted to [0..255]
  4056              color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
  4057              color.g = (unsigned char)(((float *)srcPtr)[0]*255.0f);
  4058              color.b = (unsigned char)(((float *)srcPtr)[0]*255.0f);
  4059              color.a = 255;
  4060  
  4061          } break;
  4062          case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  4063          {
  4064              // NOTE: Pixel normalized float value is converted to [0..255]
  4065              color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
  4066              color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
  4067              color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
  4068              color.a = 255;
  4069  
  4070          } break;
  4071          case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  4072          {
  4073              // NOTE: Pixel normalized float value is converted to [0..255]
  4074              color.r = (unsigned char)(((float *)srcPtr)[0]*255.0f);
  4075              color.g = (unsigned char)(((float *)srcPtr)[1]*255.0f);
  4076              color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f);
  4077              color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f);
  4078  
  4079          } break;
  4080          default: break;
  4081      }
  4082  
  4083      return color;
  4084  }
  4085  
  4086  // Set pixel color formatted into destination pointer
  4087  void SetPixelColor(void *dstPtr, Color color, int format)
  4088  {
  4089      switch (format)
  4090      {
  4091          case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  4092          {
  4093              // NOTE: Calculate grayscale equivalent color
  4094              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  4095              unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
  4096  
  4097              ((unsigned char *)dstPtr)[0] = gray;
  4098  
  4099          } break;
  4100          case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  4101          {
  4102              // NOTE: Calculate grayscale equivalent color
  4103              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  4104              unsigned char gray = (unsigned char)((coln.x*0.299f + coln.y*0.587f + coln.z*0.114f)*255.0f);
  4105  
  4106              ((unsigned char *)dstPtr)[0] = gray;
  4107              ((unsigned char *)dstPtr)[1] = color.a;
  4108  
  4109          } break;
  4110          case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  4111          {
  4112              // NOTE: Calculate R5G6B5 equivalent color
  4113              Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f };
  4114  
  4115              unsigned char r = (unsigned char)(round(coln.x*31.0f));
  4116              unsigned char g = (unsigned char)(round(coln.y*63.0f));
  4117              unsigned char b = (unsigned char)(round(coln.z*31.0f));
  4118  
  4119              ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 5 | (unsigned short)b;
  4120  
  4121          } break;
  4122          case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  4123          {
  4124              // NOTE: Calculate R5G5B5A1 equivalent color
  4125              Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
  4126  
  4127              unsigned char r = (unsigned char)(round(coln.x*31.0f));
  4128              unsigned char g = (unsigned char)(round(coln.y*31.0f));
  4129              unsigned char b = (unsigned char)(round(coln.z*31.0f));
  4130              unsigned char a = (coln.w > ((float)PIXELFORMAT_UNCOMPRESSED_R5G5B5A1_ALPHA_THRESHOLD/255.0f))? 1 : 0;
  4131  
  4132              ((unsigned short *)dstPtr)[0] = (unsigned short)r << 11 | (unsigned short)g << 6 | (unsigned short)b << 1 | (unsigned short)a;
  4133  
  4134          } break;
  4135          case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  4136          {
  4137              // NOTE: Calculate R5G5B5A1 equivalent color
  4138              Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f };
  4139  
  4140              unsigned char r = (unsigned char)(round(coln.x*15.0f));
  4141              unsigned char g = (unsigned char)(round(coln.y*15.0f));
  4142              unsigned char b = (unsigned char)(round(coln.z*15.0f));
  4143              unsigned char a = (unsigned char)(round(coln.w*15.0f));
  4144  
  4145              ((unsigned short *)dstPtr)[0] = (unsigned short)r << 12 | (unsigned short)g << 8 | (unsigned short)b << 4 | (unsigned short)a;
  4146  
  4147          } break;
  4148          case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  4149          {
  4150              ((unsigned char *)dstPtr)[0] = color.r;
  4151              ((unsigned char *)dstPtr)[1] = color.g;
  4152              ((unsigned char *)dstPtr)[2] = color.b;
  4153  
  4154          } break;
  4155          case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  4156          {
  4157              ((unsigned char *)dstPtr)[0] = color.r;
  4158              ((unsigned char *)dstPtr)[1] = color.g;
  4159              ((unsigned char *)dstPtr)[2] = color.b;
  4160              ((unsigned char *)dstPtr)[3] = color.a;
  4161  
  4162          } break;
  4163          default: break;
  4164      }
  4165  }
  4166  
  4167  // Get pixel data size in bytes for certain format
  4168  // NOTE: Size can be requested for Image or Texture data
  4169  int GetPixelDataSize(int width, int height, int format)
  4170  {
  4171      int dataSize = 0;       // Size in bytes
  4172      int bpp = 0;            // Bits per pixel
  4173  
  4174      switch (format)
  4175      {
  4176          case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
  4177          case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  4178          case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  4179          case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  4180          case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
  4181          case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
  4182          case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
  4183          case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
  4184          case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
  4185          case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
  4186          case PIXELFORMAT_COMPRESSED_DXT1_RGB:
  4187          case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
  4188          case PIXELFORMAT_COMPRESSED_ETC1_RGB:
  4189          case PIXELFORMAT_COMPRESSED_ETC2_RGB:
  4190          case PIXELFORMAT_COMPRESSED_PVRT_RGB:
  4191          case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
  4192          case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
  4193          case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
  4194          case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
  4195          case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
  4196          case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
  4197          default: break;
  4198      }
  4199  
  4200      dataSize = width*height*bpp/8;  // Total data size in bytes
  4201  
  4202      // Most compressed formats works on 4x4 blocks,
  4203      // if texture is smaller, minimum dataSize is 8 or 16
  4204      if ((width < 4) && (height < 4))
  4205      {
  4206          if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) dataSize = 8;
  4207          else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) dataSize = 16;
  4208      }
  4209  
  4210      return dataSize;
  4211  }
  4212  
  4213  //----------------------------------------------------------------------------------
  4214  // Module specific Functions Definition
  4215  //----------------------------------------------------------------------------------
  4216  // Get pixel data from image as Vector4 array (float normalized)
  4217  static Vector4 *LoadImageDataNormalized(Image image)
  4218  {
  4219      Vector4 *pixels = (Vector4 *)RL_MALLOC(image.width*image.height*sizeof(Vector4));
  4220  
  4221      if (image.format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "IMAGE: Pixel data retrieval not supported for compressed image formats");
  4222      else
  4223      {
  4224          for (int i = 0, k = 0; i < image.width*image.height; i++)
  4225          {
  4226              switch (image.format)
  4227              {
  4228                  case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE:
  4229                  {
  4230                      pixels[i].x = (float)((unsigned char *)image.data)[i]/255.0f;
  4231                      pixels[i].y = (float)((unsigned char *)image.data)[i]/255.0f;
  4232                      pixels[i].z = (float)((unsigned char *)image.data)[i]/255.0f;
  4233                      pixels[i].w = 1.0f;
  4234  
  4235                  } break;
  4236                  case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
  4237                  {
  4238                      pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
  4239                      pixels[i].y = (float)((unsigned char *)image.data)[k]/255.0f;
  4240                      pixels[i].z = (float)((unsigned char *)image.data)[k]/255.0f;
  4241                      pixels[i].w = (float)((unsigned char *)image.data)[k + 1]/255.0f;
  4242  
  4243                      k += 2;
  4244                  } break;
  4245                  case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
  4246                  {
  4247                      unsigned short pixel = ((unsigned short *)image.data)[i];
  4248  
  4249                      pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
  4250                      pixels[i].y = (float)((pixel & 0b0000011111000000) >> 6)*(1.0f/31);
  4251                      pixels[i].z = (float)((pixel & 0b0000000000111110) >> 1)*(1.0f/31);
  4252                      pixels[i].w = ((pixel & 0b0000000000000001) == 0)? 0.0f : 1.0f;
  4253  
  4254                  } break;
  4255                  case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
  4256                  {
  4257                      unsigned short pixel = ((unsigned short *)image.data)[i];
  4258  
  4259                      pixels[i].x = (float)((pixel & 0b1111100000000000) >> 11)*(1.0f/31);
  4260                      pixels[i].y = (float)((pixel & 0b0000011111100000) >> 5)*(1.0f/63);
  4261                      pixels[i].z = (float)(pixel & 0b0000000000011111)*(1.0f/31);
  4262                      pixels[i].w = 1.0f;
  4263  
  4264                  } break;
  4265                  case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4:
  4266                  {
  4267                      unsigned short pixel = ((unsigned short *)image.data)[i];
  4268  
  4269                      pixels[i].x = (float)((pixel & 0b1111000000000000) >> 12)*(1.0f/15);
  4270                      pixels[i].y = (float)((pixel & 0b0000111100000000) >> 8)*(1.0f/15);
  4271                      pixels[i].z = (float)((pixel & 0b0000000011110000) >> 4)*(1.0f/15);
  4272                      pixels[i].w = (float)(pixel & 0b0000000000001111)*(1.0f/15);
  4273  
  4274                  } break;
  4275                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8:
  4276                  {
  4277                      pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
  4278                      pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
  4279                      pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
  4280                      pixels[i].w = (float)((unsigned char *)image.data)[k + 3]/255.0f;
  4281  
  4282                      k += 4;
  4283                  } break;
  4284                  case PIXELFORMAT_UNCOMPRESSED_R8G8B8:
  4285                  {
  4286                      pixels[i].x = (float)((unsigned char *)image.data)[k]/255.0f;
  4287                      pixels[i].y = (float)((unsigned char *)image.data)[k + 1]/255.0f;
  4288                      pixels[i].z = (float)((unsigned char *)image.data)[k + 2]/255.0f;
  4289                      pixels[i].w = 1.0f;
  4290  
  4291                      k += 3;
  4292                  } break;
  4293                  case PIXELFORMAT_UNCOMPRESSED_R32:
  4294                  {
  4295                      pixels[i].x = ((float *)image.data)[k];
  4296                      pixels[i].y = 0.0f;
  4297                      pixels[i].z = 0.0f;
  4298                      pixels[i].w = 1.0f;
  4299  
  4300                  } break;
  4301                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32:
  4302                  {
  4303                      pixels[i].x = ((float *)image.data)[k];
  4304                      pixels[i].y = ((float *)image.data)[k + 1];
  4305                      pixels[i].z = ((float *)image.data)[k + 2];
  4306                      pixels[i].w = 1.0f;
  4307  
  4308                      k += 3;
  4309                  } break;
  4310                  case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32:
  4311                  {
  4312                      pixels[i].x = ((float *)image.data)[k];
  4313                      pixels[i].y = ((float *)image.data)[k + 1];
  4314                      pixels[i].z = ((float *)image.data)[k + 2];
  4315                      pixels[i].w = ((float *)image.data)[k + 3];
  4316  
  4317                      k += 4;
  4318                  }
  4319                  default: break;
  4320              }
  4321          }
  4322      }
  4323  
  4324      return pixels;
  4325  }
  4326  
  4327  #endif      // SUPPORT_MODULE_RTEXTURES