imageio: split HDRDecoder into its own file

This commit is contained in:
Philip Rideout
2021-05-19 19:40:36 -07:00
parent 613ae56cc1
commit c71c7c70d4
5 changed files with 260 additions and 159 deletions

View File

@@ -27,6 +27,7 @@
#include <png.h>
// for ntohs
#if defined(WIN32)
# include <Winsock2.h>
# include <utils/unwindows.h>
@@ -44,6 +45,8 @@
#include <image/ColorTransform.h>
#include <image/ImageOps.h>
#include <imageio/HDRDecoder.h>
namespace image {
class PNGDecoder : public ImageDecoder::Decoder {
@@ -77,29 +80,6 @@ private:
// -----------------------------------------------------------------------------------------------
class HDRDecoder : public ImageDecoder::Decoder {
public:
static HDRDecoder* create(std::istream& stream);
static bool checkSignature(char const* buf);
HDRDecoder(const HDRDecoder&) = delete;
HDRDecoder& operator=(const HDRDecoder&) = delete;
private:
explicit HDRDecoder(std::istream& stream);
~HDRDecoder() override;
// ImageDecoder::Decoder interface
LinearImage decode() override;
static const char sigRadiance[];
static const char sigRGBE[];
std::istream& mStream;
std::streampos mStreamStartPos;
};
// -----------------------------------------------------------------------------------------------
class PSDDecoder : public ImageDecoder::Decoder {
public:
static PSDDecoder* create(std::istream& stream);
@@ -333,142 +313,6 @@ void PNGDecoder::error() {
// -----------------------------------------------------------------------------------------------
const char HDRDecoder::sigRadiance[] = { '#', '?', 'R', 'A', 'D', 'I', 'A', 'N', 'C', 'E', 0xa };
const char HDRDecoder::sigRGBE[] = { '#', '?', 'R', 'G', 'B', 'E', 0xa };
HDRDecoder* HDRDecoder::create(std::istream& stream) {
HDRDecoder* decoder = new HDRDecoder(stream);
return decoder;
}
bool HDRDecoder::checkSignature(char const* buf) {
return !memcmp(buf, sigRadiance, sizeof(sigRadiance)) ||
!memcmp(buf, sigRGBE, sizeof(sigRGBE));
}
HDRDecoder::HDRDecoder(std::istream& stream)
: mStream(stream), mStreamStartPos(stream.tellg()) {
}
HDRDecoder::~HDRDecoder() = default;
LinearImage HDRDecoder::decode() {
try {
float gamma;
float exposure;
char sy, sx;
unsigned int height, width;
{
char buf[1024];
do {
char format[128];
mStream.getline(buf, sizeof(buf), 0xa);
if (buf[0] == '#') continue;
sscanf(buf, "FORMAT=%127s", format); // NOLINT
sscanf(buf, "GAMMA=%f", &gamma); // NOLINT
sscanf(buf, "EXPOSURE=%f", &exposure); // NOLINT
if ((sscanf(buf, "%cY %u %cX %u", &sy, &height, &sx, &width) == 4)|| // NOLINT
(sscanf(buf, "%cX %u %cY %u", &sx, &width, &sy, &height) == 4)) { // NOLINT
break;
}
} while (true);
}
LinearImage image(width, height, 3);
if (sx == '-') image = horizontalFlip(image);
if (sy == '+') image = verticalFlip(image);
// Allocate memory to hold one row of decoded pixel data.
std::unique_ptr<uint8_t[]> rgbe(new uint8_t[width * 4]);
// First, test for non-RLE images.
const auto pos = mStream.tellg();
mStream.read((char*) rgbe.get(), 3);
mStream.seekg(pos);
if (rgbe[0] != 0x2 || rgbe[1] != 0x2 || (rgbe[2] & 0x80) || width < 8 || width > 32767) {
for (uint32_t y = 0; y < height; y++) {
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
mStream.read((char*) rgbe.get(), width * 4);
// (rgb/256) * 2^(e-128)
size_t pixel = 0;
for (size_t x = 0; x < width; x++, pixel += 4) {
if (rgbe[pixel + 3] == 0.0f) {
dst[x] = filament::math::float3{0.0f};
} else {
filament::math::float3 v(rgbe[pixel], rgbe[pixel + 1], rgbe[pixel + 2]);
dst[x] = (v + 0.5f) * std::ldexp(1.0f, rgbe[pixel + 3] - (128 + 8));
}
}
}
} else {
for (uint32_t y = 0; y < height; y++) {
uint16_t magic;
mStream.read((char*) &magic, 2);
if (magic != 0x0202) {
throw std::runtime_error("invalid scanline (magic)");
}
uint16_t w;
mStream.read((char*) &w, 2);
if (ntohs(w) != width) {
throw std::runtime_error("invalid scanline (width)");
}
char* d = (char*) rgbe.get();
for (size_t p = 0; p < 4; p++) {
size_t num_bytes = 0;
while (num_bytes < width) {
uint8_t rle_count;
mStream.read((char*) &rle_count, 1);
if (rle_count > 128) {
char v;
mStream.read(&v, 1);
memset(d, v, size_t(rle_count - 128));
d += rle_count - 128;
num_bytes += rle_count - 128;
} else {
if (rle_count == 0) {
throw std::runtime_error("run length is zero");
}
mStream.read(d, rle_count);
d += rle_count;
num_bytes += rle_count;
}
}
}
uint8_t const* r = &rgbe[0];
uint8_t const* g = &rgbe[width];
uint8_t const* b = &rgbe[2 * width];
uint8_t const* e = &rgbe[3 * width];
filament::math::float3* dst = reinterpret_cast<filament::math::float3*>(image.getPixelRef(0, y));
// (rgb/256) * 2^(e-128)
for (size_t x = 0; x < width; x++, r++, g++, b++, e++) {
if (e[0] == 0.0f) {
dst[x] = filament::math::float3{0.0f};
} else {
filament::math::float3 v(r[0], g[0], b[0]);
dst[x] = (v + 0.5f) * std::ldexp(1.0f, e[0] - (128 + 8));
}
}
}
}
return image;
} catch(std::runtime_error& e) {
// reset the stream, like we found it
std::cerr << "Runtime error while decoding HDR: " << e.what() << std::endl;
mStream.seekg(mStreamStartPos);
}
return LinearImage();
}
// -----------------------------------------------------------------------------------------------
const char PSDDecoder::sig[] = { '8', 'B', 'P', 'S', 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
PSDDecoder* PSDDecoder::create(std::istream& stream) {