1 // SPDX-License-Identifier: Apache-2.0
2 // ----------------------------------------------------------------------------
3 // Copyright 2011-2022 Arm Limited
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 // use this file except in compliance with the License. You may obtain a copy
7 // of the License at:
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 // License for the specific language governing permissions and limitations
15 // under the License.
16 // ----------------------------------------------------------------------------
17
18 /**
19 * @brief Functions for loading/storing uncompressed and compressed images.
20 */
21
22 #include <array>
23 #include <cassert>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <cstring>
27 #include <fstream>
28
29 #include "astcenccli_internal.h"
30
31 #include "stb_image.h"
32 #include "stb_image_write.h"
33 #include "tinyexr.h"
34
35 /* ============================================================================
36 Image load and store through the stb_iamge and tinyexr libraries
37 ============================================================================ */
38
39 /**
40 * @brief Load a .exr image using TinyExr to provide the loader.
41 *
42 * @param filename The name of the file to load.
43 * @param y_flip Should the image be vertically flipped?
44 * @param[out] is_hdr Is this an HDR image load? Always @c true for this function.
45 * @param[out] component_count The number of components in the data.
46 *
47 * @return The loaded image data in a canonical 4 channel format.
48 */
load_image_with_tinyexr(const char * filename,bool y_flip,bool & is_hdr,unsigned int & component_count)49 static astcenc_image* load_image_with_tinyexr(
50 const char* filename,
51 bool y_flip,
52 bool& is_hdr,
53 unsigned int& component_count
54 ) {
55 int dim_x, dim_y;
56 float* image;
57 const char* err;
58
59 int load_res = LoadEXR(&image, &dim_x, &dim_y, filename, &err);
60 if (load_res != TINYEXR_SUCCESS)
61 {
62 printf("ERROR: Failed to load image %s (%s)\n", filename, err);
63 free(reinterpret_cast<void*>(const_cast<char*>(err)));
64 return nullptr;
65 }
66
67 astcenc_image* res_img = astc_img_from_floatx4_array(image, dim_x, dim_y, y_flip);
68 free(image);
69
70 is_hdr = true;
71 component_count = 4;
72 return res_img;
73 }
74
75 /**
76 * @brief Load an image using STBImage to provide the loader.
77 *
78 * @param filename The name of the file to load.
79 * @param y_flip Should the image be vertically flipped?
80 * @param[out] is_hdr Is this an HDR image load?
81 * @param[out] component_count The number of components in the data.
82 *
83 * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
84 */
load_image_with_stb(const char * filename,bool y_flip,bool & is_hdr,unsigned int & component_count)85 static astcenc_image* load_image_with_stb(
86 const char* filename,
87 bool y_flip,
88 bool& is_hdr,
89 unsigned int& component_count
90 ) {
91 int dim_x, dim_y;
92
93 if (stbi_is_hdr(filename))
94 {
95 float* data = stbi_loadf(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha);
96 if (data)
97 {
98 astcenc_image* img = astc_img_from_floatx4_array(data, dim_x, dim_y, y_flip);
99 stbi_image_free(data);
100 is_hdr = true;
101 component_count = 4;
102 return img;
103 }
104 }
105 else
106 {
107 uint8_t* data = stbi_load(filename, &dim_x, &dim_y, nullptr, STBI_rgb_alpha);
108 if (data)
109 {
110 astcenc_image* img = astc_img_from_unorm8x4_array(data, dim_x, dim_y, y_flip);
111 stbi_image_free(data);
112 is_hdr = false;
113 component_count = 4;
114 return img;
115 }
116 }
117
118 printf("ERROR: Failed to load image %s (%s)\n", filename, stbi_failure_reason());
119 return nullptr;
120 }
121
122 /**
123 * @brief Save an EXR image using TinyExr to provide the store routine.
124 *
125 * @param img The source data for the image.
126 * @param filename The name of the file to save.
127 * @param y_flip Should the image be vertically flipped?
128 *
129 * @return @c true if the image saved OK, @c false on error.
130 */
store_exr_image_with_tinyexr(const astcenc_image * img,const char * filename,int y_flip)131 static bool store_exr_image_with_tinyexr(
132 const astcenc_image* img,
133 const char* filename,
134 int y_flip
135 ) {
136 float *buf = floatx4_array_from_astc_img(img, y_flip);
137 int res = SaveEXR(buf, img->dim_x, img->dim_y, 4, 1, filename, nullptr);
138 delete[] buf;
139 return res >= 0;
140 }
141
142 /**
143 * @brief Save a PNG image using STBImageWrite to provide the store routine.
144 *
145 * @param img The source data for the image.
146 * @param filename The name of the file to save.
147 * @param y_flip Should the image be vertically flipped?
148 *
149 * @return @c true if the image saved OK, @c false on error.
150 */
store_png_image_with_stb(const astcenc_image * img,const char * filename,int y_flip)151 static bool store_png_image_with_stb(
152 const astcenc_image* img,
153 const char* filename,
154 int y_flip
155 ) {
156 assert(img->data_type == ASTCENC_TYPE_U8);
157 uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[0]);
158
159 stbi_flip_vertically_on_write(y_flip);
160 int res = stbi_write_png(filename, img->dim_x, img->dim_y, 4, buf, img->dim_x * 4);
161 return res != 0;
162 }
163
164 /**
165 * @brief Save a TGA image using STBImageWrite to provide the store routine.
166 *
167 * @param img The source data for the image.
168 * @param filename The name of the file to save.
169 * @param y_flip Should the image be vertically flipped?
170 *
171 * @return @c true if the image saved OK, @c false on error.
172 */
store_tga_image_with_stb(const astcenc_image * img,const char * filename,int y_flip)173 static bool store_tga_image_with_stb(
174 const astcenc_image* img,
175 const char* filename,
176 int y_flip
177 ) {
178 assert(img->data_type == ASTCENC_TYPE_U8);
179 uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[0]);
180
181 stbi_flip_vertically_on_write(y_flip);
182 int res = stbi_write_tga(filename, img->dim_x, img->dim_y, 4, buf);
183 return res != 0;
184 }
185
186 /**
187 * @brief Save a BMP image using STBImageWrite to provide the store routine.
188 *
189 * @param img The source data for the image.
190 * @param filename The name of the file to save.
191 * @param y_flip Should the image be vertically flipped?
192 *
193 * @return @c true if the image saved OK, @c false on error.
194 */
store_bmp_image_with_stb(const astcenc_image * img,const char * filename,int y_flip)195 static bool store_bmp_image_with_stb(
196 const astcenc_image* img,
197 const char* filename,
198 int y_flip
199 ) {
200 assert(img->data_type == ASTCENC_TYPE_U8);
201 uint8_t* buf = reinterpret_cast<uint8_t*>(img->data[0]);
202
203 stbi_flip_vertically_on_write(y_flip);
204 int res = stbi_write_bmp(filename, img->dim_x, img->dim_y, 4, buf);
205 return res != 0;
206 }
207
208 /**
209 * @brief Save a HDR image using STBImageWrite to provide the store routine.
210 *
211 * @param img The source data for the image.
212 * @param filename The name of the file to save.
213 * @param y_flip Should the image be vertically flipped?
214 *
215 * @return @c true if the image saved OK, @c false on error.
216 */
store_hdr_image_with_stb(const astcenc_image * img,const char * filename,int y_flip)217 static bool store_hdr_image_with_stb(
218 const astcenc_image* img,
219 const char* filename,
220 int y_flip
221 ) {
222 float* buf = floatx4_array_from_astc_img(img, y_flip);
223 int res = stbi_write_hdr(filename, img->dim_x, img->dim_y, 4, buf);
224 delete[] buf;
225 return res != 0;
226 }
227
228 /* ============================================================================
229 Native Load and store of KTX and DDS file formats.
230
231 Unlike "regular" 2D image formats, which are mostly supported through stb_image
232 and tinyexr, these formats are supported directly; this involves a relatively
233 large number of pixel formats.
234
235 The following restrictions apply to loading of these file formats:
236
237 * Only uncompressed data supported
238 * Only first mipmap in mipmap pyramid supported
239 * KTX: Cube-map arrays are not supported
240 ============================================================================ */
241 enum scanline_transfer
242 {
243 R8_TO_RGBA8,
244 RG8_TO_RGBA8,
245 RGB8_TO_RGBA8,
246 RGBA8_TO_RGBA8,
247 BGR8_TO_RGBA8,
248 BGRA8_TO_RGBA8,
249 L8_TO_RGBA8,
250 LA8_TO_RGBA8,
251
252 RGBX8_TO_RGBA8,
253 BGRX8_TO_RGBA8,
254
255 R16_TO_RGBA16F,
256 RG16_TO_RGBA16F,
257 RGB16_TO_RGBA16F,
258 RGBA16_TO_RGBA16F,
259 BGR16_TO_RGBA16F,
260 BGRA16_TO_RGBA16F,
261 L16_TO_RGBA16F,
262 LA16_TO_RGBA16F,
263
264 R16F_TO_RGBA16F,
265 RG16F_TO_RGBA16F,
266 RGB16F_TO_RGBA16F,
267 RGBA16F_TO_RGBA16F,
268 BGR16F_TO_RGBA16F,
269 BGRA16F_TO_RGBA16F,
270 L16F_TO_RGBA16F,
271 LA16F_TO_RGBA16F,
272
273 R32F_TO_RGBA16F,
274 RG32F_TO_RGBA16F,
275 RGB32F_TO_RGBA16F,
276 RGBA32F_TO_RGBA16F,
277 BGR32F_TO_RGBA16F,
278 BGRA32F_TO_RGBA16F,
279 L32F_TO_RGBA16F,
280 LA32F_TO_RGBA16F
281 };
282
283 /**
284 * @brief Copy a scanline from a source file and expand to a canonical format.
285 *
286 * Outputs are always 4 component RGBA, stored as U8 (LDR) or FP16 (HDR).
287 *
288 * @param[out] dst The start of the line to store to.
289 * @param src The start of the line to load.
290 * @param pixel_count The number of pixels in the scanline.
291 * @param method The conversion function.
292 */
copy_scanline(void * dst,const void * src,int pixel_count,scanline_transfer method)293 static void copy_scanline(
294 void* dst,
295 const void* src,
296 int pixel_count,
297 scanline_transfer method
298 ) {
299
300 #define id(x) (x)
301 #define u16_sf16(x) float_to_float16(x * (1.0f/65535.0f))
302 #define f32_sf16(x) float_to_float16(x)
303
304 #define COPY_R(dsttype, srctype, convfunc, oneval) \
305 do { \
306 const srctype* s = reinterpret_cast<const srctype*>(src); \
307 dsttype* d = reinterpret_cast<dsttype*>(dst); \
308 for (int i = 0; i < pixel_count; i++) \
309 { \
310 d[4 * i ] = convfunc(s[i]); \
311 d[4 * i + 1] = 0; \
312 d[4 * i + 2] = 0; \
313 d[4 * i + 3] = oneval; \
314 } \
315 } while (0); \
316 break
317
318 #define COPY_RG(dsttype, srctype, convfunc, oneval) \
319 do { \
320 const srctype* s = reinterpret_cast<const srctype*>(src); \
321 dsttype* d = reinterpret_cast<dsttype*>(dst); \
322 for (int i = 0; i < pixel_count; i++) \
323 { \
324 d[4 * i ] = convfunc(s[2 * i ]); \
325 d[4 * i + 1] = convfunc(s[2 * i + 1]); \
326 d[4 * i + 2] = 0; \
327 d[4 * i + 3] = oneval; \
328 } \
329 } while (0); \
330 break
331
332 #define COPY_RGB(dsttype, srctype, convfunc, oneval) \
333 do { \
334 const srctype* s = reinterpret_cast<const srctype*>(src); \
335 dsttype* d = reinterpret_cast<dsttype*>(dst); \
336 for (int i = 0; i < pixel_count; i++) \
337 { \
338 d[4 * i ] = convfunc(s[3 * i ]); \
339 d[4 * i + 1] = convfunc(s[3 * i + 1]); \
340 d[4 * i + 2] = convfunc(s[3 * i + 2]); \
341 d[4 * i + 3] = oneval; \
342 } \
343 } while (0); \
344 break
345
346 #define COPY_BGR(dsttype, srctype, convfunc, oneval) \
347 do { \
348 const srctype* s = reinterpret_cast<const srctype*>(src); \
349 dsttype* d = reinterpret_cast<dsttype*>(dst); \
350 for (int i = 0; i < pixel_count; i++)\
351 { \
352 d[4 * i ] = convfunc(s[3 * i + 2]); \
353 d[4 * i + 1] = convfunc(s[3 * i + 1]); \
354 d[4 * i + 2] = convfunc(s[3 * i ]); \
355 d[4 * i + 3] = oneval; \
356 } \
357 } while (0); \
358 break
359
360 #define COPY_RGBX(dsttype, srctype, convfunc, oneval) \
361 do { \
362 const srctype* s = reinterpret_cast<const srctype*>(src); \
363 dsttype* d = reinterpret_cast<dsttype*>(dst); \
364 for (int i = 0; i < pixel_count; i++)\
365 { \
366 d[4 * i ] = convfunc(s[4 * i ]); \
367 d[4 * i + 1] = convfunc(s[4 * i + 1]); \
368 d[4 * i + 2] = convfunc(s[4 * i + 2]); \
369 d[4 * i + 3] = oneval; \
370 } \
371 } while (0); \
372 break
373
374 #define COPY_BGRX(dsttype, srctype, convfunc, oneval) \
375 do { \
376 const srctype* s = reinterpret_cast<const srctype*>(src); \
377 dsttype* d = reinterpret_cast<dsttype*>(dst); \
378 for (int i = 0; i < pixel_count; i++)\
379 { \
380 d[4 * i ] = convfunc(s[4 * i + 2]); \
381 d[4 * i + 1] = convfunc(s[4 * i + 1]); \
382 d[4 * i + 2] = convfunc(s[4 * i ]); \
383 d[4 * i + 3] = oneval; \
384 } \
385 } while (0); \
386 break
387
388 #define COPY_RGBA(dsttype, srctype, convfunc, oneval) \
389 do { \
390 const srctype* s = reinterpret_cast<const srctype*>(src); \
391 dsttype* d = reinterpret_cast<dsttype*>(dst); \
392 for (int i = 0; i < pixel_count; i++) \
393 { \
394 d[4 * i ] = convfunc(s[4 * i ]); \
395 d[4 * i + 1] = convfunc(s[4 * i + 1]); \
396 d[4 * i + 2] = convfunc(s[4 * i + 2]); \
397 d[4 * i + 3] = convfunc(s[4 * i + 3]); \
398 } \
399 } while (0); \
400 break
401
402 #define COPY_BGRA(dsttype, srctype, convfunc, oneval) \
403 do { \
404 const srctype* s = reinterpret_cast<const srctype*>(src); \
405 dsttype* d = reinterpret_cast<dsttype*>(dst); \
406 for (int i = 0; i < pixel_count; i++) \
407 { \
408 d[4 * i ] = convfunc(s[4 * i + 2]); \
409 d[4 * i + 1] = convfunc(s[4 * i + 1]); \
410 d[4 * i + 2] = convfunc(s[4 * i ]); \
411 d[4 * i + 3] = convfunc(s[4 * i + 3]); \
412 } \
413 } while (0); \
414 break
415
416 #define COPY_L(dsttype, srctype, convfunc, oneval) \
417 do { \
418 const srctype* s = reinterpret_cast<const srctype*>(src); \
419 dsttype* d = reinterpret_cast<dsttype*>(dst); \
420 for (int i = 0; i < pixel_count; i++) \
421 { \
422 d[4 * i ] = convfunc(s[i]); \
423 d[4 * i + 1] = convfunc(s[i]); \
424 d[4 * i + 2] = convfunc(s[i]); \
425 d[4 * i + 3] = oneval; \
426 } \
427 } while (0); \
428 break
429
430 #define COPY_LA(dsttype, srctype, convfunc, oneval) \
431 do { \
432 const srctype* s = reinterpret_cast<const srctype*>(src); \
433 dsttype* d = reinterpret_cast<dsttype*>(dst); \
434 for (int i = 0; i < pixel_count; i++) \
435 { \
436 d[4 * i ] = convfunc(s[2 * i ]); \
437 d[4 * i + 1] = convfunc(s[2 * i ]); \
438 d[4 * i + 2] = convfunc(s[2 * i ]); \
439 d[4 * i + 3] = convfunc(s[2 * i + 1]); \
440 } \
441 } while (0); \
442 break
443
444 switch (method)
445 {
446 case R8_TO_RGBA8:
447 COPY_R(uint8_t, uint8_t, id, 0xFF);
448 case RG8_TO_RGBA8:
449 COPY_RG(uint8_t, uint8_t, id, 0xFF);
450 case RGB8_TO_RGBA8:
451 COPY_RGB(uint8_t, uint8_t, id, 0xFF);
452 case RGBA8_TO_RGBA8:
453 COPY_RGBA(uint8_t, uint8_t, id, 0xFF);
454 case BGR8_TO_RGBA8:
455 COPY_BGR(uint8_t, uint8_t, id, 0xFF);
456 case BGRA8_TO_RGBA8:
457 COPY_BGRA(uint8_t, uint8_t, id, 0xFF);
458 case RGBX8_TO_RGBA8:
459 COPY_RGBX(uint8_t, uint8_t, id, 0xFF);
460 case BGRX8_TO_RGBA8:
461 COPY_BGRX(uint8_t, uint8_t, id, 0xFF);
462 case L8_TO_RGBA8:
463 COPY_L(uint8_t, uint8_t, id, 0xFF);
464 case LA8_TO_RGBA8:
465 COPY_LA(uint8_t, uint8_t, id, 0xFF);
466
467 case R16F_TO_RGBA16F:
468 COPY_R(uint16_t, uint16_t, id, 0x3C00);
469 case RG16F_TO_RGBA16F:
470 COPY_RG(uint16_t, uint16_t, id, 0x3C00);
471 case RGB16F_TO_RGBA16F:
472 COPY_RGB(uint16_t, uint16_t, id, 0x3C00);
473 case RGBA16F_TO_RGBA16F:
474 COPY_RGBA(uint16_t, uint16_t, id, 0x3C00);
475 case BGR16F_TO_RGBA16F:
476 COPY_BGR(uint16_t, uint16_t, id, 0x3C00);
477 case BGRA16F_TO_RGBA16F:
478 COPY_BGRA(uint16_t, uint16_t, id, 0x3C00);
479 case L16F_TO_RGBA16F:
480 COPY_L(uint16_t, uint16_t, id, 0x3C00);
481 case LA16F_TO_RGBA16F:
482 COPY_LA(uint16_t, uint16_t, id, 0x3C00);
483
484 case R16_TO_RGBA16F:
485 COPY_R(uint16_t, uint16_t, u16_sf16, 0x3C00);
486 case RG16_TO_RGBA16F:
487 COPY_RG(uint16_t, uint16_t, u16_sf16, 0x3C00);
488 case RGB16_TO_RGBA16F:
489 COPY_RGB(uint16_t, uint16_t, u16_sf16, 0x3C00);
490 case RGBA16_TO_RGBA16F:
491 COPY_RGBA(uint16_t, uint16_t, u16_sf16, 0x3C00);
492 case BGR16_TO_RGBA16F:
493 COPY_BGR(uint16_t, uint16_t, u16_sf16, 0x3C00);
494 case BGRA16_TO_RGBA16F:
495 COPY_BGRA(uint16_t, uint16_t, u16_sf16, 0x3C00);
496 case L16_TO_RGBA16F:
497 COPY_L(uint16_t, uint16_t, u16_sf16, 0x3C00);
498 case LA16_TO_RGBA16F:
499 COPY_LA(uint16_t, uint16_t, u16_sf16, 0x3C00);
500
501 case R32F_TO_RGBA16F:
502 COPY_R(uint16_t, float, f32_sf16, 0x3C00);
503 case RG32F_TO_RGBA16F:
504 COPY_RG(uint16_t, float, f32_sf16, 0x3C00);
505 case RGB32F_TO_RGBA16F:
506 COPY_RGB(uint16_t, float, f32_sf16, 0x3C00);
507 case RGBA32F_TO_RGBA16F:
508 COPY_RGBA(uint16_t, float, f32_sf16, 0x3C00);
509 case BGR32F_TO_RGBA16F:
510 COPY_BGR(uint16_t, float, f32_sf16, 0x3C00);
511 case BGRA32F_TO_RGBA16F:
512 COPY_BGRA(uint16_t, float, f32_sf16, 0x3C00);
513 case L32F_TO_RGBA16F:
514 COPY_L(uint16_t, float, f32_sf16, 0x3C00);
515 case LA32F_TO_RGBA16F:
516 COPY_LA(uint16_t, float, f32_sf16, 0x3C00);
517 }
518 }
519
520 /**
521 * @brief Swap endianness of N two byte values.
522 *
523 * @param[in,out] dataptr The data to convert.
524 * @param byte_count The number of bytes to convert.
525 */
switch_endianness2(void * dataptr,int byte_count)526 static void switch_endianness2(
527 void* dataptr,
528 int byte_count
529 ) {
530 uint8_t* data = reinterpret_cast<uint8_t*>(dataptr);
531 for (int i = 0; i < byte_count / 2; i++)
532 {
533 uint8_t d0 = data[0];
534 uint8_t d1 = data[1];
535 data[0] = d1;
536 data[1] = d0;
537 data += 2;
538 }
539 }
540
541 /**
542 * @brief Swap endianness of N four byte values.
543 *
544 * @param[in,out] dataptr The data to convert.
545 * @param byte_count The number of bytes to convert.
546 */
switch_endianness4(void * dataptr,int byte_count)547 static void switch_endianness4(
548 void* dataptr,
549 int byte_count
550 ) {
551 uint8_t* data = reinterpret_cast<uint8_t*>(dataptr);
552 for (int i = 0; i < byte_count / 4; i++)
553 {
554 uint8_t d0 = data[0];
555 uint8_t d1 = data[1];
556 uint8_t d2 = data[2];
557 uint8_t d3 = data[3];
558 data[0] = d3;
559 data[1] = d2;
560 data[2] = d1;
561 data[3] = d0;
562 data += 4;
563 }
564 }
565
566 /**
567 * @brief Swap endianness of a u32 value.
568 *
569 * @param v The data to convert.
570 *
571 * @return The converted value.
572 */
u32_byterev(uint32_t v)573 static uint32_t u32_byterev(uint32_t v)
574 {
575 return (v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24);
576 }
577
578 /*
579 Notes about KTX:
580
581 After the header and the key/value data area, the actual image data follows.
582 Each image starts with a 4-byte "imageSize" value indicating the number of bytes of image data follow.
583 (For cube-maps, this value appears only after first image; the remaining 5 images are all of equal size.)
584 If the size of an image is not a multiple of 4, then it is padded to the next multiple of 4.
585 Note that this padding is NOT included in the "imageSize" field.
586 In a cubemap, the padding appears after each face note that in a 2D/3D texture, padding does
587 NOT appear between the lines/planes of the texture!
588
589 In a KTX file, there may be multiple images; they are organized as follows:
590
591 For each mipmap_level in numberOfMipmapLevels
592 UInt32 imageSize;
593 For each array_element in numberOfArrayElements
594 * for each face in numberOfFaces
595 * for each z_slice in pixelDepth
596 * for each row or row_of_blocks in pixelHeight
597 * for each pixel or block_of_pixels in pixelWidth
598 Byte data[format-specific-number-of-bytes]
599 * end
600 * end
601 *end
602 Byte cubePadding[0-3]
603 *end
604 Byte mipPadding[3 - ((imageSize+ 3) % 4)]
605 *end
606
607 In the ASTC codec, we will, for the time being only harvest the first image,
608 and we will support only a limited set of formats:
609
610 gl_type: UNSIGNED_BYTE UNSIGNED_SHORT HALF_FLOAT FLOAT UNSIGNED_INT_8_8_8_8 UNSIGNED_INT_8_8_8_8_REV
611 gl_format: RED, RG. RGB, RGBA BGR, BGRA
612 gl_internal_format: used for upload to OpenGL; we can ignore it on uncompressed-load, but
613 need to provide a reasonable value on store: RGB8 RGBA8 RGB16F RGBA16F
614 gl_base_internal_format: same as gl_format unless texture is compressed (well, BGR is turned into RGB)
615 RED, RG, RGB, RGBA
616 */
617
618 // Khronos enums
619 #define GL_RED 0x1903
620 #define GL_RG 0x8227
621 #define GL_RGB 0x1907
622 #define GL_RGBA 0x1908
623 #define GL_BGR 0x80E0
624 #define GL_BGRA 0x80E1
625 #define GL_LUMINANCE 0x1909
626 #define GL_LUMINANCE_ALPHA 0x190A
627
628 #define GL_UNSIGNED_BYTE 0x1401
629 #define GL_UNSIGNED_SHORT 0x1403
630 #define GL_HALF_FLOAT 0x140B
631 #define GL_FLOAT 0x1406
632
633 #define GL_COMPRESSED_RGBA_ASTC_4x4 0x93B0
634 #define GL_COMPRESSED_RGBA_ASTC_5x4 0x93B1
635 #define GL_COMPRESSED_RGBA_ASTC_5x5 0x93B2
636 #define GL_COMPRESSED_RGBA_ASTC_6x5 0x93B3
637 #define GL_COMPRESSED_RGBA_ASTC_6x6 0x93B4
638 #define GL_COMPRESSED_RGBA_ASTC_8x5 0x93B5
639 #define GL_COMPRESSED_RGBA_ASTC_8x6 0x93B6
640 #define GL_COMPRESSED_RGBA_ASTC_8x8 0x93B7
641 #define GL_COMPRESSED_RGBA_ASTC_10x5 0x93B8
642 #define GL_COMPRESSED_RGBA_ASTC_10x6 0x93B9
643 #define GL_COMPRESSED_RGBA_ASTC_10x8 0x93BA
644 #define GL_COMPRESSED_RGBA_ASTC_10x10 0x93BB
645 #define GL_COMPRESSED_RGBA_ASTC_12x10 0x93BC
646 #define GL_COMPRESSED_RGBA_ASTC_12x12 0x93BD
647
648 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4 0x93D0
649 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4 0x93D1
650 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5 0x93D2
651 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5 0x93D3
652 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6 0x93D4
653 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5 0x93D5
654 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6 0x93D6
655 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8 0x93D7
656 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5 0x93D8
657 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6 0x93D9
658 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8 0x93DA
659 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10 0x93DB
660 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10 0x93DC
661 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12 0x93DD
662
663 #define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES 0x93C0
664 #define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES 0x93C1
665 #define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES 0x93C2
666 #define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES 0x93C3
667 #define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES 0x93C4
668 #define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES 0x93C5
669 #define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES 0x93C6
670 #define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES 0x93C7
671 #define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES 0x93C8
672 #define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES 0x93C9
673
674 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES 0x93E0
675 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES 0x93E1
676 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES 0x93E2
677 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES 0x93E3
678 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES 0x93E4
679 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES 0x93E5
680 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES 0x93E6
681 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES 0x93E7
682 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES 0x93E8
683 #define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES 0x93E9
684
685 struct format_entry
686 {
687 unsigned int x;
688 unsigned int y;
689 unsigned int z;
690 bool is_srgb;
691 unsigned int format;
692 };
693
694 static const std::array<format_entry, 48> ASTC_FORMATS =
695 {{
696 // 2D Linear RGB
697 { 4, 4, 1, false, GL_COMPRESSED_RGBA_ASTC_4x4},
698 { 5, 4, 1, false, GL_COMPRESSED_RGBA_ASTC_5x4},
699 { 5, 5, 1, false, GL_COMPRESSED_RGBA_ASTC_5x5},
700 { 6, 5, 1, false, GL_COMPRESSED_RGBA_ASTC_6x5},
701 { 6, 6, 1, false, GL_COMPRESSED_RGBA_ASTC_6x6},
702 { 8, 5, 1, false, GL_COMPRESSED_RGBA_ASTC_8x5},
703 { 8, 6, 1, false, GL_COMPRESSED_RGBA_ASTC_8x6},
704 { 8, 8, 1, false, GL_COMPRESSED_RGBA_ASTC_8x8},
705 {10, 5, 1, false, GL_COMPRESSED_RGBA_ASTC_10x5},
706 {10, 6, 1, false, GL_COMPRESSED_RGBA_ASTC_10x6},
707 {10, 8, 1, false, GL_COMPRESSED_RGBA_ASTC_10x8},
708 {10, 10, 1, false, GL_COMPRESSED_RGBA_ASTC_10x10},
709 {12, 10, 1, false, GL_COMPRESSED_RGBA_ASTC_12x10},
710 {12, 12, 1, false, GL_COMPRESSED_RGBA_ASTC_12x12},
711 // 2D SRGB
712 { 4, 4, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4},
713 { 5, 4, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4},
714 { 5, 5, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5},
715 { 6, 5, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5},
716 { 6, 6, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6},
717 { 8, 5, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5},
718 { 8, 6, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6},
719 { 8, 8, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8},
720 {10, 5, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5},
721 {10, 6, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6},
722 {10, 8, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8},
723 {10, 10, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10},
724 {12, 10, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10},
725 {12, 12, 1, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12},
726 // 3D Linear RGB
727 { 3, 3, 3, false, GL_COMPRESSED_RGBA_ASTC_3x3x3_OES},
728 { 4, 3, 3, false, GL_COMPRESSED_RGBA_ASTC_4x3x3_OES},
729 { 4, 4, 3, false, GL_COMPRESSED_RGBA_ASTC_4x4x3_OES},
730 { 4, 4, 4, false, GL_COMPRESSED_RGBA_ASTC_4x4x4_OES},
731 { 5, 4, 4, false, GL_COMPRESSED_RGBA_ASTC_5x4x4_OES},
732 { 5, 5, 4, false, GL_COMPRESSED_RGBA_ASTC_5x5x4_OES},
733 { 5, 5, 5, false, GL_COMPRESSED_RGBA_ASTC_5x5x5_OES},
734 { 6, 5, 5, false, GL_COMPRESSED_RGBA_ASTC_6x5x5_OES},
735 { 6, 6, 5, false, GL_COMPRESSED_RGBA_ASTC_6x6x5_OES},
736 { 6, 6, 6, false, GL_COMPRESSED_RGBA_ASTC_6x6x6_OES},
737 // 3D SRGB
738 { 3, 3, 3, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES},
739 { 4, 3, 3, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES},
740 { 4, 4, 3, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES},
741 { 4, 4, 4, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES},
742 { 5, 4, 4, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES},
743 { 5, 5, 4, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES},
744 { 5, 5, 5, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES},
745 { 6, 5, 5, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES},
746 { 6, 6, 5, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES},
747 { 6, 6, 6, true, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES}
748 }};
749
get_format(unsigned int format)750 static const format_entry* get_format(
751 unsigned int format
752 ) {
753 for (auto& it : ASTC_FORMATS)
754 {
755 if (it.format == format)
756 {
757 return ⁢
758 }
759 }
760 return nullptr;
761 }
762
get_format(unsigned int x,unsigned int y,unsigned int z,bool is_srgb)763 static unsigned int get_format(
764 unsigned int x,
765 unsigned int y,
766 unsigned int z,
767 bool is_srgb
768 ) {
769 for (auto& it : ASTC_FORMATS)
770 {
771 if ((it.x == x) && (it.y == y) && (it.z == z) && (it.is_srgb == is_srgb))
772 {
773 return it.format;
774 }
775 }
776 return 0;
777 }
778
779 struct ktx_header
780 {
781 uint8_t magic[12];
782 uint32_t endianness; // should be 0x04030201; if it is instead 0x01020304, then the endianness of everything must be switched.
783 uint32_t gl_type; // 0 for compressed textures, otherwise value from table 3.2 (page 162) of OpenGL 4.0 spec
784 uint32_t gl_type_size; // size of data elements to do endianness swap on (1=endian-neutral data)
785 uint32_t gl_format; // 0 for compressed textures, otherwise value from table 3.3 (page 163) of OpenGL spec
786 uint32_t gl_internal_format; // sized-internal-format, corresponding to table 3.12 to 3.14 (pages 182-185) of OpenGL spec
787 uint32_t gl_base_internal_format; // unsized-internal-format: corresponding to table 3.11 (page 179) of OpenGL spec
788 uint32_t pixel_width; // texture dimensions; not rounded up to block size for compressed.
789 uint32_t pixel_height; // must be 0 for 1D textures.
790 uint32_t pixel_depth; // must be 0 for 1D, 2D and cubemap textures.
791 uint32_t number_of_array_elements; // 0 if not a texture array
792 uint32_t number_of_faces; // 6 for cubemaps, 1 for non-cubemaps
793 uint32_t number_of_mipmap_levels; // 0 or 1 for non-mipmapped textures; 0 indicates that auto-mipmap-gen should be done at load time.
794 uint32_t bytes_of_key_value_data; // size in bytes of the key-and-value area immediately following the header.
795 };
796
797 // magic 12-byte sequence that must appear at the beginning of every KTX file.
798 static uint8_t ktx_magic[12] {
799 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A
800 };
801
ktx_header_switch_endianness(ktx_header * kt)802 static void ktx_header_switch_endianness(ktx_header * kt)
803 {
804 #define REV(x) kt->x = u32_byterev(kt->x)
805 REV(endianness);
806 REV(gl_type);
807 REV(gl_type_size);
808 REV(gl_format);
809 REV(gl_internal_format);
810 REV(gl_base_internal_format);
811 REV(pixel_width);
812 REV(pixel_height);
813 REV(pixel_depth);
814 REV(number_of_array_elements);
815 REV(number_of_faces);
816 REV(number_of_mipmap_levels);
817 REV(bytes_of_key_value_data);
818 #undef REV
819 }
820
821 /**
822 * @brief Load an uncompressed KTX image using the local custom loader.
823 *
824 * @param filename The name of the file to load.
825 * @param y_flip Should the image be vertically flipped?
826 * @param[out] is_hdr Is this an HDR image load?
827 * @param[out] component_count The number of components in the data.
828 *
829 * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
830 */
load_ktx_uncompressed_image(const char * filename,bool y_flip,bool & is_hdr,unsigned int & component_count)831 static astcenc_image* load_ktx_uncompressed_image(
832 const char* filename,
833 bool y_flip,
834 bool& is_hdr,
835 unsigned int& component_count
836 ) {
837 FILE *f = fopen(filename, "rb");
838 if (!f)
839 {
840 printf("Failed to open file %s\n", filename);
841 return nullptr;
842 }
843
844 ktx_header hdr;
845 size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f);
846
847 if (header_bytes_read != sizeof(hdr))
848 {
849 printf("Failed to read header of KTX file %s\n", filename);
850 fclose(f);
851 return nullptr;
852 }
853
854 if (memcmp(hdr.magic, ktx_magic, 12) != 0 || (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304))
855 {
856 printf("File %s does not have a valid KTX header\n", filename);
857 fclose(f);
858 return nullptr;
859 }
860
861 int switch_endianness = 0;
862 if (hdr.endianness == 0x01020304)
863 {
864 ktx_header_switch_endianness(&hdr);
865 switch_endianness = 1;
866 }
867
868 if (hdr.gl_type == 0 || hdr.gl_format == 0)
869 {
870 printf("File %s appears to be compressed, not supported as input\n", filename);
871 fclose(f);
872 return nullptr;
873 }
874
875 // the formats we support are:
876
877 // Cartesian product of gl_type=(UNSIGNED_BYTE, UNSIGNED_SHORT, HALF_FLOAT, FLOAT) x gl_format=(RED, RG, RGB, RGBA, BGR, BGRA)
878
879 int components;
880 switch (hdr.gl_format)
881 {
882 case GL_RED:
883 components = 1;
884 break;
885 case GL_RG:
886 components = 2;
887 break;
888 case GL_RGB:
889 components = 3;
890 break;
891 case GL_RGBA:
892 components = 4;
893 break;
894 case GL_BGR:
895 components = 3;
896 break;
897 case GL_BGRA:
898 components = 4;
899 break;
900 case GL_LUMINANCE:
901 components = 1;
902 break;
903 case GL_LUMINANCE_ALPHA:
904 components = 2;
905 break;
906 default:
907 printf("KTX file %s has unsupported GL type\n", filename);
908 fclose(f);
909 return nullptr;
910 }
911
912 // Although these are set up later, we include a default initializer to remove warnings
913 int bytes_per_component = 1; // bytes per component in the KTX file.
914 int bitness = 8; // internal precision we will use in the codec.
915 scanline_transfer copy_method = R8_TO_RGBA8;
916
917 switch (hdr.gl_type)
918 {
919 case GL_UNSIGNED_BYTE:
920 {
921 bitness = 8;
922 bytes_per_component = 1;
923 switch (hdr.gl_format)
924 {
925 case GL_RED:
926 copy_method = R8_TO_RGBA8;
927 break;
928 case GL_RG:
929 copy_method = RG8_TO_RGBA8;
930 break;
931 case GL_RGB:
932 copy_method = RGB8_TO_RGBA8;
933 break;
934 case GL_RGBA:
935 copy_method = RGBA8_TO_RGBA8;
936 break;
937 case GL_BGR:
938 copy_method = BGR8_TO_RGBA8;
939 break;
940 case GL_BGRA:
941 copy_method = BGRA8_TO_RGBA8;
942 break;
943 case GL_LUMINANCE:
944 copy_method = L8_TO_RGBA8;
945 break;
946 case GL_LUMINANCE_ALPHA:
947 copy_method = LA8_TO_RGBA8;
948 break;
949 }
950 break;
951 }
952 case GL_UNSIGNED_SHORT:
953 {
954 bitness = 16;
955 bytes_per_component = 2;
956 switch (hdr.gl_format)
957 {
958 case GL_RED:
959 copy_method = R16_TO_RGBA16F;
960 break;
961 case GL_RG:
962 copy_method = RG16_TO_RGBA16F;
963 break;
964 case GL_RGB:
965 copy_method = RGB16_TO_RGBA16F;
966 break;
967 case GL_RGBA:
968 copy_method = RGBA16_TO_RGBA16F;
969 break;
970 case GL_BGR:
971 copy_method = BGR16_TO_RGBA16F;
972 break;
973 case GL_BGRA:
974 copy_method = BGRA16_TO_RGBA16F;
975 break;
976 case GL_LUMINANCE:
977 copy_method = L16_TO_RGBA16F;
978 break;
979 case GL_LUMINANCE_ALPHA:
980 copy_method = LA16_TO_RGBA16F;
981 break;
982 }
983 break;
984 }
985 case GL_HALF_FLOAT:
986 {
987 bitness = 16;
988 bytes_per_component = 2;
989 switch (hdr.gl_format)
990 {
991 case GL_RED:
992 copy_method = R16F_TO_RGBA16F;
993 break;
994 case GL_RG:
995 copy_method = RG16F_TO_RGBA16F;
996 break;
997 case GL_RGB:
998 copy_method = RGB16F_TO_RGBA16F;
999 break;
1000 case GL_RGBA:
1001 copy_method = RGBA16F_TO_RGBA16F;
1002 break;
1003 case GL_BGR:
1004 copy_method = BGR16F_TO_RGBA16F;
1005 break;
1006 case GL_BGRA:
1007 copy_method = BGRA16F_TO_RGBA16F;
1008 break;
1009 case GL_LUMINANCE:
1010 copy_method = L16F_TO_RGBA16F;
1011 break;
1012 case GL_LUMINANCE_ALPHA:
1013 copy_method = LA16F_TO_RGBA16F;
1014 break;
1015 }
1016 break;
1017 }
1018 case GL_FLOAT:
1019 {
1020 bitness = 32;
1021 bytes_per_component = 4;
1022 switch (hdr.gl_format)
1023 {
1024 case GL_RED:
1025 copy_method = R32F_TO_RGBA16F;
1026 break;
1027 case GL_RG:
1028 copy_method = RG32F_TO_RGBA16F;
1029 break;
1030 case GL_RGB:
1031 copy_method = RGB32F_TO_RGBA16F;
1032 break;
1033 case GL_RGBA:
1034 copy_method = RGBA32F_TO_RGBA16F;
1035 break;
1036 case GL_BGR:
1037 copy_method = BGR32F_TO_RGBA16F;
1038 break;
1039 case GL_BGRA:
1040 copy_method = BGRA32F_TO_RGBA16F;
1041 break;
1042 case GL_LUMINANCE:
1043 copy_method = L32F_TO_RGBA16F;
1044 break;
1045 case GL_LUMINANCE_ALPHA:
1046 copy_method = LA32F_TO_RGBA16F;
1047 break;
1048 }
1049 break;
1050 }
1051 default:
1052 printf("KTX file %s has unsupported GL format\n", filename);
1053 fclose(f);
1054 return nullptr;
1055 }
1056
1057 if (hdr.number_of_mipmap_levels > 1)
1058 {
1059 printf("WARNING: KTX file %s has %d mipmap levels; only the first one will be encoded.\n", filename, hdr.number_of_mipmap_levels);
1060 }
1061
1062 if (hdr.number_of_array_elements > 1)
1063 {
1064 printf("WARNING: KTX file %s contains a texture array with %d layers; only the first one will be encoded.\n", filename, hdr.number_of_array_elements);
1065 }
1066
1067 if (hdr.number_of_faces > 1)
1068 {
1069 printf("WARNING: KTX file %s contains a cubemap with 6 faces; only the first one will be encoded.\n", filename);
1070 }
1071
1072
1073 unsigned int dim_x = hdr.pixel_width;
1074 unsigned int dim_y = astc::max(hdr.pixel_height, 1u);
1075 unsigned int dim_z = astc::max(hdr.pixel_depth, 1u);
1076
1077 // ignore the key/value data
1078 fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR);
1079
1080 uint32_t specified_bytes_of_surface = 0;
1081 size_t sb_read = fread(&specified_bytes_of_surface, 1, 4, f);
1082 if (sb_read != 4)
1083 {
1084 printf("Failed to read header of KTX file %s\n", filename);
1085 fclose(f);
1086 return nullptr;
1087 }
1088
1089 if (switch_endianness)
1090 {
1091 specified_bytes_of_surface = u32_byterev(specified_bytes_of_surface);
1092 }
1093
1094 // read the surface
1095 uint32_t xstride = bytes_per_component * components * dim_x;
1096 uint32_t ystride = xstride * dim_y;
1097 uint32_t computed_bytes_of_surface = dim_z * ystride;
1098 if (computed_bytes_of_surface != specified_bytes_of_surface)
1099 {
1100 fclose(f);
1101 printf("%s: KTX file inconsistency: computed surface size is %d bytes, but specified size is %d bytes\n", filename, computed_bytes_of_surface, specified_bytes_of_surface);
1102 return nullptr;
1103 }
1104
1105 uint8_t *buf = new uint8_t[specified_bytes_of_surface];
1106 size_t bytes_read = fread(buf, 1, specified_bytes_of_surface, f);
1107 fclose(f);
1108 if (bytes_read != specified_bytes_of_surface)
1109 {
1110 delete[] buf;
1111 printf("Failed to read file %s\n", filename);
1112 return nullptr;
1113 }
1114
1115 // perform an endianness swap on the surface if needed.
1116 if (switch_endianness)
1117 {
1118 if (hdr.gl_type_size == 2)
1119 {
1120 switch_endianness2(buf, specified_bytes_of_surface);
1121 }
1122
1123 if (hdr.gl_type_size == 4)
1124 {
1125 switch_endianness4(buf, specified_bytes_of_surface);
1126 }
1127 }
1128
1129 // then transfer data from the surface to our own image-data-structure.
1130 astcenc_image *astc_img = alloc_image(bitness, dim_x, dim_y, dim_z);
1131
1132 for (unsigned int z = 0; z < dim_z; z++)
1133 {
1134 for (unsigned int y = 0; y < dim_y; y++)
1135 {
1136 unsigned int ymod = y_flip ? dim_y - y - 1 : y;
1137 unsigned int ydst = ymod;
1138 void *dst;
1139
1140 if (astc_img->data_type == ASTCENC_TYPE_U8)
1141 {
1142 uint8_t* data8 = static_cast<uint8_t*>(astc_img->data[z]);
1143 dst = static_cast<void*>(&data8[4 * dim_x * ydst]);
1144 }
1145 else // if (astc_img->data_type == ASTCENC_TYPE_F16)
1146 {
1147 assert(astc_img->data_type == ASTCENC_TYPE_F16);
1148 uint16_t* data16 = static_cast<uint16_t*>(astc_img->data[z]);
1149 dst = static_cast<void*>(&data16[4 * dim_x * ydst]);
1150 }
1151
1152 uint8_t *src = buf + (z * ystride) + (y * xstride);
1153 copy_scanline(dst, src, dim_x, copy_method);
1154 }
1155 }
1156
1157 delete[] buf;
1158 is_hdr = bitness == 32;
1159 component_count = components;
1160 return astc_img;
1161 }
1162
1163 /**
1164 * @brief Load a KTX compressed image using the local custom loader.
1165 *
1166 * @param filename The name of the file to load.
1167 * @param[out] is_srgb @c true if this is an sRGB image, @c false otherwise.
1168 * @param[out] img The output image to populate.
1169 *
1170 * @return @c true on error, @c false otherwise.
1171 */
load_ktx_compressed_image(const char * filename,bool & is_srgb,astc_compressed_image & img)1172 bool load_ktx_compressed_image(
1173 const char* filename,
1174 bool& is_srgb,
1175 astc_compressed_image& img
1176 ) {
1177 FILE *f = fopen(filename, "rb");
1178 if (!f)
1179 {
1180 printf("Failed to open file %s\n", filename);
1181 return true;
1182 }
1183
1184 ktx_header hdr;
1185 size_t actual = fread(&hdr, 1, sizeof(hdr), f);
1186 if (actual != sizeof(hdr))
1187 {
1188 printf("Failed to read header from %s\n", filename);
1189 fclose(f);
1190 return true;
1191 }
1192
1193 if (memcmp(hdr.magic, ktx_magic, 12) != 0 ||
1194 (hdr.endianness != 0x04030201 && hdr.endianness != 0x01020304))
1195 {
1196 printf("File %s does not have a valid KTX header\n", filename);
1197 fclose(f);
1198 return true;
1199 }
1200
1201 bool switch_endianness = false;
1202 if (hdr.endianness == 0x01020304)
1203 {
1204 switch_endianness = true;
1205 ktx_header_switch_endianness(&hdr);
1206 }
1207
1208 if (hdr.gl_type != 0 || hdr.gl_format != 0 || hdr.gl_type_size != 1 ||
1209 hdr.gl_base_internal_format != GL_RGBA)
1210 {
1211 printf("File %s is not a compressed ASTC file\n", filename);
1212 fclose(f);
1213 return true;
1214 }
1215
1216 const format_entry* fmt = get_format(hdr.gl_internal_format);
1217 if (!fmt)
1218 {
1219 printf("File %s is not a compressed ASTC file\n", filename);
1220 fclose(f);
1221 return true;
1222 }
1223
1224 // Skip over any key-value pairs
1225 int seekerr;
1226 seekerr = fseek(f, hdr.bytes_of_key_value_data, SEEK_CUR);
1227 if (seekerr)
1228 {
1229 printf("Failed to skip key-value pairs in %s\n", filename);
1230 fclose(f);
1231 return true;
1232 }
1233
1234 // Read the length of the data and endianess convert
1235 unsigned int data_len;
1236 actual = fread(&data_len, 1, sizeof(data_len), f);
1237 if (actual != sizeof(data_len))
1238 {
1239 printf("Failed to read mip 0 size from %s\n", filename);
1240 fclose(f);
1241 return true;
1242 }
1243
1244 if (switch_endianness)
1245 {
1246 data_len = u32_byterev(data_len);
1247 }
1248
1249 // Read the data
1250 unsigned char* data = new unsigned char[data_len];
1251 actual = fread(data, 1, data_len, f);
1252 if (actual != data_len)
1253 {
1254 printf("Failed to read mip 0 data from %s\n", filename);
1255 fclose(f);
1256 delete[] data;
1257 return true;
1258 }
1259
1260 img.block_x = fmt->x;
1261 img.block_y = fmt->y;
1262 img.block_z = fmt->z == 0 ? 1 : fmt->z;
1263
1264 img.dim_x = hdr.pixel_width;
1265 img.dim_y = hdr.pixel_height;
1266 img.dim_z = hdr.pixel_depth == 0 ? 1 : hdr.pixel_depth;
1267
1268 img.data_len = data_len;
1269 img.data = data;
1270
1271 is_srgb = fmt->is_srgb;
1272
1273 fclose(f);
1274 return false;
1275 }
1276
1277 /**
1278 * @brief Store a KTX compressed image using a local store routine.
1279 *
1280 * @param img The image data to store.
1281 * @param filename The name of the file to save.
1282 * @param is_srgb @c true if this is an sRGB image, @c false if linear.
1283 *
1284 * @return @c true on error, @c false otherwise.
1285 */
store_ktx_compressed_image(const astc_compressed_image & img,const char * filename,bool is_srgb)1286 bool store_ktx_compressed_image(
1287 const astc_compressed_image& img,
1288 const char* filename,
1289 bool is_srgb
1290 ) {
1291 unsigned int fmt = get_format(img.block_x, img.block_y, img.block_z, is_srgb);
1292
1293 ktx_header hdr;
1294 memcpy(hdr.magic, ktx_magic, 12);
1295 hdr.endianness = 0x04030201;
1296 hdr.gl_type = 0;
1297 hdr.gl_type_size = 1;
1298 hdr.gl_format = 0;
1299 hdr.gl_internal_format = fmt;
1300 hdr.gl_base_internal_format = GL_RGBA;
1301 hdr.pixel_width = img.dim_x;
1302 hdr.pixel_height = img.dim_y;
1303 hdr.pixel_depth = (img.dim_z == 1) ? 0 : img.dim_z;
1304 hdr.number_of_array_elements = 0;
1305 hdr.number_of_faces = 1;
1306 hdr.number_of_mipmap_levels = 1;
1307 hdr.bytes_of_key_value_data = 0;
1308
1309 size_t expected = sizeof(ktx_header) + 4 + img.data_len;
1310 size_t actual = 0;
1311
1312 FILE *wf = fopen(filename, "wb");
1313 if (!wf)
1314 {
1315 return true;
1316 }
1317
1318 actual += fwrite(&hdr, 1, sizeof(ktx_header), wf);
1319 actual += fwrite(&img.data_len, 1, 4, wf);
1320 actual += fwrite(img.data, 1, img.data_len, wf);
1321 fclose(wf);
1322
1323 if (actual != expected)
1324 {
1325 return true;
1326 }
1327
1328 return false;
1329 }
1330
1331 /**
1332 * @brief Save a KTX uncompressed image using a local store routine.
1333 *
1334 * @param img The source data for the image.
1335 * @param filename The name of the file to save.
1336 * @param y_flip Should the image be vertically flipped?
1337 *
1338 * @return @c true if the image saved OK, @c false on error.
1339 */
store_ktx_uncompressed_image(const astcenc_image * img,const char * filename,int y_flip)1340 static bool store_ktx_uncompressed_image(
1341 const astcenc_image* img,
1342 const char* filename,
1343 int y_flip
1344 ) {
1345 unsigned int dim_x = img->dim_x;
1346 unsigned int dim_y = img->dim_y;
1347 unsigned int dim_z = img->dim_z;
1348
1349 int bitness = img->data_type == ASTCENC_TYPE_U8 ? 8 : 16;
1350 int image_components = determine_image_components(img);
1351
1352 ktx_header hdr;
1353
1354 static const int gl_format_of_components[4] {
1355 GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA
1356 };
1357
1358 memcpy(hdr.magic, ktx_magic, 12);
1359 hdr.endianness = 0x04030201;
1360 hdr.gl_type = (bitness == 16) ? GL_HALF_FLOAT : GL_UNSIGNED_BYTE;
1361 hdr.gl_type_size = bitness / 8;
1362 hdr.gl_format = gl_format_of_components[image_components - 1];
1363 hdr.gl_internal_format = gl_format_of_components[image_components - 1];
1364 hdr.gl_base_internal_format = gl_format_of_components[image_components - 1];
1365 hdr.pixel_width = dim_x;
1366 hdr.pixel_height = dim_y;
1367 hdr.pixel_depth = (dim_z == 1) ? 0 : dim_z;
1368 hdr.number_of_array_elements = 0;
1369 hdr.number_of_faces = 1;
1370 hdr.number_of_mipmap_levels = 1;
1371 hdr.bytes_of_key_value_data = 0;
1372
1373 // Collect image data to write
1374 uint8_t ***row_pointers8 = nullptr;
1375 uint16_t ***row_pointers16 = nullptr;
1376 if (bitness == 8)
1377 {
1378 row_pointers8 = new uint8_t **[dim_z];
1379 row_pointers8[0] = new uint8_t *[dim_y * dim_z];
1380 row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components + 3];
1381
1382 for (unsigned int z = 1; z < dim_z; z++)
1383 {
1384 row_pointers8[z] = row_pointers8[0] + dim_y * z;
1385 row_pointers8[z][0] = row_pointers8[0][0] + dim_y * dim_x * image_components * z;
1386 }
1387
1388 for (unsigned int z = 0; z < dim_z; z++)
1389 {
1390 for (unsigned int y = 1; y < dim_y; y++)
1391 {
1392 row_pointers8[z][y] = row_pointers8[z][0] + dim_x * image_components * y;
1393 }
1394 }
1395
1396 for (unsigned int z = 0; z < dim_z; z++)
1397 {
1398 uint8_t* data8 = static_cast<uint8_t*>(img->data[z]);
1399 for (unsigned int y = 0; y < dim_y; y++)
1400 {
1401 int ym = y_flip ? dim_y - y - 1 : y;
1402 switch (image_components)
1403 {
1404 case 1: // single-component, treated as Luminance
1405 for (unsigned int x = 0; x < dim_x; x++)
1406 {
1407 row_pointers8[z][y][x] = data8[(4 * dim_x * ym) + (4 * x )];
1408 }
1409 break;
1410 case 2: // two-component, treated as Luminance-Alpha
1411 for (unsigned int x = 0; x < dim_x; x++)
1412 {
1413 row_pointers8[z][y][2 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
1414 row_pointers8[z][y][2 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 3)];
1415 }
1416 break;
1417 case 3: // three-component, treated a
1418 for (unsigned int x = 0; x < dim_x; x++)
1419 {
1420 row_pointers8[z][y][3 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
1421 row_pointers8[z][y][3 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
1422 row_pointers8[z][y][3 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
1423 }
1424 break;
1425 case 4: // four-component, treated as RGBA
1426 for (unsigned int x = 0; x < dim_x; x++)
1427 {
1428 row_pointers8[z][y][4 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
1429 row_pointers8[z][y][4 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
1430 row_pointers8[z][y][4 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
1431 row_pointers8[z][y][4 * x + 3] = data8[(4 * dim_x * ym) + (4 * x + 3)];
1432 }
1433 break;
1434 }
1435 }
1436 }
1437 }
1438 else // if bitness == 16
1439 {
1440 row_pointers16 = new uint16_t **[dim_z];
1441 row_pointers16[0] = new uint16_t *[dim_y * dim_z];
1442 row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components + 1];
1443
1444 for (unsigned int z = 1; z < dim_z; z++)
1445 {
1446 row_pointers16[z] = row_pointers16[0] + dim_y * z;
1447 row_pointers16[z][0] = row_pointers16[0][0] + dim_y * dim_x * image_components * z;
1448 }
1449
1450 for (unsigned int z = 0; z < dim_z; z++)
1451 {
1452 for (unsigned int y = 1; y < dim_y; y++)
1453 {
1454 row_pointers16[z][y] = row_pointers16[z][0] + dim_x * image_components * y;
1455 }
1456 }
1457
1458 for (unsigned int z = 0; z < dim_z; z++)
1459 {
1460 uint16_t* data16 = static_cast<uint16_t*>(img->data[z]);
1461 for (unsigned int y = 0; y < dim_y; y++)
1462 {
1463 int ym = y_flip ? dim_y - y - 1 : y;
1464 switch (image_components)
1465 {
1466 case 1: // single-component, treated as Luminance
1467 for (unsigned int x = 0; x < dim_x; x++)
1468 {
1469 row_pointers16[z][y][x] = data16[(4 * dim_x * ym) + (4 * x )];
1470 }
1471 break;
1472 case 2: // two-component, treated as Luminance-Alpha
1473 for (unsigned int x = 0; x < dim_x; x++)
1474 {
1475 row_pointers16[z][y][2 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
1476 row_pointers16[z][y][2 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 3)];
1477 }
1478 break;
1479 case 3: // three-component, treated as RGB
1480 for (unsigned int x = 0; x < dim_x; x++)
1481 {
1482 row_pointers16[z][y][3 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
1483 row_pointers16[z][y][3 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
1484 row_pointers16[z][y][3 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
1485 }
1486 break;
1487 case 4: // four-component, treated as RGBA
1488 for (unsigned int x = 0; x < dim_x; x++)
1489 {
1490 row_pointers16[z][y][4 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
1491 row_pointers16[z][y][4 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
1492 row_pointers16[z][y][4 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
1493 row_pointers16[z][y][4 * x + 3] = data16[(4 * dim_x * ym) + (4 * x + 3)];
1494 }
1495 break;
1496 }
1497 }
1498 }
1499 }
1500
1501 bool retval { true };
1502 uint32_t image_bytes = dim_x * dim_y * dim_z * image_components * (bitness / 8);
1503 uint32_t image_write_bytes = (image_bytes + 3) & ~3;
1504
1505 FILE *wf = fopen(filename, "wb");
1506 if (wf)
1507 {
1508 void* dataptr = (bitness == 16) ?
1509 reinterpret_cast<void*>(row_pointers16[0][0]) :
1510 reinterpret_cast<void*>(row_pointers8[0][0]);
1511
1512 size_t expected_bytes_written = sizeof(ktx_header) + image_write_bytes + 4;
1513 size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(ktx_header), wf);
1514 size_t bytecount_bytes_written = fwrite(&image_bytes, 1, 4, wf);
1515 size_t data_bytes_written = fwrite(dataptr, 1, image_write_bytes, wf);
1516 fclose(wf);
1517 if (hdr_bytes_written + bytecount_bytes_written + data_bytes_written != expected_bytes_written)
1518 {
1519 retval = false;
1520 }
1521 }
1522 else
1523 {
1524 retval = false;
1525 }
1526
1527 if (row_pointers8)
1528 {
1529 delete[] row_pointers8[0][0];
1530 delete[] row_pointers8[0];
1531 delete[] row_pointers8;
1532 }
1533
1534 if (row_pointers16)
1535 {
1536 delete[] row_pointers16[0][0];
1537 delete[] row_pointers16[0];
1538 delete[] row_pointers16;
1539 }
1540
1541 return retval;
1542 }
1543
1544 /*
1545 Loader for DDS files.
1546
1547 Note that after the header, data are densely packed with no padding;
1548 in the case of multiple surfaces, they appear one after another in
1549 the file, again with no padding.
1550
1551 This code is NOT endian-neutral.
1552 */
1553 struct dds_pixelformat
1554 {
1555 uint32_t size; // structure size, set to 32.
1556 /*
1557 flags bits are a combination of the following: 0x1 : Texture contains alpha data 0x2 : ---- (older files: texture contains alpha data, for Alpha-only texture) 0x4 : The fourcc field is valid,
1558 indicating a compressed or DX10 texture format 0x40 : texture contains uncompressed RGB data 0x200 : ---- (YUV in older files) 0x20000 : Texture contains Luminance data (can be combined with
1559 0x1 for Lum-Alpha) */
1560 uint32_t flags;
1561 uint32_t fourcc; // "DX10" to indicate a DX10 format, "DXTn" for the DXT formats
1562 uint32_t rgbbitcount; // number of bits per texel; up to 32 for non-DX10 formats.
1563 uint32_t rbitmask; // bitmap indicating position of red/luminance color component
1564 uint32_t gbitmask; // bitmap indicating position of green color component
1565 uint32_t bbitmask; // bitmap indicating position of blue color component
1566 uint32_t abitmask; // bitmap indicating position of alpha color component
1567 };
1568
1569 struct dds_header
1570 {
1571 uint32_t size; // header size; must be exactly 124.
1572 /*
1573 flag field is an OR or the following bits, that indicate fields containing valid data:
1574 1: caps/caps2/caps3/caps4 (set in all DDS files, ignore on read)
1575 2: height (set in all DDS files, ignore on read)
1576 4: width (set in all DDS files, ignore on read)
1577 8: pitch (for uncompressed texture)
1578 0x1000: the pixel format field (set in all DDS files, ignore on read)
1579 0x20000: mipmap count (for mipmapped textures with >1 level)
1580 0x80000: pitch (for compressed texture)
1581 0x800000: depth (for 3d textures)
1582 */
1583 uint32_t flags;
1584 uint32_t height;
1585 uint32_t width;
1586 uint32_t pitch_or_linear_size; // scanline pitch for uncompressed; total size in bytes for compressed
1587 uint32_t depth;
1588 uint32_t mipmapcount;
1589 // unused, set to 0
1590 uint32_t reserved1[11];
1591 dds_pixelformat ddspf;
1592 /*
1593 caps field is an OR of the following values:
1594 8 : should be set for a file that contains more than 1 surface (ignore on read)
1595 0x400000 : should be set for a mipmapped texture
1596 0x1000 : should be set if the surface is a texture at all (all DDS files, ignore on read)
1597 */
1598 uint32_t caps;
1599 /*
1600 caps2 field is an OR of the following values:
1601 0x200 : texture is cubemap
1602 0x400 : +X face of cubemap is present
1603 0x800 : -X face of cubemap is present
1604 0x1000 : +Y face of cubemap is present
1605 0x2000 : -Y face of cubemap is present
1606 0x4000 : +Z face of cubemap is present
1607 0x8000 : -Z face of cubemap is present
1608 0x200000 : texture is a 3d texture.
1609 */
1610 uint32_t caps2;
1611 // unused, set to 0
1612 uint32_t caps3;
1613 // unused, set to 0
1614 uint32_t caps4;
1615 // unused, set to 0
1616 uint32_t reserved2;
1617 };
1618
1619 struct dds_header_dx10
1620 {
1621 uint32_t dxgi_format;
1622 uint32_t resource_dimension; // 2=1d-texture, 3=2d-texture or cubemap, 4=3d-texture
1623 uint32_t misc_flag; // 4 if cubemap, else 0
1624 uint32_t array_size; // size of array in case of a texture array; set to 1 for a non-array
1625 uint32_t reserved; // set to 0.
1626 };
1627
1628 #define DDS_MAGIC 0x20534444
1629 #define DX10_MAGIC 0x30315844
1630
1631 /**
1632 * @brief Load an uncompressed DDS image using the local custom loader.
1633 *
1634 * @param filename The name of the file to load.
1635 * @param y_flip Should the image be vertically flipped?
1636 * @param[out] is_hdr Is this an HDR image load?
1637 * @param[out] component_count The number of components in the data.
1638 *
1639 * @return The loaded image data in a canonical 4 channel format, or @c nullptr on error.
1640 */
load_dds_uncompressed_image(const char * filename,bool y_flip,bool & is_hdr,unsigned int & component_count)1641 static astcenc_image* load_dds_uncompressed_image(
1642 const char* filename,
1643 bool y_flip,
1644 bool& is_hdr,
1645 unsigned int& component_count
1646 ) {
1647 FILE *f = fopen(filename, "rb");
1648 if (!f)
1649 {
1650 printf("Failed to open file %s\n", filename);
1651 return nullptr;
1652 }
1653
1654 uint8_t magic[4];
1655
1656 dds_header hdr;
1657 size_t magic_bytes_read = fread(magic, 1, 4, f);
1658 size_t header_bytes_read = fread(&hdr, 1, sizeof(hdr), f);
1659 if (magic_bytes_read != 4 || header_bytes_read != sizeof(hdr))
1660 {
1661 printf("Failed to read header of DDS file %s\n", filename);
1662 fclose(f);
1663 return nullptr;
1664 }
1665
1666 uint32_t magicx = magic[0] | (magic[1] << 8) | (magic[2] << 16) | (magic[3] << 24);
1667
1668 if (magicx != DDS_MAGIC || hdr.size != 124)
1669 {
1670 printf("File %s does not have a valid DDS header\n", filename);
1671 fclose(f);
1672 return nullptr;
1673 }
1674
1675 int use_dx10_header = 0;
1676 if (hdr.ddspf.flags & 4)
1677 {
1678 if (hdr.ddspf.fourcc == DX10_MAGIC)
1679 {
1680 use_dx10_header = 1;
1681 }
1682 else
1683 {
1684 printf("DDS file %s is compressed, not supported\n", filename);
1685 fclose(f);
1686 return nullptr;
1687 }
1688 }
1689
1690 dds_header_dx10 dx10_header;
1691 if (use_dx10_header)
1692 {
1693 size_t dx10_header_bytes_read = fread(&dx10_header, 1, sizeof(dx10_header), f);
1694 if (dx10_header_bytes_read != sizeof(dx10_header))
1695 {
1696 printf("Failed to read header of DDS file %s\n", filename);
1697 fclose(f);
1698 return nullptr;
1699 }
1700 }
1701
1702 unsigned int dim_x = hdr.width;
1703 unsigned int dim_y = hdr.height;
1704 unsigned int dim_z = (hdr.flags & 0x800000) ? hdr.depth : 1;
1705
1706 // The bitcount that we will use internally in the codec
1707 int bitness = 0;
1708
1709 // The bytes per component in the DDS file itself
1710 int bytes_per_component = 0;
1711 int components = 0;
1712 scanline_transfer copy_method = R8_TO_RGBA8;
1713
1714 // figure out the format actually used in the DDS file.
1715 if (use_dx10_header)
1716 {
1717 // DX10 header present; use the DXGI format.
1718 #define DXGI_FORMAT_R32G32B32A32_FLOAT 2
1719 #define DXGI_FORMAT_R32G32B32_FLOAT 6
1720 #define DXGI_FORMAT_R16G16B16A16_FLOAT 10
1721 #define DXGI_FORMAT_R16G16B16A16_UNORM 11
1722 #define DXGI_FORMAT_R32G32_FLOAT 16
1723 #define DXGI_FORMAT_R8G8B8A8_UNORM 28
1724 #define DXGI_FORMAT_R16G16_FLOAT 34
1725 #define DXGI_FORMAT_R16G16_UNORM 35
1726 #define DXGI_FORMAT_R32_FLOAT 41
1727 #define DXGI_FORMAT_R8G8_UNORM 49
1728 #define DXGI_FORMAT_R16_FLOAT 54
1729 #define DXGI_FORMAT_R16_UNORM 56
1730 #define DXGI_FORMAT_R8_UNORM 61
1731 #define DXGI_FORMAT_B8G8R8A8_UNORM 86
1732 #define DXGI_FORMAT_B8G8R8X8_UNORM 87
1733
1734 struct dxgi_params
1735 {
1736 int bitness;
1737 int bytes_per_component;
1738 int components;
1739 scanline_transfer copy_method;
1740 uint32_t dxgi_format_number;
1741 };
1742
1743 static const dxgi_params format_params[] {
1744 {16, 4, 4, RGBA32F_TO_RGBA16F, DXGI_FORMAT_R32G32B32A32_FLOAT},
1745 {16, 4, 3, RGB32F_TO_RGBA16F, DXGI_FORMAT_R32G32B32_FLOAT},
1746 {16, 2, 4, RGBA16F_TO_RGBA16F, DXGI_FORMAT_R16G16B16A16_FLOAT},
1747 {16, 2, 4, RGBA16_TO_RGBA16F, DXGI_FORMAT_R16G16B16A16_UNORM},
1748 {16, 4, 2, RG32F_TO_RGBA16F, DXGI_FORMAT_R32G32_FLOAT},
1749 {8, 1, 4, RGBA8_TO_RGBA8, DXGI_FORMAT_R8G8B8A8_UNORM},
1750 {16, 2, 2, RG16F_TO_RGBA16F, DXGI_FORMAT_R16G16_FLOAT},
1751 {16, 2, 2, RG16_TO_RGBA16F, DXGI_FORMAT_R16G16_UNORM},
1752 {16, 4, 1, R32F_TO_RGBA16F, DXGI_FORMAT_R32_FLOAT},
1753 {8, 1, 2, RG8_TO_RGBA8, DXGI_FORMAT_R8G8_UNORM},
1754 {16, 2, 1, R16F_TO_RGBA16F, DXGI_FORMAT_R16_FLOAT},
1755 {16, 2, 1, R16_TO_RGBA16F, DXGI_FORMAT_R16_UNORM},
1756 {8, 1, 1, R8_TO_RGBA8, DXGI_FORMAT_R8_UNORM},
1757 {8, 1, 4, BGRA8_TO_RGBA8, DXGI_FORMAT_B8G8R8A8_UNORM},
1758 {8, 1, 4, BGRX8_TO_RGBA8, DXGI_FORMAT_B8G8R8X8_UNORM},
1759 };
1760
1761 int dxgi_modes_supported = sizeof(format_params) / sizeof(format_params[0]);
1762 int did_select_format = 0;
1763 for (int i = 0; i < dxgi_modes_supported; i++)
1764 {
1765 if (dx10_header.dxgi_format == format_params[i].dxgi_format_number)
1766 {
1767 bitness = format_params[i].bitness;
1768 bytes_per_component = format_params[i].bytes_per_component;
1769 components = format_params[i].components;
1770 copy_method = format_params[i].copy_method;
1771 did_select_format = 1;
1772 break;
1773 }
1774 }
1775
1776 if (!did_select_format)
1777 {
1778 printf("DDS file %s: DXGI format not supported by codec\n", filename);
1779 fclose(f);
1780 return nullptr;
1781 }
1782 }
1783 else
1784 {
1785 // No DX10 header present. Then try to match the bitcount and bitmask against
1786 // a set of prepared patterns.
1787 uint32_t flags = hdr.ddspf.flags;
1788 uint32_t bitcount = hdr.ddspf.rgbbitcount;
1789 uint32_t rmask = hdr.ddspf.rbitmask;
1790 uint32_t gmask = hdr.ddspf.gbitmask;
1791 uint32_t bmask = hdr.ddspf.bbitmask;
1792 uint32_t amask = hdr.ddspf.abitmask;
1793
1794 // RGBA-unorm8
1795 if ((flags & 0x41) == 0x41 && bitcount == 32 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000 && amask == 0xFF000000)
1796 {
1797 bytes_per_component = 1;
1798 components = 4;
1799 copy_method = RGBA8_TO_RGBA8;
1800 }
1801 // BGRA-unorm8
1802 else if ((flags & 0x41) == 0x41 && bitcount == 32 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF && amask == 0xFF000000)
1803 {
1804 bytes_per_component = 1;
1805 components = 4;
1806 copy_method = BGRA8_TO_RGBA8;
1807 }
1808 // RGBX-unorm8
1809 else if ((flags & 0x40) && bitcount == 32 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000)
1810 {
1811 bytes_per_component = 1;
1812 components = 4;
1813 copy_method = RGBX8_TO_RGBA8;
1814 }
1815 // BGRX-unorm8
1816 else if ((flags & 0x40) && bitcount == 32 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF)
1817 {
1818 bytes_per_component = 1;
1819 components = 4;
1820 copy_method = BGRX8_TO_RGBA8;
1821 }
1822 // RGB-unorm8
1823 else if ((flags & 0x40) && bitcount == 24 && rmask == 0xFF && gmask == 0xFF00 && bmask == 0xFF0000)
1824 {
1825 bytes_per_component = 1;
1826 components = 3;
1827 copy_method = RGB8_TO_RGBA8;
1828 }
1829 // BGR-unorm8
1830 else if ((flags & 0x40) && bitcount == 24 && rmask == 0xFF0000 && gmask == 0xFF00 && bmask == 0xFF)
1831 {
1832 bytes_per_component = 1;
1833 components = 3;
1834 copy_method = BGR8_TO_RGBA8;
1835 }
1836 // RG-unorm16
1837 else if ((flags & 0x40) && bitcount == 16 && rmask == 0xFFFF && gmask == 0xFFFF0000)
1838 {
1839 bytes_per_component = 2;
1840 components = 2;
1841 copy_method = RG16_TO_RGBA16F;
1842 }
1843 // A8L8
1844 else if ((flags & 0x20001) == 0x20001 && bitcount == 16 && rmask == 0xFF && amask == 0xFF00)
1845 {
1846 bytes_per_component = 1;
1847 components = 2;
1848 copy_method = LA8_TO_RGBA8;
1849 }
1850 // L8
1851 else if ((flags & 0x20000) && bitcount == 8 && rmask == 0xFF)
1852 {
1853 bytes_per_component = 1;
1854 components = 1;
1855 copy_method = L8_TO_RGBA8;
1856 }
1857 // L16
1858 else if ((flags & 0x20000) && bitcount == 16 && rmask == 0xFFFF)
1859 {
1860 bytes_per_component = 2;
1861 components = 1;
1862 copy_method = L16_TO_RGBA16F;
1863 }
1864 else
1865 {
1866 printf("DDS file %s: Non-DXGI format not supported by codec\n", filename);
1867 fclose(f);
1868 return nullptr;
1869 }
1870
1871 bitness = bytes_per_component * 8;
1872 }
1873
1874 // then, load the actual file.
1875 uint32_t xstride = bytes_per_component * components * dim_x;
1876 uint32_t ystride = xstride * dim_y;
1877 uint32_t bytes_of_surface = ystride * dim_z;
1878
1879 uint8_t *buf = new uint8_t[bytes_of_surface];
1880 size_t bytes_read = fread(buf, 1, bytes_of_surface, f);
1881 fclose(f);
1882 if (bytes_read != bytes_of_surface)
1883 {
1884 delete[] buf;
1885 printf("Failed to read file %s\n", filename);
1886 return nullptr;
1887 }
1888
1889 // then transfer data from the surface to our own image-data-structure.
1890 astcenc_image *astc_img = alloc_image(bitness, dim_x, dim_y, dim_z);
1891
1892 for (unsigned int z = 0; z < dim_z; z++)
1893 {
1894 for (unsigned int y = 0; y < dim_y; y++)
1895 {
1896 unsigned int ymod = y_flip ? dim_y - y - 1 : y;
1897 unsigned int ydst = ymod;
1898 void* dst;
1899
1900 if (astc_img->data_type == ASTCENC_TYPE_U8)
1901 {
1902 uint8_t* data8 = static_cast<uint8_t*>(astc_img->data[z]);
1903 dst = static_cast<void*>(&data8[4 * dim_x * ydst]);
1904 }
1905 else // if (astc_img->data_type == ASTCENC_TYPE_F16)
1906 {
1907 assert(astc_img->data_type == ASTCENC_TYPE_F16);
1908 uint16_t* data16 = static_cast<uint16_t*>(astc_img->data[z]);
1909 dst = static_cast<void*>(&data16[4 * dim_x * ydst]);
1910 }
1911
1912 uint8_t *src = buf + (z * ystride) + (y * xstride);
1913 copy_scanline(dst, src, dim_x, copy_method);
1914 }
1915 }
1916
1917 delete[] buf;
1918 is_hdr = bitness == 16;
1919 component_count = components;
1920 return astc_img;
1921 }
1922
1923 /**
1924 * @brief Save a DDS uncompressed image using a local store routine.
1925 *
1926 * @param img The source data for the image.
1927 * @param filename The name of the file to save.
1928 * @param y_flip Should the image be vertically flipped?
1929 *
1930 * @return @c true if the image saved OK, @c false on error.
1931 */
store_dds_uncompressed_image(const astcenc_image * img,const char * filename,int y_flip)1932 static bool store_dds_uncompressed_image(
1933 const astcenc_image* img,
1934 const char* filename,
1935 int y_flip
1936 ) {
1937 unsigned int dim_x = img->dim_x;
1938 unsigned int dim_y = img->dim_y;
1939 unsigned int dim_z = img->dim_z;
1940
1941 int bitness = img->data_type == ASTCENC_TYPE_U8 ? 8 : 16;
1942 int image_components = (bitness == 16) ? 4 : determine_image_components(img);
1943
1944 // DDS-pixel-format structures to use when storing LDR image with 1,2,3 or 4 components.
1945 static const dds_pixelformat format_of_image_components[4] =
1946 {
1947 {32, 0x20000, 0, 8, 0xFF, 0, 0, 0}, // luminance
1948 {32, 0x20001, 0, 16, 0xFF, 0, 0, 0xFF00}, // L8A8
1949 {32, 0x40, 0, 24, 0xFF, 0xFF00, 0xFF0000, 0}, // RGB8
1950 {32, 0x41, 0, 32, 0xFF, 0xFF00, 0xFF0000, 0xFF000000} // RGBA8
1951 };
1952
1953 // DDS-pixel-format structures to use when storing HDR image.
1954 static const dds_pixelformat dxt10_diverter =
1955 {
1956 32, 4, DX10_MAGIC, 0, 0, 0, 0, 0
1957 };
1958
1959 // Header handling; will write:
1960 // * DDS magic value
1961 // * DDS header
1962 // * DDS DX10 header, if the file is floating-point
1963 // * pixel data
1964
1965 // Main header data
1966 dds_header hdr;
1967 hdr.size = 124;
1968 hdr.flags = 0x100F | (dim_z > 1 ? 0x800000 : 0);
1969 hdr.height = dim_y;
1970 hdr.width = dim_x;
1971 hdr.pitch_or_linear_size = image_components * (bitness / 8) * dim_x;
1972 hdr.depth = dim_z;
1973 hdr.mipmapcount = 1;
1974 for (unsigned int i = 0; i < 11; i++)
1975 {
1976 hdr.reserved1[i] = 0;
1977 }
1978 hdr.caps = 0x1000;
1979 hdr.caps2 = (dim_z > 1) ? 0x200000 : 0;
1980 hdr.caps3 = 0;
1981 hdr.caps4 = 0;
1982
1983 // Pixel-format data
1984 if (bitness == 8)
1985 {
1986 hdr.ddspf = format_of_image_components[image_components - 1];
1987 }
1988 else
1989 {
1990 hdr.ddspf = dxt10_diverter;
1991 }
1992
1993 // DX10 data
1994 dds_header_dx10 dx10;
1995 dx10.dxgi_format = DXGI_FORMAT_R16G16B16A16_FLOAT;
1996 dx10.resource_dimension = (dim_z > 1) ? 4 : 3;
1997 dx10.misc_flag = 0;
1998 dx10.array_size = 1;
1999 dx10.reserved = 0;
2000
2001 // Collect image data to write
2002 uint8_t ***row_pointers8 = nullptr;
2003 uint16_t ***row_pointers16 = nullptr;
2004
2005 if (bitness == 8)
2006 {
2007 row_pointers8 = new uint8_t **[dim_z];
2008 row_pointers8[0] = new uint8_t *[dim_y * dim_z];
2009 row_pointers8[0][0] = new uint8_t[dim_x * dim_y * dim_z * image_components];
2010
2011 for (unsigned int z = 1; z < dim_z; z++)
2012 {
2013 row_pointers8[z] = row_pointers8[0] + dim_y * z;
2014 row_pointers8[z][0] = row_pointers8[0][0] + dim_y * dim_z * image_components * z;
2015 }
2016
2017 for (unsigned int z = 0; z < dim_z; z++)
2018 {
2019 for (unsigned int y = 1; y < dim_y; y++)
2020 {
2021 row_pointers8[z][y] = row_pointers8[z][0] + dim_x * image_components * y;
2022 }
2023 }
2024
2025 for (unsigned int z = 0; z < dim_z; z++)
2026 {
2027 uint8_t* data8 = static_cast<uint8_t*>(img->data[z]);
2028
2029 for (unsigned int y = 0; y < dim_y; y++)
2030 {
2031 int ym = y_flip ? dim_y - y - 1 : y;
2032 switch (image_components)
2033 {
2034 case 1: // single-component, treated as Luminance
2035 for (unsigned int x = 0; x < dim_x; x++)
2036 {
2037 row_pointers8[z][y][x] = data8[(4 * dim_x * ym) + (4 * x )];
2038 }
2039 break;
2040 case 2: // two-component, treated as Luminance-Alpha
2041 for (unsigned int x = 0; x < dim_x; x++)
2042 {
2043 row_pointers8[z][y][2 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
2044 row_pointers8[z][y][2 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 3)];
2045 }
2046 break;
2047 case 3: // three-component, treated as RGB
2048 for (unsigned int x = 0; x < dim_x; x++)
2049 {
2050 row_pointers8[z][y][3 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
2051 row_pointers8[z][y][3 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
2052 row_pointers8[z][y][3 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
2053 }
2054 break;
2055 case 4: // four-component, treated as RGBA
2056 for (unsigned int x = 0; x < dim_x; x++)
2057 {
2058 row_pointers8[z][y][4 * x ] = data8[(4 * dim_x * ym) + (4 * x )];
2059 row_pointers8[z][y][4 * x + 1] = data8[(4 * dim_x * ym) + (4 * x + 1)];
2060 row_pointers8[z][y][4 * x + 2] = data8[(4 * dim_x * ym) + (4 * x + 2)];
2061 row_pointers8[z][y][4 * x + 3] = data8[(4 * dim_x * ym) + (4 * x + 3)];
2062 }
2063 break;
2064 }
2065 }
2066 }
2067 }
2068 else // if bitness == 16
2069 {
2070 row_pointers16 = new uint16_t **[dim_z];
2071 row_pointers16[0] = new uint16_t *[dim_y * dim_z];
2072 row_pointers16[0][0] = new uint16_t[dim_x * dim_y * dim_z * image_components];
2073
2074 for (unsigned int z = 1; z < dim_z; z++)
2075 {
2076 row_pointers16[z] = row_pointers16[0] + dim_y * z;
2077 row_pointers16[z][0] = row_pointers16[0][0] + dim_y * dim_x * image_components * z;
2078 }
2079
2080 for (unsigned int z = 0; z < dim_z; z++)
2081 {
2082 for (unsigned int y = 1; y < dim_y; y++)
2083 {
2084 row_pointers16[z][y] = row_pointers16[z][0] + dim_x * image_components * y;
2085 }
2086 }
2087
2088 for (unsigned int z = 0; z < dim_z; z++)
2089 {
2090 uint16_t* data16 = static_cast<uint16_t*>(img->data[z]);
2091
2092 for (unsigned int y = 0; y < dim_y; y++)
2093 {
2094 int ym = y_flip ? dim_y - y - 1: y;
2095 switch (image_components)
2096 {
2097 case 1: // single-component, treated as Luminance
2098 for (unsigned int x = 0; x < dim_x; x++)
2099 {
2100 row_pointers16[z][y][x] = data16[(4 * dim_x * ym) + (4 * x )];
2101 }
2102 break;
2103 case 2: // two-component, treated as Luminance-Alpha
2104 for (unsigned int x = 0; x < dim_x; x++)
2105 {
2106 row_pointers16[z][y][2 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
2107 row_pointers16[z][y][2 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 3)];
2108 }
2109 break;
2110 case 3: // three-component, treated as RGB
2111 for (unsigned int x = 0; x < dim_x; x++)
2112 {
2113 row_pointers16[z][y][3 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
2114 row_pointers16[z][y][3 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
2115 row_pointers16[z][y][3 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
2116 }
2117 break;
2118 case 4: // four-component, treated as RGBA
2119 for (unsigned int x = 0; x < dim_x; x++)
2120 {
2121 row_pointers16[z][y][4 * x ] = data16[(4 * dim_x * ym) + (4 * x )];
2122 row_pointers16[z][y][4 * x + 1] = data16[(4 * dim_x * ym) + (4 * x + 1)];
2123 row_pointers16[z][y][4 * x + 2] = data16[(4 * dim_x * ym) + (4 * x + 2)];
2124 row_pointers16[z][y][4 * x + 3] = data16[(4 * dim_x * ym) + (4 * x + 3)];
2125 }
2126 break;
2127 }
2128 }
2129 }
2130 }
2131
2132 bool retval { true };
2133 uint32_t image_bytes = dim_x * dim_y * dim_z * image_components * (bitness / 8);
2134
2135 uint32_t dds_magic = DDS_MAGIC;
2136
2137 FILE *wf = fopen(filename, "wb");
2138 if (wf)
2139 {
2140 void *dataptr = (bitness == 16) ?
2141 reinterpret_cast<void*>(row_pointers16[0][0]) :
2142 reinterpret_cast<void*>(row_pointers8[0][0]);
2143
2144 size_t expected_bytes_written = 4 + sizeof(dds_header) + (bitness > 8 ? sizeof(dds_header_dx10) : 0) + image_bytes;
2145
2146 size_t magic_bytes_written = fwrite(&dds_magic, 1, 4, wf);
2147 size_t hdr_bytes_written = fwrite(&hdr, 1, sizeof(dds_header), wf);
2148
2149 size_t dx10_bytes_written;
2150 if (bitness > 8)
2151 {
2152 dx10_bytes_written = fwrite(&dx10, 1, sizeof(dx10), wf);
2153 }
2154 else
2155 {
2156 dx10_bytes_written = 0;
2157 }
2158
2159 size_t data_bytes_written = fwrite(dataptr, 1, image_bytes, wf);
2160
2161 fclose(wf);
2162 if (magic_bytes_written + hdr_bytes_written + dx10_bytes_written + data_bytes_written != expected_bytes_written)
2163 {
2164 retval = false;
2165 }
2166 }
2167 else
2168 {
2169 retval = false;
2170 }
2171
2172 if (row_pointers8)
2173 {
2174 delete[] row_pointers8[0][0];
2175 delete[] row_pointers8[0];
2176 delete[] row_pointers8;
2177 }
2178
2179 if (row_pointers16)
2180 {
2181 delete[] row_pointers16[0][0];
2182 delete[] row_pointers16[0];
2183 delete[] row_pointers16;
2184 }
2185
2186 return retval;
2187 }
2188
2189 /**
2190 * @brief Supported uncompressed image load functions, and their associated file extensions.
2191 */
2192 static const struct
2193 {
2194 const char* ending1;
2195 const char* ending2;
2196 astcenc_image* (*loader_func)(const char*, bool, bool&, unsigned int&);
2197 } loader_descs[] {
2198 // LDR formats
2199 {".png", ".PNG", load_png_with_wuffs},
2200 // HDR formats
2201 {".exr", ".EXR", load_image_with_tinyexr },
2202 // Container formats
2203 {".ktx", ".KTX", load_ktx_uncompressed_image },
2204 {".dds", ".DDS", load_dds_uncompressed_image },
2205 // Generic catch all; this one must be last in the list
2206 { nullptr, nullptr, load_image_with_stb }
2207 };
2208
2209 static const int loader_descr_count = sizeof(loader_descs) / sizeof(loader_descs[0]);
2210
2211 /**
2212 * @brief Supported uncompressed image store functions, and their associated file extensions.
2213 */
2214 static const struct
2215 {
2216 const char *ending1;
2217 const char *ending2;
2218 int enforced_bitness;
2219 bool (*storer_func)(const astcenc_image *output_image, const char *filename, int y_flip);
2220 } storer_descs[] {
2221 // LDR formats
2222 {".bmp", ".BMP", 8, store_bmp_image_with_stb},
2223 {".png", ".PNG", 8, store_png_image_with_stb},
2224 {".tga", ".TGA", 8, store_tga_image_with_stb},
2225 // HDR formats
2226 {".exr", ".EXR", 16, store_exr_image_with_tinyexr},
2227 {".hdr", ".HDR", 16, store_hdr_image_with_stb},
2228 // Container formats
2229 {".dds", ".DDS", 0, store_dds_uncompressed_image},
2230 {".ktx", ".KTX", 0, store_ktx_uncompressed_image}
2231 };
2232
2233 static const int storer_descr_count = sizeof(storer_descs) / sizeof(storer_descs[0]);
2234
2235 /* See header for documentation. */
get_output_filename_enforced_bitness(const char * filename)2236 int get_output_filename_enforced_bitness(
2237 const char* filename
2238 ) {
2239 const char *eptr = strrchr(filename, '.');
2240 if (!eptr)
2241 {
2242 return 0;
2243 }
2244
2245 for (int i = 0; i < storer_descr_count; i++)
2246 {
2247 if (strcmp(eptr, storer_descs[i].ending1) == 0
2248 || strcmp(eptr, storer_descs[i].ending2) == 0)
2249 {
2250 return storer_descs[i].enforced_bitness;
2251 }
2252 }
2253
2254 return -1;
2255 }
2256
2257 /* See header for documentation. */
load_ncimage(const char * filename,bool y_flip,bool & is_hdr,unsigned int & component_count)2258 astcenc_image* load_ncimage(
2259 const char* filename,
2260 bool y_flip,
2261 bool& is_hdr,
2262 unsigned int& component_count
2263 ) {
2264 // Get the file extension
2265 const char* eptr = strrchr(filename, '.');
2266 if (!eptr)
2267 {
2268 eptr = filename;
2269 }
2270
2271 // Scan through descriptors until a matching loader is found
2272 for (unsigned int i = 0; i < loader_descr_count; i++)
2273 {
2274 if (loader_descs[i].ending1 == nullptr
2275 || strcmp(eptr, loader_descs[i].ending1) == 0
2276 || strcmp(eptr, loader_descs[i].ending2) == 0)
2277 {
2278 return loader_descs[i].loader_func(filename, y_flip, is_hdr, component_count);
2279 }
2280 }
2281
2282 // Should never reach here - stb_image provides a generic handler
2283 return nullptr;
2284 }
2285
2286 /* See header for documentation. */
store_ncimage(const astcenc_image * output_image,const char * filename,int y_flip)2287 bool store_ncimage(
2288 const astcenc_image* output_image,
2289 const char* filename,
2290 int y_flip
2291 ) {
2292 const char* eptr = strrchr(filename, '.');
2293 if (!eptr)
2294 {
2295 eptr = ".ktx"; // use KTX file format if we don't have an ending.
2296 }
2297
2298 for (int i=0; i < storer_descr_count; i++)
2299 {
2300 if (strcmp(eptr, storer_descs[i].ending1) == 0
2301 || strcmp(eptr, storer_descs[i].ending2) == 0)
2302 {
2303 return storer_descs[i].storer_func(output_image, filename, y_flip);
2304 }
2305 }
2306
2307 // Should never reach here - get_output_filename_enforced_bitness should
2308 // have acted as a preflight check
2309 return false;
2310 }
2311
2312 /* ============================================================================
2313 ASTC compressed file loading
2314 ============================================================================ */
2315 struct astc_header
2316 {
2317 uint8_t magic[4];
2318 uint8_t block_x;
2319 uint8_t block_y;
2320 uint8_t block_z;
2321 uint8_t dim_x[3]; // dims = dim[0] + (dim[1] << 8) + (dim[2] << 16)
2322 uint8_t dim_y[3]; // Sizes are given in texels;
2323 uint8_t dim_z[3]; // block count is inferred
2324 };
2325
2326 static const uint32_t ASTC_MAGIC_ID = 0x5CA1AB13;
2327
unpack_bytes(uint8_t a,uint8_t b,uint8_t c,uint8_t d)2328 static unsigned int unpack_bytes(
2329 uint8_t a,
2330 uint8_t b,
2331 uint8_t c,
2332 uint8_t d
2333 ) {
2334 return (static_cast<unsigned int>(a) ) +
2335 (static_cast<unsigned int>(b) << 8) +
2336 (static_cast<unsigned int>(c) << 16) +
2337 (static_cast<unsigned int>(d) << 24);
2338 }
2339
2340 /* See header for documentation. */
2341 // TODO: Return a bool?
load_cimage(const char * filename,astc_compressed_image & img)2342 int load_cimage(
2343 const char* filename,
2344 astc_compressed_image& img
2345 ) {
2346 std::ifstream file(filename, std::ios::in | std::ios::binary);
2347 if (!file)
2348 {
2349 printf("ERROR: File open failed '%s'\n", filename);
2350 return 1;
2351 }
2352
2353 astc_header hdr;
2354 file.read(reinterpret_cast<char*>(&hdr), sizeof(astc_header));
2355 if (!file)
2356 {
2357 printf("ERROR: File read failed '%s'\n", filename);
2358 return 1;
2359 }
2360
2361 unsigned int magicval = unpack_bytes(hdr.magic[0], hdr.magic[1], hdr.magic[2], hdr.magic[3]);
2362 if (magicval != ASTC_MAGIC_ID)
2363 {
2364 printf("ERROR: File not recognized '%s'\n", filename);
2365 return 1;
2366 }
2367
2368 // Ensure these are not zero to avoid div by zero
2369 unsigned int block_x = astc::max(static_cast<unsigned int>(hdr.block_x), 1u);
2370 unsigned int block_y = astc::max(static_cast<unsigned int>(hdr.block_y), 1u);
2371 unsigned int block_z = astc::max(static_cast<unsigned int>(hdr.block_z), 1u);
2372
2373 unsigned int dim_x = unpack_bytes(hdr.dim_x[0], hdr.dim_x[1], hdr.dim_x[2], 0);
2374 unsigned int dim_y = unpack_bytes(hdr.dim_y[0], hdr.dim_y[1], hdr.dim_y[2], 0);
2375 unsigned int dim_z = unpack_bytes(hdr.dim_z[0], hdr.dim_z[1], hdr.dim_z[2], 0);
2376
2377 if (dim_x == 0 || dim_y == 0 || dim_z == 0)
2378 {
2379 printf("ERROR: File corrupt '%s'\n", filename);
2380 return 1;
2381 }
2382
2383 unsigned int xblocks = (dim_x + block_x - 1) / block_x;
2384 unsigned int yblocks = (dim_y + block_y - 1) / block_y;
2385 unsigned int zblocks = (dim_z + block_z - 1) / block_z;
2386
2387 size_t data_size = xblocks * yblocks * zblocks * 16;
2388 uint8_t *buffer = new uint8_t[data_size];
2389
2390 file.read(reinterpret_cast<char*>(buffer), data_size);
2391 if (!file)
2392 {
2393 printf("ERROR: File read failed '%s'\n", filename);
2394 return 1;
2395 }
2396
2397 img.data = buffer;
2398 img.data_len = data_size;
2399 img.block_x = block_x;
2400 img.block_y = block_y;
2401 img.block_z = block_z;
2402 img.dim_x = dim_x;
2403 img.dim_y = dim_y;
2404 img.dim_z = dim_z;
2405 return 0;
2406 }
2407
2408 /* See header for documentation. */
2409 // TODO: Return a bool?
store_cimage(const astc_compressed_image & img,const char * filename)2410 int store_cimage(
2411 const astc_compressed_image& img,
2412 const char* filename
2413 ) {
2414 astc_header hdr;
2415 hdr.magic[0] = ASTC_MAGIC_ID & 0xFF;
2416 hdr.magic[1] = (ASTC_MAGIC_ID >> 8) & 0xFF;
2417 hdr.magic[2] = (ASTC_MAGIC_ID >> 16) & 0xFF;
2418 hdr.magic[3] = (ASTC_MAGIC_ID >> 24) & 0xFF;
2419
2420 hdr.block_x = static_cast<uint8_t>(img.block_x);
2421 hdr.block_y = static_cast<uint8_t>(img.block_y);
2422 hdr.block_z = static_cast<uint8_t>(img.block_z);
2423
2424 hdr.dim_x[0] = img.dim_x & 0xFF;
2425 hdr.dim_x[1] = (img.dim_x >> 8) & 0xFF;
2426 hdr.dim_x[2] = (img.dim_x >> 16) & 0xFF;
2427
2428 hdr.dim_y[0] = img.dim_y & 0xFF;
2429 hdr.dim_y[1] = (img.dim_y >> 8) & 0xFF;
2430 hdr.dim_y[2] = (img.dim_y >> 16) & 0xFF;
2431
2432 hdr.dim_z[0] = img.dim_z & 0xFF;
2433 hdr.dim_z[1] = (img.dim_z >> 8) & 0xFF;
2434 hdr.dim_z[2] = (img.dim_z >> 16) & 0xFF;
2435
2436 std::ofstream file(filename, std::ios::out | std::ios::binary);
2437 if (!file)
2438 {
2439 printf("ERROR: File open failed '%s'\n", filename);
2440 return 1;
2441 }
2442
2443 file.write(reinterpret_cast<char*>(&hdr), sizeof(astc_header));
2444 file.write(reinterpret_cast<char*>(img.data), img.data_len);
2445 return 0;
2446 }
2447