#include #include #if defined(WIN32) #undef _CRT_SECURE_NO_WARNINGS #define _USE_MATH_DEFINES #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) { } }; /* ========================== * * 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((m_width * m_height * m_bpp) / 8.0f); m_data = static_cast(allocAligned(m_size)); } Bitmap::Bitmap(EFileFormat format, Stream *stream) : m_data(NULL) { if (format == EPNG) loadPNG(stream); else if (format == ETGA) loadTGA(stream); else if (format == EJPEG) loadJPEG(stream); else if (format == EEXR) loadEXR(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; ijmpbuf)) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); if (rows) delete[] rows; Log(EError, "Error reading the PNG file"); } /* Set read helper function */ png_set_read_fn(png_ptr, stream, (png_rw_ptr) png_read_data); int bitdepth, colortype, interlacetype, compressiontype, filtertype; png_read_info(png_ptr, info_ptr); png_uint_32 width=0, height=0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitdepth, &colortype, &interlacetype, &compressiontype, &filtertype); int newDepth = bitdepth; if (bitdepth == 1) { png_set_packing(png_ptr); // Unpack and later re-pack } else if (colortype == PNG_COLOR_TYPE_PALETTE) { png_set_expand(png_ptr); // expand indexed files newDepth = 8; } else if (colortype == PNG_COLOR_TYPE_GRAY && bitdepth < 8) { png_set_gray_1_2_4_to_8(png_ptr); // convert grayscale to 8bit newDepth = 8; } else if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_expand(png_ptr); // transparency } else if (bitdepth < 8) { newDepth = 8; png_set_expand(png_ptr); } // handle interlacing if (interlacetype != PNG_INTERLACE_NONE) png_set_interlace_handling(png_ptr); if (bitdepth == 1) { m_bpp = 1; } else if (colortype == PNG_COLOR_TYPE_GRAY) { m_bpp = newDepth; } else if (colortype == PNG_COLOR_TYPE_GRAY_ALPHA) { m_bpp = newDepth*2; } else { m_bpp = newDepth * ((colortype & PNG_COLOR_MASK_ALPHA) ? 4 : 3); } int intent; double gamma; if (png_get_sRGB(png_ptr, info_ptr, &intent)) { m_gamma = -1; } else if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { m_gamma = (Float) gamma; } else { m_gamma = 1.0f/2.2f; } Log(ETrace, "Reading %ix%ix%i PNG file", width, height, m_bpp); /* Update the information */ png_read_update_info(png_ptr, info_ptr); /* re-read */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bitdepth, &colortype, &interlacetype, &compressiontype, &filtertype); m_width = width; m_height = height; m_size = (size_t) std::ceil((m_width * m_height * m_bpp) / 8.0f); m_data = static_cast(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); 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; ijmpbuf)) { png_destroy_write_struct(&png_ptr, &info_ptr); Log(EError, "Error writing the PNG file"); } png_set_write_fn(png_ptr, stream, (png_rw_ptr) png_write_data, (png_flush_ptr) png_flush_data); // png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); png_set_compression_level(png_ptr, 5); memset(text, 0, sizeof(png_text)*4); text[0].key = (char *) "Generated by"; text[0].text = (char *) "Vitsuba version " MTS_VERSION; text[0].compression = PNG_TEXT_COMPRESSION_NONE; text[1].key = (char *) "Title"; text[1].text = (char *) m_title.c_str(); text[1].compression = PNG_TEXT_COMPRESSION_NONE; text[2].key = (char *) "Author"; text[2].text = (char *) m_author.c_str(); text[2].compression = PNG_TEXT_COMPRESSION_NONE; text[3].key = (char *) "Comment"; text[3].text = (char *) m_comment.c_str(); text[3].compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, text, 4); if (m_gamma == -1) png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE); else png_set_gAMA(png_ptr, info_ptr, m_gamma); int colortype; switch (m_bpp) { case 32: colortype = PNG_COLOR_TYPE_RGBA; break; case 24: colortype = PNG_COLOR_TYPE_RGB; break; case 16: colortype = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 8: colortype = PNG_COLOR_TYPE_GRAY; break; default: colortype = PNG_COLOR_TYPE_PALETTE; } /* Simple 8 bit/color RGB/RGBA format or 1-bit mask */ png_set_IHDR(png_ptr, info_ptr, m_width, m_height, m_bpp == 1 ? 1 : 8, colortype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (m_bpp == 1) { png_color palette[2]; palette[0].blue = palette[0].red = palette[0].green = 0; palette[1].blue = palette[1].red = palette[1].green = 0xFF; png_set_PLTE(png_ptr, info_ptr, palette, 2); } png_write_info(png_ptr, info_ptr); png_set_packing(png_ptr); rows = new png_bytep[m_height]; if (m_bpp == 1) { /* Convert to 8 bit */ for (int i=0; i