PortablePicture.cpp

Go to the documentation of this file.
00001 
00011 #include "standard.h"
00012 #include "PortablePicture.h"
00013 
00014 
00015 
00016 
00017 
00019 char *fileErrorString(FILE *fp, const char *filename, const char *function_name)
00020 {
00021     if (!fp) return fmtString("%s: file i/o error for file: %s", function_name, filename);
00022     if (feof(fp))
00023         return fmtString("%s: Reached end of file for file: %s", function_name, filename);
00024     else if (ferror(fp))
00025         return fmtString("%s: Error reading file (%s): %s", function_name, filename, strerror(ferror(fp)));
00026     else
00027         return fmtString("%s: Unknown error reading file (%s).", function_name, filename);
00028 }
00029 
00030  
00031 
00032 
00038 std::string getNextLine(FILE *fp, const char *filename)
00039 {
00040     using std::string;
00041     string s;
00042     char line[71]; // lines should be no longer than 70 chars
00043 
00044     while (fgets (line, sizeof(line), fp) != NULL)
00045     {
00046         s = line;
00047         
00048         // remove "#" comments
00049         unsigned int position;
00050         if ((position = s.find_first_of ('#')) != string::npos)
00051             s.resize(position);
00052     
00053         // replace newlines & tabs with spaces
00054         std::replace (s.begin(), s.end(), '\n', ' ');
00055         std::replace (s.begin(), s.end(), '\r', ' ');
00056         std::replace (s.begin(), s.end(), '\t', ' ');
00057 
00058         // erase trailing spaces
00059         while (s.size() && isspace(s[s.size()-1]))
00060             s.resize (s.size() - 1);
00061 
00062         // erase leading spaces
00063         while (s.size() && isspace(s[0]))
00064             s.erase (0,1);
00065 
00066         if (s.length())
00067             return s;
00068     }
00069 
00070     throw DiskError(fp, "getNextLine()");
00071 
00072     return s; // never gets here
00073 }
00074 
00075 // Removes the first set of nonspace characters from "str"
00076 void removeFirstToken(std::string &str)
00077 {
00078     // remove leading nonspace chars
00079     while (str.size() && !isspace(str[0]))
00080         str.erase (0,1);      
00081     
00082     // remove leading space characters
00083     while (str.size() && isspace(str[0]))
00084         str.erase (0,1);        
00085 }
00086 
00090 PictureHeaderInfo readHeader(FILE *fp, const char *filename)
00091 {
00092     PictureHeaderInfo info;
00093 
00094     std::string line = getNextLine (fp, filename);
00095 
00096     if (1 != sscanf (line.c_str(), "P%c", &info.magicNumber))
00097         throw RuntimeError ("readHeader(): Unable to get magic number from file: %s", filename);
00098 
00099 /*
00100  * Find the "magic number". This will tell us whether the file is binary or ascii, and 
00101  * whether the pixel size is a bit, a byte, or a float.
00102  */
00103 
00104     switch (info.magicNumber)
00105     {
00106         case PBM_ASCII:
00107             info.bAscii = true;
00108             info.numChannels = 1;
00109             break;
00110         case PBM_BINARY:
00111             info.bAscii = false;
00112             info.numChannels = 1;
00113             throw ArgError("readHeader(): Can't read file (%s): Binary PBM's are unsupported at this time.", filename);
00114             break;
00115         case PGM_ASCII:
00116             info.bAscii = true;
00117             info.numChannels = 1;
00118             break;
00119         case PGM_BINARY:
00120             info.bAscii = false;
00121             info.numChannels = 1;
00122             break;
00123         case PPM_ASCII:
00124             info.bAscii = true;
00125             info.numChannels = 3;
00126             break;
00127         case PPM_BINARY:
00128             info.bAscii = false;
00129             info.numChannels = 3;
00130             break;
00131         case PFM_BINARY:
00132             info.bAscii = false;
00133             info.numChannels = 3;
00134             break;
00135         default:
00136             throw RuntimeError(
00137                 "PortableFloatMap::loadFile(): bad magic number (P%c) for file (%s). The file should start with P1, P2, P3, P4, P5, P6, or PF.",
00138                 info.magicNumber, filename);
00139     }
00140 
00141     
00142     // move on to next token we can find
00143     removeFirstToken(line);
00144     if (line.length() == 0)
00145         line = getNextLine (fp, filename); // throws exception on EOF
00146 
00147 /*
00148  * Read the width. Just in case someone writes the width in floating point,
00149  * we will grab it as a float and then round.
00150  */
00151     double fWidth;
00152     if (sscanf(line.c_str(), "%lf", &fWidth) != 1)
00153         throw RuntimeError("readHeader(): bad header characters for file (%s) while trying to read width:\n%s", filename, line.c_str());
00154     info.width = ROUND (fWidth);
00155 
00156     // move on to next token we can find
00157     removeFirstToken(line);
00158     if (line.length() == 0)
00159         line = getNextLine (fp, filename); // throws exception on EOF
00160 
00161 /*
00162  * Read the height; same deal as above.
00163  */
00164     double fHeight;
00165     if (sscanf(line.c_str(), "%lf", &fHeight) != 1)
00166         throw RuntimeError ("readHeader(): bad header characters for file (%s) while trying to read height:\n%s", filename, line.c_str());
00167     info.height = ROUND (fHeight);
00168    
00169 /*
00170  * Each pixel will have a maximum possible value. In a PBM, that value
00171  * is implicitly "1"; otherwise, we have to read it in.
00172  */
00173     if (info.magicNumber == PBM_BINARY || info.magicNumber == PBM_ASCII)
00174         info.maximum = 1;
00175     else
00176     {
00177         removeFirstToken(line);
00178         if (line.length() == 0)
00179             line = getNextLine (fp, filename);
00180 
00181         if (sscanf(line.c_str(), "%lf", &info.maximum) != 1)
00182             throw RuntimeError ("Bad header characters for file (%s) while trying to read maximum:\n%s", filename, line.c_str());
00183 
00184         if (info.magicNumber == PFM_BINARY)
00185             info.maximum = 1.;
00186         else if (ROUND(info.maximum) != 255)
00187         {
00188             int max = ROUND(info.maximum);
00189             throw RuntimeError("readHeader(): This picture has a maximum value of %d; but this library can't deal with maximums other than 255.",
00190                 max);
00191         }
00192     }
00193 
00194     return info;
00195 }
00196 
00197 template< typename T >
00198 void readBinaryNumbers (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbColor< T > > &picture)
00199 {
00200     picture.resize (info.height * info.width);
00201 
00202     std::vector< T > scanline;
00203     scanline.resize (info.width * info.numChannels);
00204 
00205     for (int row = info.height-1; row >=0; row--)
00206     {
00207         if (scanline.size() != fread (&scanline[0], sizeof(T), scanline.size(), fp))
00208             throw DiskError (fp, "readBinaryNumbers()");
00209         
00210         for (unsigned int col = 0; col < scanline.size();)
00211         {
00212             if (info.numChannels == 1)
00213             {
00214                 T currentPixelValue = scanline[col];
00215                                         
00216                 RgbColor< T > pixel(
00217                     currentPixelValue,
00218                     currentPixelValue,
00219                     currentPixelValue);
00220                 
00221                 picture[row*info.width+col] = pixel;
00222                 col+=1;
00223             }
00224             else // numChannels == 3
00225             {
00226                 RgbColor< T > pixel;
00227                 pixel[0] = scanline[col];
00228                 pixel[1] = scanline[col+1];
00229                 pixel[2] = scanline[col+2];
00230                 
00231                 picture[row*info.width+col/3] = pixel;
00232                 col+=3;
00233             }
00234         }
00235     }
00236     
00237     assert ((int)picture.size() == info.width * info.height);
00238 }
00239 
00240 void readBinaryBytes (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbByte > &picture)
00241 {
00242     readBinaryNumbers< unsigned char >(fp, filename, info, picture);
00243 }
00244 
00245 void readBinaryFloats (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbFloat > &picture)
00246 {
00247     readBinaryNumbers< float >(fp, filename, info, picture);
00248 }
00249 
00250 void readAscii (FILE *fp, const char *filename, PictureHeaderInfo info, std::vector< RgbByte > &picture)
00251 {
00252     picture.resize (info.height * info.width);
00253 
00254     int currentPixelValue;
00255     if (info.numChannels == 1)
00256     {
00257         for (int row = info.height-1; row >=0; row--)
00258         {
00259             for (int col = 0; col < info.width; col++)
00260             {
00261                 if (fscanf (fp, "%d", &currentPixelValue) != 1)
00262                     throw DiskError(fp, "readPixels()");
00263                 
00264                 RgbByte pixel(
00265                     (unsigned char)currentPixelValue,
00266                     (unsigned char)currentPixelValue,
00267                     (unsigned char)currentPixelValue);
00268                 
00269                 picture[row*info.width + col] = pixel;
00270             }
00271         }
00272     }
00273     else // numChannels == 3
00274     {
00275         for (int row = info.height-1; row >=0; row--)
00276         {
00277             for (int col = 0; col < info.width; col++)
00278             {
00279                 RgbByte pixel;
00280                 
00281                 for (int c = 0; c < 3; c++)
00282                 {
00283                     if (fscanf (fp, "%d", &currentPixelValue) != 1)
00284                     {
00285                         throw fileErrorString (fp, filename, "readPixels()");
00286                     }
00287                     pixel[c] = currentPixelValue;
00288                 }
00289                 
00290                 picture[row*info.width+col] = pixel;
00291             }
00292         }
00293     }
00294 }
00295 
00296 
00297 
00298 
00299 
00300 #include "MemoryMap.h"
00301 #include <windows.h>
00302 #include <atlbase.h>
00303 
00304 CComPtr<IPicture> loadIPicture (const char *filename)
00305 {
00306 /*
00307  * Load the file as a memory map
00308  */
00309     MemoryMap map;
00310     map.load (filename);
00311     void *data = map.getData();
00312 
00313     if (map.fileSizeHigh)
00314         throw Error ("loadIPicture(): file too big: %s", filename);
00315 
00316     unsigned int size = map.fileSizeLow;
00317 
00318 /*
00319  * Get a global block of memory, and copy our file data into that block
00320  */
00321         HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, size);
00322 
00323         if(!hGlobal)
00324         throw Error("loadIPicture(): failed to allocate enough memory for file %s", filename);
00325 
00326         void* pData = GlobalLock(hGlobal);
00327         memcpy(pData, map.getData(), size);
00328         GlobalUnlock(hGlobal);
00329 
00330 /*
00331  *  Make in in-memory stream representation from our global memory
00332  */
00333         CComPtr<IStream> pStream;
00334 
00335         HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
00336 
00337     if (FAILED (hr)) //  either hr == E_OUTOFMEMORY, or hr == E_INVALIDARG
00338     {
00339         GlobalFree (hGlobal);
00340         throw Error("loadBits(): Can't allocate memory for file %s", filename);
00341     }
00342 
00343 /*
00344  * Load a picture, via the stream representation
00345  */
00346     CComPtr<IPicture> pIPicture;
00347     hr = OleLoadPicture (pStream, size, FALSE, IID_IPicture, (LPVOID*)&pIPicture);
00348     if (E_NOINTERFACE == hr)
00349         throw "IPicture interface not supported.";
00350 
00351     if (FAILED (hr))
00352         throw Error ("loadBits(): OleLoadPicture failure for file: %s (Unsupported file format?)", filename);
00353 
00354     return pIPicture;
00355 }
00356 
00357 
00358 
00359 
00365 class BitmapDeviceContext
00366 {
00367 public:
00368     BitmapDeviceContext(int width, int height) 
00369     { 
00370         // initialize everything, in case we throw an exception
00371         hdc = NULL;
00372         hBitmap = NULL;
00373         hBitmapOld = NULL;
00374 
00375         // Create our device context
00376         hdc = CreateCompatibleDC (NULL); 
00377         if (!hdc) throw Error("SelectedBitmap: couldn't CreateCompatibleDC");
00378 
00379         // Create a BITMAPINFOHEADER structure to describe the DIB
00380         BITMAPV4HEADER BIH ;
00381         int iSize = sizeof(BITMAPV4HEADER) ;
00382         memset(&BIH, 0, iSize);
00383         
00384         // Fill in the header info.
00385         BIH.bV4Size = iSize;
00386         BIH.bV4Width = width;
00387         BIH.bV4Height = height;
00388         BIH.bV4Planes = 1;
00389         BIH.bV4BitCount = 24;
00390         
00391         // Create the DIB section.
00392         hBitmap = CreateDIBSection( 
00393             hdc,
00394             (BITMAPINFO*)&BIH,
00395             DIB_RGB_COLORS,
00396             (void**)&bitmapBits,
00397             NULL,
00398             0);
00399         
00400         if (!hBitmap || !bitmapBits) 
00401         {
00402             cleanup();
00403             throw Error("BitmapDeviceContext::BitmapDeviceContext(): Unable to create bitmap");
00404         }
00405         
00406         hBitmapOld = (HBITMAP)::SelectObject(
00407             hdc,
00408             hBitmap);
00409     }
00410 
00411     void cleanup() 
00412     {
00413         if (hBitmapOld) ::SelectObject (hdc, hBitmapOld);
00414         if (hBitmap) DeleteObject (hBitmap);
00415         if (hdc) DeleteDC (hdc);
00416         
00417         hBitmapOld = NULL;
00418         hBitmap = NULL;
00419         hdc = NULL;
00420     }
00421 
00422     ~BitmapDeviceContext()
00423     {
00424         cleanup();
00425     }
00426 
00427     HDC getDC() { return hdc; }
00428 
00429     unsigned char *getBitmapBits() { return bitmapBits; }
00430 
00431 private:
00432     HDC hdc;
00433     HBITMAP hBitmap, hBitmapOld;
00434     unsigned char *bitmapBits;
00435 };
00436 
00437 void loadBits (const char *filename, PortablePixMap &p)
00438 {
00439     CComPtr<IPicture> pIPicture = loadIPicture (filename);
00440     
00441     OLE_YSIZE_HIMETRIC ipictHeight;
00442     OLE_XSIZE_HIMETRIC ipictWidth;
00443     
00444     pIPicture->get_Height (&ipictHeight);
00445     pIPicture->get_Width (&ipictWidth);
00446     
00447     #define HIMETRIC_PER_INCH 2540
00448     // Could use AtlHiMetricToPixel here, instead of doing this...
00449     unsigned int destHeight = MulDiv(ipictHeight, 96, HIMETRIC_PER_INCH);
00450     unsigned int destWidth  = MulDiv(ipictWidth,  96, HIMETRIC_PER_INCH);
00451     
00452     BitmapDeviceContext pDC (destWidth, destHeight);
00453     
00454     RECT r = {0,0,0,0};
00455     HRESULT hr = pIPicture->Render(
00456         pDC.getDC(),
00457         0, 0,
00458         destWidth, destHeight,
00459         0, ipictHeight, 
00460         ipictWidth, -ipictHeight,
00461         &r);
00462     
00463     if (FAILED(hr))
00464         throw Error("loadBits(): pIPicture->Render failure");
00465     
00466 
00467     p.setDimensions (destWidth,destHeight);
00468     
00469     unsigned char* destData = (unsigned char*)p[0];
00470     unsigned int destRowWidth = 3*p.getWidth();
00471     
00472     unsigned int srcRowWidth = 3*p.getWidth();
00473     srcRowWidth = (srcRowWidth + 3) & ~3;
00474     
00475     unsigned char *srcData = pDC.getBitmapBits();
00476     for (int row = 0; row < p.getHeight(); row++)
00477     {
00478         unsigned int destRow = row;
00479         memcpy (
00480             destData+destRow*destRowWidth, 
00481             srcData+row*srcRowWidth, 
00482             p.getWidth()*3);
00483     }
00484     
00485     for (row = 0; row < p.getHeight(); row++)
00486     {
00487         for (int col = 0; col < p.getWidth(); col++)
00488         {
00489             RGBTRIPLE *r = (RGBTRIPLE*)&p[row][col].r();
00490             std::swap (r->rgbtBlue, r->rgbtRed);
00491         }
00492     }
00493 }

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