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