#include #include #include #include #include #if defined _WIN32 && !defined OSG_LIBRARY_STATIC //Make the half format work against openEXR libs #define OPENEXR_DLL #endif #include #include #include #include #include #include using namespace std; using namespace Imf; using namespace Imath; /**************************************************************************** * * Follows is code written by FOI (www.foi.se) * it is a wraper of openEXR(www.openexr.com) * to add suport of exr images into osg * * Ported to a OSG-plugin, Ragnar Hammarqvist. * For patches, bugs and new features * please send them direct to the OSG dev team. **********************************************************************/ class C_IStream: public Imf::IStream { public: C_IStream (istream *fin) : IStream(""),_inStream(fin){} virtual bool read (char c[/*n*/], int n) { return _inStream->read(c,n).good(); }; virtual Int64 tellg () { return _inStream->tellg(); }; virtual void seekg (Int64 pos) { _inStream->seekg(pos); }; virtual void clear () { _inStream->clear(); }; private: std::istream * _inStream; }; class C_OStream: public Imf::OStream { public: C_OStream (ostream *fin) : OStream(""),_outStream(fin) {}; virtual void write (const char c[/*n*/], int n) { _outStream->write(c,n); }; virtual Int64 tellp () { return _outStream->tellp(); }; virtual void seekp (Int64 pos) { _outStream->seekp(pos); }; private: std::ostream * _outStream; }; unsigned char *exr_load(std::istream& fin, int *width_ret, int *height_ret, int *numComponents_ret, unsigned int *dataType_ret) { unsigned char *buffer=NULL; // returned to sender & as read from the disk bool inputError = false; Array2D pixels; int width,height,numComponents; try { C_IStream inStream(&fin); RgbaInputFile rgbafile(inStream); Box2i dw = rgbafile.dataWindow(); /*RgbaChannels channels =*/ rgbafile.channels(); (*width_ret) = width = dw.max.x - dw.min.x + 1; (*height_ret)=height = dw.max.y - dw.min.y + 1; (*dataType_ret) = GL_HALF_FLOAT; pixels.resizeErase (height, width); rgbafile.setFrameBuffer((&pixels)[0][0] - dw.min.x - dw.min.y * width, 1, width); rgbafile.readPixels(dw.min.y, dw.max.y); } catch( char * str ) { inputError = true; } //If error during stream read return a empty pointer if (inputError) { return buffer; } //If there is no information in alpha channel do not store the alpha channel numComponents = 3; for (long i = height-1; i >= 0; i--) { for (long j = 0 ; j < width; j++) { if (pixels[i][j].a != half(1.0f) ) { numComponents = 4; break; } } } (*numComponents_ret) = numComponents; if (!( numComponents == 3 || numComponents == 4)) { return NULL; } //Copy and allocate data to a unsigned char array that OSG can use for texturing unsigned dataSize = (sizeof(half) * height * width * numComponents); //buffer = new unsigned char[dataSize]; buffer = (unsigned char*)malloc(dataSize); half* pOut = (half*) buffer; for (long i = height-1; i >= 0; i--) { for (long j = 0 ; j < width; j++) { (*pOut) = pixels[i][j].r; pOut++; (*pOut) = pixels[i][j].g; pOut++; (*pOut) = pixels[i][j].b; pOut++; if (numComponents >= 4) { (*pOut) = pixels[i][j].a; pOut++; } } } return buffer; } class ReaderWriterEXR : public osgDB::ReaderWriter { public: ReaderWriterEXR() { } virtual bool acceptsExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension,"exr"); } virtual const char* className() const { return "EXR Image Reader"; } virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const { return readImage(fin, options); } virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const { return readImage(file, options); } virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const { return readEXRStream(fin); } virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const { std::string ext = osgDB::getLowerCaseFileExtension(file); if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED; std::string fileName = osgDB::findDataFile( file, options ); if (fileName.empty()) return ReadResult::FILE_NOT_FOUND; osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary); if(!istream) return ReadResult::FILE_NOT_HANDLED; ReadResult rr = readEXRStream(istream); if(rr.validImage()) { rr.getImage()->setFileName(fileName); } return rr; } virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const Options*) const { bool success = writeEXRStream(image, fout, ""); if(success) return WriteResult::FILE_SAVED; else return WriteResult::ERROR_IN_WRITING_FILE; } virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options*) const { std::string ext = osgDB::getFileExtension(fileName); if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED; osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary); if(!fout) return WriteResult::ERROR_IN_WRITING_FILE; bool success = writeEXRStream(img, fout, fileName); fout.close(); if(success) return WriteResult::FILE_SAVED; else return WriteResult::ERROR_IN_WRITING_FILE; } protected: bool writeEXRStream(const osg::Image &img, std::ostream& fout, const std::string &fileName) const { bool writeOK = true; //Obtain data from texture int width = img.s(); int height = img.t(); unsigned int pixelFormat = img.getPixelFormat(); int numComponents = img.computeNumComponents(pixelFormat); unsigned int dataType = img.getDataType(); //Validates image data //if numbers of components matches if (!( numComponents == 3 || numComponents == 4)) { writeOK = false; return false; } if (!( dataType == GL_HALF_FLOAT || dataType == GL_FLOAT)) { writeOK = false; return false; } //Create a stream to save to C_OStream outStream(&fout); //Copy data from texture to rgba pixel format Array2D outPixels(height,width); //If texture is half format if (dataType == GL_HALF_FLOAT) { for (long i = height-1; i >= 0; i--) { half* pOut = (half*) img.data(0,i); for (long j = 0 ; j < width; j++) { outPixels[i][j].r = (*pOut); pOut++; outPixels[i][j].g = (*pOut); pOut++; outPixels[i][j].b = (*pOut); pOut++; if (numComponents >= 4) { outPixels[i][j].a = (*pOut); pOut++; } else{outPixels[i][j].a = 1.0f;} } } } else if (dataType == GL_FLOAT) { float* pOut = (float*) img.data(); for (long i = height-1; i >= 0; i--) { for (long j = 0 ; j < width; j++) { outPixels[i][j].r = half(*pOut); pOut++; outPixels[i][j].g = half(*pOut); pOut++; outPixels[i][j].b = half(*pOut); pOut++; if (numComponents >= 4) { outPixels[i][j].a = half(*pOut); pOut++; } else {outPixels[i][j].a = 1.0f;} } } } else { //If texture format not supported return false; } try { //Write to stream Header outHeader(width, height); RgbaOutputFile rgbaFile (outStream, outHeader, WRITE_RGBA); rgbaFile.setFrameBuffer ((&outPixels)[0][0], 1, width); rgbaFile.writePixels (height); } catch( char * str ) { writeOK = false; } return writeOK; } ReadResult readEXRStream(std::istream& fin) const { unsigned char *imageData = NULL; int width_ret = 0; int height_ret = 0; int numComponents_ret = 4; unsigned int dataType_ret = GL_UNSIGNED_BYTE; unsigned int pixelFormat = GL_RGB; unsigned int interNalTextureFormat = GL_RGB; imageData = exr_load(fin,&width_ret,&height_ret,&numComponents_ret,&dataType_ret); if (imageData==NULL) return ReadResult::FILE_NOT_HANDLED; int s = width_ret; int t = height_ret; int r = 1; if (dataType_ret == GL_HALF_FLOAT) { interNalTextureFormat = numComponents_ret == 1 ? GL_LUMINANCE16F_ARB : numComponents_ret == 2 ? GL_LUMINANCE_ALPHA16F_ARB : numComponents_ret == 3 ? GL_RGB16F_ARB : numComponents_ret == 4 ? GL_RGBA16F_ARB : (GLenum)-1; } else if (dataType_ret == GL_FLOAT) { interNalTextureFormat = numComponents_ret == 1 ? GL_LUMINANCE32F_ARB : numComponents_ret == 2 ? GL_LUMINANCE_ALPHA32F_ARB : numComponents_ret == 3 ? GL_RGB32F_ARB : numComponents_ret == 4 ? GL_RGBA32F_ARB : (GLenum)-1; } pixelFormat = numComponents_ret == 1 ? GL_LUMINANCE : numComponents_ret == 2 ? GL_LUMINANCE_ALPHA : numComponents_ret == 3 ? GL_RGB : numComponents_ret == 4 ? GL_RGBA : (GLenum)-1; unsigned int dataType = dataType_ret; osg::Image* pOsgImage = new osg::Image; pOsgImage->setImage(s,t,r, interNalTextureFormat, pixelFormat, dataType, imageData, osg::Image::USE_MALLOC_FREE); return pOsgImage; } }; // now register with Registry to instantiate the exr suport // reader/writer. REGISTER_OSGPLUGIN(exr, ReaderWriterEXR)