Panda3D
pnmFileTypePNG.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 pnmFileTypePNG.cxx
10  * @author drose
11  * @date 2004-03-16
12  */
13 
14 #include "pnmFileTypePNG.h"
15 
16 #ifdef HAVE_PNG
17 
18 #include "config_pnmimagetypes.h"
19 
20 #include "pnmFileTypeRegistry.h"
21 #include "bamReader.h"
22 #include "thread.h"
23 
24 using std::istream;
25 using std::ostream;
26 using std::string;
27 
28 static const char * const extensions_png[] = {
29  "png"
30 };
31 static const int num_extensions_png = sizeof(extensions_png) / sizeof(const char *);
32 
33 TypeHandle PNMFileTypePNG::_type_handle;
34 
35 static const int png_max_palette = 256;
36 
37 // This STL comparison functor is used in write_data(), below. It sorts the
38 // non-maxval alpha pixels to the front of the list.
39 class LowAlphaCompare {
40 public:
41  bool operator() (const PNMImageHeader::PixelSpec &a,
42  const PNMImageHeader::PixelSpec &b) {
43  if (a._alpha != b._alpha) {
44  return a._alpha < b._alpha;
45  }
46  return a < b;
47  }
48 };
49 
50 /**
51  *
52  */
53 PNMFileTypePNG::
54 PNMFileTypePNG() {
55  // This constructor may run at static init time, so we use the ->
56  // dereferencing convention on the notify category.
57  if (pnmimage_png_cat->is_debug()) {
58  pnmimage_png_cat->debug()
59  << "PNG version " << PNG_LIBPNG_VER << "\n";
60  }
61 }
62 
63 /**
64  * Returns a few words describing the file type.
65  */
66 string PNMFileTypePNG::
67 get_name() const {
68  return "PNG";
69 }
70 
71 /**
72  * Returns the number of different possible filename extensions associated
73  * with this particular file type.
74  */
75 int PNMFileTypePNG::
76 get_num_extensions() const {
77  return num_extensions_png;
78 }
79 
80 /**
81  * Returns the nth possible filename extension associated with this particular
82  * file type, without a leading dot.
83  */
84 string PNMFileTypePNG::
85 get_extension(int n) const {
86  nassertr(n >= 0 && n < num_extensions_png, string());
87  return extensions_png[n];
88 }
89 
90 /**
91  * Returns a suitable filename extension (without a leading dot) to suggest
92  * for files of this type, or empty string if no suggestions are available.
93  */
94 string PNMFileTypePNG::
95 get_suggested_extension() const {
96  return "png";
97 }
98 
99 /**
100  * Returns true if this particular file type uses a magic number to identify
101  * it, false otherwise.
102  */
103 bool PNMFileTypePNG::
104 has_magic_number() const {
105  return true;
106 }
107 
108 /**
109  * Returns true if the indicated "magic number" byte stream (the initial few
110  * bytes read from the file) matches this particular file type, false
111  * otherwise.
112  */
113 bool PNMFileTypePNG::
114 matches_magic_number(const string &magic_number) const {
115  return png_sig_cmp((png_bytep)magic_number.data(), 0, magic_number.length()) == 0;
116 }
117 
118 /**
119  * Allocates and returns a new PNMReader suitable for reading from this file
120  * type, if possible. If reading from this file type is not supported,
121  * returns NULL.
122  */
123 PNMReader *PNMFileTypePNG::
124 make_reader(istream *file, bool owns_file, const string &magic_number) {
125  init_pnm();
126  return new Reader(this, file, owns_file, magic_number);
127 }
128 
129 /**
130  * Allocates and returns a new PNMWriter suitable for reading from this file
131  * type, if possible. If writing files of this type is not supported, returns
132  * NULL.
133  */
134 PNMWriter *PNMFileTypePNG::
135 make_writer(ostream *file, bool owns_file) {
136  init_pnm();
137  return new Writer(this, file, owns_file);
138 }
139 
140 /**
141  * Registers the current object as something that can be read from a Bam file.
142  */
143 void PNMFileTypePNG::
144 register_with_read_factory() {
146  register_factory(get_class_type(), make_PNMFileTypePNG);
147 }
148 
149 /**
150  * This method is called by the BamReader when an object of this type is
151  * encountered in a Bam file; it should allocate and return a new object with
152  * all the data read.
153  *
154  * In the case of the PNMFileType objects, since these objects are all shared,
155  * we just pull the object from the registry.
156  */
157 TypedWritable *PNMFileTypePNG::
158 make_PNMFileTypePNG(const FactoryParams &params) {
159  return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
160 }
161 
162 /**
163  *
164  */
165 PNMFileTypePNG::Reader::
166 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
167  PNMReader(type, file, owns_file)
168 {
169  _png = nullptr;
170  _info = nullptr;
171  _is_valid = false;
172 
173  _png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
174  png_error, png_warning);
175  if (_png == nullptr) {
176  return;
177  }
178 
179  _info = png_create_info_struct(_png);
180  if (_info == nullptr) {
181  png_destroy_read_struct(&_png, nullptr, nullptr);
182  return;
183  }
184 
185  _is_valid = true;
186 
187  if (setjmp(_jmpbuf)) {
188  // This is the ANSI C way to handle exceptions. If setjmp(), above,
189  // returns true, it means that libpng detected an exception while
190  // executing the code that reads the header info, below.
191  free_png();
192  return;
193  }
194 
195  png_set_read_fn(_png, (void *)this, png_read_data);
196  png_set_sig_bytes(_png, magic_number.length());
197 
198  png_read_info(_png, _info);
199 
200  png_uint_32 width;
201  png_uint_32 height;
202  int bit_depth;
203  int color_type;
204  int srgb_intent;
205  double gamma;
206 
207  png_get_IHDR(_png, _info, &width, &height,
208  &bit_depth, &color_type, nullptr, nullptr, nullptr);
209 
210  // Look for an sRGB chunk.
211  if (png_get_sRGB(_png, _info, &srgb_intent) == PNG_INFO_sRGB) {
212  _color_space = CS_sRGB;
213 
214  } else if (png_get_gAMA(_png, _info, &gamma) == PNG_INFO_gAMA) {
215  // File specifies a gamma.
216  if (gamma >= 0.99 && gamma <= 1.01) {
217  _color_space = CS_linear;
218 
219  } else if (gamma >= 0.44999 && gamma <= 0.455001) {
220  // It's probably close enough to sRGB.
221  _color_space = CS_sRGB;
222 
223  } else {
224  pnmimage_png_cat.warning()
225  << "Unsupported image gamma " << gamma << ", "
226  << "please re-export image as sRGB or linear.\n";
227  }
228  }
229 
230  if (pnmimage_png_cat.is_debug()) {
231  pnmimage_png_cat.debug()
232  << "width = " << width << " height = " << height << " bit_depth = "
233  << bit_depth << " color_type = " << color_type
234  << " color_space = " << _color_space << "\n";
235  }
236 
237  _x_size = width;
238  _y_size = height;
239  _maxval = (1 << bit_depth) - 1;
240 
241  if (bit_depth < 8) {
242  png_set_packing(_png);
243  }
244 
245  switch (color_type) {
246  case PNG_COLOR_TYPE_GRAY:
247  if (pnmimage_png_cat.is_debug()) {
248  pnmimage_png_cat.debug()
249  << "PNG_COLOR_TYPE_GRAY\n";
250  }
251  _num_channels = 1;
252  break;
253 
254  case PNG_COLOR_TYPE_GRAY_ALPHA:
255  if (pnmimage_png_cat.is_debug()) {
256  pnmimage_png_cat.debug()
257  << "PNG_COLOR_TYPE_GRAY_ALPHA\n";
258  }
259  _num_channels = 2;
260  break;
261 
262  case PNG_COLOR_TYPE_RGB:
263  if (pnmimage_png_cat.is_debug()) {
264  pnmimage_png_cat.debug()
265  << "PNG_COLOR_TYPE_RGB\n";
266  }
267  _num_channels = 3;
268  break;
269 
270  case PNG_COLOR_TYPE_RGB_ALPHA:
271  if (pnmimage_png_cat.is_debug()) {
272  pnmimage_png_cat.debug()
273  << "PNG_COLOR_TYPE_RGB_ALPHA\n";
274  }
275  _num_channels = 4;
276  break;
277 
278  case PNG_COLOR_TYPE_PALETTE:
279  if (pnmimage_png_cat.is_debug()) {
280  pnmimage_png_cat.debug()
281  << "PNG_COLOR_TYPE_PALETTE\n";
282  }
283  png_set_palette_to_rgb(_png);
284  _maxval = 255;
285  _num_channels = 3;
286  break;
287 
288  default:
289  pnmimage_png_cat.error()
290  << "Unsupported color type: " << color_type << "\n";
291  free_png();
292  return;
293  }
294 
295  if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
296  png_set_tRNS_to_alpha(_png);
297  if (!has_alpha()) {
298  _num_channels++;
299  }
300  }
301 
302  png_read_update_info(_png, _info);
303 }
304 
305 /**
306  *
307  */
308 PNMFileTypePNG::Reader::
309 ~Reader() {
310  free_png();
311 }
312 
313 /**
314  * Reads in an entire image all at once, storing it in the pre-allocated
315  * _x_size * _y_size array and alpha pointers. (If the image type has no
316  * alpha channel, alpha is ignored.) Returns the number of rows correctly
317  * read.
318  *
319  * Derived classes need not override this if they instead provide
320  * supports_read_row() and read_row(), below.
321  */
322 int PNMFileTypePNG::Reader::
323 read_data(xel *array, xelval *alpha_data) {
324  if (!is_valid()) {
325  return 0;
326  }
327 
328  if (setjmp(_jmpbuf)) {
329  // This is the ANSI C way to handle exceptions. If setjmp(), above,
330  // returns true, it means that libpng detected an exception while
331  // executing the code that reads the image, below.
332  free_png();
333  return 0;
334  }
335 
336  int row_byte_length = _x_size * _num_channels;
337  if (_maxval > 255) {
338  row_byte_length *= 2;
339  }
340 
341  int num_rows = _y_size;
342 
343  if (pnmimage_png_cat.is_debug()) {
344  pnmimage_png_cat.debug()
345  << "Allocating " << num_rows << " rows of " << row_byte_length
346  << " bytes each.\n";
347  }
348 
349  // We need to read a full copy of the image in first, in libpng's 2-d array
350  // format, mainly because we keep array and alpha data separately, and there
351  // doesn't appear to be good support to get this stuff out row-at-a-time for
352  // interlaced files.
353  png_bytep *rows = (png_bytep *)alloca(num_rows * sizeof(png_bytep));
354  int yi;
355 
356  png_byte *alloc = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte) * num_rows);
357  for (yi = 0; yi < num_rows; yi++) {
358  rows[yi] = alloc + (row_byte_length * sizeof(png_byte)) * yi;
359  }
360 
361  png_read_image(_png, rows);
362 
363  bool get_color = !is_grayscale();
364  bool get_alpha = has_alpha();
365 
366  int pi = 0;
367  for (yi = 0; yi < num_rows; yi++) {
368  png_bytep source = rows[yi];
369  for (int xi = 0; xi < _x_size; xi++) {
370  int red = 0;
371  int green = 0;
372  int blue = 0;
373  int alpha = 0;
374 
375  if (_maxval > 255) {
376  if (get_color) {
377  red = (source[0] << 8) | source[1];
378  source += 2;
379 
380  green = (source[0] << 8) | source[1];
381  source += 2;
382  }
383 
384  blue = (source[0] << 8) | source[1];
385  source += 2;
386 
387  if (get_alpha) {
388  alpha = (source[0] << 8) | source[1];
389  source += 2;
390  }
391 
392  } else {
393  if (get_color) {
394  red = *source;
395  source++;
396 
397  green = *source;
398  source++;
399  }
400 
401  blue = *source;
402  source++;
403 
404  if (get_alpha) {
405  alpha = *source;
406  source++;
407  }
408  }
409 
410  PPM_ASSIGN(array[pi], red, green, blue);
411  if (get_alpha) {
412  alpha_data[pi] = alpha;
413  }
414  pi++;
415  }
416 
417  nassertr(source <= rows[yi] + row_byte_length, yi);
418  }
419 
420  png_read_end(_png, nullptr);
421  PANDA_FREE_ARRAY(alloc);
422 
423  return _y_size;
424 }
425 
426 /**
427  * Releases the internal PNG structures and marks the reader invalid.
428  */
429 void PNMFileTypePNG::Reader::
430 free_png() {
431  if (_is_valid) {
432  png_destroy_read_struct(&_png, &_info, nullptr);
433  _is_valid = false;
434  }
435 }
436 
437 /**
438  * A callback handler that PNG uses to read data from the iostream.
439  */
440 void PNMFileTypePNG::Reader::
441 png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
442  Reader *self = (Reader *)png_get_io_ptr(png_ptr);
443  self->_file->read((char *)data, length);
444  if (length != (png_size_t)self->_file->gcount()) {
445  pnmimage_png_cat.error()
446  << "Didn't read enough bytes.\n";
447  // Is there no way to indicate a read failure to libpng?
448  }
450 }
451 
452 /**
453  * This is our own warning handler. It is called by the png library to issue
454  * a warning message.
455  */
456 void PNMFileTypePNG::Reader::
457 png_warning(png_structp, png_const_charp warning_msg) {
458  pnmimage_png_cat.warning()
459  << warning_msg << "\n";
460 }
461 
462 /**
463  * This is our own error handler. It is called by the png library to issue a
464  * fatal error message.
465  */
466 void PNMFileTypePNG::Reader::
467 png_error(png_structp png_ptr, png_const_charp error_msg) {
468  pnmimage_png_cat.error()
469  << error_msg << "\n";
470 
471  // The PNG library insists we should not return, so instead of returning, we
472  // will do a longjmp out of the png code.
473  Reader *self = (Reader *)png_get_io_ptr(png_ptr);
474  if (self == nullptr) {
475  // Oops, we haven't got a self pointer yet. Return anyway and hope we'll
476  // be ok.
477  pnmimage_png_cat.error()
478  << "Returning before opening file.\n";
479  return;
480  }
481 
482  longjmp(self->_jmpbuf, true);
483 }
484 
485 /**
486  *
487  */
488 PNMFileTypePNG::Writer::
489 Writer(PNMFileType *type, ostream *file, bool owns_file) :
490  PNMWriter(type, file, owns_file)
491 {
492  _png = nullptr;
493  _info = nullptr;
494  _is_valid = false;
495 
496  _png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr,
497  png_error, png_warning);
498  if (_png == nullptr) {
499  return;
500  }
501 
502  _info = png_create_info_struct(_png);
503  if (_info == nullptr) {
504  png_destroy_write_struct(&_png, nullptr);
505  return;
506  }
507 
508  _is_valid = true;
509 }
510 
511 /**
512  *
513  */
514 PNMFileTypePNG::Writer::
515 ~Writer() {
516  free_png();
517 }
518 
519 /**
520  * Writes in an entire image all at once, storing it in the pre-allocated
521  * _x_size * _y_size array and alpha pointers. (If the image type has no
522  * alpha channel, alpha is ignored.) Returns the number of rows correctly
523  * write.
524  *
525  * Derived classes need not override this if they instead provide
526  * supports_write_row() and write_row(), below.
527  */
528 int PNMFileTypePNG::Writer::
529 write_data(xel *array, xelval *alpha_data) {
530  if (!is_valid()) {
531  return 0;
532  }
533 
534  if (setjmp(_jmpbuf)) {
535  // This is the ANSI C way to handle exceptions. If setjmp(), above,
536  // returns true, it means that libpng detected an exception while
537  // executing the code that writes the image, below.
538  free_png();
539  return 0;
540  }
541 
542  png_set_write_fn(_png, (void *)this, png_write_data, png_flush_data);
543 
544  // The compression level corresponds directly to the compression levels for
545  // zlib.
546  png_set_compression_level(_png, png_compression_level);
547 
548  // First, write the header.
549 
550  int true_bit_depth = pm_maxvaltobits(_maxval);
551  int png_bit_depth = make_png_bit_depth(true_bit_depth);
552 
553  png_color_8 sig_bit;
554  sig_bit.red = true_bit_depth;
555  sig_bit.green = true_bit_depth;
556  sig_bit.blue = true_bit_depth;
557  sig_bit.gray = true_bit_depth;
558  sig_bit.alpha = true_bit_depth;
559 
560  int color_type = 0;
561 
562  if (!is_grayscale()) {
563  color_type |= PNG_COLOR_MASK_COLOR;
564  }
565  if (has_alpha()) {
566  color_type |= PNG_COLOR_MASK_ALPHA;
567  }
568 
569  // Determine if we should make a palettized image out of this. In order for
570  // this to be possible and effective, we must have no more than 256 unique
571  // coloralpha combinations for a color image, and the resulting bitdepth
572  // should be smaller than what we would have otherwise.
573  Palette palette;
574  HistMap palette_lookup;
575  png_color png_palette_table[png_max_palette];
576  png_byte png_trans[png_max_palette];
577 
578  if (png_palette) {
579  if (png_bit_depth <= 8) {
580  if (compute_palette(palette, array, alpha_data, png_max_palette)) {
581  if (pnmimage_png_cat.is_debug()) {
582  pnmimage_png_cat.debug()
583  << palette.size() << " colors found.\n";
584  }
585 
586  int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1));
587 
588  int total_bits = png_bit_depth;
589  if (!is_grayscale()) {
590  total_bits *= 3;
591  }
592  if (has_alpha()) {
593  total_bits += png_bit_depth;
594  }
595 
596  if (palette_bit_depth < total_bits ||
597  _maxval != (1 << true_bit_depth) - 1) {
598  if (pnmimage_png_cat.is_debug()) {
599  pnmimage_png_cat.debug()
600  << "palette bit depth of " << palette_bit_depth
601  << " improves on bit depth of " << total_bits
602  << "; making a palette image.\n";
603  }
604 
605  color_type = PNG_COLOR_TYPE_PALETTE;
606 
607  // Re-sort the palette to put the semitransparent pixels at the
608  // beginning.
609  sort(palette.begin(), palette.end(), LowAlphaCompare());
610 
611  double palette_scale = 255.0 / _maxval;
612 
613  int num_alpha = 0;
614  for (int i = 0; i < (int)palette.size(); i++) {
615  png_palette_table[i].red = (int)(palette[i]._red * palette_scale + 0.5);
616  png_palette_table[i].green = (int)(palette[i]._green * palette_scale + 0.5);
617  png_palette_table[i].blue = (int)(palette[i]._blue * palette_scale + 0.5);
618  png_trans[i] = (int)(palette[i]._alpha * palette_scale + 0.5);
619  if (palette[i]._alpha != _maxval) {
620  num_alpha = i + 1;
621  }
622 
623  // Also build a reverse-lookup from color to palette index in the
624  // "histogram" structure.
625  palette_lookup[palette[i]] = i;
626  }
627 
628  png_set_PLTE(_png, _info, png_palette_table, palette.size());
629  if (has_alpha()) {
630  if (pnmimage_png_cat.is_debug()) {
631  pnmimage_png_cat.debug()
632  << "palette contains " << num_alpha << " transparent entries.\n";
633  }
634  png_set_tRNS(_png, _info, png_trans, num_alpha, nullptr);
635  }
636  } else if (pnmimage_png_cat.is_debug()) {
637  pnmimage_png_cat.debug()
638  << "palette bit depth of " << palette_bit_depth
639  << " does not improve on bit depth of " << total_bits
640  << "; not making a palette image.\n";
641  }
642 
643  } else if (pnmimage_png_cat.is_debug()) {
644  pnmimage_png_cat.debug()
645  << "more than " << png_max_palette
646  << " colors found; not making a palette image.\n";
647  }
648  } else if (pnmimage_png_cat.is_debug()) {
649  pnmimage_png_cat.debug()
650  << "maxval exceeds 255; not making a palette image.\n";
651  }
652  } else if (pnmimage_png_cat.is_debug()) {
653  pnmimage_png_cat.debug()
654  << "palette images are not enabled.\n";
655  }
656 
657  if (pnmimage_png_cat.is_debug()) {
658  pnmimage_png_cat.debug()
659  << "width = " << _x_size << " height = " << _y_size
660  << " maxval = " << _maxval << " bit_depth = "
661  << png_bit_depth << " color_type = " << color_type
662  << " color_space = " << _color_space << "\n";
663  }
664 
665  png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth,
666  color_type, PNG_INTERLACE_NONE,
667  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
668 
669  // Set the true bit depth of the image data.
670  if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
671  png_set_sBIT(_png, _info, &sig_bit);
672  }
673 
674  // Set the color space, if we know it.
675  switch (_color_space) {
676  case CS_linear:
677  png_set_gAMA(_png, _info, 1.0);
678  // Not sure if we should set cHRM to anything.
679  break;
680 
681  case CS_sRGB:
682  png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
683  break;
684 
685  default:
686  break;
687  }
688 
689  png_write_info(_png, _info);
690 
691 
692  // Now set up the transformations to write the image data.
693  if (png_bit_depth < 8) {
694  png_set_packing(_png);
695  }
696 
697  double val_scale = 1.0;
698 
699  if (color_type != PNG_COLOR_TYPE_PALETTE) {
700  if (png_bit_depth != true_bit_depth) {
701  png_set_shift(_png, &sig_bit);
702  }
703  // Since this assumes that _maxval is one less than a power of 2, we set
704  // val_scale to the appropriate factor in case it is not.
705  int png_maxval = (1 << png_bit_depth) - 1;
706  val_scale = (double)png_maxval / (double)_maxval;
707  }
708 
709  int row_byte_length = _x_size * _num_channels;
710  if (png_bit_depth > 8) {
711  row_byte_length *= 2;
712  }
713 
714  int num_rows = _y_size;
715 
716  if (pnmimage_png_cat.is_debug()) {
717  pnmimage_png_cat.debug()
718  << "Allocating one row of " << row_byte_length
719  << " bytes.\n";
720  }
721 
722  // When writing, we only need to copy the image out one row at a time,
723  // because we don't mess around with writing interlaced files. If we were
724  // writing an interlaced file, we'd have to copy the whole image first.
725 
726  png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte));
727 
728  bool save_color = !is_grayscale();
729  bool save_alpha = has_alpha();
730 
731  if (val_scale == 1.0) {
732  // No scale needed; we're already a power of 2.
733  int pi = 0;
734  for (int yi = 0; yi < num_rows; yi++) {
735  png_bytep dest = row;
736 
737  if (color_type == PNG_COLOR_TYPE_PALETTE) {
738  for (int xi = 0; xi < _x_size; xi++) {
739  int index;
740 
741  if (save_color) {
742  if (save_alpha) {
743  index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
744  } else {
745  index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
746  }
747  } else {
748  if (save_alpha) {
749  index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
750  } else {
751  index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
752  }
753  }
754 
755  *dest++ = index;
756  pi++;
757  }
758 
759  } else if (png_bit_depth > 8) {
760  for (int xi = 0; xi < _x_size; xi++) {
761  if (save_color) {
762  xelval red = PPM_GETR(array[pi]);
763  *dest++ = (red >> 8) & 0xff;
764  *dest++ = red & 0xff;
765  xelval green = PPM_GETG(array[pi]);
766  *dest++ = (green >> 8) & 0xff;
767  *dest++ = green & 0xff;
768  }
769  xelval blue = PPM_GETB(array[pi]);
770  *dest++ = (blue >> 8) & 0xff;
771  *dest++ = blue & 0xff;
772 
773  if (save_alpha) {
774  xelval alpha = alpha_data[pi];
775  *dest++ = (alpha >> 8) & 0xff;
776  *dest++ = alpha & 0xff;
777  }
778  pi++;
779  }
780 
781  } else {
782  for (int xi = 0; xi < _x_size; xi++) {
783  if (save_color) {
784  *dest++ = PPM_GETR(array[pi]);
785  *dest++ = PPM_GETG(array[pi]);
786  }
787 
788  *dest++ = PPM_GETB(array[pi]);
789 
790  if (save_alpha) {
791  *dest++ = alpha_data[pi];
792  }
793  pi++;
794  }
795  }
796 
797  nassertr(dest <= row + row_byte_length, yi);
798  png_write_row(_png, row);
800  }
801  } else {
802  // Here we might need to scale each component to match the png
803  // requirement.
804  nassertr(color_type != PNG_COLOR_TYPE_PALETTE, 0);
805  int pi = 0;
806  for (int yi = 0; yi < num_rows; yi++) {
807  png_bytep dest = row;
808 
809  if (png_bit_depth > 8) {
810  for (int xi = 0; xi < _x_size; xi++) {
811  if (save_color) {
812  xelval red = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
813  *dest++ = (red >> 8) & 0xff;
814  *dest++ = red & 0xff;
815  xelval green = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
816  *dest++ = (green >> 8) & 0xff;
817  *dest++ = green & 0xff;
818  }
819  xelval blue = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
820  *dest++ = (blue >> 8) & 0xff;
821  *dest++ = blue & 0xff;
822 
823  if (save_alpha) {
824  xelval alpha = (xelval)(alpha_data[pi] * val_scale + 0.5);
825  *dest++ = (alpha >> 8) & 0xff;
826  *dest++ = alpha & 0xff;
827  }
828  pi++;
829  }
830 
831  } else {
832  for (int xi = 0; xi < _x_size; xi++) {
833  if (save_color) {
834  *dest++ = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
835  *dest++ = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
836  }
837 
838  *dest++ = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
839 
840  if (save_alpha) {
841  *dest++ = (xelval)(alpha_data[pi] * val_scale + 0.5);
842  }
843  pi++;
844  }
845  }
846 
847  nassertr(dest <= row + row_byte_length, yi);
848  png_write_row(_png, row);
850  }
851  }
852  PANDA_FREE_ARRAY(row);
853 
854  png_write_end(_png, nullptr);
855 
856  return _y_size;
857 }
858 
859 /**
860  * Releases the internal PNG structures and marks the writer invalid.
861  */
862 void PNMFileTypePNG::Writer::
863 free_png() {
864  if (_is_valid) {
865  png_destroy_write_struct(&_png, &_info);
866  _is_valid = false;
867  }
868 }
869 
870 /**
871  * Elevates the indicated bit depth to one of the legal PNG bit depths: 1, 2,
872  * 4, 8, or 16.
873  */
874 int PNMFileTypePNG::Writer::
875 make_png_bit_depth(int bit_depth) {
876  switch (bit_depth) {
877  case 0:
878  case 1:
879  return 1;
880 
881  case 2:
882  return 2;
883 
884  case 3:
885  case 4:
886  return 4;
887 
888  case 5:
889  case 6:
890  case 7:
891  case 8:
892  return 8;
893 
894  default:
895  return 16;
896  }
897 }
898 
899 /**
900  * A callback handler that PNG uses to write data to the iostream.
901  */
902 void PNMFileTypePNG::Writer::
903 png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
904  Writer *self = (Writer *)png_get_io_ptr(png_ptr);
905  self->_file->write((char *)data, length);
906  if (self->_file->fail()) {
907  pnmimage_png_cat.error()
908  << "Unable to write to the iostream.\n";
909  // Is there no way to indicate a write failure to libpng?
910  }
911 }
912 
913 /**
914  * A callback handler that PNG uses to write data to the iostream.
915  */
916 void PNMFileTypePNG::Writer::
917 png_flush_data(png_structp png_ptr) {
918  Writer *self = (Writer *)png_get_io_ptr(png_ptr);
919  self->_file->flush();
920 }
921 
922 /**
923  * This is our own warning handler. It is called by the png library to issue
924  * a warning message.
925  */
926 void PNMFileTypePNG::Writer::
927 png_warning(png_structp, png_const_charp warning_msg) {
928  pnmimage_png_cat.warning()
929  << warning_msg << "\n";
930 }
931 
932 /**
933  * This is our own error handler. It is called by the png library to issue a
934  * fatal error message.
935  */
936 void PNMFileTypePNG::Writer::
937 png_error(png_structp png_ptr, png_const_charp error_msg) {
938  pnmimage_png_cat.error()
939  << error_msg << "\n";
940 
941  // The PNG library insists we should not return, so instead of returning, we
942  // will do a longjmp out of the png code.
943  Writer *self = (Writer *)png_get_io_ptr(png_ptr);
944  if (self == nullptr) {
945  // Oops, we haven't got a self pointer yet. Return anyway and hope we'll
946  // be ok.
947  pnmimage_png_cat.error()
948  << "Returning before opening file.\n";
949  return;
950  }
951 
952  longjmp(self->_jmpbuf, true);
953 }
954 
955 
956 #endif // HAVE_PNG
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e....
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
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
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int pm_maxvaltobits(int maxval)
Returns the number of bits sufficient to hold the indicated maxval value.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.