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