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 
readImage(std::istream & fin,const osgDB::ReaderWriter::Options * options=NULL) const229         virtual ReadResult readImage(std::istream& fin, const osgDB::ReaderWriter::Options* options=NULL) const
230         {
231             int ppmtype = 0;    /* P1, P2, etc. */
232             int width = 0;
233             int height = 0;
234             int max_value = 0;
235 
236             // Read header items.
237             std::string line;
238             int row;
239             for (row = 1; row <= 3; row++)
240             {
241                 getline(fin, line);
242                 if (!fin.good())
243                     return ReadResult::ERROR_IN_READING_FILE;
244 
245                 const char *cp = line.c_str();
246                 while (*cp && isspace(*cp))
247                     cp++;
248                 if (! *cp || *cp == '#')
249                 {
250                     // Skip comment lines.
251                     row--;
252                 }
253                 else if (row == 1)
254                 {
255                     // Get the image type.
256                     if (line[0] == 'p' || line[0] == 'P')
257                     {
258                         ppmtype = line[1] - '0';
259                     }
260                 }
261                 else if (row == 2)
262                 {
263                     std::istringstream istr(line);
264 
265                     istr >> width;
266                     istr >> height;
267 
268                     // pbm files don't have row 3
269                     if (ppmtype == 1 || ppmtype == 4)
270                     {
271                         max_value = 1;
272                         break;
273                     }
274                 }
275                 else if (row == 3)
276                 {
277                     // Get the maximum value
278                     std::istringstream istr(line);
279                     istr >> max_value;
280                 }
281             }
282 
283             // Check for valid values.
284             if (width <= 0 || height <= 0 ||
285                 max_value <= 0 || max_value > 65535 ||
286                 ppmtype < 1 || ppmtype > 6)
287             {
288                 return ReadResult::ERROR_IN_READING_FILE;
289             }
290 
291             int pixelFormat = 0;
292             int dataType = 0;
293             unsigned char* data = NULL;
294 
295             if (max_value > 255)
296             {
297                 dataType = GL_UNSIGNED_SHORT;
298                 switch(ppmtype)
299                 {
300                     case 2:    // grayscale ascii
301                         pixelFormat = GL_LUMINANCE;
302                         data = read_grayscale_ascii<unsigned short>(fin, width, height, max_value);
303                         break;
304                     case 3:    // color ascii
305                         pixelFormat = GL_RGB;
306                         data = read_color_ascii<unsigned short>(fin, width, height, max_value);
307                         break;
308                     case 5:    // grayscale binary
309                         pixelFormat = GL_LUMINANCE;
310                         data = read_grayscale_binary<unsigned short>(fin, width, height);
311                         break;
312                     case 6:    // color binary
313                         pixelFormat = GL_RGB;
314                         data = read_color_binary<unsigned short>(fin, width, height);
315                         break;
316                     default:
317                         return ReadResult::ERROR_IN_READING_FILE;
318                         break;
319                 }
320             }
321             else
322             {
323                 dataType = GL_UNSIGNED_BYTE;
324                 switch(ppmtype)
325                 {
326                     case 1:    // bitmap ascii
327                         pixelFormat = GL_LUMINANCE;
328                         data = read_bitmap_ascii<unsigned char>(fin, width, height);
329                         break;
330                     case 2:    // grayscale ascii
331                         pixelFormat = GL_LUMINANCE;
332                         data = read_grayscale_ascii<unsigned char>(fin, width, height, max_value);
333                         break;
334                     case 3:    // color ascii
335                         pixelFormat = GL_RGB;
336                         data = read_color_ascii<unsigned char>(fin, width, height, max_value);
337                         break;
338                     case 4:    // bitmap binary
339                         pixelFormat = GL_LUMINANCE;
340                         data = read_bitmap_binary<unsigned char>(fin, width, height);
341                         break;
342                     case 5:    // grayscale binary
343                         pixelFormat = GL_LUMINANCE;
344                         data = read_grayscale_binary<unsigned char>(fin, width, height);
345                         break;
346                     case 6:    // color binary
347                         pixelFormat = GL_RGB;
348                         data = read_color_binary<unsigned char>(fin, width, height);
349                         break;
350                     default:
351                         return ReadResult::ERROR_IN_READING_FILE;
352                         break;
353                 }
354             }
355 
356             if (data == NULL)
357             {
358                 return ReadResult::FILE_NOT_HANDLED;
359             }
360 
361             osg::Image* pOsgImage = new osg::Image();
362 
363             pOsgImage->setImage(width, height, 1,
364                 pixelFormat,
365                 pixelFormat,
366                 dataType,
367                 data,
368                 osg::Image::USE_NEW_DELETE);
369 
370             if (options && options->getOptionString().find("flip")!=std::string::npos)
371             {
372                 pOsgImage->flipVertical();
373             }
374 
375             return pOsgImage;
376         }
377 
readImage(const std::string & file,const osgDB::ReaderWriter::Options * options) const378         virtual ReadResult readImage(const std::string& file, const osgDB::ReaderWriter::Options* options) const
379         {
380             std::string ext = osgDB::getLowerCaseFileExtension(file);
381             if (!acceptsExtension(ext)) return ReadResult::FILE_NOT_HANDLED;
382 
383             std::string fileName = osgDB::findDataFile( file, options );
384             if (fileName.empty()) return ReadResult::FILE_NOT_FOUND;
385 
386             std::ifstream fin(fileName.c_str(), std::ios_base::in | std::ios_base::binary);
387             if (!fin.good())
388                 return ReadResult::ERROR_IN_READING_FILE;
389 
390             ReadResult rr = readImage(fin, options);
391             fin.close();
392             if (rr.validImage()) rr.getImage()->setFileName(file);
393             return rr;
394         }
395 
writeImage(const osg::Image & image,std::ostream & fout,const osgDB::ReaderWriter::Options * options) const396         virtual WriteResult writeImage(const osg::Image& image,std::ostream& fout,const osgDB::ReaderWriter::Options* options) const
397         {
398             bool ascii = (options && options->getOptionString().find("ascii")!=std::string::npos);
399 
400             if (ascii)
401             {
402                 // ascii ppm format.
403                 fout<<"P3"<<std::endl;
404                 fout<<image.s()<<" "<<image.t()<<std::endl;
405                 fout<<"255"<<std::endl;
406                 for(int row = image.t()-1; row >= 0; --row)
407                 {
408                     const unsigned char* ptr = image.data(0,row);
409                     for(int col = 0; col < image.s(); ++col)
410                     {
411                         fout<<static_cast<int>(*(ptr++));
412                         fout<<" "<<static_cast<int>(*(ptr++));
413                         fout<<" "<<static_cast<int>(*(ptr++))<<"  ";
414                     }
415                     fout<<std::endl;
416                 }
417             }
418             else
419             {
420                 // binary ppm format
421                 fout<<"P6"<<std::endl;
422                 fout<<image.s()<<" "<<image.t()<<std::endl;
423                 fout<<"255"<<std::endl;
424                 for(int row = image.t()-1; row >= 0; --row)
425                 {
426                     const unsigned char* ptr = image.data(0,row);
427                     for(int col = 0; col < image.s(); ++col)
428                     {
429                         fout.put(*(ptr++));
430                         fout.put(*(ptr++));
431                         fout.put(*(ptr++));
432                     }
433                 }
434             }
435             return WriteResult::FILE_SAVED;
436         }
437 
writeImage(const osg::Image & image,const std::string & fileName,const osgDB::ReaderWriter::Options * options) const438         virtual WriteResult writeImage(const osg::Image& image,const std::string& fileName, const osgDB::ReaderWriter::Options* options) const
439         {
440             // Only ppm format output supported
441             std::string ext = osgDB::getFileExtension(fileName);
442             if ( !osgDB::equalCaseInsensitive(ext, "ppm") ) return WriteResult::FILE_NOT_HANDLED;
443 
444             // only support rgb images right now.
445             if (image.getPixelFormat()!=GL_RGB || image.getDataType()!=GL_UNSIGNED_BYTE) return WriteResult("Error image pixel format not supported by pnm writer.");
446 
447             osgDB::ofstream fout(fileName.c_str(), std::ios::out | std::ios::binary);
448             if(!fout) return WriteResult::ERROR_IN_WRITING_FILE;
449 
450             return writeImage(image,fout,options);
451         }
452 
453 
454 };
455 
456 // now register with Registry to instantiate the above
457 // reader/writer.
458 REGISTER_OSGPLUGIN(pnm, ReaderWriterPNM)
459