root/OpenSceneGraph/trunk/src/osgPlugins/exr/ReaderWriterEXR.cpp @ 12912

Revision 12912, 11.4 kB (checked in by robert, 3 years ago)

Added support for using GL_UNPACK_ROW_LENGTH in conjunction with texture's + osg::Image via new RowLength?
parameter in osg::Image. To support this Image::setData(..) now has a new optional rowLength parameter which
defaults to 0, which provides the original behaviour, Image::setRowLength(int) and int Image::getRowLength() are also provided.

With the introduction of RowLength? support in osg::Image it is now possible to create a sub image where
the t size of the image are smaller than the row length, useful for when you have a large image on the CPU
and which to use a small portion of it on the GPU. However, when these sub images are created the data
within the image is no longer contiguous so data access can no longer assume that all the data is in
one block. The new method Image::isDataContiguous() enables the user to check whether the data is contiguous,
and if not one can either access the data row by row using Image::data(column,row,image) accessor, or use the
new Image::DataIterator? for stepping through each block on memory assocatied with the image.

To support the possibility of non contiguous osg::Image usage of image objects has had to be updated to
check DataContiguous? and handle the case or use access via the DataIerator? or by row by row. To achieve
this a relatively large number of files has had to be modified, in particular the texture classes and
image plugins that doing writing.

  • Property svn:eol-style set to native
