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  }
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void pm_message(const char *format,...)
Outputs the given printf-style message to the user and returns.
int pm_maxvaltobits(int maxval)
Returns the number of bits sufficient to hold the indicated maxval value.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void pm_error(const char *format,...)
Outputs the given printf-style message to the user and terminates messily.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.