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
pnmImage.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
pm_message
void pm_message(const char *format,...)
Outputs the given printf-style message to the user and returns.
Definition: pnmimage_base.cxx:30
pm_maxvaltobits
int pm_maxvaltobits(int maxval)
Returns the number of bits sufficient to hold the indicated maxval value.
Definition: pnmimage_base.cxx:81
pnmFileTypeBMP.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
bmp.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmWriter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
config_pnmimagetypes.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pixel
Definition: pnmimage_base.h:41
PNMWriter
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
PNMFileType
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
thread.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
colorhist_item
Definition: ppmcmap.h:14
colorhist_list_item
Definition: ppmcmap.h:21