PortablePicture.h

Go to the documentation of this file.
00001 
00011 #pragma once
00012 
00013 #include "standard.h"
00014 
00015 
00016 #pragma warning (push, 3)
00017 #include <vector>
00018 #include <string>
00019 #include <algorithm>
00020 #include <limits>
00021 #pragma warning (pop)
00022 
00023 #include "MatrixTypes.h"
00024 
00025 template< typename T >
00026 class typeTest
00027 { public: enum {isFloat = 0}; enum {isDouble = 0}; };
00028 
00029 template<>
00030 class typeTest< float >
00031 { public: enum {isFloat = 1}; enum {isDouble = 0}; };
00032 
00033 template<>
00034 class typeTest< double >
00035 { public: enum {isFloat = 0}; enum {isDouble = 1}; };
00036 
00037 
00040 typedef struct
00041 {
00043     char magicNumber; 
00044 
00046     int width;
00047 
00049     int height;
00050 
00052     bool bAscii;      
00053     
00055     double maximum;   
00056 
00058     int numChannels;  
00059 
00060 } PictureHeaderInfo;
00061 
00062 char *fileErrorString(FILE *fp, const char *filename, const char *function_name);
00063 std::string getNextLine(FILE *fp, const char *filename);
00064 void removeFirstToken(std::string &str);
00065 PictureHeaderInfo readHeader(FILE *fp, const char *filename);    
00066 void readBinaryBytes (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbByte > &picture);
00067 void readBinaryFloats(FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbFloat > &picture);
00068 void readAscii (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbByte > &picture);
00069 
00070 
00074 class File
00075 {
00076 public:
00077 
00078     File() { fp = NULL; }
00079 
00080     void openForWriting(const char *filename)
00081     {
00082         fp = fopen(filename, "wb");
00083         if (!fp) throw DiskError(fp, "File::openForWriting()");
00084     }
00085     void openForReading(const char *filename)
00086     {
00087         fp = fopen(filename, "rb");
00088         if (!fp) throw DiskError (fp, "File::openForReading()");
00089     }
00090 
00091     ~File() { if (fp) fclose(fp); }
00092 
00093     FILE *fp;
00094 
00095 }; // class File
00096 
00097 
00098 #define PBM_ASCII  '1'
00099 #define PGM_ASCII  '2'
00100 #define PPM_ASCII  '3'
00101 #define PBM_BINARY '4'
00102 #define PGM_BINARY '5'
00103 #define PPM_BINARY '6'
00104 #define PFM_BINARY 'F'
00105 
00106  
00107 
00108 
00114 template< typename T >
00115 class PortablePicture
00116 {
00117     typedef RgbColor< T > ColorType;
00118 
00119     public:
00120 
00122         PortablePicture()
00123         {
00124             width = 0;
00125             height = 0;
00126         }
00127 
00129         PortablePicture(int width_in, int height_in)
00130         {
00131             width = width_in;
00132             height = height_in;
00133             colorBuffer.resize(width*height);        
00134         }
00135 
00136 
00141         void getRange (T &our_min, T &our_max)
00142         {
00143             if (typeTest< T >::isFloat || typeTest< T >::isDouble)
00144             {
00145                 our_min = (T)0.;
00146                 our_max = (T)1.;
00147             }
00148             else
00149             {
00150                 our_min = std::numeric_limits< T >::min();
00151                 our_max = std::numeric_limits< T >::max();
00152             }            
00153         }
00154 
00155 
00161         void loadFile (const char *filename)
00162         {
00163             if (strlen(filename) < 4)
00164                 throw Error("PortableFloatMap::saveAs(): extension not supported.");
00165 
00166             // our file extension
00167             std::string ext = filename + strlen(filename) - 4;
00168 
00169             if (!(ext == ".pfm" ||
00170                   ext == ".ppm" ||
00171                   ext == ".pgm"))
00172             {
00173                 PortablePicture< unsigned char > pixmap;
00174                 loadBits (filename, pixmap);
00175                 T our_min, our_max;
00176                 getRange (our_min, our_max);
00177 
00178                 width = pixmap.getWidth();
00179                 height = pixmap.getHeight();
00180                 colorBuffer.resize (pixmap.getWidth() * pixmap.getHeight());
00181                 copyData(pixmap.colorBuffer, 0, 255, our_min, our_max, colorBuffer);
00182                 return;
00183             }
00184 
00185             
00186             assert(filename);
00187             File f;
00188 
00189             f.openForReading(filename);
00190 
00191             // (1) Read the header in. 
00192             PictureHeaderInfo header = readHeader(f.fp, filename);
00193     
00194             // (2) Read the pixels in.
00195             std::vector< ColorType > picture;
00196 
00197             readPixels (
00198                 f.fp,     // in 
00199                 filename, // in
00200                 header,   // in
00201                 picture); // out
00202 
00203             // (3) If an exception wasn't thrown, we can now set our pixels
00204             // and dimensions to the ones we've read in.
00205             width = header.width;
00206             height = header.height;
00207             colorBuffer = picture;
00208         }
00209 
00211         void setDimensions (int width_in, int height_in)
00212         {
00213             if (width_in <= 0 || height_in <= 0)
00214                 throw ArgError("PortablePicture::setDimensions(): the width and height must be >= 0.");
00215 
00216             width = width_in;
00217             height = height_in;
00218 
00219             colorBuffer.resize(width*height);
00220 
00221             // initialize everything to black
00222             memset(&colorBuffer[0], 0, sizeof(ColorType) * width * height);
00223         }
00224 
00225 
00227         int getWidth()      const { return width; }
00228 
00230         int getHeight()     const { return height; }
00231 
00232 
00233 
00234         T getMaxColor() const
00235         {
00236             T theMax = 0;
00237             for (unsigned int i = 0; i < colorBuffer.size(); i++)
00238             {
00239                 theMax = MAX (theMax, colorBuffer[i].r());
00240                 theMax = MAX (theMax, colorBuffer[i].g());
00241                 theMax = MAX (theMax, colorBuffer[i].b());
00242             }
00243             return theMax;
00244         }
00245 
00246         
00247 
00249         ColorType getInterpolated (float y, float x) const
00250         {
00251             assert (y <= getHeight() - 1);
00252             assert (x <= getWidth() - 1);
00253 
00254             // 1. Make sure we get values inside our array
00255             int xLower = (int)MAX(floor(x), 0);
00256             int xUpper = (int)MIN(ceil(x),getWidth()-1);
00257 
00258             int yLower = (int)MAX(floor(y), 0);
00259             int yUpper = (int)MIN(ceil(y),getHeight()-1);
00260 
00261             double pctX = x - xLower;
00262             double pctY = y - yLower;
00263 
00264             PortablePicture< T > & pp = const_cast< PortablePicture< T >& >(*this);
00265             ColorType & aColor = pp[yUpper][xLower];
00266             ColorType & bColor = pp[yUpper][xUpper];
00267             ColorType & cColor = pp[yLower][xLower];
00268             ColorType & dColor = pp[yLower][xUpper];
00269 
00270             ColorType ab;
00271             ab = aColor*(1-pctX) + bColor*(pctX);
00272             ColorType cd;
00273             cd = cColor*(1-pctX) + dColor*(pctX);
00274 
00275             ColorType abcd;
00276             abcd = ab*(pctY) + cd*(1-pctY);
00277 
00278             return abcd;
00279         }
00280 
00281 
00282         
00284         ColorType at (unsigned int y, unsigned int x) const
00285         {
00286             return colorBuffer[width*y + x];
00287         }
00288 
00289 
00291         ColorType * operator[] (int row) { return &colorBuffer[width*row]; }
00292         
00293         
00299         void saveAs (const char *filename) const
00300         {
00301             if (strlen(filename) < 4)
00302                 throw ArgError("PortablePicture::saveAs(): extension not supported.");
00303 
00304             // our file extension
00305             std::string ext = filename + strlen(filename) - 4;
00306 
00307             if (!(ext == ".pfm" ||
00308                   ext == ".ppm" ||
00309                   ext == ".pgm"))
00310             {
00311                 throw ArgError("PortableFloatMap::saveAs(): unsupported file type. Supported types are .ppm, .pgm, and .pfm.");
00312             }
00313 
00314             File f; // this will close itself
00315             f.openForWriting (filename);
00316 
00317             if (ext == ".pfm")
00318             {
00319                 writeHeader(f.fp, "PF", "-1.0000", getWidth(), getHeight(), filename);
00320 
00321                 if (colorBuffer.size() != fwrite(&colorBuffer[0], sizeof(colorBuffer[0]), colorBuffer.size(), f.fp))
00322                     throw DiskError(f.fp, "PortableFloatMap::saveAs");
00323 
00324                 return;
00325             }
00326 
00327             bool bIsPixmap = (ext == ".ppm");
00328             char *magicNumber;
00329 
00330             if (bIsPixmap)
00331                     magicNumber = "P6";
00332             else // is a greymap
00333                     magicNumber = "P5";
00334 
00335             writeHeader(f.fp,
00336                 magicNumber,
00337                 "255",
00338                 getWidth(), getHeight(),
00339                 filename);
00340 
00341             T our_min, our_max;
00342 
00343             if (typeTest< T >::isFloat || typeTest< T >::isDouble)
00344             {
00345                 our_min = (T)0.;
00346                 our_max = (T)1.;
00347             }
00348             else
00349             {
00350                 our_min = std::numeric_limits< T >::min();
00351                 our_max = std::numeric_limits< T >::max();
00352             }
00353 
00354 
00355             for (int row = getHeight()-1; row >= 0; row--)
00356             {
00357                 for (int col = 0; col < getWidth(); col++)
00358                 {
00359                     const ColorType & curColor = colorBuffer[row*getWidth()+col];
00360 
00361                     RgbByte newColor;
00362                     newColor.r() = (unsigned char)RESCALE(curColor.r(), our_min, our_max, 0, 255);
00363                     newColor.g() = (unsigned char)RESCALE(curColor.g(), our_min, our_max, 0, 255);
00364                     newColor.b() = (unsigned char)RESCALE(curColor.b(), our_min, our_max, 0, 255);
00365                     if (bIsPixmap)
00366                     {
00367                         fputc(newColor.r(), f.fp);
00368                         fputc(newColor.g(), f.fp);
00369                         fputc(newColor.b(), f.fp);
00370                     }
00371                     else
00372                     {
00373                         fputc((newColor.r()+newColor.g()+newColor.b())/3, f.fp);
00374                     }
00375                 }
00376             }
00377         }
00378     
00379 
00380 
00382         std::vector< ColorType > colorBuffer;
00383 
00384     private:
00385         int width;
00386         int height;
00387         
00388     
00389         template< typename N >
00390         void copyData (std::vector< RgbColor< N > > &data, double their_min, double their_max, T our_min, T our_max, std::vector< ColorType > &picture)
00391         {
00392             for (int row = 0; row < height; row++)
00393             {
00394                 for (int col = 0; col < width; col++)
00395                 {
00396                     unsigned int index = row*width+col;
00397                     
00398                     ColorType color;
00399                     
00400                     color[0] = (T)RESCALE(data[index][0], their_min, their_max, our_min, our_max);
00401                     color[1] = (T)RESCALE(data[index][1], their_min, their_max, our_min, our_max);
00402                     color[2] = (T)RESCALE(data[index][2], their_min, their_max, our_min, our_max);
00403                     picture[index] = color;
00404                 }
00405             }
00406         }
00407 
00408         
00409         
00421         void writeHeader (FILE *f, const char *magicNumber, const char *maxNumber, int width, int height, const char *filename) const
00422         {
00423             if (0 >= fprintf(f, "%s\n%d %d\n%s%s", 
00424                 magicNumber, 
00425                 width, height, 
00426                 maxNumber?maxNumber:"",
00427                 maxNumber?"\n":""))
00428             {
00429                 throw DiskError (f, "PortablePicture::saveAs()");
00430             }
00431         }        
00432 
00433 
00434 
00443         void readPixels (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< ColorType > & picture)
00444         {
00445             picture.resize(info.height * info.width);
00446             width = info.width;
00447             height = info.height;
00448 
00449             T our_min, our_max;
00450 
00451             getRange (our_min, our_max);
00452 
00453             if (info.magicNumber == PFM_BINARY)
00454             {
00455                 // if we're floats, and they're floats, we can read the values directly.
00456                 if (typeTest< T >::isFloat)
00457                 {
00458                     if (picture.size() != (int)fread(&picture[0], sizeof(ColorType), picture.size(), fp))
00459                         throw fileErrorString(fp, filename, "readPixels()");    
00460                     return;
00461                 }
00462 
00463                 // okay, we're not a float.
00464                 std::vector< RgbFloat > floatPicture;
00465                 readBinaryFloats (fp, filename, info, floatPicture);
00466 
00467                 copyData(floatPicture, 0, info.maximum, our_min, our_max, picture);
00468                 return;
00469             }
00470             
00471             std::vector< RgbByte > bytePicture;
00472             if (info.bAscii)
00473                 readAscii (fp, filename, info, bytePicture);
00474             else
00475                 readBinaryBytes (fp, filename, info, bytePicture);
00476 
00477             copyData(bytePicture, 0, info.maximum, our_min, our_max, picture);
00478         }
00479 };
00480 
00481 
00482 
00483 
00485 typedef PortablePicture< float > PortableFloatMap;
00486 
00488 typedef PortablePicture< unsigned char > PortablePixMap;
00489 
00490 void loadBits (const char *filename, PortablePixMap &p);

Generated on Tue May 21 03:34:51 2002 for Archimedes by doxygen1.2.15