/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2010 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#if defined(WIN32)
#undef _CRT_SECURE_NO_WARNINGS
#define _MATH_DEFINES_DEFINED
#endif
#include
#include
#include
#include
#include
extern "C" {
#include
#include
};
MTS_NAMESPACE_BEGIN
/* ========================== *
* EXR helper classes *
* ========================== */
class EXRIStream : public Imf::IStream {
public:
EXRIStream(Stream *stream) : IStream(stream->toString().c_str()),
m_stream(stream) {
m_offset = stream->getPos();
}
bool read(char *c, int n) {
m_stream->read(c, n);
return m_stream->isEOF();
}
Imf::Int64 tellg() {
return m_stream->getPos()-m_offset;
}
void seekg(Imf::Int64 pos) {
m_stream->setPos((size_t) pos + m_offset);
}
void clear() {
}
private:
ref m_stream;
size_t m_offset;
};
class EXROStream : public Imf::OStream {
public:
EXROStream(Stream *stream) : OStream(stream->toString().c_str()),
m_stream(stream) {
}
void write(const char *c, int n) {
m_stream->write(c, n);
}
Imf::Int64 tellp() {
return m_stream->getPos();
}
void seekp(Imf::Int64 pos) {
m_stream->setPos((size_t) pos);
}
void clear() {
}
private:
ref m_stream;
};
/* ========================== *
* PNG helper functions *
* ========================== */
static void png_flush_data(png_structp png_ptr) {
voidp flush_io_ptr = png_get_io_ptr(png_ptr);
((Stream *) flush_io_ptr)->flush();
}
static void png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
voidp read_io_ptr = png_get_io_ptr(png_ptr);
((Stream *) read_io_ptr)->read(data, length);
}
static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
voidp write_io_ptr = png_get_io_ptr(png_ptr);
((Stream *) write_io_ptr)->write(data, length);
}
static void png_error_func(png_structp png_ptr, png_const_charp msg) {
SLog(EError, "Fatal libpng error: %s\n", msg);
exit(-1);
}
/* ========================== *
* JPEG helper functions *
* ========================== */
extern "C" {
typedef struct {
struct jpeg_source_mgr pub;
JOCTET * buffer;
size_t buflen;
} jbuf_t;
METHODDEF(void) dsm_init_source(j_decompress_ptr cinfo) {
}
METHODDEF(boolean) dsm_fill_input_buffer (j_decompress_ptr cinfo) {
ERREXIT(cinfo, JERR_INPUT_EOF);
return TRUE;
}
METHODDEF(void) dsm_skip_input_data (j_decompress_ptr cinfo, long num_bytes) {
jbuf_t *p = (jbuf_t *)cinfo->src;
long i = (long) (p->pub.bytes_in_buffer - num_bytes);
if (i < 0) i = 0;
p->pub.bytes_in_buffer = i;
p->pub.next_input_byte += num_bytes;
}
METHODDEF(void) dsm_term_source (j_decompress_ptr cinfo) {
}
METHODDEF(void) dsm_error_exit (j_common_ptr cinfo) {
char msg[JMSG_LENGTH_MAX];
(*cinfo->err->format_message) (cinfo, msg);
SLog(EError, "Critcal libjpeg error: %s", msg);
}
};
/* ========================== *
* Bitmap class *
* ========================== */
Bitmap::Bitmap(int width, int height, int bpp)
: m_width(width), m_height(height), m_bpp(bpp), m_data(NULL) {
AssertEx(m_bpp == 1 || m_bpp == 8 || m_bpp == 16 || m_bpp == 24 || m_bpp == 32
|| m_bpp == 96 || m_bpp == 128, "Invalid number of bits per pixel");
AssertEx(width > 0 && height > 0, "Invalid bitmap size");
if (bpp == 96 || bpp == 128)
m_gamma = 1.0f;
else
m_gamma = -1.0f; // sRGB
// 1-bit masks are stored in a packed format.
m_size = (size_t) std::ceil(((double) m_width * m_height * m_bpp) / 8.0);
m_data = static_cast(allocAligned(m_size));
}
Bitmap::Bitmap(EFileFormat format, Stream *stream) : m_data(NULL) {
if (format == EPNG)
loadPNG(stream);
else if (format == EJPEG)
loadJPEG(stream);
else if (format == EEXR)
loadEXR(stream);
else if (format == ETGA)
loadTGA(stream);
else if (format == EBMP)
loadBMP(stream);
else
Log(EError, "Bitmap: Invalid file format!");
}
void Bitmap::loadEXR(Stream *stream) {
EXRIStream istr(stream);
Imf::RgbaInputFile file(istr);
/* Determine dimensions and allocate space */
Imath::Box2i dw = file.dataWindow();
m_width = dw.max.x - dw.min.x + 1;
m_height = dw.max.y - dw.min.y + 1;
m_size = m_width * m_height * 16;
m_bpp = 4*4*8;
m_gamma = 1.0f;
m_data = static_cast(allocAligned(m_size));
Imf::Rgba *rgba = new Imf::Rgba[m_width*m_height];
Log(ETrace, "Reading %ix%i EXR file", m_width, m_height);
/* Convert to 32-bit floating point per channel */
file.setFrameBuffer(rgba, 1, m_width);
file.readPixels(dw.min.y, dw.max.y);
float *m_buffer = getFloatData();
for (int i=0; ireadUChar();
if (stream->readUChar() != 0)
Log(EError, "Invalid TGA format -- only raw (non-RLE encoded) RGB is supported for now");
if (stream->readUChar() != 2)
Log(EError, "Invalid TGA format -- only raw (non-RLE encoded) RGB is supported for now");
stream->setPos(8);
int x1 = stream->readShort();
int y1 = stream->readShort();
int x2 = stream->readShort();
int y2 = stream->readShort();
m_width = x2-x1;
m_height = y2-y1;
Log(EInfo, "Reading %ix%i TGA file", m_width, m_height);
stream->setPos(16);
m_bpp = stream->readUChar();
if (m_bpp != 24 && m_bpp != 32)
Log(EError, "Invalid TGA format -- only 24 or 32 bpp images are supported for now");
m_gamma = -1;
int channels = m_bpp / 8;
m_size = m_width * m_height * channels;
m_data = static_cast(allocAligned(m_size));
stream->setPos(18 + headerSize);
stream->read(m_data, m_size);
/* Convert BGR to RGB */
for (size_t i=0; iread(&BMPFileHeader, sizeof(BMPFileHeader));
if (memcmp(&BMPFileHeader.magic, "BM", 2) != 0)
Log(EError, "Unsupported BMP format encountered (invalid file header)!");
stream->read(&DIBHeader, sizeof(DIBHeader));
if (DIBHeader.header_sz != 40 || DIBHeader.nplanes != 1)
Log(EError, "Unsupported BMP format encountered (strange DIB header)!");
if (DIBHeader.ncolors != 0)
Log(EError, "Only BMP images without a palette are supported for now");
if (DIBHeader.bitspp != 8 && DIBHeader.bitspp != 24)
Log(EError, "Only 8- and 24-bit BMP images are supported for now");
if (DIBHeader.compress_type != 0)
Log(EError, "Only uncompressed BMP images are supported for now");
m_width = DIBHeader.width;
m_height = std::abs(DIBHeader.height);
m_bpp = DIBHeader.bitspp;
m_size = m_width * m_height * (m_bpp / 8);
m_data = static_cast(allocAligned(m_size));
Log(ETrace, "Reading %ix%ix%i BMP file", m_width, m_height, m_bpp);
int nChannels = m_bpp / 8;
int lineWidth = m_width * nChannels;
int paddedLineWidth = lineWidth + lineWidth % 4;
uint8_t *line = new uint8_t[paddedLineWidth];
for (int y=0; yread(line, paddedLineWidth);
int targetY = y;
if (DIBHeader.height > 0)
targetY = m_height - 1 - y; // inverted rows
memcpy(&m_data[targetY * m_width * nChannels], line, lineWidth);
if (nChannels == 3) {
for (int x=0; x(allocAligned(m_size));
rows = new png_bytep[m_height];
if (m_bpp == 1) {
for (int i=0; igetSize();
memset(&jbuf, 0, sizeof(jbuf_t));
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = dsm_error_exit;
jpeg_create_decompress(&cinfo);
cinfo.src = (struct jpeg_source_mgr *) &jbuf;
jbuf.buffer = new JOCTET[length];
jbuf.pub.init_source = dsm_init_source;
jbuf.pub.fill_input_buffer = dsm_fill_input_buffer;
jbuf.pub.skip_input_data = dsm_skip_input_data;
/* Use default method (in libjpeg) */
jbuf.pub.resync_to_restart = jpeg_resync_to_restart;
jbuf.pub.term_source = dsm_term_source;
jbuf.buflen = jbuf.pub.bytes_in_buffer = length;
jbuf.pub.next_input_byte = jbuf.buffer;
stream->read(jbuf.buffer, length);
jpeg_read_header(&cinfo, TRUE);
jpeg_start_decompress(&cinfo);
m_width = cinfo.output_width;
m_height = cinfo.output_height;
m_bpp = cinfo.output_components*8;
m_gamma = 1.0f/2.2f;
m_size = m_width * m_height * cinfo.output_components;
Log(ETrace, "Reading %ix%ix%i JPG file", m_width, m_height, m_bpp);
int row_stride = cinfo.output_width * cinfo.output_components;
unsigned char **buffer = new unsigned char *[m_height];
m_data = static_cast(allocAligned(m_size));
for (int i=0; im_data, m_data, m_size);
return bitmap;
}
bool Bitmap::operator==(const Bitmap &bitmap) const {
if (bitmap.m_width == m_width &&
bitmap.m_height == m_height &&
bitmap.m_bpp == m_bpp) {
return memcmp(bitmap.m_data, m_data, m_size) == 0;
}
return false;
}
void Bitmap::save(EFileFormat format, Stream *stream) const {
if (m_bpp == 96 || m_bpp == 128)
AssertEx(format == EEXR, "Bitmap: 96/128 bpp images can only be stored "
"using the EXR file format");
else
AssertEx(format == EPNG, "Bitmap: 1-32 bpp images can only be stored "
"using the PNG file format");
if (format == EEXR)
saveEXR(stream);
else if (format == EPNG)
savePNG(stream);
else
Log(EError, "Bitmap: Invalid file format!");
}
void Bitmap::saveEXR(Stream *stream) const {
Log(EDebug, "Writing %ix%i EXR file", m_width, m_height);
EXROStream ostr(stream);
Imf::RgbaOutputFile file(ostr, Imf::Header(m_width, m_height),
Imf::WRITE_RGBA);
Imf::Rgba *rgba = new Imf::Rgba[m_width*m_height];
const float *m_buffer = getFloatData();
for (int i=0; i