GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/external/rl_gputex.h
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 54 0.0%
Branches: 0 52 0.0%

Line Branch Exec Source
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, w = width, h = height; i < mipmaps; i++)
481 {
482 data_size += get_pixel_data_size(w, h, format);
483 w /= 2; h /= 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 #endif // RL_GPUTEX_IMPLEMENTATION
818