github.com/Konstantin8105/c4go@v0.0.0-20240505174241-768bb1c65a51/tests/raylib/external/rl_gputex.h (about)

     1  /**********************************************************************************************
     2  *
     3  *   rl_gputex - GPU compressed textures loading and saving
     4  *
     5  *   DESCRIPTION:
     6  *
     7  *     Load GPU compressed image data from image files provided as memory data arrays,
     8  *     data is loaded compressed, ready to be loaded into GPU.
     9  *
    10  *     Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage.
    11  *     In those cases data is loaded uncompressed and format is returned.
    12  * 
    13  *   TODO:
    14  *     - Implement raylib function: rlGetGlTextureFormats(), required by rl_save_ktx_to_memory()
    15  *     - Review rl_load_ktx_from_memory() to support KTX v2.2 specs
    16  *
    17  *   CONFIGURATION:
    18  *
    19  *   #define RL_GPUTEX_SUPPORT_DDS
    20  *   #define RL_GPUTEX_SUPPORT_PKM
    21  *   #define RL_GPUTEX_SUPPORT_KTX
    22  *   #define RL_GPUTEX_SUPPORT_PVR
    23  *   #define RL_GPUTEX_SUPPORT_ASTC
    24  *       Define desired file formats to be supported
    25  *
    26  *
    27  *   LICENSE: zlib/libpng
    28  *
    29  *   Copyright (c) 2013-2022 Ramon Santamaria (@raysan5)
    30  *
    31  *   This software is provided "as-is", without any express or implied warranty. In no event
    32  *   will the authors be held liable for any damages arising from the use of this software.
    33  *
    34  *   Permission is granted to anyone to use this software for any purpose, including commercial
    35  *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
    36  *
    37  *     1. The origin of this software must not be misrepresented; you must not claim that you
    38  *     wrote the original software. If you use this software in a product, an acknowledgment
    39  *     in the product documentation would be appreciated but is not required.
    40  *
    41  *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
    42  *     as being the original software.
    43  *
    44  *     3. This notice may not be removed or altered from any source distribution.
    45  *
    46  **********************************************************************************************/
    47  
    48  #ifndef RL_GPUTEX_H
    49  #define RL_GPUTEX_H
    50  
    51  #ifndef RLAPI
    52      #define RLAPI       // Functions defined as 'extern' by default (implicit specifiers)
    53  #endif
    54  
    55  //----------------------------------------------------------------------------------
    56  // Module Functions Declaration
    57  //----------------------------------------------------------------------------------
    58  #if defined(__cplusplus)
    59  extern "C" {            // Prevents name mangling of functions
    60  #endif
    61  
    62  // Load image data from memory data files
    63  RLAPI void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
    64  RLAPI void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
    65  RLAPI void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
    66  RLAPI void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
    67  RLAPI void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips);
    68  
    69  RLAPI int rl_save_ktx_to_memory(const char *fileName, void *data, int width, int height, int format, int mipmaps);  // Save image data as KTX file
    70  
    71  #if defined(__cplusplus)
    72  }
    73  #endif
    74  
    75  #endif // RL_GPUTEX_H
    76  
    77  
    78  /***********************************************************************************
    79  *
    80  *   RL_GPUTEX IMPLEMENTATION
    81  *
    82  ************************************************************************************/
    83  
    84  #if defined(RL_GPUTEX_IMPLEMENTATION)
    85  
    86  // Simple log system to avoid RPNG_LOG() calls if required
    87  // NOTE: Avoiding those calls, also avoids const strings memory usage
    88  #define RL_GPUTEX_SHOW_LOG_INFO
    89  #if defined(RL_GPUTEX_SHOW_LOG_INFO) && !defined(LOG)
    90  #define LOG(...) printf(__VA_ARGS__)
    91  #else
    92  #define LOG(...)
    93  #endif
    94  
    95  //----------------------------------------------------------------------------------
    96  // Module Internal Functions Declaration
    97  //----------------------------------------------------------------------------------
    98  // Get pixel data size in bytes for certain pixel format
    99  static int get_pixel_data_size(int width, int height, int format);
   100  
   101  //----------------------------------------------------------------------------------
   102  // Module Functions Definition
   103  //----------------------------------------------------------------------------------
   104  #if defined(RL_GPUTEX_SUPPORT_DDS)
   105  // Loading DDS from memory image data (compressed or uncompressed)
   106  void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
   107  {
   108      void *image_data = NULL;        // Image data pointer
   109      int image_pixel_size = 0;       // Image pixel size
   110  
   111      unsigned char *file_data_ptr = (unsigned char *)file_data;
   112  
   113      // Required extension:
   114      // GL_EXT_texture_compression_s3tc
   115  
   116      // Supported tokens (defined by extensions)
   117      // GL_COMPRESSED_RGB_S3TC_DXT1_EXT      0x83F0
   118      // GL_COMPRESSED_RGBA_S3TC_DXT1_EXT     0x83F1
   119      // GL_COMPRESSED_RGBA_S3TC_DXT3_EXT     0x83F2
   120      // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT     0x83F3
   121  
   122      #define FOURCC_DXT1 0x31545844  // Equivalent to "DXT1" in ASCII
   123      #define FOURCC_DXT3 0x33545844  // Equivalent to "DXT3" in ASCII
   124      #define FOURCC_DXT5 0x35545844  // Equivalent to "DXT5" in ASCII
   125  
   126      // DDS Pixel Format
   127      typedef struct {
   128          unsigned int size;
   129          unsigned int flags;
   130          unsigned int fourcc;
   131          unsigned int rgb_bit_count;
   132          unsigned int r_bit_mask;
   133          unsigned int g_bit_mask;
   134          unsigned int b_bit_mask;
   135          unsigned int a_bit_mask;
   136      } dds_pixel_format;
   137  
   138      // DDS Header (124 bytes)
   139      typedef struct {
   140          unsigned int size;
   141          unsigned int flags;
   142          unsigned int height;
   143          unsigned int width;
   144          unsigned int pitch_or_linear_size;
   145          unsigned int depth;
   146          unsigned int mipmap_count;
   147          unsigned int reserved1[11];
   148          dds_pixel_format ddspf;
   149          unsigned int caps;
   150          unsigned int caps2;
   151          unsigned int caps3;
   152          unsigned int caps4;
   153          unsigned int reserved2;
   154      } dds_header;
   155  
   156      if (file_data_ptr != NULL)
   157      {
   158          // Verify the type of file
   159          unsigned char *dds_header_id = file_data_ptr;
   160          file_data_ptr += 4;
   161  
   162          if ((dds_header_id[0] != 'D') || (dds_header_id[1] != 'D') || (dds_header_id[2] != 'S') || (dds_header_id[3] != ' '))
   163          {
   164              LOG("WARNING: IMAGE: DDS file data not valid");
   165          }
   166          else
   167          {
   168              dds_header *header = (dds_header *)file_data_ptr;
   169  
   170              file_data_ptr += sizeof(dds_header);        // Skip header
   171  
   172              *width = header->width;
   173              *height = header->height;
   174              image_pixel_size = header->width*header->height;
   175  
   176              if (header->mipmap_count == 0) *mips = 1;   // Parameter not used
   177              else *mips = header->mipmap_count;
   178  
   179              if (header->ddspf.rgb_bit_count == 16)      // 16bit mode, no compressed
   180              {
   181                  if (header->ddspf.flags == 0x40)        // No alpha channel
   182                  {
   183                      int data_size = image_pixel_size*sizeof(unsigned short);
   184                      image_data = RL_MALLOC(data_size);
   185  
   186                      memcpy(image_data, file_data_ptr, data_size);
   187  
   188                      *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
   189                  }
   190                  else if (header->ddspf.flags == 0x41)           // With alpha channel
   191                  {
   192                      if (header->ddspf.a_bit_mask == 0x8000)     // 1bit alpha
   193                      {
   194                          int data_size = image_pixel_size*sizeof(unsigned short);
   195                          image_data = RL_MALLOC(data_size);
   196  
   197                          memcpy(image_data, file_data_ptr, data_size);
   198  
   199                          unsigned char alpha = 0;
   200  
   201                          // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
   202                          for (int i = 0; i < image_pixel_size; i++)
   203                          {
   204                              alpha = ((unsigned short *)image_data)[i] >> 15;
   205                              ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
   206                              ((unsigned short *)image_data)[i] += alpha;
   207                          }
   208  
   209                          *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
   210                      }
   211                      else if (header->ddspf.a_bit_mask == 0xf000)   // 4bit alpha
   212                      {
   213                          int data_size = image_pixel_size*sizeof(unsigned short);
   214                          image_data = RL_MALLOC(data_size);
   215  
   216                          memcpy(image_data, file_data_ptr, data_size);
   217  
   218                          unsigned char alpha = 0;
   219  
   220                          // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
   221                          for (int i = 0; i < image_pixel_size; i++)
   222                          {
   223                              alpha = ((unsigned short *)image_data)[i] >> 12;
   224                              ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
   225                              ((unsigned short *)image_data)[i] += alpha;
   226                          }
   227  
   228                          *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
   229                      }
   230                  }
   231              }
   232              else if (header->ddspf.flags == 0x40 && header->ddspf.rgb_bit_count == 24)   // DDS_RGB, no compressed
   233              {
   234                  int data_size = image_pixel_size*3*sizeof(unsigned char);
   235                  image_data = RL_MALLOC(data_size);
   236  
   237                  memcpy(image_data, file_data_ptr, data_size);
   238  
   239                  *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
   240              }
   241              else if (header->ddspf.flags == 0x41 && header->ddspf.rgb_bit_count == 32) // DDS_RGBA, no compressed
   242              {
   243                  int data_size = image_pixel_size*4*sizeof(unsigned char);
   244                  image_data = RL_MALLOC(data_size);
   245  
   246                  memcpy(image_data, file_data_ptr, data_size);
   247  
   248                  unsigned char blue = 0;
   249  
   250                  // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
   251                  // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
   252                  // So, we must realign B8G8R8A8 to R8G8B8A8
   253                  for (int i = 0; i < image_pixel_size*4; i += 4)
   254                  {
   255                      blue = ((unsigned char *)image_data)[i];
   256                      ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2];
   257                      ((unsigned char *)image_data)[i + 2] = blue;
   258                  }
   259  
   260                  *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   261              }
   262              else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
   263              {
   264                  int data_size = 0;
   265  
   266                  // Calculate data size, including all mipmaps
   267                  if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size*2;
   268                  else data_size = header->pitch_or_linear_size;
   269  
   270                  image_data = RL_MALLOC(data_size*sizeof(unsigned char));
   271  
   272                  memcpy(image_data, file_data_ptr, data_size);
   273  
   274                  switch (header->ddspf.fourcc)
   275                  {
   276                      case FOURCC_DXT1:
   277                      {
   278                          if (header->ddspf.flags == 0x04) *format = PIXELFORMAT_COMPRESSED_DXT1_RGB;
   279                          else *format = PIXELFORMAT_COMPRESSED_DXT1_RGBA;
   280                      } break;
   281                      case FOURCC_DXT3: *format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break;
   282                      case FOURCC_DXT5: *format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break;
   283                      default: break;
   284                  }
   285              }
   286          }
   287      }
   288  
   289      return image_data;
   290  }
   291  #endif
   292  
   293  #if defined(RL_GPUTEX_SUPPORT_PKM)
   294  // Loading PKM image data (ETC1/ETC2 compression)
   295  // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
   296  // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
   297  void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
   298  {
   299      void *image_data = NULL;        // Image data pointer
   300  
   301      unsigned char *file_data_ptr = (unsigned char *)file_data;
   302  
   303      // Required extensions:
   304      // GL_OES_compressed_ETC1_RGB8_texture  (ETC1) (OpenGL ES 2.0)
   305      // GL_ARB_ES3_compatibility  (ETC2/EAC) (OpenGL ES 3.0)
   306  
   307      // Supported tokens (defined by extensions)
   308      // GL_ETC1_RGB8_OES                 0x8D64
   309      // GL_COMPRESSED_RGB8_ETC2          0x9274
   310      // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
   311  
   312      // PKM file (ETC1) Header (16 bytes)
   313      typedef struct {
   314          char id[4];                 // "PKM "
   315          char version[2];            // "10" or "20"
   316          unsigned short format;      // Data format (big-endian) (Check list below)
   317          unsigned short width;       // Texture width (big-endian) (orig_width rounded to multiple of 4)
   318          unsigned short height;      // Texture height (big-endian) (orig_height rounded to multiple of 4)
   319          unsigned short orig_width;   // Original width (big-endian)
   320          unsigned short orig_height;  // Original height (big-endian)
   321      } pkm_header;
   322  
   323      // Formats list
   324      // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
   325      // version 20: format: 0=ETC1_RGB, 1=ETC2_RGB, 2=ETC2_RGBA_OLD, 3=ETC2_RGBA, 4=ETC2_RGBA1, 5=ETC2_R, 6=ETC2_RG, 7=ETC2_SIGNED_R, 8=ETC2_SIGNED_R
   326  
   327      // NOTE: The extended width and height are the widths rounded up to a multiple of 4.
   328      // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
   329  
   330      if (file_data_ptr != NULL)
   331      {
   332          pkm_header *header = (pkm_header *)file_data_ptr;
   333  
   334          if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' '))
   335          {
   336              LOG("WARNING: IMAGE: PKM file data not valid");
   337          }
   338          else
   339          {
   340              file_data_ptr += sizeof(pkm_header);   // Skip header
   341  
   342              // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
   343              header->format = ((header->format & 0x00FF) << 8) | ((header->format & 0xFF00) >> 8);
   344              header->width = ((header->width & 0x00FF) << 8) | ((header->width & 0xFF00) >> 8);
   345              header->height = ((header->height & 0x00FF) << 8) | ((header->height & 0xFF00) >> 8);
   346  
   347              *width = header->width;
   348              *height = header->height;
   349              *mips = 1;
   350  
   351              int bpp = 4;
   352              if (header->format == 3) bpp = 8;
   353  
   354              int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
   355  
   356              image_data = RL_MALLOC(data_size*sizeof(unsigned char));
   357  
   358              memcpy(image_data, file_data_ptr, data_size);
   359  
   360              if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
   361              else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
   362              else if (header->format == 3) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
   363          }
   364      }
   365  
   366      return image_data;
   367  }
   368  #endif
   369  
   370  #if defined(RL_GPUTEX_SUPPORT_KTX)
   371  // Load KTX compressed image data (ETC1/ETC2 compression)
   372  // TODO: Review KTX loading, many things changed!
   373  void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
   374  {
   375      void *image_data = NULL;        // Image data pointer
   376  
   377      unsigned char *file_data_ptr = (unsigned char *)file_data;
   378  
   379      // Required extensions:
   380      // GL_OES_compressed_ETC1_RGB8_texture  (ETC1)
   381      // GL_ARB_ES3_compatibility  (ETC2/EAC)
   382  
   383      // Supported tokens (defined by extensions)
   384      // GL_ETC1_RGB8_OES                 0x8D64
   385      // GL_COMPRESSED_RGB8_ETC2          0x9274
   386      // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
   387  
   388      // KTX file Header (64 bytes)
   389      // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
   390      // v2.0 - http://github.khronos.org/KTX-Specification/
   391  
   392      // KTX 1.1 Header
   393      // TODO: Support KTX 2.2 specs!
   394      typedef struct {
   395          char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"
   396          unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
   397          unsigned int gl_type;                   // For compressed textures, glType must equal 0
   398          unsigned int gl_type_size;              // For compressed texture data, usually 1
   399          unsigned int gl_format;                 // For compressed textures is 0
   400          unsigned int gl_internal_format;        // Compressed internal format
   401          unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)
   402          unsigned int width;                     // Texture image width in pixels
   403          unsigned int height;                    // Texture image height in pixels
   404          unsigned int depth;                     // For 2D textures is 0
   405          unsigned int elements;                  // Number of array elements, usually 0
   406          unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
   407          unsigned int mipmap_levels;             // Non-mipmapped textures = 1
   408          unsigned int key_value_data_size;       // Used to encode any arbitrary data...
   409      } ktx_header;
   410  
   411      // NOTE: Before start of every mipmap data block, we have: unsigned int data_size
   412  
   413      if (file_data_ptr != NULL)
   414      {
   415          ktx_header *header = (ktx_header *)file_data_ptr;
   416  
   417          if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') ||
   418              (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1'))
   419          {
   420              LOG("WARNING: IMAGE: KTX file data not valid");
   421          }
   422          else
   423          {
   424              file_data_ptr += sizeof(ktx_header);           // Move file data pointer
   425  
   426              *width = header->width;
   427              *height = header->height;
   428              *mips = header->mipmap_levels;
   429  
   430              file_data_ptr += header->key_value_data_size; // Skip value data size
   431  
   432              int data_size = ((int *)file_data_ptr)[0];
   433              file_data_ptr += sizeof(int);
   434  
   435              image_data = RL_MALLOC(data_size*sizeof(unsigned char));
   436  
   437              memcpy(image_data, file_data_ptr, data_size);
   438  
   439              if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
   440              else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
   441              else if (header->gl_internal_format == 0x9278) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
   442  
   443              // TODO: Support uncompressed data formats? Right now it returns format = 0!
   444          }
   445      }
   446  
   447      return image_data;
   448  }
   449  
   450  // Save image data as KTX file
   451  // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
   452  // TODO: Review KTX saving, many things changed!
   453  int rl_save_ktx(const char *file_name, void *data, int width, int height, int format, int mipmaps)
   454  {
   455      // KTX file Header (64 bytes)
   456      // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
   457      // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18
   458      typedef struct {
   459          char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"         // KTX 2.0: "«KTX 22»\r\n\x1A\n"
   460          unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
   461          unsigned int gl_type;                   // For compressed textures, glType must equal 0
   462          unsigned int gl_type_size;              // For compressed texture data, usually 1
   463          unsigned int gl_format;                 // For compressed textures is 0
   464          unsigned int gl_internal_format;        // Compressed internal format
   465          unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)   // KTX 2.0: UInt32 vkFormat
   466          unsigned int width;                     // Texture image width in pixels
   467          unsigned int height;                    // Texture image height in pixels
   468          unsigned int depth;                     // For 2D textures is 0
   469          unsigned int elements;                  // Number of array elements, usually 0
   470          unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
   471          unsigned int mipmap_levels;             // Non-mipmapped textures = 1
   472          unsigned int key_value_data_size;       // Used to encode any arbitrary data...     // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
   473                                                                                              // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
   474          // KTX 2.0 defines additional header elements...
   475      } ktx_header;
   476  
   477      // Calculate file data_size required
   478      int data_size = sizeof(ktx_header);
   479  
   480      for (int i = 0, width = width, height = height; i < mipmaps; i++)
   481      {
   482          data_size += get_pixel_data_size(width, height, format);
   483          width /= 2; height /= 2;
   484      }
   485  
   486      unsigned char *file_data = RL_CALLOC(data_size, 1);
   487      unsigned char *file_data_ptr = file_data;
   488  
   489      ktx_header header = { 0 };
   490  
   491      // KTX identifier (v1.1)
   492      //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
   493      //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
   494  
   495      const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
   496  
   497      // Get the image header
   498      memcpy(header.id, ktx_identifier, 12);  // KTX 1.1 signature
   499      header.endianness = 0;
   500      header.gl_type = 0;                     // Obtained from format
   501      header.gl_type_size = 1;
   502      header.gl_format = 0;                   // Obtained from format
   503      header.gl_internal_format = 0;          // Obtained from format
   504      header.gl_base_internal_format = 0;
   505      header.width = width;
   506      header.height = height;
   507      header.depth = 0;
   508      header.elements = 0;
   509      header.faces = 1;
   510      header.mipmap_levels = mipmaps;         // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
   511      header.key_value_data_size = 0;         // No extra data after the header
   512  
   513      rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type);   // rlgl module function
   514      header.gl_base_internal_format = header.gl_format;    // KTX 1.1 only
   515  
   516      // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
   517  
   518      if (header.gl_format == -1) LOG("WARNING: IMAGE: GL format not supported for KTX export (%i)", header.gl_format);
   519      else
   520      {
   521          memcpy(file_data_ptr, &header, sizeof(ktx_header));
   522          file_data_ptr += sizeof(ktx_header);
   523  
   524          int temp_width = width;
   525          int temp_height = height;
   526          int data_offset = 0;
   527  
   528          // Save all mipmaps data
   529          for (int i = 0; i < mipmaps; i++)
   530          {
   531              unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format);
   532  
   533              memcpy(file_data_ptr, &data_size, sizeof(unsigned int));
   534              memcpy(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size);
   535  
   536              temp_width /= 2;
   537              temp_height /= 2;
   538              data_offset += data_size;
   539              file_data_ptr += (4 + data_size);
   540          }
   541      }
   542  
   543      // Save file data to file
   544      int success = false;
   545      FILE *file = fopen(file_name, "wb");
   546  
   547      if (file != NULL)
   548      {
   549          unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file);
   550  
   551          if (count == 0) LOG("WARNING: FILEIO: [%s] Failed to write file", file_name);
   552          else if (count != data_size) LOG("WARNING: FILEIO: [%s] File partially written", file_name);
   553          else LOG("INFO: FILEIO: [%s] File saved successfully", file_name);
   554  
   555          int result = fclose(file);
   556          if (result == 0) success = true;
   557      }
   558      else LOG("WARNING: FILEIO: [%s] Failed to open file", file_name);
   559  
   560      RL_FREE(file_data);    // Free file data buffer
   561  
   562      // If all data has been written correctly to file, success = 1
   563      return success;
   564  }
   565  #endif
   566  
   567  #if defined(RL_GPUTEX_SUPPORT_PVR)
   568  // Loading PVR image data (uncompressed or PVRT compression)
   569  // NOTE: PVR v2 not supported, use PVR v3 instead
   570  void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
   571  {
   572      void *image_data = NULL;        // Image data pointer
   573  
   574      unsigned char *file_data_ptr = (unsigned char *)file_data;
   575  
   576      // Required extension:
   577      // GL_IMG_texture_compression_pvrtc
   578  
   579      // Supported tokens (defined by extensions)
   580      // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG       0x8C00
   581      // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG      0x8C02
   582  
   583  #if 0   // Not used...
   584      // PVR file v2 Header (52 bytes)
   585      typedef struct {
   586          unsigned int headerLength;
   587          unsigned int height;
   588          unsigned int width;
   589          unsigned int numMipmaps;
   590          unsigned int flags;
   591          unsigned int dataLength;
   592          unsigned int bpp;
   593          unsigned int bitmaskRed;
   594          unsigned int bitmaskGreen;
   595          unsigned int bitmaskBlue;
   596          unsigned int bitmaskAlpha;
   597          unsigned int pvrTag;
   598          unsigned int numSurfs;
   599      } PVRHeaderV2;
   600  #endif
   601  
   602      // PVR file v3 Header (52 bytes)
   603      // NOTE: After it could be metadata (15 bytes?)
   604      typedef struct {
   605          char id[4];
   606          unsigned int flags;
   607          unsigned char channels[4];      // pixelFormat high part
   608          unsigned char channel_depth[4];  // pixelFormat low part
   609          unsigned int color_space;
   610          unsigned int channel_type;
   611          unsigned int height;
   612          unsigned int width;
   613          unsigned int depth;
   614          unsigned int num_surfaces;
   615          unsigned int num_faces;
   616          unsigned int num_mipmaps;
   617          unsigned int metadata_size;
   618      } pvr_header;
   619  
   620  #if 0   // Not used...
   621      // Metadata (usually 15 bytes)
   622      typedef struct {
   623          unsigned int devFOURCC;
   624          unsigned int key;
   625          unsigned int data_size;      // Not used?
   626          unsigned char *data;        // Not used?
   627      } PVRMetadata;
   628  #endif
   629  
   630      if (file_data_ptr != NULL)
   631      {
   632          // Check PVR image version
   633          unsigned char pvr_version = file_data_ptr[0];
   634  
   635          // Load different PVR data formats
   636          if (pvr_version == 0x50)
   637          {
   638              pvr_header *header = (pvr_header *)file_data_ptr;
   639  
   640              if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3))
   641              {
   642                  LOG("WARNING: IMAGE: PVR file data not valid");
   643              }
   644              else
   645              {
   646                  file_data_ptr += sizeof(pvr_header);   // Skip header
   647  
   648                  *width = header->width;
   649                  *height = header->height;
   650                  *mips = header->num_mipmaps;
   651  
   652                  // Check data format
   653                  if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
   654                  else if (((header->channels[0] == 'l') && (header->channels[1] == 'a')) && ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8))) *format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA;
   655                  else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b'))
   656                  {
   657                      if (header->channels[3] == 'a')
   658                      {
   659                          if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 5) && (header->channel_depth[2] == 5) && (header->channel_depth[3] == 1)) *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
   660                          else if ((header->channel_depth[0] == 4) && (header->channel_depth[1] == 4) && (header->channel_depth[2] == 4) && (header->channel_depth[3] == 4)) *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
   661                          else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8) && (header->channel_depth[3] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
   662                      }
   663                      else if (header->channels[3] == 0)
   664                      {
   665                          if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
   666                          else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
   667                      }
   668                  }
   669                  else if (header->channels[0] == 2) *format = PIXELFORMAT_COMPRESSED_PVRT_RGB;
   670                  else if (header->channels[0] == 3) *format = PIXELFORMAT_COMPRESSED_PVRT_RGBA;
   671  
   672                  file_data_ptr += header->metadata_size;    // Skip meta data header
   673  
   674                  // Calculate data size (depends on format)
   675                  int bpp = 0;
   676                  switch (*format)
   677                  {
   678                      case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
   679                      case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   680                      case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   681                      case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   682                      case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
   683                      case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
   684                      case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
   685                      case PIXELFORMAT_COMPRESSED_PVRT_RGB:
   686                      case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
   687                      default: break;
   688                  }
   689  
   690                  int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
   691                  image_data = RL_MALLOC(data_size*sizeof(unsigned char));
   692  
   693                  memcpy(image_data, file_data_ptr, data_size);
   694              }
   695          }
   696          else if (pvr_version == 52) LOG("INFO: IMAGE: PVRv2 format not supported, update your files to PVRv3");
   697      }
   698  
   699      return image_data;
   700  }
   701  #endif
   702  
   703  #if defined(RL_GPUTEX_SUPPORT_ASTC)
   704  // Load ASTC compressed image data (ASTC compression)
   705  void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
   706  {
   707      void *image_data = NULL;        // Image data pointer
   708  
   709      unsigned char *file_data_ptr = (unsigned char *)file_data;
   710  
   711      // Required extensions:
   712      // GL_KHR_texture_compression_astc_hdr
   713      // GL_KHR_texture_compression_astc_ldr
   714  
   715      // Supported tokens (defined by extensions)
   716      // GL_COMPRESSED_RGBA_ASTC_4x4_KHR      0x93b0
   717      // GL_COMPRESSED_RGBA_ASTC_8x8_KHR      0x93b7
   718  
   719      // ASTC file Header (16 bytes)
   720      typedef struct {
   721          unsigned char id[4];        // Signature: 0x13 0xAB 0xA1 0x5C
   722          unsigned char blockX;       // Block X dimensions
   723          unsigned char blockY;       // Block Y dimensions
   724          unsigned char blockZ;       // Block Z dimensions (1 for 2D images)
   725          unsigned char width[3];     // void *width in pixels (24bit value)
   726          unsigned char height[3];    // void *height in pixels (24bit value)
   727          unsigned char length[3];    // void *Z-size (1 for 2D images)
   728      } astc_header;
   729  
   730      if (file_data_ptr != NULL)
   731      {
   732          astc_header *header = (astc_header *)file_data_ptr;
   733  
   734          if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13))
   735          {
   736              LOG("WARNING: IMAGE: ASTC file data not valid");
   737          }
   738          else
   739          {
   740              file_data_ptr += sizeof(astc_header);   // Skip header
   741  
   742              // NOTE: Assuming Little Endian (could it be wrong?)
   743              *width = 0x00000000 | ((int)header->width[2] << 16) | ((int)header->width[1] << 8) | ((int)header->width[0]);
   744              *height = 0x00000000 | ((int)header->height[2] << 16) | ((int)header->height[1] << 8) | ((int)header->height[0]);
   745              *mips = 1;      // NOTE: ASTC format only contains one mipmap level
   746  
   747              // NOTE: Each block is always stored in 128bit so we can calculate the bpp
   748              int bpp = 128/(header->blockX*header->blockY);
   749  
   750              // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
   751              if ((bpp == 8) || (bpp == 2))
   752              {
   753                  int data_size = (*width)*(*height)*bpp/8;  // Data size in bytes
   754  
   755                  image_data = RL_MALLOC(data_size*sizeof(unsigned char));
   756  
   757                  memcpy(image_data, file_data_ptr, data_size);
   758  
   759                  if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
   760                  else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
   761              }
   762              else LOG("WARNING: IMAGE: ASTC block size configuration not supported");
   763          }
   764      }
   765  
   766      return image_data;
   767  }
   768  #endif
   769  
   770  //----------------------------------------------------------------------------------
   771  // Module Internal Functions Definition
   772  //----------------------------------------------------------------------------------
   773  // Get pixel data size in bytes for certain pixel format
   774  static int get_pixel_data_size(int width, int height, int format)
   775  {
   776      int data_size = 0;       // Size in bytes
   777      int bpp = 0;            // Bits per pixel
   778  
   779      switch (format)
   780      {
   781          case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
   782          case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
   783          case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
   784          case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
   785          case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
   786          case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
   787          case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
   788          case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
   789          case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
   790          case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
   791          case PIXELFORMAT_COMPRESSED_DXT1_RGB:
   792          case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
   793          case PIXELFORMAT_COMPRESSED_ETC1_RGB:
   794          case PIXELFORMAT_COMPRESSED_ETC2_RGB:
   795          case PIXELFORMAT_COMPRESSED_PVRT_RGB:
   796          case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
   797          case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
   798          case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
   799          case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
   800          case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
   801          case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
   802          default: break;
   803      }
   804  
   805      data_size = width*height*bpp/8;  // Total data size in bytes
   806  
   807      // Most compressed formats works on 4x4 blocks,
   808      // if texture is smaller, minimum dataSize is 8 or 16
   809      if ((width < 4) && (height < 4))
   810      {
   811          if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8;
   812          else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16;
   813      }
   814  
   815      return data_size;
   816  }
   817  
   818  #endif // RL_GPUTEX_IMPLEMENTATION