649 lines
21 KiB
C++
649 lines
21 KiB
C++
#include <memory>
|
|
|
|
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include <imageio/ImageDecoder.h>
|
|
|
|
#include <cstdint>
|
|
#include <istream>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <sstream>
|
|
|
|
#include <png.h>
|
|
|
|
#if defined(WIN32)
|
|
# include <Winsock2.h>
|
|
# include <utils/unwindows.h>
|
|
#else
|
|
# include <arpa/inet.h>
|
|
#endif
|
|
|
|
#include <math/vec3.h>
|
|
#include <math/vec4.h>
|
|
|
|
#include <tinyexr.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <image/utilities.h>
|
|
|
|
namespace image {
|
|
|
|
class PNGDecoder : public ImageDecoder::Decoder {
|
|
public:
|
|
static PNGDecoder* create(std::istream& stream);
|
|
static bool checkSignature(char const* buf);
|
|
|
|
private:
|
|
explicit PNGDecoder(std::istream& stream);
|
|
PNGDecoder(const PNGDecoder&) = delete;
|
|
~PNGDecoder();
|
|
|
|
PNGDecoder& operator = (const PNGDecoder&) = delete;
|
|
|
|
void init();
|
|
|
|
// ImageDecoder::Decoder interface
|
|
virtual Image decode() override;
|
|
|
|
static void cb_error(png_structp, png_const_charp);
|
|
static void cb_stream(png_structp png, png_bytep buffer, png_size_t size);
|
|
|
|
void error();
|
|
void stream(void* buffer, size_t size);
|
|
|
|
png_structp mPNG = nullptr;
|
|
png_infop mInfo = nullptr;
|
|
std::istream& mStream;
|
|
std::streampos mStreamStartPos;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
class HDRDecoder : public ImageDecoder::Decoder {
|
|
friend class ImageDecoder;
|
|
static HDRDecoder* create(std::istream& stream);
|
|
static bool checkSignature(char const* buf);
|
|
|
|
private:
|
|
explicit HDRDecoder(std::istream& stream);
|
|
HDRDecoder(const HDRDecoder&) = delete;
|
|
~HDRDecoder();
|
|
|
|
HDRDecoder& operator=(const HDRDecoder&) = delete;
|
|
|
|
// ImageDecoder::Decoder interface
|
|
virtual Image decode() override;
|
|
|
|
static const char sigRadiance[];
|
|
static const char sigRGBE[];
|
|
std::istream& mStream;
|
|
std::streampos mStreamStartPos;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
class PSDDecoder : public ImageDecoder::Decoder {
|
|
friend class ImageDecoder;
|
|
static PSDDecoder* create(std::istream& stream);
|
|
static bool checkSignature(char const* buf);
|
|
|
|
private:
|
|
explicit PSDDecoder(std::istream& stream);
|
|
PSDDecoder(const PSDDecoder&) = delete;
|
|
~PSDDecoder();
|
|
|
|
PSDDecoder& operator = (const PSDDecoder&) = delete;
|
|
|
|
// ImageDecoder::Decoder interface
|
|
virtual Image decode() override;
|
|
|
|
static const char sig[];
|
|
std::istream& mStream;
|
|
std::streampos mStreamStartPos;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
class EXRDecoder : public ImageDecoder::Decoder {
|
|
friend class ImageDecoder;
|
|
static EXRDecoder* create(std::istream& stream, const std::string& sourceName);
|
|
static bool checkSignature(char const* buf);
|
|
|
|
private:
|
|
EXRDecoder(std::istream& stream, const std::string& sourceName);
|
|
EXRDecoder(const EXRDecoder&) = delete;
|
|
~EXRDecoder();
|
|
|
|
EXRDecoder& operator = (const EXRDecoder&) = delete;
|
|
|
|
// ImageDecoder::Decoder interface
|
|
virtual Image decode() override;
|
|
|
|
static const char sig[];
|
|
std::istream& mStream;
|
|
std::streampos mStreamStartPos;
|
|
std::string mSourceName;
|
|
};
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
Image ImageDecoder::decode(std::istream& stream, const std::string& sourceName,
|
|
ColorSpace sourceSpace) {
|
|
|
|
Format format = Format::NONE;
|
|
|
|
std::streampos pos = stream.tellg();
|
|
char buf[16];
|
|
stream.read(buf, sizeof(buf));
|
|
|
|
if (PNGDecoder::checkSignature(buf)) {
|
|
format = Format::PNG;
|
|
} else if (HDRDecoder::checkSignature(buf)) {
|
|
format = Format::HDR;
|
|
} else if (PSDDecoder::checkSignature(buf)) {
|
|
format = Format::PSD;
|
|
} else if (EXRDecoder::checkSignature(buf)) {
|
|
format = Format::EXR;
|
|
}
|
|
|
|
stream.seekg(pos);
|
|
|
|
std::unique_ptr<Decoder> decoder;
|
|
switch (format) {
|
|
case Format::NONE:
|
|
return Image();
|
|
case Format::PNG:
|
|
decoder.reset(PNGDecoder::create(stream));
|
|
decoder->setColorSpace(sourceSpace);
|
|
break;
|
|
case Format::HDR:
|
|
decoder.reset(HDRDecoder::create(stream));
|
|
decoder->setColorSpace(ColorSpace::LINEAR);
|
|
break;
|
|
case Format::PSD:
|
|
decoder.reset(PSDDecoder::create(stream));
|
|
decoder->setColorSpace(ColorSpace::LINEAR);
|
|
break;
|
|
case Format::EXR:
|
|
decoder.reset(EXRDecoder::create(stream, sourceName));
|
|
decoder->setColorSpace(ColorSpace::LINEAR);
|
|
break;
|
|
}
|
|
|
|
return decoder->decode();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
template<typename T, typename PROCESS, typename TRANSFORM>
|
|
static Image toLinear(size_t w, size_t h, size_t bpr,
|
|
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
|
|
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(math::float3)]);
|
|
math::float3* d = reinterpret_cast<math::float3*>(dst.get());
|
|
for (size_t y = 0; y < h; ++y) {
|
|
T const* p = reinterpret_cast<T const*>(src.get() + y * bpr);
|
|
for (size_t x = 0; x < w; ++x, p += 3) {
|
|
math::float3 sRGB(proc(p[0]), proc(p[1]), proc(p[2]));
|
|
sRGB /= std::numeric_limits<T>::max();
|
|
*d++ = transform(sRGB);
|
|
}
|
|
}
|
|
return Image(std::move(dst), w, h, w * sizeof(math::float3), sizeof(math::float3));
|
|
}
|
|
|
|
template<typename T, typename PROCESS, typename TRANSFORM>
|
|
static Image toLinearWithAlpha(size_t w, size_t h, size_t bpr,
|
|
const std::unique_ptr<uint8_t[]>& src, PROCESS proc, TRANSFORM transform) {
|
|
std::unique_ptr<uint8_t[]> dst(new uint8_t[w * h * sizeof(math::float4)]);
|
|
math::float4* d = reinterpret_cast<math::float4*>(dst.get());
|
|
for (size_t y = 0; y < h; ++y) {
|
|
T const* p = reinterpret_cast<T const*>(src.get() + y * bpr);
|
|
for (size_t x = 0; x < w; ++x, p += 4) {
|
|
math::float4 sRGB(proc(p[0]), proc(p[1]), proc(p[2]), proc(p[3]));
|
|
sRGB /= std::numeric_limits<T>::max();
|
|
*d++ = transform(sRGB);
|
|
}
|
|
}
|
|
return Image(std::move(dst), w, h, w * sizeof(math::float4), sizeof(math::float4), 4);
|
|
}
|
|
|
|
|
|
static inline float read32(std::istream& istream) {
|
|
uint32_t data;
|
|
istream.read(reinterpret_cast<char*>(&data), sizeof(uint32_t));
|
|
data = ntohl(data);
|
|
return *reinterpret_cast<float*>(&data);
|
|
}
|
|
|
|
static inline float read16(std::istream& istream) {
|
|
uint16_t data;
|
|
istream.read(reinterpret_cast<char*>(&data), sizeof(uint16_t));
|
|
return static_cast<float>(ntohs(data)) / std::numeric_limits<uint16_t>::max();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
PNGDecoder* PNGDecoder::create(std::istream& stream) {
|
|
PNGDecoder* decoder = new PNGDecoder(stream);
|
|
decoder->init();
|
|
return decoder;
|
|
}
|
|
|
|
bool PNGDecoder::checkSignature(char const* buf) {
|
|
return png_check_sig(png_const_bytep(buf), 8);
|
|
}
|
|
|
|
PNGDecoder::PNGDecoder(std::istream& stream)
|
|
: mStream(stream), mStreamStartPos(stream.tellg()) {
|
|
mPNG = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
}
|
|
|
|
void PNGDecoder::init() {
|
|
png_set_error_fn(mPNG, this, cb_error, NULL);
|
|
png_set_read_fn(mPNG, this, cb_stream);
|
|
}
|
|
|
|
PNGDecoder::~PNGDecoder() {
|
|
png_destroy_read_struct(&mPNG, &mInfo, NULL);
|
|
}
|
|
|
|
Image PNGDecoder::decode() {
|
|
std::unique_ptr<uint8_t[]> imageData;
|
|
try {
|
|
mInfo = png_create_info_struct(mPNG);
|
|
png_read_info(mPNG, mInfo);
|
|
|
|
int colorType = png_get_color_type(mPNG, mInfo);
|
|
int bitDepth = png_get_bit_depth(mPNG, mInfo);
|
|
|
|
if (colorType == PNG_COLOR_TYPE_PALETTE) {
|
|
png_set_palette_to_rgb(mPNG);
|
|
}
|
|
if (colorType == PNG_COLOR_TYPE_GRAY) {
|
|
png_set_gray_to_rgb(mPNG);
|
|
}
|
|
if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) {
|
|
png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_DEFAULT_sRGB);
|
|
} else {
|
|
png_set_alpha_mode(mPNG, PNG_ALPHA_PNG, PNG_GAMMA_LINEAR);
|
|
}
|
|
if (bitDepth < 16) {
|
|
png_set_expand_16(mPNG);
|
|
}
|
|
|
|
png_read_update_info(mPNG, mInfo);
|
|
uint32_t width = png_get_image_width(mPNG, mInfo);
|
|
uint32_t height = png_get_image_height(mPNG, mInfo);
|
|
size_t rowBytes = png_get_rowbytes(mPNG, mInfo);
|
|
|
|
imageData = std::make_unique<uint8_t[]>(height * rowBytes);
|
|
std::unique_ptr<png_bytep[]> rowPointers(new png_bytep[height]);
|
|
for (size_t y = 0 ; y < height ; y++) {
|
|
rowPointers[y] = &imageData[y * rowBytes];
|
|
}
|
|
png_read_image(mPNG, rowPointers.get());
|
|
png_read_end(mPNG, mInfo);
|
|
|
|
if (colorType == PNG_COLOR_TYPE_RGBA) {
|
|
if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) {
|
|
return toLinearWithAlpha<uint16_t>(width, height, rowBytes, imageData,
|
|
[ ](uint16_t v) -> uint16_t { return ntohs(v); },
|
|
sRGBToLinear<math::float4>);
|
|
} else {
|
|
return toLinearWithAlpha<uint16_t>(width, height, rowBytes, imageData,
|
|
[ ](uint16_t v) -> uint16_t { return ntohs(v); },
|
|
[ ](const math::float4& color) -> math::float4 { return color; });
|
|
}
|
|
} else {
|
|
// Convert to linear float (PNG 16 stores data in network order (big endian).
|
|
if (getColorSpace() == ImageDecoder::ColorSpace::SRGB) {
|
|
return toLinear<uint16_t>(width, height, rowBytes, imageData,
|
|
[ ](uint16_t v) -> uint16_t { return ntohs(v); },
|
|
sRGBToLinear<math::float3>);
|
|
} else {
|
|
return toLinear<uint16_t>(width, height, rowBytes, imageData,
|
|
[ ](uint16_t v) -> uint16_t { return ntohs(v); },
|
|
[ ](const math::float3& color) -> math::float3 { return color; });
|
|
}
|
|
}
|
|
} catch(std::runtime_error& e) {
|
|
// reset the stream, like we found it
|
|
std::cerr << "Runtime error while decoding PNG: " << e.what() << std::endl;
|
|
mStream.seekg(mStreamStartPos);
|
|
imageData.release();
|
|
}
|
|
return Image();
|
|
}
|
|
|
|
void PNGDecoder::cb_stream(png_structp png, png_bytep buffer, png_size_t size) {
|
|
PNGDecoder* that = static_cast<PNGDecoder*>(png_get_io_ptr(png));
|
|
that->stream(buffer, size);
|
|
}
|
|
|
|
void PNGDecoder::stream(void* buffer, size_t size) {
|
|
mStream.read(static_cast<char *>(buffer), size);
|
|
if (!mStream.good()) {
|
|
throw std::runtime_error("Problem with the PNG stream.");
|
|
}
|
|
}
|
|
|
|
void PNGDecoder::cb_error(png_structp png, png_const_charp) {
|
|
PNGDecoder* that = static_cast<PNGDecoder*>(png_get_error_ptr(png));
|
|
that->error();
|
|
}
|
|
|
|
void PNGDecoder::error() {
|
|
throw std::runtime_error("Error while decoding PNG stream.");
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
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;
|
|
|
|
Image 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);
|
|
sscanf(buf, "GAMMA=%f", &gamma);
|
|
sscanf(buf, "EXPOSURE=%f", &exposure);
|
|
if ((sscanf(buf, "%cY %u %cX %u", &sy, &height, &sx, &width) == 4)||
|
|
(sscanf(buf, "%cX %u %cY %u", &sx, &width, &sy, &height) == 4)) {
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
uint32_t flags = 0;
|
|
if (sx == '-') flags |= Image::FLIP_X;
|
|
|
|
// Experimentally, "-Y" means vertical origin at the top, "+Y" at the bottom
|
|
if (sy == '+') flags |= Image::FLIP_Y;
|
|
|
|
std::unique_ptr<uint8_t[]> data(new uint8_t[width * height * sizeof(math::float3)]);
|
|
Image image(std::move(data), width, height, width*sizeof(math::float3), sizeof(math::float3));
|
|
image.setFlags(flags);
|
|
|
|
uint16_t w;
|
|
uint16_t magic;
|
|
std::unique_ptr<uint8_t[]> rgbe(new uint8_t[width*4]);
|
|
for (size_t y=0 ; y<height ; y++) {
|
|
//std::cout << "line: " << y << std::endl;
|
|
mStream.read((char*)&magic, 2);
|
|
if (magic != 0x0202) {
|
|
throw std::runtime_error("invalid scanline (magic)");
|
|
}
|
|
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];
|
|
math::float3* i = static_cast<math::float3*>(image.getPixelRef(0, y));
|
|
// (rgb/256) * 2^(e-128)
|
|
for (size_t x=0 ; x<width ; x++, r++, g++, b++, e++) {
|
|
math::float3 v(r[0], g[0], b[0]);
|
|
i[x] = v * 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 Image();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
const char PSDDecoder::sig[] = { '8', 'B', 'P', 'S', 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
|
|
|
|
PSDDecoder* PSDDecoder::create(std::istream& stream) {
|
|
PSDDecoder* decoder = new PSDDecoder(stream);
|
|
return decoder;
|
|
}
|
|
|
|
bool PSDDecoder::checkSignature(char const* buf) {
|
|
return !memcmp(buf, sig, sizeof(sig));
|
|
}
|
|
|
|
PSDDecoder::PSDDecoder(std::istream& stream)
|
|
: mStream(stream), mStreamStartPos(stream.tellg()) {
|
|
}
|
|
|
|
PSDDecoder::~PSDDecoder() = default;
|
|
|
|
Image PSDDecoder::decode() {
|
|
#pragma pack(push, 1)
|
|
// IMPORTANT NOTE: PSD files use big endian storage
|
|
struct Header {
|
|
char signature[4];
|
|
uint16_t version;
|
|
char padding[6];
|
|
uint16_t channels;
|
|
uint32_t height;
|
|
uint32_t width;
|
|
uint16_t depth;
|
|
uint16_t mode;
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
static const uint16_t kColorModeRGB = 3;
|
|
static const uint16_t kCompressionRAW = 0;
|
|
|
|
try {
|
|
Header h;
|
|
mStream.read(reinterpret_cast<char*>(&h), sizeof(Header));
|
|
|
|
if (ntohs(h.channels) != 3) {
|
|
throw std::runtime_error("the image must have 3 channels only");
|
|
}
|
|
|
|
uint16_t depth = ntohs(h.depth);
|
|
if (depth != 16 && depth != 32) {
|
|
throw std::runtime_error("the image depth must be 16 or 32 bits per pixel");
|
|
}
|
|
|
|
if (ntohs(h.mode) != kColorModeRGB) {
|
|
throw std::runtime_error("the image must be RGB");
|
|
}
|
|
|
|
uint32_t width = ntohl(h.width);
|
|
uint32_t height = ntohl(h.height);
|
|
|
|
uint32_t length;
|
|
|
|
// color mode data section
|
|
mStream.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
|
|
mStream.seekg(ntohl(length), std::istream::cur);
|
|
|
|
// image resources
|
|
mStream.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
|
|
mStream.seekg(ntohl(length), std::istream::cur);
|
|
|
|
// layer and mask info section
|
|
mStream.read(reinterpret_cast<char*>(&length), sizeof(uint32_t));
|
|
mStream.seekg(ntohl(length), std::istream::cur);
|
|
|
|
// compression format
|
|
uint16_t compression;
|
|
mStream.read(reinterpret_cast<char*>(&compression), sizeof(uint16_t));
|
|
if (ntohs(compression) != kCompressionRAW) {
|
|
throw std::runtime_error("compressed images are not supported");
|
|
}
|
|
|
|
std::unique_ptr<uint8_t[]> data(new uint8_t[width * height * sizeof(math::float3)]);
|
|
Image image(std::move(data), width, height,
|
|
width * sizeof(math::float3), sizeof(math::float3));
|
|
|
|
if (depth == 32) {
|
|
for (size_t i = 0; i < 3; i++) {
|
|
for (size_t y = 0; y < height; y++) {
|
|
for (size_t x = 0; x < width; x++) {
|
|
math::float3& pixel = *static_cast<math::float3*>(image.getPixelRef(x, y));
|
|
pixel[i] = read32(mStream);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (size_t i = 0; i < 3; i++) {
|
|
for (size_t y = 0; y < height; y++) {
|
|
for (size_t x = 0; x < width; x++) {
|
|
math::float3& pixel = *static_cast<math::float3*>(image.getPixelRef(x, y));
|
|
pixel[i] = read16(mStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return image;
|
|
} catch(std::runtime_error& e) {
|
|
// reset the stream, like we found it
|
|
std::cerr << "Runtime error while decoding PSD: " << e.what() << std::endl;
|
|
mStream.seekg(mStreamStartPos);
|
|
}
|
|
|
|
return Image();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------
|
|
|
|
const char EXRDecoder::sig[] = { 0x76, 0x2f, 0x31, 0x01 };
|
|
|
|
EXRDecoder* EXRDecoder::create(std::istream& stream, const std::string& sourceName) {
|
|
EXRDecoder* decoder = new EXRDecoder(stream, sourceName);
|
|
return decoder;
|
|
}
|
|
|
|
bool EXRDecoder::checkSignature(char const* buf) {
|
|
return !memcmp(buf, sig, sizeof(sig));
|
|
}
|
|
|
|
EXRDecoder::EXRDecoder(std::istream& stream, const std::string& sourceName)
|
|
: mStream(stream), mStreamStartPos(stream.tellg()), mSourceName(sourceName) {
|
|
}
|
|
|
|
EXRDecoder::~EXRDecoder() = default;
|
|
|
|
Image EXRDecoder::decode() {
|
|
try {
|
|
// copy the EXR data in memory
|
|
std::vector<unsigned char> src;
|
|
unsigned char buffer[4096];
|
|
while (mStream.read(reinterpret_cast<char*>(buffer), sizeof(buffer))) {
|
|
src.insert(src.end(), &buffer[0], &buffer[4096]);
|
|
}
|
|
src.insert(src.end(), &buffer[0], &buffer[mStream.gcount()]);
|
|
|
|
int width;
|
|
int height;
|
|
float* rgba;
|
|
const char* error;
|
|
|
|
int ret = LoadEXRFromMemory(&rgba, &width, &height, src.data(), src.size(), &error);
|
|
if (ret != TINYEXR_SUCCESS) {
|
|
std::cerr << "Could not decode OpenEXR: " << error << std::endl;
|
|
mStream.seekg(mStreamStartPos);
|
|
return Image();
|
|
}
|
|
|
|
src.resize(0);
|
|
|
|
std::unique_ptr<uint8_t[]> data(new uint8_t[width * height * sizeof(math::float3)]);
|
|
Image image(std::move(data), static_cast<size_t>(width), static_cast<size_t>(height),
|
|
width * sizeof(math::float3), sizeof(math::float3));
|
|
|
|
size_t i = 0;
|
|
for (size_t y = 0; y < height; y++) {
|
|
for (size_t x = 0; x < width; x++) {
|
|
math::float3& pixel = *static_cast<math::float3*>(image.getPixelRef(x, y));
|
|
pixel.r = rgba[i++];
|
|
pixel.g = rgba[i++];
|
|
pixel.b = rgba[i++];
|
|
// skip alpha
|
|
i++;
|
|
}
|
|
}
|
|
|
|
return image;
|
|
} catch(std::runtime_error& e) {
|
|
// reset the stream, like we found it
|
|
std::cerr << "Runtime error while decoding OpenEXR: " << e.what() << std::endl;
|
|
mStream.seekg(mStreamStartPos);
|
|
}
|
|
|
|
return Image();
|
|
}
|
|
|
|
} // namespace image
|