Line 
1#include <osg/Image>
2#include <osg/Notify>
3#include <osg/Geode>
4#include <osg/Image>
5#include <osg/GL>
6
7#if defined _WIN32 && !defined OSG_LIBRARY_STATIC
8//Make the half format work against openEXR libs
9#define OPENEXR_DLL
10#endif
11
12#include <osgDB/Registry>
13#include <osgDB/FileNameUtils>
14#include <osgDB/FileUtils>
15
16#include <ImfRgbaFile.h>
17#include <ImfIO.h>
18#include <ImfArray.h>
19
20using namespace std;
21using namespace Imf;
22using namespace Imath;
23
24/****************************************************************************
25 *
26 * Follows is code written by FOI (www.foi.se)
27 * it is a wraper of openEXR(www.openexr.com)
28 * to add suport of exr images into osg
29 *
30 * Ported to a OSG-plugin, Ragnar Hammarqvist.
31 * For patches, bugs and new features
32 * please send them direct to the OSG dev team.
33 **********************************************************************/
34class C_IStream: public Imf::IStream
35{
36public:
37    C_IStream (istream *fin) :
38      IStream(""),_inStream(fin){}
39
40      virtual bool    read (char c[/*n*/], int n)
41      {
42        return _inStream->read(c,n).good();
43      };
44      virtual Int64    tellg ()
45      {
46          return _inStream->tellg();
47      };
48      virtual void    seekg (Int64 pos)
49      {
50        _inStream->seekg(pos);
51      };
52      virtual void    clear ()
53      {
54        _inStream->clear();
55      };
56
57private:
58    std::istream * _inStream;
59};
60
61class C_OStream: public Imf::OStream
62{
63public:
64    C_OStream (ostream *fin) :
65      OStream(""),_outStream(fin)
66      {};
67
68      virtual void    write (const char c[/*n*/], int n)
69      {
70        _outStream->write(c,n);
71      };
72      virtual Int64    tellp ()
73      {
74        return _outStream->tellp();
75      };
76      virtual void seekp (Int64 pos)
77      {
78        _outStream->seekp(pos);
79      };
80
81private:
82    std::ostream * _outStream;
83};
84
85
86unsigned char *exr_load(std::istream& fin,
87                        int *width_ret,
88                        int *height_ret,
89                        int *numComponents_ret,
90                        unsigned int *dataType_ret)
91{
92    unsigned char *buffer=NULL; // returned to sender & as read from the disk
93    bool inputError = false;
94    Array2D<Rgba> pixels;
95    int width,height,numComponents;
96   
97    try
98    {   
99        C_IStream inStream(&fin);
100        RgbaInputFile rgbafile(inStream);
101
102        Box2i dw = rgbafile.dataWindow();
103        /*RgbaChannels channels =*/ rgbafile.channels();
104        (*width_ret) = width = dw.max.x - dw.min.x + 1;
105        (*height_ret)=height = dw.max.y - dw.min.y + 1;
106        (*dataType_ret) = GL_HALF_FLOAT_ARB;
107
108        pixels.resizeErase (height, width);
109
110        rgbafile.setFrameBuffer((&pixels)[0][0] - dw.min.x - dw.min.y * width, 1, width);   
111        rgbafile.readPixels(dw.min.y, dw.max.y);
112    }
113    catch( char * str ) {
114        inputError = true;
115    }
116
117    //If error during stream read return a empty pointer
118    if (inputError)
119    {
120        return buffer;
121    }
122   
123    //If there is no information in alpha channel do not store the alpha channel
124    numComponents = 3;
125    for (long i = height-1; i >= 0; i--)
126    {
127        for (long j = 0 ; j < width; j++)
128        {
129            if (pixels[i][j].a != half(1.0f) )
130            {
131                numComponents = 4;
132                break;
133            }
134        }
135    }
136    (*numComponents_ret) = numComponents;
137
138    if (!(    numComponents == 3 ||
139            numComponents == 4))
140    {
141        return NULL;
142    }
143   
144    //Copy and allocate data to a unsigned char array that OSG can use for texturing
145    unsigned dataSize = (sizeof(half) * height * width * numComponents);
146    //buffer = new unsigned char[dataSize];
147    buffer = (unsigned char*)malloc(dataSize);
148    half* pOut = (half*) buffer;
149   
150    for (long i = height-1; i >= 0; i--)
151    {
152        for (long j = 0 ; j < width; j++)
153        {
154            (*pOut) = pixels[i][j].r;
155            pOut++;
156            (*pOut) = pixels[i][j].g;
157            pOut++;
158            (*pOut) = pixels[i][j].b;
159            pOut++;
160            if (numComponents >= 4)
161            {
162                (*pOut) = pixels[i][j].a;
163                pOut++;
164            }
165        }
166    }
167
168    return buffer;
169}
170
171
172 class ReaderWriterEXR : public osgDB::ReaderWriter
173{
174public:
175    ReaderWriterEXR()
176    {
177    }
178
179    virtual bool acceptsExtension(const std::string& extension) const { return osgDB::equalCaseInsensitive(extension,"exr"); }
180   
181    virtual const char* className() const { return "EXR Image Reader"; }
182   
183    virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
184    {
185        return readImage(fin, options);
186    }
187
188    virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
189    {
190        return readImage(file, options);
191    }
192
193    virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const
194    {
195        return readEXRStream(fin);
196    }
197
198    virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
199    {
200        std::string ext = osgDB::getLowerCaseFileExtension(file);
201        if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
202
203        std::string fileName = osgDB::findDataFile( file, options );
204        if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
205
206        osgDB::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
207        if(!istream) return ReadResult::FILE_NOT_HANDLED;
208       
209        ReadResult rr = readEXRStream(istream);
210        if(rr.validImage())
211        {
212            rr.getImage()->setFileName(fileName);
213        }
214        return rr;
215    }
216   
217    virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const Options*) const
218    {
219        bool success = writeEXRStream(image, fout, "<output stream>");
220
221        if(success)
222            return WriteResult::FILE_SAVED;
223        else
224            return WriteResult::ERROR_IN_WRITING_FILE;
225    }
226
227    virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options*) const
228     {
229        std::string ext = osgDB::getFileExtension(fileName);
230        if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
231
232        osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
233        if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
234
235        bool success = writeEXRStream(img, fout, fileName);
236 
237        fout.close();
238
239        if(success)
240            return WriteResult::FILE_SAVED;
241        else
242            return WriteResult::ERROR_IN_WRITING_FILE;
243     }
244protected:
245    bool writeEXRStream(const osg::Image &img, std::ostream& fout, const std::string &fileName) const
246    {
247        bool writeOK = true;
248
249        //Obtain data from texture
250        int width = img.s();
251        int height = img.t();
252        unsigned int pixelFormat = img.getPixelFormat();
253        int numComponents = img.computeNumComponents(pixelFormat);
254        unsigned int dataType = img.getDataType();
255
256        //Validates image data
257        //if numbers of components matches
258        if (!(    numComponents == 3 ||
259                numComponents == 4))
260        {
261            writeOK = false;
262            return false;
263        }
264        if (!(    dataType == GL_HALF_FLOAT_ARB ||
265                dataType == GL_FLOAT))
266        {
267            writeOK = false;
268            return false;
269        }
270
271         //Create a stream to save to
272         C_OStream outStream(&fout);
273
274         //Copy data from texture to rgba pixel format
275        Array2D<Rgba> outPixels(height,width);
276         //If texture is half format
277         if (dataType == GL_HALF_FLOAT_ARB)
278         {   
279             for (long i = height-1; i >= 0; i--)
280             {
281                half* pOut = (half*) img.data(0,i);
282                 for (long j = 0 ; j < width; j++)
283                 {
284                     outPixels[i][j].r = (*pOut);
285                     pOut++;
286                     outPixels[i][j].g = (*pOut);
287                     pOut++;
288                     outPixels[i][j].b = (*pOut);
289                     pOut++;
290                    if (numComponents >= 4)
291                    {
292                        outPixels[i][j].a = (*pOut);
293                        pOut++;
294                    }
295                    else{outPixels[i][j].a = 1.0f;}
296                 }
297             }
298         }
299        else if (dataType == GL_FLOAT)
300        {
301            float* pOut = (float*) img.data();
302            for (long i = height-1; i >= 0; i--)
303            {
304                for (long j = 0 ; j < width; j++)
305                {
306                    outPixels[i][j].r = half(*pOut);
307                    pOut++;
308                    outPixels[i][j].g = half(*pOut);
309                    pOut++;
310                    outPixels[i][j].b = half(*pOut);
311                    pOut++;
312                    if (numComponents >= 4)
313                    {
314                        outPixels[i][j].a = half(*pOut);
315                        pOut++;
316                    }
317                    else
318                    {outPixels[i][j].a = 1.0f;}
319                }
320            }       
321        }
322         else
323         {
324             //If texture format not supported
325             return false;
326         }
327 
328         try
329         {
330             //Write to stream
331             Header outHeader(width, height);
332             RgbaOutputFile rgbaFile (outStream, outHeader, WRITE_RGBA);
333             rgbaFile.setFrameBuffer ((&outPixels)[0][0], 1, width);
334             rgbaFile.writePixels (height);
335         }
336         catch( char * str )
337         {
338             writeOK = false;
339         }
340
341
342        return writeOK;
343    }
344
345    ReadResult readEXRStream(std::istream& fin) const
346    {
347        unsigned char *imageData = NULL;
348        int width_ret = 0;
349        int height_ret = 0;
350        int numComponents_ret = 4;
351        unsigned int dataType_ret = GL_UNSIGNED_BYTE;
352        unsigned int pixelFormat = GL_RGB;
353        unsigned int interNalTextureFormat = GL_RGB;
354
355        imageData = exr_load(fin,&width_ret,&height_ret,&numComponents_ret,&dataType_ret);
356
357        if (imageData==NULL)
358            return ReadResult::FILE_NOT_HANDLED;
359
360        int s = width_ret;
361        int t = height_ret;
362        int r = 1;
363
364        if (dataType_ret == GL_HALF_FLOAT_ARB)
365        {
366            interNalTextureFormat =
367                numComponents_ret == 1 ? GL_LUMINANCE16F_ARB :
368                numComponents_ret == 2 ? GL_LUMINANCE_ALPHA16F_ARB :
369                numComponents_ret == 3 ? GL_RGB16F_ARB :
370                numComponents_ret == 4 ? GL_RGBA16F_ARB : (GLenum)-1;
371        }
372        else if (dataType_ret == GL_FLOAT)
373        {
374            interNalTextureFormat =
375                numComponents_ret == 1 ? GL_LUMINANCE32F_ARB :
376                numComponents_ret == 2 ? GL_LUMINANCE_ALPHA32F_ARB :
377                numComponents_ret == 3 ? GL_RGB32F_ARB :
378                numComponents_ret == 4 ? GL_RGBA32F_ARB : (GLenum)-1;
379        }
380        pixelFormat =
381            numComponents_ret == 1 ? GL_LUMINANCE :
382            numComponents_ret == 2 ? GL_LUMINANCE_ALPHA :
383            numComponents_ret == 3 ? GL_RGB :
384            numComponents_ret == 4 ? GL_RGBA : (GLenum)-1;
385
386        unsigned int dataType = dataType_ret;
387
388        osg::Image* pOsgImage = new osg::Image;
389        pOsgImage->setImage(s,t,r,
390            interNalTextureFormat,
391            pixelFormat,
392            dataType,
393            imageData,
394            osg::Image::USE_MALLOC_FREE);
395
396        return pOsgImage;
397    }
398};
399
400// now register with Registry to instantiate the exr suport
401// reader/writer.
402REGISTER_OSGPLUGIN(exr, ReaderWriterEXR)
Note: See TracBrowser for help on using the browser.