Panda3D

pnmFileTypeBMPWriter.cxx
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file pnmFileTypeBMPWriter.cxx
10 * @author drose
11 * @date 2000-06-19
12 */
13
14 #include "pnmFileTypeBMP.h"
15
16 #ifdef HAVE_BMP
17
18 #include "config_pnmimagetypes.h"
19
20 #include "pnmImage.h"
21 #include "pnmWriter.h"
22
23 #include "bmp.h"
24 #include "ppmcmap.h"
25 #include "pnmbitio.h"
26 #include "thread.h"
27
28 // Much code in this file is borrowed from Netpbm, specifically ppmtobmp.c.
29 /*
30 * ppmtobmp.c - Converts from a PPM file to a Microsoft Windows or OS/2
31 * .BMP file.
32 *
33 * The current implementation is probably not complete, but it works for
34 * me. I welcome feedback.
35 *
36 * Copyright (C) 1992 by David W. Sanderson.
37 *
38 * Permission to use, copy, modify, and distribute this software and its
39 * documentation for any purpose and without fee is hereby granted,
40 * provided that the above copyright notice appear in all copies and
41 * that both that copyright notice and this permission notice appear
42 * in supporting documentation. This software is provided "as is"
43 * without express or implied warranty.
44 */
45
46 #define MAXCOLORS 256
47
48 using std::ostream;
49
50 /*
51 * Utilities
52 */
53
54 static char er_write[] = "stdout: write error";
55
56 /* prototypes */
57 static void PutByte (ostream *fp, char v);
58 static void PutShort (ostream *fp, short v);
59 static void PutLong (ostream *fp, long v);
60 static int BMPwritefileheader (ostream *fp, int classv, unsigned long bitcount,
61 unsigned long x, unsigned long y);
62 static int BMPwriteinfoheader (ostream *fp, int classv, unsigned long bitcount,
63 unsigned long x, unsigned long y);
64 static int BMPwritergb (ostream *fp, int classv, pixval R, pixval G, pixval B);
65 static int BMPwritergbtable (ostream *fp, int classv, int bpp, int colors,
66 pixval *R, pixval *G, pixval *B);
67 static int colorstobpp (int colors);
68 static void BMPEncode (ostream *fp, int classv, int x, int y, pixel **pixels,
69 int colors, colorhash_table cht, pixval *R, pixval *G, pixval *B);
70 static void
71 PutByte(
72 ostream *fp,
73 char v)
74 {
75 if (!fp->put(v))
76 {
77 pm_error(er_write);
78 }
79 }
80
81 static void
82 PutShort(
83 ostream *fp,
84 short v)
85 {
86 if (pm_writelittleshort(fp, v) == -1)
87 {
88 pm_error(er_write);
89 }
90 }
91
92 static void
93 PutLong(
94 ostream *fp,
95 long v)
96 {
97 if (pm_writelittlelong(fp, v) == -1)
98 {
99 pm_error(er_write);
100 }
101 }
102
103 /*
104 * BMP writing
105 */
106
107 /*
108 * returns the number of bytes written, or -1 on error.
109 */
110 static int
111 BMPwritefileheader(
112 ostream *fp,
113 int classv,
114 unsigned long bitcount,
115 unsigned long x,
116 unsigned long y)
117 {
118 PutByte(fp, 'B');
119 PutByte(fp, 'M');
120
121 /* cbSize */
122 PutLong(fp, (long)BMPlenfile(classv, bitcount, x, y));
123
124 /* xHotSpot */
125 PutShort(fp, 0);
126
127 /* yHotSpot */
128 PutShort(fp, 0);
129
130 /* offBits */
131 PutLong(fp, (long)BMPoffbits(classv, bitcount));
132
133 return 14;
134 }
135
136 /*
137 * returns the number of bytes written, or -1 on error.
138 */
139 static int
140 BMPwriteinfoheader(
141 ostream *fp,
142 int classv,
143 unsigned long bitcount,
144 unsigned long x,
145 unsigned long y)
146 {
147 long cbFix = 0;
148
149 /* cbFix */
150 switch (classv)
151 {
152 case C_WIN:
153 cbFix = 40;
154 PutLong(fp, cbFix);
155
156 /* cx */
157 PutLong(fp, (long)x);
158 /* cy */
159 PutLong(fp, (long)y);
160 /* cPlanes */
161 PutShort(fp, 1);
162 /* cBitCount */
163 PutShort(fp, (short)bitcount);
164
165 /*
166 * We've written 16 bytes so far, need to write 24 more
167 * for the required total of 40.
168 */
169
170 PutLong(fp, 0);
171 PutLong(fp, 0);
172 PutLong(fp, 0);
173 PutLong(fp, 0);
174 PutLong(fp, 0);
175 PutLong(fp, 0);
176
177
178 break;
179 case C_OS2:
180 cbFix = 12;
181 PutLong(fp, cbFix);
182
183 /* cx */
184 PutShort(fp, (short)x);
185 /* cy */
186 PutShort(fp, (short)y);
187 /* cPlanes */
188 PutShort(fp, 1);
189 /* cBitCount */
190 PutShort(fp, (short)bitcount);
191
192 break;
193 default:
194 pm_error(er_internal, "BMPwriteinfoheader");
195 }
196
197 return cbFix;
198 }
199
200 /*
201 * returns the number of bytes written, or -1 on error.
202 */
203 static int
204 BMPwritergb(
205 ostream *fp,
206 int classv,
207 pixval R,
208 pixval G,
209 pixval B)
210 {
211 switch (classv)
212 {
213 case C_WIN:
214 PutByte(fp, B);
215 PutByte(fp, G);
216 PutByte(fp, R);
217 PutByte(fp, 0);
218 return 4;
219 case C_OS2:
220 PutByte(fp, B);
221 PutByte(fp, G);
222 PutByte(fp, R);
223 return 3;
224 default:
225 pm_error(er_internal, "BMPwritergb");
226 }
227 return -1;
228 }
229
230 /*
231 * returns the number of bytes written, or -1 on error.
232 */
233 static int
234 BMPwritergbtable(
235 ostream *fp,
236 int classv,
237 int bpp,
238 int colors,
239 pixval *R,
240 pixval *G,
241 pixval *B)
242 {
243 int nbyte = 0;
244 int i;
245 long ncolors;
246
247 for (i = 0; i < colors; i++)
248 {
249 nbyte += BMPwritergb(fp,classv,R[i],G[i],B[i]);
250 }
251
252 ncolors = (1 << bpp);
253
254 for (; i < ncolors; i++)
255 {
256 nbyte += BMPwritergb(fp,classv,0,0,0);
257 }
258
259 return nbyte;
260 }
261
262 /*
263 * returns the number of bytes written, or -1 on error.
264 */
265 static int
266 BMPwriterow(
267 ostream *fp,
268 pixel *row,
269 unsigned long cx,
270 unsigned short bpp,
271 int indexed,
272 colorhash_table cht,
273 xelval maxval)
274 {
275 BITSTREAM b;
276 unsigned nbyte = 0;
277 int rc;
278 unsigned x;
279
280 if (indexed) {
281 if ((b = pm_bitinit(fp, "w")) == nullptr)
282 {
283 return -1;
284 }
285
286 for (x = 0; x < cx; x++, row++)
287 {
288 if ((rc = pm_bitwrite(b, bpp, ppm_lookupcolor(cht, row))) == -1)
289 {
290 return -1;
291 }
292 nbyte += rc;
293 }
294
295 if ((rc = pm_bitfini(b)) == -1)
296 {
297 return -1;
298 }
299 nbyte += rc;
300 } else {
301
302 for (x = 0; x < cx; x++, row++)
303 {
304 PutByte(fp, PPM_GETB(*row) * 255 / maxval);
305 PutByte(fp, PPM_GETG(*row) * 255 / maxval);
306 PutByte(fp, PPM_GETR(*row) * 255 / maxval);
307 nbyte += 3;
308 }
309 }
310
311 /*
312 * Make sure we write a multiple of 4 bytes.
313 */
314 while (nbyte % 4)
315 {
316 PutByte(fp, 0);
317 nbyte++;
318 }
319
320 return nbyte;
321 }
322
323 /*
324 * returns the number of bytes written, or -1 on error.
325 */
326 static int
327 BMPwritebits(
328 ostream *fp,
329 unsigned long cx,
330 unsigned long cy,
331 unsigned short cBitCount,
332 pixel **pixels,
333 int indexed,
334 colorhash_table cht,
335 xelval maxval)
336 {
337 int nbyte = 0;
338 long y;
339
340 if(cBitCount > 24)
341 {
342 pm_error("cannot handle cBitCount: %d"
343 ,cBitCount);
344 }
345
346 /*
347 * The picture is stored bottom line first, top line last
348 */
349
350 for (y = (long)cy - 1; y >= 0; y--)
351 {
352 int rc;
353 rc = BMPwriterow(fp, pixels[y], cx, cBitCount, indexed, cht,
354 maxval);
355
356 if(rc == -1)
357 {
358 pm_error("couldn't write row %d"
359 ,y);
360 }
361 if(rc%4)
362 {
363 pm_error("row had bad number of bytes: %d"
364 ,rc);
365 }
366 nbyte += rc;
367 }
368
369 return nbyte;
370 }
371
372 /*
373 * Return the number of bits per pixel required to represent the
374 * given number of colors.
375 */
376
377 static int
378 colorstobpp(int colors)
379 {
380 int bpp;
381
382 if (colors < 1)
383 {
384 pm_error("can't have less than one color");
385 }
386
387 if ((bpp = pm_maxvaltobits(colors - 1)) > 8)
388 {
389 pm_error("can't happen");
390 }
391
392 return bpp;
393 }
394
395 /*
396 * Write a BMP file of the given classv.
397 *
398 * Note that we must have 'colors' in order to know exactly how many
399 * colors are in the R, G, B, arrays. Entries beyond those in the
400 * arrays are undefined.
401 */
402 static void
403 BMPEncode(
404 ostream *fp,
405 int classv,
406 int x,
407 int y,
408 pixel **pixels,
409 int colors, /* number of valid entries in R,G,B */
410 colorhash_table cht,
411 pixval *R,
412 pixval *G,
413 pixval *B)
414 {
415 int bpp; /* bits per pixel */
416 unsigned long nbyte = 0;
417
418 bpp = bmp_bpp;
419 int needs_bpp = colorstobpp(colors);
420 if (bpp != 0 && bpp < needs_bpp) {
421 pnmimage_bmp_cat.info()
422 << "too many colors for " << bmp_bpp << "-bit image.\n";
423 bpp = 0;
424 }
425
426 if (bpp == 0) {
427 bpp = needs_bpp;
428
429 /*
430 * I have found empirically at least one BMP-displaying program
431 * that can't deal with (for instance) using 3 bits per pixel.
432 * I have seen no programs that can deal with using 3 bits per
433 * pixel. I have seen programs which can deal with 1, 4, and
434 * 8 bits per pixel.
435 *
436 * Based on this, I adjust actual the number of bits per pixel
437 * as follows. If anyone knows better, PLEASE tell me!
438 */
439 switch(bpp)
440 {
441 case 2:
442 case 3:
443 bpp = 4;
444 break;
445 case 5:
446 case 6:
447 case 7:
448 bpp = 8;
449 break;
450 }
451 }
452
453 pnmimage_bmp_cat.info()
454 << "Using " << bpp << " bits per pixel.\n";
455
456 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
457 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
458 nbyte += BMPwritergbtable(fp, classv, bpp, colors, R, G, B);
459
460 if(nbyte != ( BMPlenfileheader(classv)
461 + BMPleninfoheader(classv)
462 + BMPlenrgbtable(classv, bpp)))
463 {
464 pm_error(er_internal, "BMPEncode");
465 }
466
467 nbyte += BMPwritebits(fp, x, y, bpp, pixels, true, cht, 255);
468 if(nbyte != BMPlenfile(classv, bpp, x, y))
469 {
470 pm_error(er_internal, "BMPEncode");
471 }
472 }
473
474 /*
475 * Write a BMP file of the given class, with 24 bits per pixel nonindexed.
476 */
477 static void
478 BMPEncode24(
479 ostream *fp,
480 int classv,
481 int x,
482 int y,
483 pixel **pixels,
484 xelval maxval)
485 {
486 unsigned long nbyte = 0;
487 int bpp = 24;
488
489 pnmimage_bmp_cat.info()
490 << "Using " << bpp << " bits per pixel.\n";
491
492 nbyte += BMPwritefileheader(fp, classv, bpp, x, y);
493 nbyte += BMPwriteinfoheader(fp, classv, bpp, x, y);
494
495 if(nbyte != ( BMPlenfileheader(classv)
496 + BMPleninfoheader(classv)))
497 {
498 pm_error(er_internal, "BMPEncode24");
499 }
500
501 nbyte += BMPwritebits(fp, x, y, bpp, pixels, false, colorhash_table(),
502 maxval);
503 if(nbyte != BMPlenfile(classv, bpp, x, y))
504 {
505 pm_error(er_internal, "BMPEncode24");
506 }
507 }
508
509
510 /**
511 *
512 */
513 PNMFileTypeBMP::Writer::
514 Writer(PNMFileType *type, ostream *file, bool owns_file) :
515 PNMWriter(type, file, owns_file)
516 {
517 }
518
519
520 /**
521 * Writes out an entire image all at once, including the header, based on the
522 * image data stored in the given _x_size * _y_size array and alpha pointers.
523 * (If the image type has no alpha channel, alpha is ignored.) Returns the
524 * number of rows correctly written.
525 *
526 * It is the user's responsibility to fill in the header data via calls to
527 * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
528 * calling write_data().
529 *
530 * It is important to delete the PNMWriter class after successfully writing
531 * the data. Failing to do this may result in some data not getting flushed!
532 *
533 * Derived classes need not override this if they instead provide
534 * supports_streaming() and write_row(), below.
535 */
536 int PNMFileTypeBMP::Writer::
537 write_data(xel *array, xelval *) {
538 if (_y_size<=0 || _x_size<=0) {
539 return 0;
540 }
541
542 int classv = C_WIN;
543
544 int colors;
545 int i;
546 colorhist_vector chv;
547 pixval Red[MAXCOLORS];
548 pixval Green[MAXCOLORS];
549 pixval Blue[MAXCOLORS];
550
551 pixel** pixels;
552 colorhash_table cht;
553
554 #if 0
555 {
556 char *name;
557 switch (classv)
558 {
559 case C_WIN:
560 name = "a Windows";
561 break;
562 case C_OS2:
563 name = "an OS/2";
564 break;
565 default:
566 pm_error(er_internal, "report");
567 break;
568 }
569 pm_message("generating %s BMP file", name);
570 }
571 #endif
572
573 // We need an honest 2-d array of pixels, instead of one big 1-d array.
574 pixels = (pixel **)alloca(sizeof(pixel *) * _y_size);
575 for (i = 0; i < _y_size; i++) {
576 pixels[i] = (pixel *)(array + i * _x_size);
577 }
578
579 /* Figure out the colormap. */
580 chv = ppm_computecolorhist(pixels, _x_size, _y_size, MAXCOLORS, &colors);
581 if (bmp_bpp > 8) {
582 // Quietly generate a 24-bit image.
583 BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
584
585 } else if (chv == nullptr) {
586 if (bmp_bpp != 0) {
587 // Even though we asked for fewer bits, we have to settle for 24-bit.
588 pnmimage_bmp_cat.info()
589 << "too many colors for " << bmp_bpp << "-bit image.\n";
590 }
591
592 BMPEncode24(_file, classv, _x_size, _y_size, pixels, _maxval);
593
594 } else {
595 pnmimage_bmp_cat.debug()
596 << colors << " colors found\n";
597
598 /*
599 * Now turn the ppm colormap into the appropriate BMP colormap.
600 */
601 if (_maxval > 255) {
602 pnmimage_bmp_cat.debug()
603 << "maxval is not 255 - automatically rescaling colors\n";
604 }
605
606 for (i = 0; i < colors; ++i) {
607 if (_maxval == 255) {
608 Red[i] = PPM_GETR(chv[i].color);
609 Green[i] = PPM_GETG(chv[i].color);
610 Blue[i] = PPM_GETB(chv[i].color);
611 } else {
612 Red[i] = (pixval) PPM_GETR(chv[i].color) * 255 / _maxval;
613 Green[i] = (pixval) PPM_GETG(chv[i].color) * 255 / _maxval;
614 Blue[i] = (pixval) PPM_GETB(chv[i].color) * 255 / _maxval;
615 }
616 }
617
618 /* And make a hash table for fast lookup. */
619 cht = ppm_colorhisttocolorhash(chv, colors);
620 ppm_freecolorhist(chv);
621
622 BMPEncode(_file, classv, _x_size, _y_size, pixels, colors, cht,
623 Red, Green, Blue);
624 }
625 Thread::consider_yield();
626
627 return _y_size;
628 }
629
630 /**
631 * Returns true if this particular PNMWriter understands grayscale images. If
632 * this is false, then the rgb values of the xel array will be pre-filled with
633 * the same value across all three channels, to allow the writer to simply
634 * write out RGB data for a grayscale image.
635 */
636 bool PNMFileTypeBMP::Writer::
637 supports_grayscale() const {
638 return false;
639 }
640
641 #endif // HAVE_BMP
bmp.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMFileType
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
PNMWriter
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
Thread::consider_yield
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:212
config_pnmimagetypes.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmFileTypeBMP.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pm_maxvaltobits
int pm_maxvaltobits(int maxval)
Returns the number of bits sufficient to hold the indicated maxval value.
Definition: pnmimage_base.cxx:81
pm_error
void pm_error(const char *format,...)
Outputs the given printf-style message to the user and terminates messily.
Definition: pnmimage_base.cxx:54
pm_message
void pm_message(const char *format,...)
Outputs the given printf-style message to the user and returns.
Definition: pnmimage_base.cxx:30
colorhist_item
Definition: ppmcmap.h:14
colorhist_list_item
Definition: ppmcmap.h:21
pixel
Definition: pnmimage_base.h:41
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.