diff --git a/include/bimg/bimg.h b/include/bimg/bimg.h index b1f4f56..194e264 100644 --- a/include/bimg/bimg.h +++ b/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; diff --git a/src/image.cpp b/src/image.cpp index 55ea249..3ff970a 100644 --- a/src/image.cpp +++ b/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; diff --git a/src/image_decode.cpp b/src/image_decode.cpp index b1df55f..11d4035 100644 --- a/src/image_decode.cpp +++ b/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)