00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "pnmFileTypeBMP.h"
00016
00017 #ifdef HAVE_BMP
00018
00019 #include "config_pnmimagetypes.h"
00020 #include "bmp.h"
00021 #include "pnmbitio.h"
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045 static int GetByte (istream * fp);
00046 static short GetShort (istream * fp);
00047 static long GetLong (istream * fp);
00048 static void readto (istream *fp, unsigned long *ppos, unsigned long dst);
00049 static void BMPreadfileheader (istream *fp, unsigned long *ppos,
00050 unsigned long *poffBits);
00051 static void BMPreadinfoheader (istream *fp, unsigned long *ppos,
00052 unsigned long *pcx, unsigned long *pcy, unsigned short *pcBitCount,
00053 int *pclassv);
00054 static int BMPreadrgbtable (istream *fp, unsigned long *ppos,
00055 unsigned short cBitCount, int classv, pixval *R, pixval *G, pixval *B);
00056
00057 static const char *ifname = "BMP";
00058 static char er_read[] = "%s: read error";
00059
00060
00061 static int
00062 GetByte(istream *fp)
00063 {
00064 int v;
00065
00066 if ((v = fp->get()) == EOF)
00067 {
00068 pm_error(er_read, ifname);
00069 }
00070
00071 return v;
00072 }
00073
00074 static short
00075 GetShort(istream *fp)
00076 {
00077 short v;
00078
00079 if (pm_readlittleshort(fp, &v) == -1)
00080 {
00081 pm_error(er_read, ifname);
00082 }
00083
00084 return v;
00085 }
00086
00087 static long
00088 GetLong(istream *fp)
00089 {
00090 long v;
00091
00092 if (pm_readlittlelong(fp, &v) == -1)
00093 {
00094 pm_error(er_read, ifname);
00095 }
00096
00097 return v;
00098 }
00099
00100
00101
00102
00103
00104
00105 static void
00106 readto(istream *fp,
00107 unsigned long *ppos,
00108 unsigned long dst)
00109 {
00110 unsigned long pos;
00111
00112 if(!fp || !ppos)
00113 return;
00114
00115 pos = *ppos;
00116
00117 if(pos > dst)
00118 pm_error("%s: internal error in readto()", ifname);
00119
00120 for(; pos < dst; pos++)
00121 {
00122 if (fp->get() == EOF)
00123 {
00124 pm_error(er_read, ifname);
00125 }
00126 }
00127
00128 *ppos = pos;
00129 }
00130
00131
00132
00133
00134
00135
00136 static void
00137 BMPreadfileheader(
00138 istream *fp,
00139 unsigned long *ppos,
00140 unsigned long *poffBits)
00141 {
00142
00143
00144
00145
00146
00147 unsigned long offBits;
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161 GetLong(fp);
00162 GetShort(fp);
00163 GetShort(fp);
00164 offBits = GetLong(fp);
00165
00166 *poffBits = offBits;
00167
00168 *ppos += 14;
00169 }
00170
00171 static void
00172 BMPreadinfoheader(
00173 istream *fp,
00174 unsigned long *ppos,
00175 unsigned long *pcx,
00176 unsigned long *pcy,
00177 unsigned short *pcBitCount,
00178 int *pclassv)
00179 {
00180 unsigned long cbFix;
00181 unsigned short cPlanes = 0;
00182
00183 unsigned long cx = 0;
00184 unsigned long cy = 0;
00185 unsigned short cBitCount = 0;
00186 int classv = 0;
00187
00188 cbFix = GetLong(fp);
00189
00190 switch (cbFix)
00191 {
00192 case 12:
00193 classv = C_OS2;
00194
00195 cx = GetShort(fp);
00196 cy = GetShort(fp);
00197 cPlanes = GetShort(fp);
00198 cBitCount = GetShort(fp);
00199
00200 break;
00201 case 40:
00202 classv = C_WIN;
00203
00204 cx = GetLong(fp);
00205 cy = GetLong(fp);
00206 cPlanes = GetShort(fp);
00207 cBitCount = GetShort(fp);
00208
00209
00210
00211
00212
00213
00214 GetLong(fp);
00215 GetLong(fp);
00216 GetLong(fp);
00217 GetLong(fp);
00218 GetLong(fp);
00219 GetLong(fp);
00220
00221 break;
00222 default:
00223 pm_error("%s: unknown cbFix: %d", ifname, cbFix);
00224 break;
00225 }
00226
00227 if (cPlanes != 1)
00228 {
00229 pm_error("%s: don't know how to handle cPlanes = %d"
00230 ,ifname
00231 ,cPlanes);
00232 }
00233
00234 switch (classv)
00235 {
00236 case C_WIN:
00237 pm_message("Windows BMP, %dx%dx%d"
00238 ,cx
00239 ,cy
00240 ,cBitCount);
00241 break;
00242 case C_OS2:
00243 pm_message("OS/2 BMP, %dx%dx%d"
00244 ,cx
00245 ,cy
00246 ,cBitCount);
00247 break;
00248 }
00249
00250 #ifdef DEBUG
00251 pm_message("cbFix: %d", cbFix);
00252 pm_message("cx: %d", cx);
00253 pm_message("cy: %d", cy);
00254 pm_message("cPlanes: %d", cPlanes);
00255 pm_message("cBitCount: %d", cBitCount);
00256 #endif
00257
00258 *pcx = cx;
00259 *pcy = cy;
00260 *pcBitCount = cBitCount;
00261 *pclassv = classv;
00262
00263 *ppos += cbFix;
00264 }
00265
00266
00267
00268
00269 static int
00270 BMPreadrgbtable(
00271 istream *fp,
00272 unsigned long *ppos,
00273 unsigned short cBitCount,
00274 int classv,
00275 pixval *R,
00276 pixval *G,
00277 pixval *B)
00278 {
00279 int i;
00280 int nbyte = 0;
00281
00282 long ncolors = (1 << cBitCount);
00283
00284 for (i = 0; i < ncolors; i++)
00285 {
00286 B[i] = (pixval) GetByte(fp);
00287 G[i] = (pixval) GetByte(fp);
00288 R[i] = (pixval) GetByte(fp);
00289 nbyte += 3;
00290
00291 if (classv == C_WIN)
00292 {
00293 (void) GetByte(fp);
00294 nbyte++;
00295 }
00296 }
00297
00298 *ppos += nbyte;
00299 return nbyte;
00300 }
00301
00302
00303
00304
00305 static int
00306 BMPreadrow(
00307 istream *fp,
00308 unsigned long *ppos,
00309 pixel *row,
00310 unsigned long cx,
00311 unsigned short cBitCount,
00312 int indexed,
00313 pixval *R,
00314 pixval *G,
00315 pixval *B)
00316 {
00317 BITSTREAM b = NULL;
00318 unsigned nbyte = 0;
00319 int rc;
00320 unsigned x;
00321
00322 if (indexed) {
00323 if ((b = pm_bitinit(fp, "r")) == (BITSTREAM) 0)
00324 {
00325 return -1;
00326 }
00327 }
00328
00329 for (x = 0; x < cx; x++, row++)
00330 {
00331 unsigned long v;
00332
00333 if (!indexed) {
00334 int r, g, b;
00335 b = GetByte(fp);
00336 g = GetByte(fp);
00337 r = GetByte(fp);
00338 nbyte += 3;
00339 PPM_ASSIGN(*row, r, g, b);
00340 } else {
00341 if ((rc = pm_bitread(b, cBitCount, &v)) == -1)
00342 {
00343 return -1;
00344 }
00345 nbyte += rc;
00346
00347 PPM_ASSIGN(*row, R[v], G[v], B[v]);
00348 }
00349 }
00350
00351 if (indexed) {
00352 if ((rc = pm_bitfini(b)) != 0)
00353 {
00354 return -1;
00355 }
00356 }
00357
00358
00359
00360
00361 while (nbyte % 4)
00362 {
00363 GetByte(fp);
00364 nbyte++;
00365 }
00366
00367 *ppos += nbyte;
00368 return nbyte;
00369 }
00370
00371 static void
00372 BMPreadbits(xel *array,
00373 istream *fp,
00374 unsigned long *ppos,
00375 unsigned long offBits,
00376 unsigned long cx,
00377 unsigned long cy,
00378 unsigned short cBitCount,
00379 int ,
00380 int indexed,
00381 pixval *R,
00382 pixval *G,
00383 pixval *B)
00384 {
00385 long y;
00386
00387 readto(fp, ppos, offBits);
00388
00389 if(cBitCount > 24)
00390 {
00391 pm_error("%s: cannot handle cBitCount: %d"
00392 ,ifname
00393 ,cBitCount);
00394 }
00395
00396
00397
00398
00399
00400 for (y = (long)cy - 1; y >= 0; y--)
00401 {
00402 int rc;
00403 rc = BMPreadrow(fp, ppos, array + y*cx, cx, cBitCount, indexed, R, G, B);
00404 if(rc == -1)
00405 {
00406 pm_error("%s: couldn't read row %d"
00407 ,ifname
00408 ,y);
00409 }
00410 if(rc%4)
00411 {
00412 pm_error("%s: row had bad number of bytes: %d"
00413 ,ifname
00414 ,rc);
00415 }
00416 }
00417
00418 }
00419
00420
00421
00422
00423
00424
00425 PNMFileTypeBMP::Reader::
00426 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
00427 PNMReader(type, file, owns_file)
00428 {
00429 if (!read_magic_number(_file, magic_number, 2)) {
00430
00431 if (pnmimage_bmp_cat.is_debug()) {
00432 pnmimage_bmp_cat.debug()
00433 << "BMP image file appears to be empty.\n";
00434 }
00435 _is_valid = false;
00436 return;
00437 }
00438
00439 if (magic_number != string("BM")) {
00440 pnmimage_bmp_cat.error()
00441 << "File is not a valid BMP file.\n";
00442 _is_valid = false;
00443 return;
00444 }
00445
00446 int rc;
00447
00448 unsigned long cx;
00449 unsigned long cy;
00450
00451 pos = 0;
00452
00453 BMPreadfileheader(file, &pos, &offBits);
00454 BMPreadinfoheader(file, &pos, &cx, &cy, &cBitCount, &classv);
00455
00456 if (offBits != BMPoffbits(classv, cBitCount)) {
00457 pnmimage_bmp_cat.warning()
00458 << "offBits is " << offBits << ", expected "
00459 << BMPoffbits(classv, cBitCount) << "\n";
00460 }
00461
00462 indexed = false;
00463
00464 if (cBitCount <= 8) {
00465 indexed = true;
00466 rc = BMPreadrgbtable(file, &pos, cBitCount, classv, R, G, B);
00467
00468 if (rc != (int)BMPlenrgbtable(classv, cBitCount)) {
00469 pnmimage_bmp_cat.warning()
00470 << rc << "-byte RGB table, expected "
00471 << BMPlenrgbtable(classv, cBitCount) << " bytes\n";
00472 }
00473 }
00474
00475 _num_channels = 3;
00476 _x_size = (int)cx;
00477 _y_size = (int)cy;
00478 _maxval = 255;
00479
00480 if (pnmimage_bmp_cat.is_debug()) {
00481 pnmimage_bmp_cat.debug()
00482 << "Reading BMP " << *this << "\n";
00483 }
00484 }
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500 int PNMFileTypeBMP::Reader::
00501 read_data(xel *array, xelval *) {
00502 BMPreadbits(array, _file, &pos, offBits, _x_size, _y_size,
00503 cBitCount, classv, indexed, R, G, B);
00504
00505 if (pos != BMPlenfile(classv, cBitCount, _x_size, _y_size)) {
00506 pnmimage_bmp_cat.warning()
00507 << "Read " << pos << " bytes, expected to read "
00508 << BMPlenfile(classv, cBitCount, _x_size, _y_size) << " bytes\n";
00509 }
00510
00511 return _y_size;
00512 }
00513
00514 #endif // HAVE_BMP