Parcourir la source

Added EXIF parser for jpeg image orientation.

Branimir Karadžić il y a 8 ans
Parent
commit
aca9cf49c6
3 fichiers modifiés avec 226 ajouts et 71 suppressions
  1. 14 0
      include/bimg/bimg.h
  2. 68 63
      src/image.cpp
  3. 144 8
      src/image_decode.cpp

+ 14 - 0
include/bimg/bimg.h

@@ -133,6 +133,19 @@ namespace bimg
 		};
 	};
 
+	///
+	struct Orientation
+	{
+		///
+		enum Enum
+		{
+			R0,
+			R90,
+			R180,
+			R270,
+		};
+	};
+
 	/// Texture info.
 	///
 	/// @attention C99 equivalent is `bgfx_texture_info_t`.
@@ -156,6 +169,7 @@ namespace bimg
 		void*           m_data;
 
 		TextureFormat::Enum m_format;
+		Orientation::Enum m_orientation;
 
 		uint32_t m_size;
 		uint32_t m_offset;

+ 68 - 63
src/image.cpp

@@ -1756,21 +1756,22 @@ namespace bimg
 
 		ImageContainer* imageContainer = (ImageContainer*)BX_ALLOC(_allocator, size + sizeof(ImageContainer) );
 
-		imageContainer->m_allocator = _allocator;
-		imageContainer->m_data      = imageContainer + 1;
-		imageContainer->m_format    = _format;
-		imageContainer->m_size      = size;
-		imageContainer->m_offset    = 0;
-		imageContainer->m_width     = _width;
-		imageContainer->m_height    = _height;
-		imageContainer->m_depth     = _depth;
-		imageContainer->m_numLayers = _numLayers;
-		imageContainer->m_numMips   = numMips;
-		imageContainer->m_hasAlpha  = false;
-		imageContainer->m_cubeMap   = _cubeMap;
-		imageContainer->m_ktx       = false;
-		imageContainer->m_ktxLE     = false;
-		imageContainer->m_srgb      = false;
+		imageContainer->m_allocator   = _allocator;
+		imageContainer->m_data        = imageContainer + 1;
+		imageContainer->m_format      = _format;
+		imageContainer->m_orientation = Orientation::R0;
+		imageContainer->m_size        = size;
+		imageContainer->m_offset      = 0;
+		imageContainer->m_width       = _width;
+		imageContainer->m_height      = _height;
+		imageContainer->m_depth       = _depth;
+		imageContainer->m_numLayers   = _numLayers;
+		imageContainer->m_numMips     = numMips;
+		imageContainer->m_hasAlpha    = false;
+		imageContainer->m_cubeMap     = _cubeMap;
+		imageContainer->m_ktx         = false;
+		imageContainer->m_ktxLE       = false;
+		imageContainer->m_srgb        = false;
 
 		if (NULL != _data)
 		{
@@ -2171,21 +2172,22 @@ namespace bimg
 			return false;
 		}
 
-		_imageContainer.m_allocator = NULL;
-		_imageContainer.m_data      = NULL;
-		_imageContainer.m_size      = 0;
-		_imageContainer.m_offset    = (uint32_t)bx::seek(_reader);
-		_imageContainer.m_width     = width;
-		_imageContainer.m_height    = height;
-		_imageContainer.m_depth     = depth;
-		_imageContainer.m_format    = format;
-		_imageContainer.m_numLayers = uint16_t(arraySize);
-		_imageContainer.m_numMips   = uint8_t( (caps[0] & DDSCAPS_MIPMAP) ? mips : 1);
-		_imageContainer.m_hasAlpha  = hasAlpha;
-		_imageContainer.m_cubeMap   = cubeMap;
-		_imageContainer.m_ktx       = false;
-		_imageContainer.m_ktxLE     = false;
-		_imageContainer.m_srgb      = srgb;
+		_imageContainer.m_allocator   = NULL;
+		_imageContainer.m_data        = NULL;
+		_imageContainer.m_size        = 0;
+		_imageContainer.m_offset      = (uint32_t)bx::seek(_reader);
+		_imageContainer.m_width       = width;
+		_imageContainer.m_height      = height;
+		_imageContainer.m_depth       = depth;
+		_imageContainer.m_format      = format;
+		_imageContainer.m_orientation = Orientation::R0;
+		_imageContainer.m_numLayers   = uint16_t(arraySize);
+		_imageContainer.m_numMips     = uint8_t( (caps[0] & DDSCAPS_MIPMAP) ? mips : 1);
+		_imageContainer.m_hasAlpha    = hasAlpha;
+		_imageContainer.m_cubeMap     = cubeMap;
+		_imageContainer.m_ktx         = false;
+		_imageContainer.m_ktxLE       = false;
+		_imageContainer.m_srgb        = srgb;
 
 		return true;
 	}
