From 51fee6f75c1da3bfd769035049a80ac32d7cbc46 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Wed, 26 Nov 2008 12:35:25 +0000 Subject: [PATCH] From Ragnar Hammarqvist, "I wrote an EXR image plug-in to osg, I would like to contribute this plug-in to the osg project if you find it useful. The plug-in is a wrapper around open-exr (http://www.openexr.com) that consists of two projects, ilmbase-1.0.1 and openexr-1.6.1. I have only tested it on windows XP 32 machine. So there might be some work making it work on other platforms. The plug-in supports writing and reading EXR files. When writing it can use the data type GL_HALF_FLOAT_ARB(se ilmbase-1.0.1) and GL_FLOAT. When reading the data type always becomes GL_HALF_FLOAT_ARB. It supports textures with three and four channels. When reading an exr file it automatically removes Alfa channel if it didn't store any information." -- From Robert Osfield, started work on ported it to other platforms, but could fix some problems relating to error: ?Imf::OStream::OStream(const Imf::OStream&)? is private I'm checking in now so that others can have a bash at completing the port. --- src/osgPlugins/CMakeLists.txt | 3 + src/osgPlugins/exr/CMakeLists.txt | 8 + src/osgPlugins/exr/ReaderWriterEXR.cpp | 403 +++++++++++++++++++++++++ 3 files changed, 414 insertions(+) create mode 100644 src/osgPlugins/exr/CMakeLists.txt create mode 100644 src/osgPlugins/exr/ReaderWriterEXR.cpp diff --git a/src/osgPlugins/CMakeLists.txt b/src/osgPlugins/CMakeLists.txt index c21076c4d..a54e04cb5 100644 --- a/src/osgPlugins/CMakeLists.txt +++ b/src/osgPlugins/CMakeLists.txt @@ -86,6 +86,9 @@ ENDIF(JPEG_FOUND) IF(JASPER_FOUND) ADD_SUBDIRECTORY(jp2) ENDIF(JASPER_FOUND) +IF(OPENEXR_FOUND) +# ADD_SUBDIRECTORY(exr) +ENDIF(OPENEXR_FOUND) IF(GIFLIB_FOUND) ADD_SUBDIRECTORY(gif) ENDIF(GIFLIB_FOUND) diff --git a/src/osgPlugins/exr/CMakeLists.txt b/src/osgPlugins/exr/CMakeLists.txt new file mode 100644 index 000000000..e2bb83b5e --- /dev/null +++ b/src/osgPlugins/exr/CMakeLists.txt @@ -0,0 +1,8 @@ +INCLUDE_DIRECTORIES( ${OPENEXR_INCLUDE_DIR}/OpenEXR ) + +SET(TARGET_SRC ReaderWriterEXR.cpp ) + +SET(TARGET_LIBRARIES_VARS OPENEXR_LIBRARY ) + +#### end var setup ### +SETUP_PLUGIN(exr) diff --git a/src/osgPlugins/exr/ReaderWriterEXR.cpp b/src/osgPlugins/exr/ReaderWriterEXR.cpp new file mode 100644 index 000000000..d76013259 --- /dev/null +++ b/src/osgPlugins/exr/ReaderWriterEXR.cpp @@ -0,0 +1,403 @@ +#include +#include +#include +#include +#include + +//Make the half format work against openEXR libs +// #define OPENEXR_DLL + +#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 = C_IStream(&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_ARB; + + 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; + + std::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; + + std::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 intenalTextureFormat = img.getInternalTextureFormat(); + 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_ARB || + dataType == GL_FLOAT)) + { + writeOK = false; + return false; + } + + //Create a stream to save to + C_OStream outStream = C_OStream(&fout); + + //Copy data from texture to rgba pixel format + Array2D outPixels(height,width); + //If texture is half format + if (dataType == GL_HALF_FLOAT_ARB) + { + half* pOut = (half*) img.data(); + for (long i = height-1; i >= 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; + + int internalFormat = numComponents_ret; + + if (dataType_ret == GL_HALF_FLOAT_ARB) + { + 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)