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