@@ -2491,21 +2493,22 @@ namespace bimg
 			}
 		}
 
-		_imageContainer.m_allocator = NULL;
-		_imageContainer.m_data      = NULL;
-		_imageContainer.m_size      = 0;
-		_imageContainer.m_offset    = (uint32_t)offset;
-		_imageContainer.m_width     = width;
-		_imageContainer.m_height    = height;
-		_imageContainer.m_depth     = depth;
-		_imageContainer.m_format    = format;
-		_imageContainer.m_numLayers = uint16_t(bx::uint32_max(numberOfArrayElements, 1) );
-		_imageContainer.m_numMips   = uint8_t(bx::uint32_max(numMips, 1) );
-		_imageContainer.m_hasAlpha  = hasAlpha;
-		_imageContainer.m_cubeMap   = numFaces > 1;
-		_imageContainer.m_ktx       = true;
-		_imageContainer.m_ktxLE     = fromLittleEndian;
-		_imageContainer.m_srgb      = false;
+		_imageContainer.m_allocator   = NULL;
+		_imageContainer.m_data        = NULL;
+		_imageContainer.m_size        = 0;
+		_imageContainer.m_offset      = (uint32_t)offset;
+		_imageContainer.m_width       = width;
+		_imageContainer.m_height      = height;
+		_imageContainer.m_depth       = depth;
+		_imageContainer.m_format      = format;
+		_imageContainer.m_orientation = Orientation::R0;
+		_imageContainer.m_numLayers   = uint16_t(bx::uint32_max(numberOfArrayElements, 1) );
+		_imageContainer.m_numMips     = uint8_t(bx::uint32_max(numMips, 1) );
+		_imageContainer.m_hasAlpha    = hasAlpha;
+		_imageContainer.m_cubeMap     = numFaces > 1;
+		_imageContainer.m_ktx         = true;
+		_imageContainer.m_ktxLE       = fromLittleEndian;
+		_imageContainer.m_srgb        = false;
 
 		if (TextureFormat::Unknown == format)
 		{
@@ -2655,21 +2658,22 @@ namespace bimg
 			}
 		}
 
-		_imageContainer.m_allocator = NULL;
-		_imageContainer.m_data      = NULL;
-		_imageContainer.m_size      = 0;
-		_imageContainer.m_offset    = (uint32_t)offset;
-		_imageContainer.m_width     = width;
-		_imageContainer.m_height    = height;
-		_imageContainer.m_depth     = depth;
-		_imageContainer.m_format    = format;
-		_imageContainer.m_numLayers = 1;
-		_imageContainer.m_numMips   = uint8_t(bx::uint32_max(numMips, 1) );
-		_imageContainer.m_hasAlpha  = hasAlpha;
-		_imageContainer.m_cubeMap   = numFaces > 1;
-		_imageContainer.m_ktx       = false;
-		_imageContainer.m_ktxLE     = false;
-		_imageContainer.m_srgb      = colorSpace > 0;
+		_imageContainer.m_allocator   = NULL;
+		_imageContainer.m_data        = NULL;
+		_imageContainer.m_size        = 0;
+		_imageContainer.m_offset      = (uint32_t)offset;
+		_imageContainer.m_width       = width;
+		_imageContainer.m_height      = height;
+		_imageContainer.m_depth       = depth;
+		_imageContainer.m_format      = format;
+		_imageContainer.m_orientation = Orientation::R0;
+		_imageContainer.m_numLayers   = 1;
+		_imageContainer.m_numMips     = uint8_t(bx::uint32_max(numMips, 1) );
+		_imageContainer.m_hasAlpha    = hasAlpha;
+		_imageContainer.m_cubeMap     = numFaces > 1;
+		_imageContainer.m_ktx         = false;
+		_imageContainer.m_ktxLE       = false;
+		_imageContainer.m_srgb        = colorSpace > 0;
 
 		return TextureFormat::Unknown != format;
 	}
