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
00021 #include "pnmImage.h"
00022 #include "pnmWriter.h"
00023
00024 #include "bmp.h"
00025 #include "ppmcmap.h"
00026 #include "pnmbitio.h"
00027 #include "thread.h"
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 #define MAXCOLORS 256
00048
00049
00050
00051
00052
00053 static char er_write[] = "stdout: write error";
00054
00055
00056 static void PutByte (ostream *fp, char v);
00057 static void PutShort (ostream *fp, short v);
00058 static void PutLong (ostream *fp, long v);
00059 static int BMPwritefileheader (ostream *fp, int classv, unsigned long bitcount,
00060 unsigned long x, unsigned long y);
00061 static int BMPwriteinfoheader (ostream *fp, int classv, unsigned long bitcount,
00062 unsigned long x, unsigned long y);
00063 static int BMPwritergb (ostream *fp, int classv, pixval R, pixval G, pixval B);
00064 static int BMPwritergbtable (ostream *fp, int classv, int bpp, int colors,
00065 pixval *R, pixval *G, pixval *B);
00066 static int colorstobpp (int colors);
00067 static void BMPEncode (ostream *fp, int classv, int x, int y, pixel **pixels,
00068 int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B);
00069 static void
00070 PutByte(
00071 ostream *fp,
00072 char v)
00073 {
00074 if (!fp->put(v))
00075 {
00076 pm_error(er_write);
00077 }
00078 }
00079
00080 static void
00081 PutShort(
00082 ostream *fp,
00083 short v)
00084 {
00085 if (pm_writelittleshort(fp, v) == -1)
00086 {
00087 pm_error(er_write);
00088 }
00089 }
00090
00091 static void
00092 PutLong(
00093 ostream *fp,
00094 long v)
00095 {
00096 if (pm_writelittlelong(fp, v) == -1)
00097 {
00098 pm_error(er_write);
00099 }
00100 }
00101
00102
00103
00104
00105
00106
00107
00108
00109 static int
00110 BMPwritefileheader(
00111 ostream *fp,
00112 int classv,
00113 unsigned long bitcount,
00114 unsigned long x,
00115 unsigned long y)
00116 {
00117 PutByte(fp, 'B');
00118 PutByte(fp, 'M');
00119
00120
00121 PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y));
00122
00123
00124 PutShort(fp, 0);
00125
00126
00127 PutShort(fp, 0);
00128
00129
00130 PutLong(fp, (long)BMPoffbits(classv, bitcount));
00131
00132 return 14;
00133 }
00134
00135
00136
00137
00138 static int
00139 BMPwriteinfoheader(
00140 ostream *fp,
00141 int classv,
00142 unsigned long bitcount,
00143 unsigned long x,
00144 unsigned long y)
00145 {
00146 long cbFix = 0;
00147
00148
00149 switch (classv)
00150 {
00151 case C_WIN:
00152 cbFix = 40;
00153 PutLong(fp, cbFix);
00154
00155
00156 PutLong(fp, (long)x);
00157
00158 PutLong(fp, (long)y);
00159
00160 PutShort(fp, 1);
00161
00162 PutShort(fp, (short)bitcount);
00163
00164
00165
00166
00167
00168
00169 PutLong(fp, 0);
00170 PutLong(fp, 0);
00171 PutLong(fp, 0);
00172 PutLong(fp, 0);
00173 PutLong(fp, 0);
00174 PutLong(fp, 0);
00175
00176
00177 break;
00178 case C_OS2:
00179 cbFix = 12;
00180 PutLong(fp, cbFix);
00181
00182
00183 PutShort(fp, (short)x);
00184
00185 PutShort(fp, (short)y);
00186
00187 PutShort(fp, 1);
00188
00189 PutShort(fp, (short)bitcount);
00190
00191 break;
00192 default:
00193 pm_error(er_internal, "BMPwriteinfoheader");
00194 }
00195
00196 return cbFix;
00197 }
00198
00199
00200
00201
00202 static int
00203 BMPwritergb(
00204 ostream *fp,
00205 int classv,
00206 pixval R,
00207 pixval G,
00208 pixval B)
00209 {
00210 switch (classv)
00211 {
00212 case C_WIN:
00213 PutByte(fp, B);
00214 PutByte(fp, G);
00215 PutByte(fp, R);
00216 PutByte(fp, 0);
00217 return 4;
00218 case C_OS2:
00219 PutByte(fp, B);
00220 PutByte(fp, G);
00221 PutByte(fp, R);
00222 return 3;
00223 default:
00224 pm_error(er_internal, "BMPwritergb");
00225 }
00226 return -1;
00227 }
00228
00229
00230
00231
00232 static int
00233 BMPwritergbtable(
00234 ostream *fp,
00235 int classv,
00236 int bpp,
00237 int colors,
00238 pixval *R,
00239 pixval *G,
00240 pixval *B)
00241 {
00242 int nbyte = 0;
00243 int i;
00244 long ncolors;
00245
00246 for (i = 0; i < colors; i++)
00247 {
00248 nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]);
00249 }
00250
00251 ncolors = (1 << bpp);
00252
00253 for (; i < ncolors; i++)
00254 {
00255 nbyte += BMPwritergb(fp,classv,0,0,0);
00256 }
00257
00258 return nbyte;
00259 }
00260
00261
00262
00263
00264 static int
00265 BMPwriterow(
00266 ostream *fp,
00267 pixel *row,
00268 unsigned long cx,
00269 unsigned short bpp,
00270 int indexed,
00271 colorhash_table cht,
00272 xelval maxval)
00273 {
00274 BITSTREAM b;
00275 unsigned nbyte = 0;
00276 int rc;
00277 unsigned x;
00278
00279 if (indexed) {
00280 if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
00281 {
00282 return -1;
00283 }
00284
00285 for (x = 0; x < cx; x++, row++)
00286 {
00287 if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
00288 {
00289 return -1;
00290 }
00291 nbyte += rc;
00292 }
00293
00294 if ((rc = pm_bitfini(b)) == -1)
00295 {
00296 return -1;
00297 }
00298 nbyte += rc;
00299 } else {
00300
00301 for (x = 0; x < cx; x++, row++)
00302 {
00303 PutByte(fp, PPM_GETB(*row) * 255 / maxval);
00304 PutByte(fp, PPM_GETG(*row) * 255 / maxval);
00305 PutByte(fp, PPM_GETR(*row) * 255 / maxval);
00306 nbyte += 3;
00307 }
00308 }
00309
00310
00311
00312
00313 while (nbyte % 4)
00314 {
00315 PutByte(fp, 0);
00316 nbyte++;
00317 }
00318
00319 return nbyte;
00320 }
00321
00322
00323
00324
00325 static int
00326 BMPwritebits(
00327 ostream *fp,
00328 unsigned long cx,
00329 unsigned long cy,
00330 unsigned short cBitCount,
00331 pixel **pixels,
00332 int indexed,
00333 colorhash_table cht,
00334 xelval maxval)
00335 {
00336 int nbyte = 0;
00337 long y;
00338
00339 if(cBitCount > 24)
00340 {
00341 pm_error("cannot handle cBitCount: %d"
00342 ,cBitCount);
00343 }
00344
00345
00346
00347
00348
00349 for (y = (long)cy - 1; y >= 0; y--)
00350 {
00351 int rc;
00352 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht,
00353 maxval);
00354
00355 if(rc == -1)
00356 {
00357 pm_error("couldn't write row %d"
00358 ,y);
00359 }
00360 if(rc%4)
00361 {
00362 pm_error("row had bad number of bytes: %d"
00363 ,rc);
00364 }
00365 nbyte += rc;
00366 }
00367
00368 return nbyte;
00369 }
00370
00371
00372
00373
00374
00375
00376 static int
00377 colorstobpp(int colors)
00378 {
00379 int bpp;
00380
00381 if (colors < 1)
00382 {
00383 pm_error("can't have less than one color");
00384 }
00385
00386 if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
00387 {
00388 pm_error("can't happen");
00389 }
00390
00391 return bpp;
00392 }
00393
00394
00395
00396
00397
00398
00399
00400
00401 static void
00402 BMPEncode(
00403 ostream *fp,
00404 int classv,
00405 int x,
00406 int y,
00407 pixel **pixels,
00408 int colors,
00409 colorhash_table cht,
00410 pixval *R,
00411 pixval *G,
00412 pixval *B)
00413 {
00414 int bpp;
00415 unsigned long nbyte = 0;
00416
00417 bpp = bmp_bpp;
00418 int needs_bpp = colorstobpp(colors);
00419 if (bpp != 0 && bpp < needs_bpp) {
00420 pnmimage_bmp_cat.info()
00421 << "too many colors for " << bmp_bpp << "-bit image.\n";
00422 bpp = 0;
00423 }
00424
00425 if (bpp == 0) {
00426 bpp = needs_bpp;
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 switch(bpp)
00439 {
00440 case 2:
00441 case 3:
00442 bpp = 4;
00443 break;
00444 case 5:
00445 case 6:
00446 case 7:
00447 bpp = 8;
00448 break;
00449 }
00450 }
00451
00452 pnmimage_bmp_cat.info()
00453 << "Using " << bpp << " bits per pixel.\n";
00454
00455 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00456 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00457 nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
00458
00459 if(nbyte != ( BMPlenfileheader(classv)
00460 + BMPleninfoheader(classv)
00461 + BMPlenrgbtable(classv, bpp)))
00462 {
00463 pm_error(er_internal, "BMPEncode");
00464 }
00465
00466 nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht, 255);
00467 if(nbyte != BMPlenfile(classv, bpp, x, y))
00468 {
00469 pm_error(er_internal, "BMPEncode");
00470 }
00471 }
00472
00473
00474
00475
00476 static void
00477 BMPEncode24(
00478 ostream *fp,
00479 int classv,
00480 int x,
00481 int y,
00482 pixel **pixels,
00483 xelval maxval)
00484 {
00485 unsigned long nbyte = 0;
00486 int bpp = 24;
00487
00488 pnmimage_bmp_cat.info()
00489 << "Using " << bpp << " bits per pixel.\n";
00490
00491 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00492 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00493
00494 if(nbyte != ( BMPlenfileheader(classv)
00495 + BMPleninfoheader(classv)))
00496 {
00497 pm_error(er_internal, "BMPEncode24");
00498 }
00499
00500 nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table(),
00501 maxval);
00502 if(nbyte != BMPlenfile(classv, bpp, x, y))
00503 {
00504 pm_error(er_internal, "BMPEncode24");
00505 }
00506 }
00507
00508
00509
00510
00511
00512
00513
00514 PNMFileTypeBMP::Writer::
00515 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00516 PNMWriter(type, file, owns_file)
00517 {
00518 }
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 int PNMFileTypeBMP::Writer::
00544 write_data(xel *array, xelval *) {
00545 if (_y_size<=0 || _x_size<=0) {
00546 return 0;
00547 }
00548
00549 int classv = C_WIN;
00550
00551 int colors;
00552 int i;
00553 colorhist_vector chv;
00554 pixval Red[MAXCOLORS];
00555 pixval Green[MAXCOLORS];
00556 pixval Blue[MAXCOLORS];
00557
00558 pixel** pixels;
00559 colorhash_table cht;
00560
00561 #if 0
00562 {
00563 char *name;
00564 switch (classv)
00565 {
00566 case C_WIN:
00567 name = "a Windows";
00568 break;
00569 case C_OS2:
00570 name = "an OS/2";
00571 break;
00572 default:
00573 pm_error(er_internal, "report");
00574 break;
00575 }
00576 pm_message("generating %s BMP file", name);
00577 }
00578 #endif
00579
00580
00581 pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
00582 for (i = 0; i < _y_size; i++) {
00583 pixels[i] = (pixel *)(array + i * _x_size);
00584 }
00585
00586
00587 chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
00588 if (bmp_bpp > 8) {
00589
00590 BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
00591
00592 } else if (chv == (colorhist_vector) 0) {
00593 if (bmp_bpp != 0) {
00594
00595 pnmimage_bmp_cat.info()
00596 << "too many colors for " << bmp_bpp << "-bit image.\n";
00597 }
00598
00599 BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
00600
00601 } else {
00602 pnmimage_bmp_cat.debug()
00603 << colors << " colors found\n";
00604
00605
00606
00607
00608 if (_maxval > 255) {
00609 pnmimage_bmp_cat.debug()
00610 << "maxval is not 255 - automatically rescaling colors\n";
00611 }
00612
00613 for (i = 0; i < colors; ++i) {
00614 if (_maxval == 255) {
00615 Red[i] = PPM_GETR(chv[i].color);
00616 Green[i] = PPM_GETG(chv[i].color);
00617 Blue[i] = PPM_GETB(chv[i].color);
00618 } else {
00619 Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
00620 Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
00621 Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
00622 }
00623 }
00624
00625
00626 cht = ppm_colorhisttocolorhash(chv, colors);
00627 ppm_freecolorhist(chv);
00628
00629 BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
00630 Red, Green, Blue);
00631 }
00632 Thread::consider_yield();
00633
00634 return _y_size;
00635 }
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647 bool PNMFileTypeBMP::Writer::
00648 supports_grayscale() const {
00649 return false;
00650 }
00651
00652 #endif // HAVE_BMP