1 // PNM Reader -- Written by Eric Sokolowsky
2 // Reads Ascii and Binary files in the PPM, PGM, and PBM formats.
3 
4 #include <osg/Image>
5 #include <osg/Notify>
6 #include <osg/Endian>
7 
8 #include <osgDB/Registry>
9 #include <osgDB/FileNameUtils>
10 #include <osgDB/FileUtils>
11 #include <osgDB/fstream>
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <sstream>
18 
19 using namespace osg;
20 
21 template <class T>
read_bitmap_ascii(std::istream & fin,int width,int height)22     unsigned char* read_bitmap_ascii(std::istream& fin, int width, int height)
23 {
24     T* data = new T[width*height];
25     T* end = data + width*height;
26 
27     int x = 0;
28     T* dst = end - width;
29     int value = 0;
30 
31     while(dst >= data)
32     {
33         fin >> value;
34         if (!fin.good())
35         {
36             delete [] data;
37             return NULL;
38         }
39 
40         // place value in the image. 0 is white and anything else is black.
41         *(dst++) = value ? 0 : 255;
42 
43         // At end of each row, jump back two rows
44         ++x;
45         if (x == width)
46         {
47             x = 0;
48             dst -= 2*width;
49         }
50     }
51 
52     return reinterpret_cast<unsigned char*>(data);
53 }
54 
55 template <class T>
read_grayscale_ascii(std::istream & fin,int width,int height,int max_value)56     unsigned char* read_grayscale_ascii(std::istream& fin, int width, int height, int max_value)
57 {
58     T* data = new T[width*height];
59     T* end = data + width*height;
60 
61     int x = 0;
62     T* dst = end - width;
63     int value = 0;
64     float max = (sizeof(T) == 1) ? 255.0 : 65535.0;
65 
66     while(dst >= data)
67     {
68         fin >> value;
69         if (!fin.good())
70         {
71             delete [] data;
72             return NULL;
73         }
74 
75         // place value in the image
76         *(dst++) = T(float(value)/float(max_value)*max);
77 
78         // At end of each row, jump back two rows
79         ++x;
80         if (x == width)
81         {
82             x = 0;
83             dst -= 2*width;
84         }
85     }
86 
87     return reinterpret_cast<unsigned char*>(data);
88 }
89 
90 template <class T>
read_color_ascii(std::istream & fin,int width,int height,int max_value)91     unsigned char* read_color_ascii(std::istream& fin, int width, int height, int max_value)
92 {
93     T* data = new T[3*width*height];
94     T* end = data + 3*width*height;
95 
96     int x = 0;
97     T* dst = end - 3*width;
98     int value = 0;
99     float max = (sizeof(T) == 1) ? 255.0 : 65535.0;
100 
101     while(dst >= data)
102     {
103         fin >> value;
104         if (!fin.good())
105         {
106             delete [] data;
107             return NULL;
108         }
109 
110         // place value in the image
111         *(dst++) = T(float(value)/float(max_value)*max);
112 
113         // At end of the row, jump back two rows
114         ++x;
115         if (x == width*3)
116         {
117             x = 0;
118             dst -= 6*width;
119         }
120     }
121 
122     return reinterpret_cast<unsigned char*>(data);
123 }
124 
125 template <class T>
read_bitmap_binary(std::istream & fin,int width,int height)126     unsigned char* read_bitmap_binary(std::istream& fin, int width, int height)
127 {
128     T* data = new T[width*height];
129 
130     for(int y = height-1; y >= 0; --y)
131     {
132         T* dst = data + (y+0)*width;
133         T* end = data + (y+1)*width;
134 
135         while(dst < end)
136         {
137             unsigned char b = (unsigned char) fin.get();
138             if (!fin.good())
139             {
140                 delete [] data;
141                 return NULL;
142             }
143 
144             for(int i = 7; i >= 0 && dst < end; i--)
145             {
146                 // 1 means black, 0 means white
147                 T data_value = (b & (1<<i)) ? 0 : 255;
148                 *(dst++) = data_value;
149             }
150         }
151     }
152 
153     return reinterpret_cast<unsigned char*>(data);
154 }
155 
156 template <class T>
read_grayscale_binary(std::istream & fin,int width,int height)157     unsigned char* read_grayscale_binary(std::istream& fin, int width, int height)
158 {
159     T* data = new T[width*height];
160 
161     for(int y = height-1; y >= 0; --y)
162     {
163         T* dst = data + y*width;
164         fin.read((char*)dst, sizeof(T)*width);
165         if (!fin.good())
166         {
167             delete [] data;
168             return NULL;
169         }
170     }
171 
172     // if the machine is little endian swap the bytes around
173     if (sizeof(T) == 2 && getCpuByteOrder() == osg::LittleEndian)
174     {
175         unsigned char *bs = reinterpret_cast<unsigned char *>(data);
176         unsigned char *end = bs + sizeof(T)*width*height;
177         for (; bs < end; bs += 2)
178         {
179             std::swap(bs[0], bs[1]);
180         }
181     }
182 
183     return reinterpret_cast<unsigned char *>(data);
184 }
185 
186 template <class T>
read_color_binary(std::istream & fin,int width,int height)187     unsigned char* read_color_binary(std::istream& fin, int width, int height)
188 {
189     T* data = new T[3*width*height];
190 
191     for(int y = height-1; y >= 0; --y)
192     {
193         T* dst = data + y*3*width;
194         fin.read((char*)dst, sizeof(T)*3*width);
195         if (!fin.good())
196         {
197             delete [] data;
198             return NULL;
199         }
200     }
201 
202     // if the machine is little endian swap the bytes around
203     if (sizeof(T) == 2 && getCpuByteOrder() == osg::LittleEndian)
204     {
205         unsigned char *bs = reinterpret_cast<unsigned char *>(data);
206         unsigned char *end = bs + sizeof(T)*3*width*height;
207         for (; bs < end; bs+=2)
208         {
209             std::swap(bs[0], bs[1]);
210         }
211     }
212 
213     return reinterpret_cast<unsigned char*>(data);
214 }
215 
216 class ReaderWriterPNM : public osgDB::ReaderWriter
217 {
218     public:
ReaderWriterPNM()219         ReaderWriterPNM()
220         {
221             supportsExtension("pnm","PNM Image format");
222             supportsExtension("ppm","PNM Image format");
223             supportsExtension("pgm","PNM Image format");
224             supportsExtension("pbm","PNM Image format");
225         }
226 
className() const227         virtual const char* className() const { return "PNM Image Reader/Writer"; }
228 
readObject(std::istream & fin,const Options * options) const229         virtual ReadResult readObject(std::istream& fin, const Options* options) const
230         {
231             return readImage(fin,options);
232         }
233 
readImage(std::istream & fin,const osgDB::ReaderWriter::Options * options=NULL) const234         virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* options=NULL) const
235         {
236             int ppmtype = 0;    /* P1, P2, etc. */
237             int width = 0;
238             int height = 0;
239             int max_value = 0;
240 
241             // Read header items.
242             std::string line;
243             int row;
244             for (row = 1; row <= 3; row++)
245             {
246                 getline(fin, line);
247                 if (!fin.good())
248                     return ReadResult::ERROR_IN_READING_FILE;
249 
250                 const char *cp = line.c_str();
251                 while (*cp && isspace(*cp))
252                     cp++;
253                 if (! *cp || *cp == '#')
254                 {
255                     // Skip comment lines.
256                     row--;
257                 }
258                 else if (row == 1)
259                 {
260                     // Get the image type.
261                     if (line[0] == 'p' || line[0] == 'P')
262                     {
263                         ppmtype = line[1] - '0';
264                     }
265                 }
266                 else if (row == 2)
267                 {
268                     std::istringstream istr(line);
269 
270                     istr >> width;
271                     istr >> height;
272 
273                     // pbm files don't have row 3
274                     if (ppmtype == 1 || ppmtype == 4)
275                     {
276                         max_value = 1;
277                         break;
278                     }
279                 }
280                 else if (row == 3)
281                 {
282                     // Get the maximum value
283                     std::istringstream istr(line);
284                     istr >> max_value;
285                 }
286             }
287 
288             // Check for valid values.
289             if (width <= 0 || height <= 0 ||
290                 max_value <= 0 || max_value > 65535 ||
291                 ppmtype < 1 || ppmtype > 6)
292             {
293                 return ReadResult::ERROR_IN_READING_FILE;
294             }
295 
296             int pixelFormat = 0;
297             int dataType = 0;
298             unsigned char* data = NULL;
299 
300             if (max_value > 255)
301             {
302                 dataType = GL_UNSIGNED_SHORT;
303                 switch(ppmtype)
304                 {
305                     case 2:    // grayscale ascii
306                         pixelFormat = GL_LUMINANCE;
307                         data = read_grayscale_ascii<unsigned short>(fin, width, height, max_value);
308                         break;
309                     case 3:    // color ascii
310                         pixelFormat = GL_RGB;
311                         data = read_color_ascii<unsigned short>(fin, width, height, max_value);
312                         break;
313                     case 5:    // grayscale binary
314                         pixelFormat = GL_LUMINANCE;
315                         data = read_grayscale_binary<unsigned short>(fin, width, height);
316                         break;
317                     case 6:    // color binary
318                         pixelFormat = GL_RGB;
319                         data = read_color_binary<unsigned short>(fin, width, height);
320                         break;
321                     default:
322                         return ReadResult::ERROR_IN_READING_FILE;
323                         break;
324                 }
325             }
326             else
327             {
328                 dataType = GL_UNSIGNED_BYTE;
329                 switch(ppmtype)
330                 {
331                     case 1:    // bitmap ascii
332                         pixelFormat = GL_LUMINANCE;
333                         data = read_bitmap_ascii<unsigned char>(fin, width, height);
334                         break;
335                     case 2:    // grayscale ascii
336                         pixelFormat = GL_LUMINANCE;
337                         data = read_grayscale_ascii<unsigned char>(fin, width, height, max_value);
338                         break;
339                     case 3:    // color ascii
340                         pixelFormat = GL_RGB;
341                         data = read_color_ascii<unsigned char>(fin, width, height, max_value);
342                         break;
343                     case 4:    // bitmap binary
344                         pixelFormat = GL_LUMINANCE;
345                         data = read_bitmap_binary<unsigned char>(fin, width, height);
346                         break;
347                     case 5:    // grayscale binary
348                         pixelFormat = GL_LUMINANCE;
349                         data = read_grayscale_binary<unsigned char>(fin, width, height);
350                         break;
351                     case 6:    // color binary
352                         pixelFormat = GL_RGB;
353                         data = read_color_binary<unsigned char>(fin, width, height);
354                         break;
355                     default:
356                         return ReadResult::ERROR_IN_READING_FILE;
357                         break;
358                 }
359             }
360 
361             if (data == NULL)
362             {
363                 return ReadResult::FILE_NOT_HANDLED;
364             }
365 
366             osg::Image* pOsgImage = new osg::Image();
367 
368             pOsgImage->setImage(width, height, 1,
369                 pixelFormat,
370                 pixelFormat,
371                 dataType,
372                 data,
373                 osg::Image::USE_NEW_DELETE);
374 
375             if (options && options->getOptionString().find("flip")!=std::string::npos)
376             {
377                 pOsgImage->flipVertical();
378             }
379 
380             return pOsgImage;
381         }
382 
readObject(const std::string & file,const osgDB::ReaderWriter::Options * options) const383         virtual ReadResult readObject(const std::string& file, const osgDB::ReaderWriter::Options* options) const
384         {
385             return readImage(file,options);
386         }
387 
readImage(const std::string & file,const osgDB::ReaderWriter::Options * options) const388         virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
389         {
390             std::string ext = osgDB::getLowerCaseFileExtension(file);
391             if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
392 
393             std::string fileName = osgDB::findDataFile( file, options );
394             if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
395 
396             std::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
397             if (!fin.good())
398                 return ReadResult::ERROR_IN_READING_FILE;
399 
400             ReadResult rr = readImage(fin, options);
401             fin.close();
402             if (rr.validImage()) rr.getImage()->setFileName(file);
403             return rr;
404         }
405 
writeImage(const osg::Image & image,std::ostream & fout,const osgDB::ReaderWriter::Options * options) const406         virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const osgDB::ReaderWriter::Options* options) const
407         {
408             bool ascii = (options && options->getOptionString().find("ascii")!=std::string::npos);
409 
410             if (ascii)
411             {
412                 // ascii ppm format.
413                 fout<<"P3"<<std::endl;
414                 fout<<image.s()<<" "<<image.t()<<std::endl;
415                 fout<<"255"<<std::endl;
416                 for(int row = image.t()-1; row >= 0; --row)
417                 {
418                     const unsigned char* ptr = image.data(0,row);
419                     for(int col = 0; col < image.s(); ++col)
420                     {
421                         fout<<static_cast<int>(*(ptr++));
422                         fout<<" "<<static_cast<int>(*(ptr++));
423                         fout<<" "<<static_cast<int>(*(ptr++))<<"  ";
424                     }
425                     fout<<std::endl;
426                 }
427             }
428             else
429             {
430                 // binary ppm format
431                 fout<<"P6"<<std::endl;
432                 fout<<image.s()<<" "<<image.t()<<std::endl;
433                 fout<<"255"<<std::endl;
434                 for(int row = image.t()-1; row >= 0; --row)
435                 {
436                     const unsigned char* ptr = image.data(0,row);
437                     for(int col = 0; col < image.s(); ++col)
438                     {
439                         fout.put(*(ptr++));
440                         fout.put(*(ptr++));
441                         fout.put(*(ptr++));
442                     }
443                 }
444             }
445             return WriteResult::FILE_SAVED;
446         }
447 
writeImage(const osg::Image & image,const std::string & fileName,const osgDB::ReaderWriter::Options * options) const448         virtual WriteResult writeImage(const osg::Image& image,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
449         {
450             // Only ppm format output supported
451             std::string ext = osgDB::getFileExtension(fileName);
452             if ( !osgDB::equalCaseInsensitive(ext, "ppm") ) return WriteResult::FILE_NOT_HANDLED;
453 
454             // only support rgb images right now.
455             if (image.getPixelFormat()!=GL_RGB || image.getDataType()!=GL_UNSIGNED_BYTE) return WriteResult("Error image pixel format not supported by pnm writer.");
456 
457             osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
458             if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
459 
460             return writeImage(image,fout,options);
461         }
462 
463 
464 };
465 
466 // now register with Registry to instantiate the above
467 // reader/writer.
468 REGISTER_OSGPLUGIN(pnm, ReaderWriterPNM)
469