@@ -2703,9 +2707,10 @@ namespace bimg
 			TextureCreate tc;
 			bx::read(_reader, tc);
 
-			_imageContainer.m_format = tc.m_format;
-			_imageContainer.m_offset = UINT32_MAX;
-			_imageContainer.m_allocator = NULL;
+			_imageContainer.m_format      = tc.m_format;
+			_imageContainer.m_orientation = Orientation::R0;
+			_imageContainer.m_offset      = UINT32_MAX;
+			_imageContainer.m_allocator   = NULL;
 			if (NULL == tc.m_mem)
 			{
 				_imageContainer.m_data = NULL;

+ 144 - 8
src/image_decode.cpp

@@ -466,31 +466,41 @@ namespace bimg
 	{
 		BX_ERROR_SCOPE(_err);
 
-		const int isHdr = stbi_is_hdr_from_memory((const uint8_t*)_data, (int)_size);
+		const int isHdr = stbi_is_hdr_from_memory( (const uint8_t*)_data, (int)_size);
 
 		void* data;
 		uint32_t width  = 0;
 		uint32_t height = 0;
 		int comp = 0;
-		if (isHdr) { data = stbi_loadf_from_memory((const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 4); }
-		else       { data = stbi_load_from_memory ((const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 0); }
+		if (isHdr)
+		{
+			data = stbi_loadf_from_memory( (const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 4);
+		}
+		else
+		{
+			data = stbi_load_from_memory ( (const uint8_t*)_data, (int)_size, (int*)&width, (int*)&height, &comp, 0);
+		}
 
 		if (NULL == data)
 		{
 			return NULL;
 		}
 
-		bimg::TextureFormat::Enum format;
+		bimg::TextureFormat::Enum format = bimg::TextureFormat::RGBA8;
+
 		if (isHdr)
 		{
 			format = bimg::TextureFormat::RGBA32F;
 		}
 		else
 		{
-			if       (1 == comp)   { format = bimg::TextureFormat::R8;    }
-			else  if (2 == comp)   { format = bimg::TextureFormat::RG8;   }
-			else  if (3 == comp)   { format = bimg::TextureFormat::RGB8;  }
-			else/*if (4 == comp)*/ { format = bimg::TextureFormat::RGBA8; }
+			switch (comp)
+			{
+				case 1:  format = bimg::TextureFormat::R8;   break;
+				case 2:  format = bimg::TextureFormat::RG8;  break;
+				case 3:  format = bimg::TextureFormat::RGB8; break;
+				default: break;
+			}
 		}
 
 		ImageContainer* output = imageAlloc(_allocator
@@ -508,6 +518,131 @@ namespace bimg
 		return output;
 	}
 
+	static ImageContainer* imageParseJpeg(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err)
+	{
+		bx::MemoryReader reader(_data, _size);
+
+		bx::Error err;
+
+		uint16_t magic = 0;
+		bx::readHE(&reader, magic, false, &err);
+
+		if (!err.isOk()
+		||  0xffd8 != magic)
+		{
+			return NULL;
+		}
+
+		Orientation::Enum orientation = Orientation::R0;
+
+		while (err.isOk() )
+		{
+			bx::readHE(&reader, magic, false, &err);
+
+			uint16_t size;
+			bx::readHE(&reader, size, false, &err);
+
+			if (!err.isOk() )
+			{
+				return NULL;
+			}
+
+			if (0xffe1 != magic)
+			{
+				bx::seek(&reader, size-2);
+				continue;
+			}
+
+			char exif00[6];
+			bx::read(&reader, exif00, 6, &err);
+
+			if (0 == bx::memCmp(exif00, "Exif\0\0", 6) )
+			{
+				uint16_t iimm = 0;
+				bx::read(&reader, iimm, &err);
+
+				const bool littleEndian = iimm == 0x4949; //II - Intel - little endian
+				if (!err.isOk()
+				&&  !littleEndian
+				&&   iimm != 0x4d4d) // MM - Motorola - big endian
+				{
+					return NULL;
+				}
+
+				bx::readHE(&reader, magic, littleEndian, &err);
+				if (!err.isOk()
+				||  0x2a != magic)
+				{
+					return NULL;
+				}
+
+				uint32_t ifd0;
+				bx::readHE(&reader, ifd0, littleEndian, &err);
+
+				if (!err.isOk()
+				||  8 > ifd0)
+				{
+					return NULL;
+				}
+
+				bx::seek(&reader, ifd0-8);
+
+				uint16_t numEntries;
+				bx::readHE(&reader, numEntries, littleEndian, &err);
+
+				for (uint32_t ii = 0; err.isOk() && ii < numEntries; ++ii)
+				{
+					// https://sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html
+					uint16_t tag;
+					bx::readHE(&reader, tag, littleEndian, &err);
+
+					uint16_t format;
+					bx::readHE(&reader, format, littleEndian, &err);
+
+					uint32_t length;
+					bx::readHE(&reader, length, littleEndian, &err);
+
+					uint32_t data;
+					bx::readHE(&reader, data, littleEndian, &err);
+
+					switch (tag)
+					{
+					case 0x112: // orientation
+						if (3 == format)
+						{
+							switch (data)
+							{
+							default:
+							case 1: orientation = Orientation::R0;   break; // Horizontal (normal)
+							case 2: orientation = Orientation::R0;   break; // Mirror horizontal
+							case 3: orientation = Orientation::R180; break; // Rotate 180
+							case 4: orientation = Orientation::R0;   break; // Mirror vertical
+							case 5: orientation = Orientation::R0;   break; // Mirror horizontal and rotate 270 CW
+							case 6: orientation = Orientation::R90;  break; // Rotate 90 CW
+							case 7: orientation = Orientation::R0;   break; // Mirror horizontal and rotate 90 CW
+							case 8: orientation = Orientation::R270; break; // Rotate 270 CW
+							}
+						}
+						break;
+
+					default:
+						break;
+					}
+				}
+			}
+
+			break;
+		}
+
+		ImageContainer* image = imageParseStbImage(_allocator, _data, _size, _err);
+		if (NULL != image)
+		{
+			image->m_orientation = orientation;
+		}
+
+		return image;
+	}
+
 	ImageContainer* imageParse(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, TextureFormat::Enum _dstFormat, bx::Error* _err)
 	{
 		BX_ERROR_SCOPE(_err);
@@ -517,6 +652,7 @@ namespace bimg
 		input = NULL == input ? imageParsePvr3    (_allocator, _data, _size, _err) : input;
 		input = NULL == input ? imageParseLodePng (_allocator, _data, _size, _err) : input;
 		input = NULL == input ? imageParseTinyExr (_allocator, _data, _size, _err) : input;
+		input = NULL == input ? imageParseJpeg    (_allocator, _data, _size, _err) : input;
 		input = NULL == input ? imageParseStbImage(_allocator, _data, _size, _err) : input;
 
 		if (NULL == input)