/* 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