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];
00043
00044 while (fgets (line, sizeof(line), fp) != NULL)
00045 {
00046 s = line;
00047
00048
00049 unsigned int position;
00050 if ((position = s.find_first_of ('#')) != string::npos)
00051 s.resize(position);
00052
00053
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
00059 while (s.size() && isspace(s[s.size()-1]))
00060 s.resize (s.size() - 1);
00061
00062
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;
00073 }
00074
00075
00076 void removeFirstToken(std::string &str)
00077 {
00078
00079 while (str.size() && !isspace(str[0]))
00080 str.erase (0,1);
00081
00082
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
00101
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
00143 removeFirstToken(line);
00144 if (line.length() == 0)
00145 line = getNextLine (fp, filename);
00146
00147
00148
00149
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
00157 removeFirstToken(line);
00158 if (line.length() == 0)
00159 line = getNextLine (fp, filename);
00160
00161
00162
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
00171
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
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", ¤tPixelValue) != 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
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", ¤tPixelValue) != 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
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
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
00332
00333 CComPtr<IStream> pStream;
00334
00335 HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
00336
00337 if (FAILED (hr))
00338 {
00339 GlobalFree (hGlobal);
00340 throw Error("loadBits(): Can't allocate memory for file %s", filename);
00341 }
00342
00343
00344
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
00371 hdc = NULL;
00372 hBitmap = NULL;
00373 hBitmapOld = NULL;
00374
00375
00376 hdc = CreateCompatibleDC (NULL);
00377 if (!hdc) throw Error("SelectedBitmap: couldn't CreateCompatibleDC");
00378
00379
00380 BITMAPV4HEADER BIH ;
00381 int iSize = sizeof(BITMAPV4HEADER) ;
00382 memset(&BIH, 0, iSize);
00383
00384
00385 BIH.bV4Size = iSize;
00386 BIH.bV4Width = width;
00387 BIH.bV4Height = height;
00388 BIH.bV4Planes = 1;
00389 BIH.bV4BitCount = 24;
00390
00391
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
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 }