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