root/OpenSceneGraph/trunk/src/osgPlugins/png/ReaderWriterPNG.cpp @ 8578

Revision 8578, 13.3 kB (checked in by robert, 6 years ago)

Converted plugins to use the new supportsExtension()/supportsOptions/supportsProtocl() methods
to help enable better querying of supported features

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
RevLine 
[2]1#include <osg/Image>
[2397]2#include <osg/Notify>
[2]3#include <osg/Geode>
[1257]4#include <osg/GL>
[2397]5#include <osg/Endian>
[2]6
[1257]7#include <osgDB/Registry>
[2501]8#include <osgDB/FileUtils>
[1257]9#include <osgDB/FileNameUtils>
[8]10
[5029]11#include <sstream>
12
[2]13using namespace osg;
14
[8]15extern "C"
16{
[2031]17    #include <zlib.h>
[8]18    #include <png.h>
[2]19}
20
[8]21
[2]22/* Transparency parameters */
[8]23#define PNG_ALPHA     -2         /* Use alpha channel in PNG file, if there is one */
24#define PNG_SOLID     -1         /* No transparency                                */
25#define PNG_STENCIL    0         /* Sets alpha to 0 for r=g=b=0, 1 otherwise       */
[2]26
[8]27typedef struct
28{
29    unsigned int Width;
30    unsigned int Height;
31    unsigned int Depth;
32    unsigned int Alpha;
[2]33} pngInfo;
34
[8378]35class PNGError
36{
37public:
38    PNGError(const char* message)
39    {
40        _message = "PNG lib error : ";
41        _message += message;
42    }
43    friend std::ostream& operator<<(std::ostream& stream, const PNGError& err)
44    {
45        stream << err._message;
46        return stream;
47    }
48private:
49    std::string _message;
50};
51
52void user_error_fn(png_structp png_ptr, png_const_charp error_msg)
53{
54    throw PNGError(error_msg);
55}
56
57void user_warning_fn(png_structp png_ptr, png_const_charp warning_msg)
58{
59    osg::notify(osg::WARN) << "PNG lib warning : " << warning_msg << std::endl;
60}
61
[4455]62void png_read_istream(png_structp png_ptr, png_bytep data, png_size_t length)
63{
64    std::istream *stream = (std::istream*)png_get_io_ptr(png_ptr); //Get pointer to istream
65    stream->read((char*)data,length); //Read requested amount of data
66}
67
[5025]68void png_write_ostream(png_structp png_ptr, png_bytep data, png_size_t length)
69{
70    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
71    stream->write((char*)data,length); //Write requested amount of data
72}
73
74void png_flush_ostream(png_structp png_ptr)
75{
76    std::ostream *stream = (std::ostream*)png_get_io_ptr(png_ptr); //Get pointer to ostream
77    stream->flush();
78}
79
[8]80class ReaderWriterPNG : public osgDB::ReaderWriter
[2]81{
82    public:
[8578]83        ReaderWriterPNG()
84        {
85            supportsExtension("png","PNG Image format");
86        }
87       
[3539]88        virtual const char* className() const { return "PNG Image Reader/Writer"; }
[2]89
[5029]90        WriteResult::WriteStatus writePngStream(std::ostream& fout, const osg::Image& img, int compression_level) const
[5025]91        {
92            png_structp png = NULL;
93            png_infop   info = NULL;
94            int color;
95            png_bytep *rows = NULL;
96
97            //Create write structure
98            png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
99            if(!png) return WriteResult::ERROR_IN_WRITING_FILE;
100
101            //Create infr structure
102            info = png_create_info_struct(png);
103            if(!info) return WriteResult::ERROR_IN_WRITING_FILE;
104
105            //Set custom write function so it will write to ostream
106            png_set_write_fn(png,&fout,png_write_ostream,png_flush_ostream);
107
[5029]108            //Set compression level
109            png_set_compression_level(png, compression_level);
110
[5025]111            switch(img.getPixelFormat()) {
112                case(GL_LUMINANCE): color = PNG_COLOR_TYPE_GRAY; break;
113                case(GL_ALPHA): color = PNG_COLOR_TYPE_GRAY; break; //Couldn't find a color type for pure alpha, using gray instead
114                case(GL_LUMINANCE_ALPHA): color = PNG_COLOR_TYPE_GRAY_ALPHA ; break;
115                case(GL_RGB): color = PNG_COLOR_TYPE_RGB; break;
116                case(GL_RGBA): color = PNG_COLOR_TYPE_RGB_ALPHA; break;
117                default: return WriteResult::ERROR_IN_WRITING_FILE; break;               
118            }
119
120            //Create row data
121            rows = new png_bytep[img.t()];
122            for(int i = 0; i < img.t(); ++i) {
123                rows[i] = (png_bytep)img.data(0,img.t() - i - 1);
124            }
125
126            //Write header info
127            png_set_IHDR(png, info, img.s(), img.t(),
128                        8, color, PNG_INTERLACE_NONE,
129                        PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
130
131            png_write_info(png, info);
132
133            //Write data
134            png_write_image(png, rows);
135
136            //End write
137            png_write_end(png, NULL);
138
139            //Cleanup
140            png_destroy_write_struct(&png,&info);
141            delete [] rows;
142
143            return WriteResult::FILE_SAVED;
144        }
145
[4455]146        ReadResult readPNGStream(std::istream& fin) const
[2]147        {
[8]148            int trans = PNG_ALPHA;
149            pngInfo pInfo;
150            pngInfo *pinfo = &pInfo;
[2]151
[8]152            unsigned char header[8];
153            png_structp png;
154            png_infop   info;
155            png_infop   endinfo;
156            png_bytep   data;    //, data2;
157            png_bytep  *row_p;
158            double  fileGamma;
[2]159
[8]160            png_uint_32 width, height;
161            int depth, color;
[2]162
[8]163            png_uint_32 i;
164            png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
[8378]165 
166            // Set custom error handlers
167            png_set_error_fn(png, png_get_error_ptr(png), user_error_fn, user_warning_fn);
[2]168
[8378]169            try
[8]170            {
[2]171
[8378]172                info = png_create_info_struct(png);
173                endinfo = png_create_info_struct(png);
[2]174
[8378]175                fin.read((char*)header,8);
176                if (fin.gcount() == 8 && png_check_sig(header, 8))
177                    png_set_read_fn(png,&fin,png_read_istream); //Use custom read function that will get data from istream
178                else
179                {
180                    png_destroy_read_struct(&png, &info, &endinfo);
181                    return ReadResult::FILE_NOT_HANDLED;
182                }
183                png_set_sig_bytes(png, 8);
[2]184
[8378]185                png_read_info(png, info);
186                png_get_IHDR(png, info, &width, &height, &depth, &color, NULL, NULL, NULL);
[2397]187
[8378]188                if (pinfo != NULL)
189                {
190                    pinfo->Width  = width;
191                    pinfo->Height = height;
192                    pinfo->Depth  = depth;
193                }
[2397]194
[8378]195                osg::notify(osg::INFO)<<"width="<<width<<" height="<<height<<" depth="<<depth<<std::endl;
196                if ( color == PNG_COLOR_TYPE_RGB) osg::notify(osg::INFO) << "color == PNG_COLOR_TYPE_RGB "<<std::endl;
197                if ( color == PNG_COLOR_TYPE_GRAY) osg::notify(osg::INFO) << "color == PNG_COLOR_TYPE_GRAY "<<std::endl;
198                if ( color == PNG_COLOR_TYPE_GRAY_ALPHA) osg::notify(osg::INFO) << "color ==  PNG_COLOR_TYPE_GRAY_ALPHA"<<std::endl;
[2397]199
[8378]200                // png default to big endian, so we'll need to swap bytes if on a little endian machine.
201                if (depth>8 && getCpuByteOrder()==osg::LittleEndian)
202                    png_set_swap(png);
[2]203
204
[8378]205                if (color == PNG_COLOR_TYPE_GRAY || color == PNG_COLOR_TYPE_GRAY_ALPHA)
206                {
207                    //png_set_gray_to_rgb(png);
208                }
[2397]209
[8378]210                if (color&PNG_COLOR_MASK_ALPHA && trans != PNG_ALPHA)
211                {
212                    png_set_strip_alpha(png);
213                    color &= ~PNG_COLOR_MASK_ALPHA;
214                }
[2397]215
[2987]216
[2]217
[8378]218                //    if (!(PalettedTextures && mipmap >= 0 && trans == PNG_SOLID))
219                //if (color == PNG_COLOR_TYPE_PALETTE)
220                //    png_set_expand(png);
[2987]221
[8378]222                // In addition to expanding the palette, we also need to check
223                // to expand greyscale and alpha images.  See libpng man page.
224                if (color == PNG_COLOR_TYPE_PALETTE)
225                    png_set_palette_to_rgb(png);
226                if (color == PNG_COLOR_TYPE_GRAY && depth < 8)
227                    png_set_gray_1_2_4_to_8(png);
228                if (png_get_valid(png, info, PNG_INFO_tRNS))
229                    png_set_tRNS_to_alpha(png);
[2987]230
[8378]231                // Make sure that files of small depth are packed properly.
232                if (depth < 8)
233                    png_set_packing(png);
[2]234
235
[8378]236                /*--GAMMA--*/
237                //    checkForGammaEnv();
238                double screenGamma = 2.2 / 1.0;
239                if (png_get_gAMA(png, info, &fileGamma))
240                    png_set_gamma(png, screenGamma, fileGamma);
[8]241                else
[8378]242                    png_set_gamma(png, screenGamma, 1.0/2.2);
[2]243
[8378]244                png_read_update_info(png, info);
[2]245
[8378]246                data = (png_bytep) new unsigned char [png_get_rowbytes(png, info)*height];
247                row_p = new png_bytep [height];
[2987]248
[8378]249                bool StandardOrientation = true;
250                for (i = 0; i < height; i++)
251                {
252                    if (StandardOrientation)
253                        row_p[height - 1 - i] = &data[png_get_rowbytes(png, info)*i];
254                    else
255                        row_p[i] = &data[png_get_rowbytes(png, info)*i];
256                }
[2987]257
[8378]258                png_read_image(png, row_p);
259                delete [] row_p;
260                png_read_end(png, endinfo);
[2]261
[8378]262                GLenum pixelFormat = 0;
263                GLenum dataType = depth<=8?GL_UNSIGNED_BYTE:GL_UNSIGNED_SHORT;
264                switch(color)
265                {
266                  case(PNG_SOLID): pixelFormat = GL_LUMINANCE; break;
267                  case(PNG_ALPHA): pixelFormat = GL_ALPHA; break;
268                  case(PNG_COLOR_TYPE_GRAY): pixelFormat =GL_LUMINANCE ; break;
269                  case(PNG_COLOR_TYPE_GRAY_ALPHA): pixelFormat = GL_LUMINANCE_ALPHA; break;
270                  case(PNG_COLOR_TYPE_RGB): pixelFormat = GL_RGB; break;
271                  case(PNG_COLOR_TYPE_PALETTE): pixelFormat = GL_RGB; break;
272                  case(PNG_COLOR_TYPE_RGB_ALPHA): pixelFormat = GL_RGBA; break;
273                  default: break;               
274                }
[2]275
[8378]276                // Some paletted images contain alpha information.  To be
277                // able to give that back to the calling program, we need to
278                // check the number of channels in the image.  However, the
279                // call might not return correct information unless
280                // png_read_end is called first.  See libpng man page.
281                if (pixelFormat == GL_RGB && png_get_channels(png, info) == 4)
282                    pixelFormat = GL_RGBA;
[2]283
[8378]284                int internalFormat = pixelFormat;
[576]285
[8378]286                png_destroy_read_struct(&png, &info, &endinfo);
[2]287
[8378]288                //    delete [] data;
[2397]289
[8378]290                if (pixelFormat==0)
291                    return ReadResult::FILE_NOT_HANDLED;
292
293                osg::Image* pOsgImage = new osg::Image();
294
295                pOsgImage->setImage(width, height, 1,
296                    internalFormat,
297                    pixelFormat,
298                    dataType,
299                    data,
300                    osg::Image::USE_NEW_DELETE);
301
302                return pOsgImage;
303            }
304            catch (PNGError& err)
305            {
306                osg::notify(osg::WARN) << err << std::endl;
307                png_destroy_read_struct(&png, &info, &endinfo);
308                return ReadResult::ERROR_IN_READING_FILE;
309            }
[2]310        }
[4455]311
[5029]312        int getCompressionLevel(const osgDB::ReaderWriter::Options *options) const
313        {
314            if(options) {
315                std::istringstream iss(options->getOptionString());
316                std::string opt;
317                while (iss >> opt) {
318                    if(opt=="PNG_COMPRESSION") {
319                        int level;
320                        iss >> level;
321                        return level;
322                    }
323                }
324            }
325
326            return Z_DEFAULT_COMPRESSION;
327        }
328
[7878]329        virtual ReadResult readObject(std::istream& fin,const osgDB::ReaderWriter::Options* options =NULL) const
330        {
331            return readImage(fin, options);
332        }
333
334        virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options =NULL) const
335        {
336            return readImage(file, options);
337        }
338
[4455]339        virtual ReadResult readImage(std::istream& fin,const Options* =NULL) const
340        {
341            return readPNGStream(fin);
342        }
343
344        virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
345        {
346            std::string ext = osgDB::getLowerCaseFileExtension(file);
347            if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
348
349            std::string fileName = osgDB::findDataFile( file, options );
350            if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
351
352            std::ifstream istream(fileName.c_str(), std::ios::in | std::ios::binary);
353            if(!istream) return ReadResult::FILE_NOT_HANDLED;
354            ReadResult rr = readPNGStream(istream);
355            if(rr.validImage()) rr.getImage()->setFileName(file);
356            return rr;
357        }
[5025]358
359        virtual WriteResult writeImage(const osg::Image& img,std::ostream& fout,const osgDB::ReaderWriter::Options *options) const
360        {
[5029]361            WriteResult::WriteStatus ws = writePngStream(fout,img,getCompressionLevel(options));
[5025]362            return ws;
363        }
364
365        virtual WriteResult writeImage(const osg::Image &img,const std::string& fileName, const osgDB::ReaderWriter::Options *options) const
366        {
367            std::string ext = osgDB::getFileExtension(fileName);
368            if (!acceptsExtension(ext)) return WriteResult::FILE_NOT_HANDLED;
369
370            std::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
371            if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
372
373            return writeImage(img,fout,options);
374        }
[2]375};
376
377// now register with Registry to instantiate the above
378// reader/writer.
[7076]379REGISTER_OSGPLUGIN(png, ReaderWriterPNG)
Note: See TracBrowser for help on using the browser.