minesweeper

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

rl_gputex.h (36115B)


      1 /**********************************************************************************************
      2 *
      3 *   rl_gputex v1.0 - 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 
    175             if (*width % 4 != 0) LOG("WARNING: IMAGE: DDS file width must be multiple of 4. Image will not display correctly");
    176             if (*height % 4 != 0) LOG("WARNING: IMAGE: DDS file height must be multiple of 4. Image will not display correctly");
    177 
    178             image_pixel_size = header->width*header->height;
    179 
    180             if (header->mipmap_count == 0) *mips = 1;   // Parameter not used
    181             else *mips = header->mipmap_count;
    182 
    183             if (header->ddspf.rgb_bit_count == 16)      // 16bit mode, no compressed
    184             {
    185                 if (header->ddspf.flags == 0x40)        // No alpha channel
    186                 {
    187                     int data_size = image_pixel_size*sizeof(unsigned short);
    188                     if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
    189                     image_data = RL_MALLOC(data_size);
    190 
    191                     memcpy(image_data, file_data_ptr, data_size);
    192 
    193                     *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
    194                 }
    195                 else if (header->ddspf.flags == 0x41)           // With alpha channel
    196                 {
    197                     if (header->ddspf.a_bit_mask == 0x8000)     // 1bit alpha
    198                     {
    199                         int data_size = image_pixel_size*sizeof(unsigned short);
    200                         if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
    201                         image_data = RL_MALLOC(data_size);
    202 
    203                         memcpy(image_data, file_data_ptr, data_size);
    204 
    205                         unsigned char alpha = 0;
    206 
    207                         // NOTE: Data comes as A1R5G5B5, it must be reordered to R5G5B5A1
    208                         for (int i = 0; i < image_pixel_size; i++)
    209                         {
    210                             alpha = ((unsigned short *)image_data)[i] >> 15;
    211                             ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 1;
    212                             ((unsigned short *)image_data)[i] += alpha;
    213                         }
    214 
    215                         *format = PIXELFORMAT_UNCOMPRESSED_R5G5B5A1;
    216                     }
    217                     else if (header->ddspf.a_bit_mask == 0xf000)   // 4bit alpha
    218                     {
    219                         int data_size = image_pixel_size*sizeof(unsigned short);
    220                         if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
    221                         image_data = RL_MALLOC(data_size);
    222 
    223                         memcpy(image_data, file_data_ptr, data_size);
    224 
    225                         unsigned char alpha = 0;
    226 
    227                         // NOTE: Data comes as A4R4G4B4, it must be reordered R4G4B4A4
    228                         for (int i = 0; i < image_pixel_size; i++)
    229                         {
    230                             alpha = ((unsigned short *)image_data)[i] >> 12;
    231                             ((unsigned short *)image_data)[i] = ((unsigned short *)image_data)[i] << 4;
    232                             ((unsigned short *)image_data)[i] += alpha;
    233                         }
    234 
    235                         *format = PIXELFORMAT_UNCOMPRESSED_R4G4B4A4;
    236                     }
    237                 }
    238             }
    239             else if ((header->ddspf.flags == 0x40) && (header->ddspf.rgb_bit_count == 24))   // DDS_RGB, no compressed
    240             {
    241                 int data_size = image_pixel_size*3*sizeof(unsigned char);
    242                 if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
    243                 image_data = RL_MALLOC(data_size);
    244 
    245                 memcpy(image_data, file_data_ptr, data_size);
    246 
    247                 *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
    248             }
    249             else if ((header->ddspf.flags == 0x41) && (header->ddspf.rgb_bit_count == 32)) // DDS_RGBA, no compressed
    250             {
    251                 int data_size = image_pixel_size*4*sizeof(unsigned char);
    252                 if (header->mipmap_count > 1) data_size = data_size + data_size / 3;
    253                 image_data = RL_MALLOC(data_size);
    254 
    255                 memcpy(image_data, file_data_ptr, data_size);
    256 
    257                 unsigned char blue = 0;
    258 
    259                 // NOTE: Data comes as A8R8G8B8, it must be reordered R8G8B8A8 (view next comment)
    260                 // DirecX understand ARGB as a 32bit DWORD but the actual memory byte alignment is BGRA
    261                 // So, we must realign B8G8R8A8 to R8G8B8A8
    262                 for (int i = 0; i < image_pixel_size*4; i += 4)
    263                 {
    264                     blue = ((unsigned char *)image_data)[i];
    265                     ((unsigned char *)image_data)[i] = ((unsigned char *)image_data)[i + 2];
    266                     ((unsigned char *)image_data)[i + 2] = blue;
    267                 }
    268 
    269                 *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
    270             }
    271             else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed
    272             {
    273                 int data_size = 0;
    274 
    275                 // Calculate data size, including all mipmaps
    276                 if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size + header->pitch_or_linear_size / 3;
    277                 else data_size = header->pitch_or_linear_size;
    278 
    279                 image_data = RL_MALLOC(data_size*sizeof(unsigned char));
    280 
    281                 memcpy(image_data, file_data_ptr, data_size);
    282 
    283                 switch (header->ddspf.fourcc)
    284                 {
    285                     case FOURCC_DXT1:
    286                     {
    287                         if (header->ddspf.flags == 0x04) *format = PIXELFORMAT_COMPRESSED_DXT1_RGB;
    288                         else *format = PIXELFORMAT_COMPRESSED_DXT1_RGBA;
    289                     } break;
    290                     case FOURCC_DXT3: *format = PIXELFORMAT_COMPRESSED_DXT3_RGBA; break;
    291                     case FOURCC_DXT5: *format = PIXELFORMAT_COMPRESSED_DXT5_RGBA; break;
    292                     default: break;
    293                 }
    294             }
    295         }
    296     }
    297 
    298     return image_data;
    299 }
    300 #endif
    301 
    302 #if defined(RL_GPUTEX_SUPPORT_PKM)
    303 // Loading PKM image data (ETC1/ETC2 compression)
    304 // NOTE: KTX is the standard Khronos Group compression format (ETC1/ETC2, mipmaps)
    305 // PKM is a much simpler file format used mainly to contain a single ETC1/ETC2 compressed image (no mipmaps)
    306 void *rl_load_pkm_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
    307 {
    308     void *image_data = NULL;        // Image data pointer
    309 
    310     unsigned char *file_data_ptr = (unsigned char *)file_data;
    311 
    312     // Required extensions:
    313     // GL_OES_compressed_ETC1_RGB8_texture  (ETC1) (OpenGL ES 2.0)
    314     // GL_ARB_ES3_compatibility  (ETC2/EAC) (OpenGL ES 3.0)
    315 
    316     // Supported tokens (defined by extensions)
    317     // GL_ETC1_RGB8_OES                 0x8D64
    318     // GL_COMPRESSED_RGB8_ETC2          0x9274
    319     // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
    320 
    321     // PKM file (ETC1) Header (16 bytes)
    322     typedef struct {
    323         char id[4];                 // "PKM "
    324         char version[2];            // "10" or "20"
    325         unsigned short format;      // Data format (big-endian) (Check list below)
    326         unsigned short width;       // Texture width (big-endian) (orig_width rounded to multiple of 4)
    327         unsigned short height;      // Texture height (big-endian) (orig_height rounded to multiple of 4)
    328         unsigned short orig_width;   // Original width (big-endian)
    329         unsigned short orig_height;  // Original height (big-endian)
    330     } pkm_header;
    331 
    332     // Formats list
    333     // version 10: format: 0=ETC1_RGB, [1=ETC1_RGBA, 2=ETC1_RGB_MIP, 3=ETC1_RGBA_MIP] (not used)
    334     // 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
    335 
    336     // NOTE: The extended width and height are the widths rounded up to a multiple of 4.
    337     // NOTE: ETC is always 4bit per pixel (64 bit for each 4x4 block of pixels)
    338 
    339     if (file_data_ptr != NULL)
    340     {
    341         pkm_header *header = (pkm_header *)file_data_ptr;
    342 
    343         if ((header->id[0] != 'P') || (header->id[1] != 'K') || (header->id[2] != 'M') || (header->id[3] != ' '))
    344         {
    345             LOG("WARNING: IMAGE: PKM file data not valid");
    346         }
    347         else
    348         {
    349             file_data_ptr += sizeof(pkm_header);   // Skip header
    350 
    351             // NOTE: format, width and height come as big-endian, data must be swapped to little-endian
    352             header->format = ((header->format & 0x00FF) << 8) | ((header->format & 0xFF00) >> 8);
    353             header->width = ((header->width & 0x00FF) << 8) | ((header->width & 0xFF00) >> 8);
    354             header->height = ((header->height & 0x00FF) << 8) | ((header->height & 0xFF00) >> 8);
    355 
    356             *width = header->width;
    357             *height = header->height;
    358             *mips = 1;
    359 
    360             int bpp = 4;
    361             if (header->format == 3) bpp = 8;
    362 
    363             int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
    364 
    365             image_data = RL_MALLOC(data_size*sizeof(unsigned char));
    366 
    367             memcpy(image_data, file_data_ptr, data_size);
    368 
    369             if (header->format == 0) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
    370             else if (header->format == 1) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
    371             else if (header->format == 3) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
    372         }
    373     }
    374 
    375     return image_data;
    376 }
    377 #endif
    378 
    379 #if defined(RL_GPUTEX_SUPPORT_KTX)
    380 // Load KTX compressed image data (ETC1/ETC2 compression)
    381 // TODO: Review KTX loading, many things changed!
    382 void *rl_load_ktx_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
    383 {
    384     void *image_data = NULL;        // Image data pointer
    385 
    386     unsigned char *file_data_ptr = (unsigned char *)file_data;
    387 
    388     // Required extensions:
    389     // GL_OES_compressed_ETC1_RGB8_texture  (ETC1)
    390     // GL_ARB_ES3_compatibility  (ETC2/EAC)
    391 
    392     // Supported tokens (defined by extensions)
    393     // GL_ETC1_RGB8_OES                 0x8D64
    394     // GL_COMPRESSED_RGB8_ETC2          0x9274
    395     // GL_COMPRESSED_RGBA8_ETC2_EAC     0x9278
    396 
    397     // KTX file Header (64 bytes)
    398     // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
    399     // v2.0 - http://github.khronos.org/KTX-Specification/
    400 
    401     // KTX 1.1 Header
    402     // TODO: Support KTX 2.2 specs!
    403     typedef struct {
    404         char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"
    405         unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
    406         unsigned int gl_type;                   // For compressed textures, glType must equal 0
    407         unsigned int gl_type_size;              // For compressed texture data, usually 1
    408         unsigned int gl_format;                 // For compressed textures is 0
    409         unsigned int gl_internal_format;        // Compressed internal format
    410         unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)
    411         unsigned int width;                     // Texture image width in pixels
    412         unsigned int height;                    // Texture image height in pixels
    413         unsigned int depth;                     // For 2D textures is 0
    414         unsigned int elements;                  // Number of array elements, usually 0
    415         unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
    416         unsigned int mipmap_levels;             // Non-mipmapped textures = 1
    417         unsigned int key_value_data_size;       // Used to encode any arbitrary data...
    418     } ktx_header;
    419 
    420     // NOTE: Before start of every mipmap data block, we have: unsigned int data_size
    421 
    422     if (file_data_ptr != NULL)
    423     {
    424         ktx_header *header = (ktx_header *)file_data_ptr;
    425 
    426         if ((header->id[1] != 'K') || (header->id[2] != 'T') || (header->id[3] != 'X') ||
    427             (header->id[4] != ' ') || (header->id[5] != '1') || (header->id[6] != '1'))
    428         {
    429             LOG("WARNING: IMAGE: KTX file data not valid");
    430         }
    431         else
    432         {
    433             file_data_ptr += sizeof(ktx_header);           // Move file data pointer
    434 
    435             *width = header->width;
    436             *height = header->height;
    437             *mips = header->mipmap_levels;
    438 
    439             file_data_ptr += header->key_value_data_size; // Skip value data size
    440 
    441             int data_size = ((int *)file_data_ptr)[0];
    442             file_data_ptr += sizeof(int);
    443 
    444             image_data = RL_MALLOC(data_size*sizeof(unsigned char));
    445 
    446             memcpy(image_data, file_data_ptr, data_size);
    447 
    448             if (header->gl_internal_format == 0x8D64) *format = PIXELFORMAT_COMPRESSED_ETC1_RGB;
    449             else if (header->gl_internal_format == 0x9274) *format = PIXELFORMAT_COMPRESSED_ETC2_RGB;
    450             else if (header->gl_internal_format == 0x9278) *format = PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA;
    451 
    452             // TODO: Support uncompressed data formats? Right now it returns format = 0!
    453         }
    454     }
    455 
    456     return image_data;
    457 }
    458 
    459 // Save image data as KTX file
    460 // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018)
    461 // TODO: Review KTX saving, many things changed!
    462 int rl_save_ktx(const char *file_name, void *data, int width, int height, int format, int mipmaps)
    463 {
    464     // KTX file Header (64 bytes)
    465     // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
    466     // v2.0 - http://github.khronos.org/KTX-Specification/ - Final specs by 2021-04-18
    467     typedef struct {
    468         char id[12];                            // Identifier: "«KTX 11»\r\n\x1A\n"         // KTX 2.0: "«KTX 22»\r\n\x1A\n"
    469         unsigned int endianness;                // Little endian: 0x01 0x02 0x03 0x04
    470         unsigned int gl_type;                   // For compressed textures, glType must equal 0
    471         unsigned int gl_type_size;              // For compressed texture data, usually 1
    472         unsigned int gl_format;                 // For compressed textures is 0
    473         unsigned int gl_internal_format;        // Compressed internal format
    474         unsigned int gl_base_internal_format;   // Same as glFormat (RGB, RGBA, ALPHA...)   // KTX 2.0: UInt32 vkFormat
    475         unsigned int width;                     // Texture image width in pixels
    476         unsigned int height;                    // Texture image height in pixels
    477         unsigned int depth;                     // For 2D textures is 0
    478         unsigned int elements;                  // Number of array elements, usually 0
    479         unsigned int faces;                     // Cubemap faces, for no-cubemap = 1
    480         unsigned int mipmap_levels;             // Non-mipmapped textures = 1
    481         unsigned int key_value_data_size;       // Used to encode any arbitrary data...     // KTX 2.0: UInt32 levelOrder - ordering of the mipmap levels, usually 0
    482                                                                                             // KTX 2.0: UInt32 supercompressionScheme - 0 (None), 1 (Crunch CRN), 2 (Zlib DEFLATE)...
    483         // KTX 2.0 defines additional header elements...
    484     } ktx_header;
    485 
    486     // Calculate file data_size required
    487     int data_size = sizeof(ktx_header);
    488 
    489     for (int i = 0, w = width, h = height; i < mipmaps; i++)
    490     {
    491         data_size += get_pixel_data_size(w, h, format);
    492         w /= 2; h /= 2;
    493     }
    494 
    495     unsigned char *file_data = RL_CALLOC(data_size, 1);
    496     unsigned char *file_data_ptr = file_data;
    497 
    498     ktx_header header = { 0 };
    499 
    500     // KTX identifier (v1.1)
    501     //unsigned char id[12] = { '«', 'K', 'T', 'X', ' ', '1', '1', '»', '\r', '\n', '\x1A', '\n' };
    502     //unsigned char id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
    503 
    504     const char ktx_identifier[12] = { 0xAB, 'K', 'T', 'X', ' ', '1', '1', 0xBB, '\r', '\n', 0x1A, '\n' };
    505 
    506     // Get the image header
    507     memcpy(header.id, ktx_identifier, 12);  // KTX 1.1 signature
    508     header.endianness = 0;
    509     header.gl_type = 0;                     // Obtained from format
    510     header.gl_type_size = 1;
    511     header.gl_format = 0;                   // Obtained from format
    512     header.gl_internal_format = 0;          // Obtained from format
    513     header.gl_base_internal_format = 0;
    514     header.width = width;
    515     header.height = height;
    516     header.depth = 0;
    517     header.elements = 0;
    518     header.faces = 1;
    519     header.mipmap_levels = mipmaps;         // If it was 0, it means mipmaps should be generated on loading (not for compressed formats)
    520     header.key_value_data_size = 0;         // No extra data after the header
    521 
    522     rlGetGlTextureFormats(format, &header.gl_internal_format, &header.gl_format, &header.gl_type);   // rlgl module function
    523     header.gl_base_internal_format = header.gl_format;    // KTX 1.1 only
    524 
    525     // NOTE: We can save into a .ktx all PixelFormats supported by raylib, including compressed formats like DXT, ETC or ASTC
    526 
    527     if (header.gl_format == -1) LOG("WARNING: IMAGE: GL format not supported for KTX export (%i)", header.gl_format);
    528     else
    529     {
    530         memcpy(file_data_ptr, &header, sizeof(ktx_header));
    531         file_data_ptr += sizeof(ktx_header);
    532 
    533         int temp_width = width;
    534         int temp_height = height;
    535         int data_offset = 0;
    536 
    537         // Save all mipmaps data
    538         for (int i = 0; i < mipmaps; i++)
    539         {
    540             unsigned int data_size = get_pixel_data_size(temp_width, temp_height, format);
    541 
    542             memcpy(file_data_ptr, &data_size, sizeof(unsigned int));
    543             memcpy(file_data_ptr + 4, (unsigned char *)data + data_offset, data_size);
    544 
    545             temp_width /= 2;
    546             temp_height /= 2;
    547             data_offset += data_size;
    548             file_data_ptr += (4 + data_size);
    549         }
    550     }
    551 
    552     // Save file data to file
    553     int success = false;
    554     FILE *file = fopen(file_name, "wb");
    555 
    556     if (file != NULL)
    557     {
    558         unsigned int count = (unsigned int)fwrite(file_data, sizeof(unsigned char), data_size, file);
    559 
    560         if (count == 0) LOG("WARNING: FILEIO: [%s] Failed to write file", file_name);
    561         else if (count != data_size) LOG("WARNING: FILEIO: [%s] File partially written", file_name);
    562         else LOG("INFO: FILEIO: [%s] File saved successfully", file_name);
    563 
    564         int result = fclose(file);
    565         if (result == 0) success = true;
    566     }
    567     else LOG("WARNING: FILEIO: [%s] Failed to open file", file_name);
    568 
    569     RL_FREE(file_data);    // Free file data buffer
    570 
    571     // If all data has been written correctly to file, success = 1
    572     return success;
    573 }
    574 #endif
    575 
    576 #if defined(RL_GPUTEX_SUPPORT_PVR)
    577 // Loading PVR image data (uncompressed or PVRT compression)
    578 // NOTE: PVR v2 not supported, use PVR v3 instead
    579 void *rl_load_pvr_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
    580 {
    581     void *image_data = NULL;        // Image data pointer
    582 
    583     unsigned char *file_data_ptr = (unsigned char *)file_data;
    584 
    585     // Required extension:
    586     // GL_IMG_texture_compression_pvrtc
    587 
    588     // Supported tokens (defined by extensions)
    589     // GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG       0x8C00
    590     // GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG      0x8C02
    591 
    592 #if 0   // Not used...
    593     // PVR file v2 Header (52 bytes)
    594     typedef struct {
    595         unsigned int headerLength;
    596         unsigned int height;
    597         unsigned int width;
    598         unsigned int numMipmaps;
    599         unsigned int flags;
    600         unsigned int dataLength;
    601         unsigned int bpp;
    602         unsigned int bitmaskRed;
    603         unsigned int bitmaskGreen;
    604         unsigned int bitmaskBlue;
    605         unsigned int bitmaskAlpha;
    606         unsigned int pvrTag;
    607         unsigned int numSurfs;
    608     } PVRHeaderV2;
    609 #endif
    610 
    611     // PVR file v3 Header (52 bytes)
    612     // NOTE: After it could be metadata (15 bytes?)
    613     typedef struct {
    614         char id[4];
    615         unsigned int flags;
    616         unsigned char channels[4];      // pixelFormat high part
    617         unsigned char channel_depth[4];  // pixelFormat low part
    618         unsigned int color_space;
    619         unsigned int channel_type;
    620         unsigned int height;
    621         unsigned int width;
    622         unsigned int depth;
    623         unsigned int num_surfaces;
    624         unsigned int num_faces;
    625         unsigned int num_mipmaps;
    626         unsigned int metadata_size;
    627     } pvr_header;
    628 
    629 #if 0   // Not used...
    630     // Metadata (usually 15 bytes)
    631     typedef struct {
    632         unsigned int devFOURCC;
    633         unsigned int key;
    634         unsigned int data_size;      // Not used?
    635         unsigned char *data;        // Not used?
    636     } PVRMetadata;
    637 #endif
    638 
    639     if (file_data_ptr != NULL)
    640     {
    641         // Check PVR image version
    642         unsigned char pvr_version = file_data_ptr[0];
    643 
    644         // Load different PVR data formats
    645         if (pvr_version == 0x50)
    646         {
    647             pvr_header *header = (pvr_header *)file_data_ptr;
    648 
    649             if ((header->id[0] != 'P') || (header->id[1] != 'V') || (header->id[2] != 'R') || (header->id[3] != 3))
    650             {
    651                 LOG("WARNING: IMAGE: PVR file data not valid");
    652             }
    653             else
    654             {
    655                 file_data_ptr += sizeof(pvr_header);   // Skip header
    656 
    657                 *width = header->width;
    658                 *height = header->height;
    659                 *mips = header->num_mipmaps;
    660 
    661                 // Check data format
    662                 if (((header->channels[0] == 'l') && (header->channels[1] == 0)) && (header->channel_depth[0] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE;
    663                 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;
    664                 else if ((header->channels[0] == 'r') && (header->channels[1] == 'g') && (header->channels[2] == 'b'))
    665                 {
    666                     if (header->channels[3] == 'a')
    667                     {
    668                         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;
    669                         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;
    670                         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;
    671                     }
    672                     else if (header->channels[3] == 0)
    673                     {
    674                         if ((header->channel_depth[0] == 5) && (header->channel_depth[1] == 6) && (header->channel_depth[2] == 5)) *format = PIXELFORMAT_UNCOMPRESSED_R5G6B5;
    675                         else if ((header->channel_depth[0] == 8) && (header->channel_depth[1] == 8) && (header->channel_depth[2] == 8)) *format = PIXELFORMAT_UNCOMPRESSED_R8G8B8;
    676                     }
    677                 }
    678                 else if (header->channels[0] == 2) *format = PIXELFORMAT_COMPRESSED_PVRT_RGB;
    679                 else if (header->channels[0] == 3) *format = PIXELFORMAT_COMPRESSED_PVRT_RGBA;
    680 
    681                 file_data_ptr += header->metadata_size;    // Skip meta data header
    682 
    683                 // Calculate data size (depends on format)
    684                 int bpp = 0;
    685                 switch (*format)
    686                 {
    687                     case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
    688                     case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
    689                     case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
    690                     case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
    691                     case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
    692                     case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
    693                     case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
    694                     case PIXELFORMAT_COMPRESSED_PVRT_RGB:
    695                     case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
    696                     default: break;
    697                 }
    698 
    699                 int data_size = (*width)*(*height)*bpp/8;  // Total data size in bytes
    700                 image_data = RL_MALLOC(data_size*sizeof(unsigned char));
    701 
    702                 memcpy(image_data, file_data_ptr, data_size);
    703             }
    704         }
    705         else if (pvr_version == 52) LOG("INFO: IMAGE: PVRv2 format not supported, update your files to PVRv3");
    706     }
    707 
    708     return image_data;
    709 }
    710 #endif
    711 
    712 #if defined(RL_GPUTEX_SUPPORT_ASTC)
    713 // Load ASTC compressed image data (ASTC compression)
    714 void *rl_load_astc_from_memory(const unsigned char *file_data, unsigned int file_size, int *width, int *height, int *format, int *mips)
    715 {
    716     void *image_data = NULL;        // Image data pointer
    717 
    718     unsigned char *file_data_ptr = (unsigned char *)file_data;
    719 
    720     // Required extensions:
    721     // GL_KHR_texture_compression_astc_hdr
    722     // GL_KHR_texture_compression_astc_ldr
    723 
    724     // Supported tokens (defined by extensions)
    725     // GL_COMPRESSED_RGBA_ASTC_4x4_KHR      0x93b0
    726     // GL_COMPRESSED_RGBA_ASTC_8x8_KHR      0x93b7
    727 
    728     // ASTC file Header (16 bytes)
    729     typedef struct {
    730         unsigned char id[4];        // Signature: 0x13 0xAB 0xA1 0x5C
    731         unsigned char blockX;       // Block X dimensions
    732         unsigned char blockY;       // Block Y dimensions
    733         unsigned char blockZ;       // Block Z dimensions (1 for 2D images)
    734         unsigned char width[3];     // void *width in pixels (24bit value)
    735         unsigned char height[3];    // void *height in pixels (24bit value)
    736         unsigned char length[3];    // void *Z-size (1 for 2D images)
    737     } astc_header;
    738 
    739     if (file_data_ptr != NULL)
    740     {
    741         astc_header *header = (astc_header *)file_data_ptr;
    742 
    743         if ((header->id[3] != 0x5c) || (header->id[2] != 0xa1) || (header->id[1] != 0xab) || (header->id[0] != 0x13))
    744         {
    745             LOG("WARNING: IMAGE: ASTC file data not valid");
    746         }
    747         else
    748         {
    749             file_data_ptr += sizeof(astc_header);   // Skip header
    750 
    751             // NOTE: Assuming Little Endian (could it be wrong?)
    752             *width = 0x00000000 | ((int)header->width[2] << 16) | ((int)header->width[1] << 8) | ((int)header->width[0]);
    753             *height = 0x00000000 | ((int)header->height[2] << 16) | ((int)header->height[1] << 8) | ((int)header->height[0]);
    754             *mips = 1;      // NOTE: ASTC format only contains one mipmap level
    755 
    756             // NOTE: Each block is always stored in 128bit so we can calculate the bpp
    757             int bpp = 128/(header->blockX*header->blockY);
    758 
    759             // NOTE: Currently we only support 2 blocks configurations: 4x4 and 8x8
    760             if ((bpp == 8) || (bpp == 2))
    761             {
    762                 int data_size = (*width)*(*height)*bpp/8;  // Data size in bytes
    763 
    764                 image_data = RL_MALLOC(data_size*sizeof(unsigned char));
    765 
    766                 memcpy(image_data, file_data_ptr, data_size);
    767 
    768                 if (bpp == 8) *format = PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA;
    769                 else if (bpp == 2) *format = PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA;
    770             }
    771             else LOG("WARNING: IMAGE: ASTC block size configuration not supported");
    772         }
    773     }
    774 
    775     return image_data;
    776 }
    777 #endif
    778 
    779 //----------------------------------------------------------------------------------
    780 // Module Internal Functions Definition
    781 //----------------------------------------------------------------------------------
    782 // Get pixel data size in bytes for certain pixel format
    783 static int get_pixel_data_size(int width, int height, int format)
    784 {
    785     int data_size = 0;       // Size in bytes
    786     int bpp = 0;            // Bits per pixel
    787 
    788     switch (format)
    789     {
    790         case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: bpp = 8; break;
    791         case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA:
    792         case PIXELFORMAT_UNCOMPRESSED_R5G6B5:
    793         case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1:
    794         case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: bpp = 16; break;
    795         case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: bpp = 32; break;
    796         case PIXELFORMAT_UNCOMPRESSED_R8G8B8: bpp = 24; break;
    797         case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break;
    798         case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break;
    799         case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break;
    800         case PIXELFORMAT_COMPRESSED_DXT1_RGB:
    801         case PIXELFORMAT_COMPRESSED_DXT1_RGBA:
    802         case PIXELFORMAT_COMPRESSED_ETC1_RGB:
    803         case PIXELFORMAT_COMPRESSED_ETC2_RGB:
    804         case PIXELFORMAT_COMPRESSED_PVRT_RGB:
    805         case PIXELFORMAT_COMPRESSED_PVRT_RGBA: bpp = 4; break;
    806         case PIXELFORMAT_COMPRESSED_DXT3_RGBA:
    807         case PIXELFORMAT_COMPRESSED_DXT5_RGBA:
    808         case PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA:
    809         case PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA: bpp = 8; break;
    810         case PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA: bpp = 2; break;
    811         default: break;
    812     }
    813 
    814     data_size = width*height*bpp/8;  // Total data size in bytes
    815 
    816     // Most compressed formats works on 4x4 blocks,
    817     // if texture is smaller, minimum dataSize is 8 or 16
    818     if ((width < 4) && (height < 4))
    819     {
    820         if ((format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) && (format < PIXELFORMAT_COMPRESSED_DXT3_RGBA)) data_size = 8;
    821         else if ((format >= PIXELFORMAT_COMPRESSED_DXT3_RGBA) && (format < PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA)) data_size = 16;
    822     }
    823 
    824     return data_size;
    825 }
    826 #endif // RL_GPUTEX_IMPLEMENTATION