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
00022 #include "pnmImage.h"
00023 #include "pnmWriter.h"
00024
00025 #include "bmp.h"
00026 #include "ppmcmap.h"
00027 #include "pnmbitio.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;
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 {
00273 BITSTREAM b;
00274 unsigned nbyte = 0;
00275 int rc;
00276 unsigned x;
00277
00278 if (indexed) {
00279 if ((b = pm_bitinit(fp, "w")) == (BITSTREAM) 0)
00280 {
00281 return -1;
00282 }
00283
00284 for (x = 0; x < cx; x++, row++)
00285 {
00286 if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
00287 {
00288 return -1;
00289 }
00290 nbyte += rc;
00291 }
00292
00293 if ((rc = pm_bitfini(b)) == -1)
00294 {
00295 return -1;
00296 }
00297 nbyte += rc;
00298 } else {
00299
00300 for (x = 0; x < cx; x++, row++)
00301 {
00302 PutByte(fp, PPM_GETB(*row));
00303 PutByte(fp, PPM_GETG(*row));
00304 PutByte(fp, PPM_GETR(*row));
00305 nbyte += 3;
00306 }
00307 }
00308
00309
00310
00311
00312 while (nbyte % 4)
00313 {
00314 PutByte(fp, 0);
00315 nbyte++;
00316 }
00317
00318 return nbyte;
00319 }
00320
00321
00322
00323
00324 static int
00325 BMPwritebits(
00326 ostream *fp,
00327 unsigned long cx,
00328 unsigned long cy,
00329 unsigned short cBitCount,
00330 pixel **pixels,
00331 int indexed,
00332 colorhash_table cht)
00333 {
00334 int nbyte = 0;
00335 long y;
00336
00337 if(cBitCount > 24)
00338 {
00339 pm_error("cannot handle cBitCount: %d"
00340 ,cBitCount);
00341 }
00342
00343
00344
00345
00346
00347 for (y = (long)cy - 1; y >= 0; y--)
00348 {
00349 int rc;
00350 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht);
00351
00352 if(rc == -1)
00353 {
00354 pm_error("couldn't write row %d"
00355 ,y);
00356 }
00357 if(rc%4)
00358 {
00359 pm_error("row had bad number of bytes: %d"
00360 ,rc);
00361 }
00362 nbyte += rc;
00363 }
00364
00365 return nbyte;
00366 }
00367
00368
00369
00370
00371
00372
00373 static int
00374 colorstobpp(int colors)
00375 {
00376 int bpp;
00377
00378 if (colors < 1)
00379 {
00380 pm_error("can't have less than one color");
00381 }
00382
00383 if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
00384 {
00385 pm_error("can't happen");
00386 }
00387
00388 return bpp;
00389 }
00390
00391
00392
00393
00394
00395
00396
00397
00398 static void
00399 BMPEncode(
00400 ostream *fp,
00401 int classv,
00402 int x,
00403 int y,
00404 pixel **pixels,
00405 int colors,
00406 colorhash_table cht,
00407 pixval *R,
00408 pixval *G,
00409 pixval *B)
00410 {
00411 int bpp;
00412 unsigned long nbyte = 0;
00413
00414 bpp = bmp_bpp;
00415 int needs_bpp = colorstobpp(colors);
00416 if (bpp != 0 && bpp < needs_bpp) {
00417 pnmimage_bmp_cat.info()
00418 << "too many colors for " << bmp_bpp << "-bit image.\n";
00419 bpp = 0;
00420 }
00421
00422 if (bpp == 0) {
00423 bpp = needs_bpp;
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435 switch(bpp)
00436 {
00437 case 2:
00438 case 3:
00439 bpp = 4;
00440 break;
00441 case 5:
00442 case 6:
00443 case 7:
00444 bpp = 8;
00445 break;
00446 }
00447 }
00448
00449 pnmimage_bmp_cat.info()
00450 << "Using " << bpp << " bits per pixel.\n";
00451
00452 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00453 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00454 nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
00455
00456 if(nbyte != ( BMPlenfileheader(classv)
00457 + BMPleninfoheader(classv)
00458 + BMPlenrgbtable(classv, bpp)))
00459 {
00460 pm_error(er_internal, "BMPEncode");
00461 }
00462
00463 nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht);
00464 if(nbyte != BMPlenfile(classv, bpp, x, y))
00465 {
00466 pm_error(er_internal, "BMPEncode");
00467 }
00468 }
00469
00470
00471
00472
00473 static void
00474 BMPEncode24(
00475 ostream *fp,
00476 int classv,
00477 int x,
00478 int y,
00479 pixel **pixels)
00480 {
00481 unsigned long nbyte = 0;
00482 int bpp = 24;
00483
00484 pnmimage_bmp_cat.info()
00485 << "Using " << bpp << " bits per pixel.\n";
00486
00487 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
00488 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
00489
00490 if(nbyte != ( BMPlenfileheader(classv)
00491 + BMPleninfoheader(classv)))
00492 {
00493 pm_error(er_internal, "BMPEncode24");
00494 }
00495
00496 nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table());
00497 if(nbyte != BMPlenfile(classv, bpp, x, y))
00498 {
00499 pm_error(er_internal, "BMPEncode24");
00500 }
00501 }
00502
00503
00504
00505
00506
00507
00508
00509 PNMFileTypeBMP::Writer::
00510 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00511 PNMWriter(type, file, owns_file)
00512 {
00513 }
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538 int PNMFileTypeBMP::Writer::
00539 write_data(xel *array, xelval *) {
00540 if (_y_size<=0 || _x_size<=0) {
00541 return 0;
00542 }
00543
00544 int classv = C_WIN;
00545
00546 int colors;
00547 int i;
00548 colorhist_vector chv;
00549 pixval Red[MAXCOLORS];
00550 pixval Green[MAXCOLORS];
00551 pixval Blue[MAXCOLORS];
00552
00553 pixel** pixels;
00554 colorhash_table cht;
00555
00556 #if 0
00557 {
00558 char *name;
00559 switch (classv)
00560 {
00561 case C_WIN:
00562 name = "a Windows";
00563 break;
00564 case C_OS2:
00565 name = "an OS/2";
00566 break;
00567 default:
00568 pm_error(er_internal, "report");
00569 break;
00570 }
00571 pm_message("generating %s BMP file", name);
00572 }
00573 #endif
00574
00575
00576 pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
00577 for (i = 0; i < _y_size; i++) {
00578 pixels[i] = (pixel *)(array + i * _x_size);
00579 }
00580
00581
00582 chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
00583 if (bmp_bpp > 8) {
00584
00585 BMPEncode24(_file, classv, _x_size, _y_size, pixels);
00586
00587 } else if (chv == (colorhist_vector) 0) {
00588 if (bmp_bpp != 0) {
00589
00590 pnmimage_bmp_cat.info()
00591 << "too many colors for " << bmp_bpp << "-bit image.\n";
00592 }
00593
00594 BMPEncode24(_file, classv, _x_size, _y_size, pixels);
00595
00596 } else {
00597 pnmimage_bmp_cat.debug()
00598 << colors << " colors found\n";
00599
00600
00601
00602
00603 if (_maxval > 255) {
00604 pnmimage_bmp_cat.debug()
00605 << "maxval is not 255 - automatically rescaling colors\n";
00606 }
00607
00608 for (i = 0; i < colors; ++i) {
00609 if (_maxval == 255) {
00610 Red[i] = PPM_GETR(chv[i].color);
00611 Green[i] = PPM_GETG(chv[i].color);
00612 Blue[i] = PPM_GETB(chv[i].color);
00613 } else {
00614 Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
00615 Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
00616 Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
00617 }
00618 }
00619
00620
00621 cht = ppm_colorhisttocolorhash(chv, colors);
00622 ppm_freecolorhist(chv);
00623
00624 BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
00625 Red, Green, Blue);
00626 }
00627
00628 return _y_size;
00629 }