1 /*
2 * Copyright 2011-2019 Branimir Karadzic. All rights reserved.
3 * License: https://github.com/bkaradzic/bimg#license-bsd-2-clause
4 */
5
6 #include "bimg_p.h"
7
8 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function")
9
10 BX_PRAGMA_DIAGNOSTIC_PUSH()
11 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wtype-limits")
12 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-parameter")
13 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-value")
14 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wdeprecated-declarations")
15 BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: '' : unreferenced formal parameter
16 BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4505) // warning C4505: 'tinyexr::miniz::def_realloc_func': unreferenced local function has been removed
17 #if BX_PLATFORM_EMSCRIPTEN
18 # include <compat/ctype.h>
19 #endif // BX_PLATFORM_EMSCRIPTEN
20 #define MINIZ_NO_ARCHIVE_APIS
21 #define MINIZ_NO_STDIO
22 #define TINYEXR_IMPLEMENTATION
23 #include <tinyexr/tinyexr.h>
24 BX_PRAGMA_DIAGNOSTIC_POP()
25
26 BX_PRAGMA_DIAGNOSTIC_PUSH();
27 BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4127) // warning C4127: conditional expression is constant
28 #define LODEPNG_NO_COMPILE_ENCODER
29 #define LODEPNG_NO_COMPILE_DISK
30 #define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
31 #define LODEPNG_NO_COMPILE_ALLOCATORS
32 #define LODEPNG_NO_COMPILE_CPP
33 #include <lodepng/lodepng.cpp>
34 BX_PRAGMA_DIAGNOSTIC_POP();
35
lodepng_malloc(size_t _size)36 void* lodepng_malloc(size_t _size)
37 {
38 return ::malloc(_size);
39 }
40
lodepng_realloc(void * _ptr,size_t _size)41 void* lodepng_realloc(void* _ptr, size_t _size)
42 {
43 return ::realloc(_ptr, _size);
44 }
45
lodepng_free(void * _ptr)46 void lodepng_free(void* _ptr)
47 {
48 ::free(_ptr);
49 }
50
51 BX_PRAGMA_DIAGNOSTIC_PUSH();
52 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wmissing-field-initializers");
53 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow");
54 BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wint-to-pointer-cast")
55 BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Warray-bounds");
56 #if BX_COMPILER_GCC >= 60000
57 BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wmisleading-indentation");
58 BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wshift-negative-value");
59 # if BX_COMPILER_GCC >= 70000
60 BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wimplicit-fallthrough");
61 # endif // BX_COMPILER_GCC >= 70000
62 #endif // BX_COMPILER_GCC >= 60000_
63 #define STBI_MALLOC(_size) lodepng_malloc(_size)
64 #define STBI_REALLOC(_ptr, _size) lodepng_realloc(_ptr, _size)
65 #define STBI_FREE(_ptr) lodepng_free(_ptr)
66 #define STB_IMAGE_IMPLEMENTATION
67 #include <stb/stb_image.h>
68 BX_PRAGMA_DIAGNOSTIC_POP();
69
70 namespace bimg
71 {
imageParseLodePng(bx::AllocatorI * _allocator,const void * _data,uint32_t _size,bx::Error * _err)72 static ImageContainer* imageParseLodePng(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err)
73 {
74 BX_ERROR_SCOPE(_err);
75
76 static uint8_t pngMagic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0d, 0x0a };
77
78 if (0 != bx::memCmp(_data, pngMagic, sizeof(pngMagic) ) )
79 {
80 return NULL;
81 }
82
83 ImageContainer* output = NULL;
84 bimg::TextureFormat::Enum format = bimg::TextureFormat::RGBA8;
85 uint32_t width = 0;
86 uint32_t height = 0;
87
88 unsigned error;
89 LodePNGState state;
90 lodepng_state_init(&state);
91 state.decoder.color_convert = 0;
92
93 uint8_t* data = NULL;
94 error = lodepng_decode(&data, &width, &height, &state, (uint8_t*)_data, _size);
95
96 if (0 != error)
97 {
98 _err->setError(BIMG_ERROR, lodepng_error_text(error) );
99 }
100 else
101 {
102 bool palette = false;
103 bool supported = false;
104
105 switch (state.info_raw.bitdepth)
106 {
107 case 1:
108 case 2:
109 case 4:
110 format = bimg::TextureFormat::R8;
111 palette = false;
112 supported = true;
113 break;
114
115 case 8:
116 switch (state.info_raw.colortype)
117 {
118 case LCT_GREY:
119 format = bimg::TextureFormat::R8;
120 supported = true;
121 break;
122
123 case LCT_GREY_ALPHA:
124 format = bimg::TextureFormat::RG8;
125 supported = true;
126 break;
127
128 case LCT_RGB:
129 format = bimg::TextureFormat::RGB8;
130 supported = true;
131 break;
132
133 case LCT_RGBA:
134 format = bimg::TextureFormat::RGBA8;
135 supported = true;
136 break;
137
138 case LCT_PALETTE:
139 format = bimg::TextureFormat::RGBA8;
140 palette = true;
141 supported = true;
142 break;
143 }
144 break;
145
146 case 16:
147 switch (state.info_raw.colortype)
148 {
149 case LCT_GREY:
150 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
151 {
152 uint16_t* rgba = (uint16_t*)data + ii;
153 rgba[0] = bx::toHostEndian(rgba[0], false);
154 }
155 format = bimg::TextureFormat::R16;
156 supported = true;
157 break;
158
159 case LCT_GREY_ALPHA:
160 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
161 {
162 uint16_t* rgba = (uint16_t*)data + ii*2;
163 rgba[0] = bx::toHostEndian(rgba[0], false);
164 rgba[1] = bx::toHostEndian(rgba[1], false);
165 }
166 format = bimg::TextureFormat::RG16;
167 supported = true;
168 break;
169
170 case LCT_RGB:
171 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
172 {
173 uint16_t* rgba = (uint16_t*)data + ii*3;
174 rgba[0] = bx::toHostEndian(rgba[0], false);
175 rgba[1] = bx::toHostEndian(rgba[1], false);
176 rgba[2] = bx::toHostEndian(rgba[2], false);
177 }
178 format = bimg::TextureFormat::RGBA16;
179 supported = true;
180 break;
181
182 case LCT_RGBA:
183 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
184 {
185 uint16_t* rgba = (uint16_t*)data + ii*4;
186 rgba[0] = bx::toHostEndian(rgba[0], false);
187 rgba[1] = bx::toHostEndian(rgba[1], false);
188 rgba[2] = bx::toHostEndian(rgba[2], false);
189 rgba[3] = bx::toHostEndian(rgba[3], false);
190 }
191 format = bimg::TextureFormat::RGBA16;
192 supported = true;
193 break;
194
195 case LCT_PALETTE:
196 break;
197 }
198 break;
199
200 default:
201 break;
202 }
203
204 if (supported)
205 {
206 const uint8_t* copyData = data;
207
208 TextureFormat::Enum dstFormat = format;
209 if (1 == state.info_raw.bitdepth
210 || 2 == state.info_raw.bitdepth
211 || 4 == state.info_raw.bitdepth)
212 {
213 copyData = NULL;
214 }
215 else if (16 == state.info_raw.bitdepth
216 && LCT_RGB == state.info_raw.colortype)
217 {
218 dstFormat = bimg::TextureFormat::RGBA16;
219 copyData = NULL;
220 }
221 else if (palette)
222 {
223 copyData = NULL;
224 }
225
226 output = imageAlloc(_allocator
227 , dstFormat
228 , uint16_t(width)
229 , uint16_t(height)
230 , 0
231 , 1
232 , false
233 , false
234 , copyData
235 );
236
237 if (1 == state.info_raw.bitdepth)
238 {
239 for (uint32_t ii = 0, num = width*height/8; ii < num; ++ii)
240 {
241 uint8_t* src = (uint8_t*)data + ii;
242 uint8_t eightBits = src[0];
243
244 uint8_t* dst = (uint8_t*)output->m_data + ii*8;
245 dst[0] = uint8_t( (eightBits>>7)&0x1)*255;
246 dst[1] = uint8_t( (eightBits>>6)&0x1)*255;
247 dst[2] = uint8_t( (eightBits>>5)&0x1)*255;
248 dst[3] = uint8_t( (eightBits>>4)&0x1)*255;
249 dst[4] = uint8_t( (eightBits>>3)&0x1)*255;
250 dst[5] = uint8_t( (eightBits>>2)&0x1)*255;
251 dst[6] = uint8_t( (eightBits>>1)&0x1)*255;
252 dst[7] = uint8_t( (eightBits )&0x1)*255;
253
254 }
255 }
256 else if (2 == state.info_raw.bitdepth)
257 {
258 for (uint32_t ii = 0, num = width*height/4; ii < num; ++ii)
259 {
260 uint8_t* src = (uint8_t*)data + ii;
261 uint8_t eightBits = src[0];
262
263 uint8_t* dst = (uint8_t*)output->m_data + ii*4;
264 // Note: not exactly precise.
265 // Correct way: dst[0] = uint8_t(float( (eightBits>>6)&0x3)*(255.0f/4.0f) );
266 dst[0] = uint8_t(uint32_t(((eightBits>>6)&0x3)*64)&0xff);
267 dst[1] = uint8_t(uint32_t(((eightBits>>4)&0x3)*64)&0xff);
268 dst[2] = uint8_t(uint32_t(((eightBits>>2)&0x3)*64)&0xff);
269 dst[3] = uint8_t(uint32_t(((eightBits )&0x3)*64)&0xff);
270 }
271 }
272 else if (4 == state.info_raw.bitdepth)
273 {
274 for (uint32_t ii = 0, num = width*height/2; ii < num; ++ii)
275 {
276 uint8_t* src = (uint8_t*)data + ii;
277 uint8_t eightBits = src[0];
278
279 uint8_t* dst = (uint8_t*)output->m_data + ii*2;
280 // Note: not exactly precise.
281 // Correct way: dst[0] = uint8_t(float( (eightBits>>4)&0xf)*(255.0f/16.0f) );
282 dst[0] = uint8_t(uint32_t(((eightBits>>4)&0xf)*16)&0xff);
283 dst[1] = uint8_t(uint32_t(((eightBits )&0xf)*16)&0xff);
284 }
285 }
286 else if (16 == state.info_raw.bitdepth
287 && LCT_RGB == state.info_raw.colortype)
288 {
289 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
290 {
291 const uint16_t* src = (uint16_t*)data + ii*3;
292 uint16_t* dst = (uint16_t*)output->m_data + ii*4;
293 dst[0] = src[0];
294 dst[1] = src[1];
295 dst[2] = src[2];
296 dst[3] = UINT16_MAX;
297 }
298 }
299 else if (palette)
300 {
301 for (uint32_t ii = 0, num = width*height; ii < num; ++ii)
302 {
303 bx::memCopy( (uint8_t*)output->m_data + ii*4, state.info_raw.palette + data[ii]*4, 4);
304 }
305 }
306 }
307 else
308 {
309 BX_ERROR_SET(_err, BIMG_ERROR, "PNG: Unsupported format.");
310 }
311 }
312
313 lodepng_state_cleanup(&state);
314 lodepng_free(data);
315
316 return output;
317 }
318
imageParseTinyExr(bx::AllocatorI * _allocator,const void * _data,uint32_t _size,bx::Error * _err)319 static ImageContainer* imageParseTinyExr(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err)
320 {
321 BX_ERROR_SCOPE(_err);
322
323 EXRVersion exrVersion;
324 int result = ParseEXRVersionFromMemory(&exrVersion, (uint8_t*)_data, _size);
325 if (TINYEXR_SUCCESS != result)
326 {
327 return NULL;
328 }
329
330 bimg::TextureFormat::Enum format = bimg::TextureFormat::RGBA8;
331 uint32_t width = 0;
332 uint32_t height = 0;
333
334 uint8_t* data = NULL;
335 const char* err = NULL;
336 EXRHeader exrHeader;
337 result = ParseEXRHeaderFromMemory(&exrHeader, &exrVersion, (uint8_t*)_data, _size, &err);
338 if (TINYEXR_SUCCESS == result)
339 {
340 EXRImage exrImage;
341 InitEXRImage(&exrImage);
342
343 result = LoadEXRImageFromMemory(&exrImage, &exrHeader, (uint8_t*)_data, _size, &err);
344 if (TINYEXR_SUCCESS == result)
345 {
346 uint8_t idxR = UINT8_MAX;
347 uint8_t idxG = UINT8_MAX;
348 uint8_t idxB = UINT8_MAX;
349 uint8_t idxA = UINT8_MAX;
350 for (uint8_t ii = 0, num = uint8_t(exrHeader.num_channels); ii < num; ++ii)
351 {
352 const EXRChannelInfo& channel = exrHeader.channels[ii];
353 if (UINT8_MAX == idxR
354 && 0 == bx::strCmp(channel.name, "R") )
355 {
356 idxR = ii;
357 }
358 else if (UINT8_MAX == idxG
359 && 0 == bx::strCmp(channel.name, "G") )
360 {
361 idxG = ii;
362 }
363 else if (UINT8_MAX == idxB
364 && 0 == bx::strCmp(channel.name, "B") )
365 {
366 idxB = ii;
367 }
368 else if (UINT8_MAX == idxA
369 && 0 == bx::strCmp(channel.name, "A") )
370 {
371 idxA = ii;
372 }
373 }
374
375 if (UINT8_MAX != idxR)
376 {
377 const bool asFloat = exrHeader.pixel_types[idxR] == TINYEXR_PIXELTYPE_FLOAT;
378 uint32_t srcBpp = 32;
379 uint32_t dstBpp = asFloat ? 32 : 16;
380 format = asFloat ? TextureFormat::R32F : TextureFormat::R16F;
381 uint32_t stepR = 1;
382 uint32_t stepG = 0;
383 uint32_t stepB = 0;
384 uint32_t stepA = 0;
385
386 if (UINT8_MAX != idxG)
387 {
388 srcBpp += 32;
389 dstBpp = asFloat ? 64 : 32;
390 format = asFloat ? TextureFormat::RG32F : TextureFormat::RG16F;
391 stepG = 1;
392 }
393
394 if (UINT8_MAX != idxB)
395 {
396 srcBpp += 32;
397 dstBpp = asFloat ? 128 : 64;
398 format = asFloat ? TextureFormat::RGBA32F : TextureFormat::RGBA16F;
399 stepB = 1;
400 }
401
402 if (UINT8_MAX != idxA)
403 {
404 srcBpp += 32;
405 dstBpp = asFloat ? 128 : 64;
406 format = asFloat ? TextureFormat::RGBA32F : TextureFormat::RGBA16F;
407 stepA = 1;
408 }
409
410 data = (uint8_t*)BX_ALLOC(_allocator, exrImage.width * exrImage.height * dstBpp/8);
411 width = exrImage.width;
412 height = exrImage.height;
413
414 if (asFloat)
415 {
416 const float zero = 0.0f;
417 const float* srcR = UINT8_MAX == idxR ? &zero : (const float*)(exrImage.images)[idxR];
418 const float* srcG = UINT8_MAX == idxG ? &zero : (const float*)(exrImage.images)[idxG];
419 const float* srcB = UINT8_MAX == idxB ? &zero : (const float*)(exrImage.images)[idxB];
420 const float* srcA = UINT8_MAX == idxA ? &zero : (const float*)(exrImage.images)[idxA];
421
422 const uint32_t bytesPerPixel = dstBpp/8;
423 for (uint32_t ii = 0, num = exrImage.width * exrImage.height; ii < num; ++ii)
424 {
425 float rgba[4] =
426 {
427 *srcR,
428 *srcG,
429 *srcB,
430 *srcA,
431 };
432 bx::memCopy(&data[ii * bytesPerPixel], rgba, bytesPerPixel);
433
434 srcR += stepR;
435 srcG += stepG;
436 srcB += stepB;
437 srcA += stepA;
438 }
439 }
440 else
441 {
442 const uint16_t zero = 0;
443 const uint16_t* srcR = UINT8_MAX == idxR ? &zero : (const uint16_t*)(exrImage.images)[idxR];
444 const uint16_t* srcG = UINT8_MAX == idxG ? &zero : (const uint16_t*)(exrImage.images)[idxG];
445 const uint16_t* srcB = UINT8_MAX == idxB ? &zero : (const uint16_t*)(exrImage.images)[idxB];
446 const uint16_t* srcA = UINT8_MAX == idxA ? &zero : (const uint16_t*)(exrImage.images)[idxA];
447
448 const uint32_t bytesPerPixel = dstBpp/8;
449 for (uint32_t ii = 0, num = exrImage.width * exrImage.height; ii < num; ++ii)
450 {
451 uint16_t rgba[4] =
452 {
453 *srcR,
454 *srcG,
455 *srcB,
456 *srcA,
457 };
458 bx::memCopy(&data[ii * bytesPerPixel], rgba, bytesPerPixel);
459
460 srcR += stepR;
461 srcG += stepG;
462 srcB += stepB;
463 srcA += stepA;
464 }
465 }
466 }
467 else
468 {
469 BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Couldn't find R channel.");
470 }
471
472 FreeEXRImage(&exrImage);
473 }
474 else
475 {
476 switch (result)
477 {
478 case TINYEXR_ERROR_INVALID_MAGIC_NUMBER: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid magic number."); break;
479 case TINYEXR_ERROR_INVALID_EXR_VERSION: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid EXR version."); break;
480 case TINYEXR_ERROR_INVALID_ARGUMENT: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid argument."); break;
481 case TINYEXR_ERROR_INVALID_DATA: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid data."); break;
482 case TINYEXR_ERROR_INVALID_FILE: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid file."); break;
483 // case TINYEXR_ERROR_INVALID_PARAMETER: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid parameter."); break;
484 case TINYEXR_ERROR_CANT_OPEN_FILE: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Can't open file."); break;
485 case TINYEXR_ERROR_UNSUPPORTED_FORMAT: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Unsupported format."); break;
486 case TINYEXR_ERROR_INVALID_HEADER: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Invalid header."); break;
487 case TINYEXR_ERROR_UNSUPPORTED_FEATURE: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Unsupported feature."); break;
488 case TINYEXR_ERROR_CANT_WRITE_FILE: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Can't write file."); break;
489 case TINYEXR_ERROR_SERIALZATION_FAILED: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image. Serialization failed."); break;
490 default: BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse image."); break;
491 }
492 }
493
494 FreeEXRHeader(&exrHeader);
495 }
496 else
497 {
498 BX_ERROR_SET(_err, BIMG_ERROR, "EXR: Failed to parse header.");
499 }
500
501 ImageContainer* output = NULL;
502 if (NULL != data)
503 {
504 output = imageAlloc(_allocator
505 , format
506 , uint16_t(width)
507 , uint16_t(height)
508 , 0
509 , 1
510 , false
511 , false
512 , data
513 );
514 BX_FREE(_allocator, data);
515 }
516
517 return output;
518 }
519
imageParseStbImage(bx::AllocatorI * _allocator,const void * _data,uint32_t _size,bx::Error * _err)520 static ImageContainer* imageParseStbImage(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err)
521 {
522 BX_ERROR_SCOPE(_err);
523
524 const int isHdr = stbi_is_hdr_from_memory( (const uint8_t*)_data, (int)_size);
525
526 void* data;
527 uint32_t width = 0;
528 uint32_t height = 0;
529 int comp = 0;
530 if (isHdr)
531 {
532 data = stbi_loadf_from_memory( (const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 4);
533 }
534 else
535 {
536 data = stbi_load_from_memory ( (const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 0);
537 }
538
539 if (NULL == data)
540 {
541 return NULL;
542 }
543
544 bimg::TextureFormat::Enum format = bimg::TextureFormat::RGBA8;
545
546 if (isHdr)
547 {
548 format = bimg::TextureFormat::RGBA32F;
549 }
550 else
551 {
552 switch (comp)
553 {
554 case 1: format = bimg::TextureFormat::R8; break;
555 case 2: format = bimg::TextureFormat::RG8; break;
556 case 3: format = bimg::TextureFormat::RGB8; break;
557 default: break;
558 }
559 }
560
561 ImageContainer* output = imageAlloc(_allocator
562 , format
563 , uint16_t(width)
564 , uint16_t(height)
565 , 0
566 , 1
567 , false
568 , false
569 , data
570 );
571 stbi_image_free(data);
572
573 return output;
574 }
575
imageParseJpeg(bx::AllocatorI * _allocator,const void * _data,uint32_t _size,bx::Error * _err)576 static ImageContainer* imageParseJpeg(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err)
577 {
578 bx::MemoryReader reader(_data, _size);
579
580 bx::Error err;
581
582 uint16_t magic = 0;
583 bx::readHE(&reader, magic, false, &err);
584
585 if (!err.isOk()
586 || 0xffd8 != magic)
587 {
588 return NULL;
589 }
590
591 Orientation::Enum orientation = Orientation::R0;
592
593 while (err.isOk() )
594 {
595 bx::readHE(&reader, magic, false, &err);
596
597 uint16_t size;
598 bx::readHE(&reader, size, false, &err);
599
600 if (!err.isOk() )
601 {
602 return NULL;
603 }
604
605 if (0xffe1 != magic)
606 {
607 bx::seek(&reader, size-2);
608 continue;
609 }
610
611 char exif00[6];
612 bx::read(&reader, exif00, 6, &err);
613
614 if (0 == bx::memCmp(exif00, "Exif\0\0", 6) )
615 {
616 uint16_t iimm = 0;
617 bx::read(&reader, iimm, &err);
618
619 const bool littleEndian = iimm == 0x4949; //II - Intel - little endian
620 if (!err.isOk()
621 && !littleEndian
622 && iimm != 0x4d4d) // MM - Motorola - big endian
623 {
624 return NULL;
625 }
626
627 bx::readHE(&reader, magic, littleEndian, &err);
628 if (!err.isOk()
629 || 0x2a != magic)
630 {
631 return NULL;
632 }
633
634 uint32_t ifd0;
635 bx::readHE(&reader, ifd0, littleEndian, &err);
636
637 if (!err.isOk()
638 || 8 > ifd0)
639 {
640 return NULL;
641 }
642
643 bx::seek(&reader, ifd0-8);
644
645 uint16_t numEntries;
646 bx::readHE(&reader, numEntries, littleEndian, &err);
647
648 for (uint32_t ii = 0; err.isOk() && ii < numEntries; ++ii)
649 {
650 // Reference(s):
651 // - EXIF Tags
652 // https://web.archive.org/web/20190218005249/https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
653 //
654 uint16_t tag;
655 bx::readHE(&reader, tag, littleEndian, &err);
656
657 uint16_t format;
658 bx::readHE(&reader, format, littleEndian, &err);
659
660 uint32_t length;
661 bx::readHE(&reader, length, littleEndian, &err);
662
663 uint32_t data;
664 bx::readHE(&reader, data, littleEndian, &err);
665
666 switch (tag)
667 {
668 case 0x112: // orientation
669 if (3 == format)
670 {
671 bx::seek(&reader, -4);
672
673 uint16_t u16;
674 bx::readHE(&reader, u16, littleEndian, &err);
675
676 uint16_t pad;
677 bx::read(&reader, pad, &err);
678
679 switch (u16)
680 {
681 default:
682 case 1: orientation = Orientation::R0; break; // Horizontal (normal)
683 case 2: orientation = Orientation::HFlip; break; // Mirror horizontal
684 case 3: orientation = Orientation::R180; break; // Rotate 180
685 case 4: orientation = Orientation::VFlip; break; // Mirror vertical
686 case 5: orientation = Orientation::HFlipR270; break; // Mirror horizontal and rotate 270 CW
687 case 6: orientation = Orientation::R90; break; // Rotate 90 CW
688 case 7: orientation = Orientation::HFlipR90; break; // Mirror horizontal and rotate 90 CW
689 case 8: orientation = Orientation::R270; break; // Rotate 270 CW
690 }
691 }
692 break;
693
694 default:
695 break;
696 }
697 }
698 }
699
700 break;
701 }
702
703 ImageContainer* image = imageParseStbImage(_allocator, _data, _size, _err);
704 if (NULL != image)
705 {
706 image->m_orientation = orientation;
707 }
708
709 return image;
710 }
711
imageParse(bx::AllocatorI * _allocator,const void * _data,uint32_t _size,TextureFormat::Enum _dstFormat,bx::Error * _err)712 ImageContainer* imageParse(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, TextureFormat::Enum _dstFormat, bx::Error* _err)
713 {
714 BX_ERROR_SCOPE(_err);
715
716 ImageContainer* input = imageParseDds (_allocator, _data, _size, _err) ;
717 input = NULL == input ? imageParseKtx (_allocator, _data, _size, _err) : input;
718 input = NULL == input ? imageParsePvr3 (_allocator, _data, _size, _err) : input;
719 input = NULL == input ? imageParseGnf (_allocator, _data, _size, _err) : input;
720 input = NULL == input ? imageParseLodePng (_allocator, _data, _size, _err) : input;
721 input = NULL == input ? imageParseTinyExr (_allocator, _data, _size, _err) : input;
722 input = NULL == input ? imageParseJpeg (_allocator, _data, _size, _err) : input;
723 input = NULL == input ? imageParseStbImage(_allocator, _data, _size, _err) : input;
724
725 if (NULL == input)
726 {
727 return NULL;
728 }
729
730 _dstFormat = TextureFormat::Count == _dstFormat
731 ? input->m_format
732 : _dstFormat
733 ;
734
735 if (_dstFormat == input->m_format)
736 {
737 return input;
738 }
739
740 ImageContainer* output = imageConvert(_allocator, _dstFormat, *input);
741 imageFree(input);
742
743 return output;
744 }
745
746 } // namespace bimg
747