28 static const char *
const extensions_png[] = {
31 static const int num_extensions_png =
sizeof(extensions_png) /
sizeof(
const char *);
35 static const int png_max_palette = 256;
39 class LowAlphaCompare {
43 if (a._alpha != b._alpha) {
44 return a._alpha < b._alpha;
57 if (pnmimage_png_cat->is_debug()) {
58 pnmimage_png_cat->debug()
59 <<
"PNG version " << PNG_LIBPNG_VER <<
"\n";
66 string PNMFileTypePNG::
76 get_num_extensions()
const {
77 return num_extensions_png;
84 string PNMFileTypePNG::
85 get_extension(
int n)
const {
86 nassertr(n >= 0 && n < num_extensions_png,
string());
87 return extensions_png[n];
94 string PNMFileTypePNG::
95 get_suggested_extension()
const {
103 bool PNMFileTypePNG::
104 has_magic_number()
const {
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;
124 make_reader(istream *file,
bool owns_file,
const string &magic_number) {
126 return new Reader(
this, file, owns_file, magic_number);
135 make_writer(ostream *file,
bool owns_file) {
137 return new Writer(
this, file, owns_file);
143 void PNMFileTypePNG::
144 register_with_read_factory() {
146 register_factory(get_class_type(), make_PNMFileTypePNG);
165 PNMFileTypePNG::Reader::
166 Reader(
PNMFileType *type, istream *file,
bool owns_file,
string magic_number) :
173 _png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
nullptr,
174 png_error, png_warning);
175 if (_png ==
nullptr) {
179 _info = png_create_info_struct(_png);
180 if (_info ==
nullptr) {
181 png_destroy_read_struct(&_png,
nullptr,
nullptr);
187 if (setjmp(_jmpbuf)) {
195 png_set_read_fn(_png, (
void *)
this, png_read_data);
196 png_set_sig_bytes(_png, magic_number.length());
198 png_read_info(_png, _info);
207 png_get_IHDR(_png, _info, &width, &height,
208 &bit_depth, &color_type,
nullptr,
nullptr,
nullptr);
211 if (png_get_sRGB(_png, _info, &srgb_intent) == PNG_INFO_sRGB) {
212 _color_space = CS_sRGB;
214 }
else if (png_get_gAMA(_png, _info, &gamma) == PNG_INFO_gAMA) {
216 if (gamma >= 0.99 && gamma <= 1.01) {
217 _color_space = CS_linear;
219 }
else if (gamma >= 0.44999 && gamma <= 0.455001) {
221 _color_space = CS_sRGB;
224 pnmimage_png_cat.warning()
225 <<
"Unsupported image gamma " << gamma <<
", " 226 <<
"please re-export image as sRGB or linear.\n";
230 pnmimage_png_cat.debug()
231 <<
"width = " << width <<
" height = " << height <<
" bit_depth = " 232 << bit_depth <<
" color_type = " << color_type
233 <<
" color_space = " << _color_space <<
"\n";
237 _maxval = (1 << bit_depth) - 1;
240 png_set_packing(_png);
243 switch (color_type) {
244 case PNG_COLOR_TYPE_GRAY:
245 pnmimage_png_cat.debug()
246 <<
"PNG_COLOR_TYPE_GRAY\n";
250 case PNG_COLOR_TYPE_GRAY_ALPHA:
251 pnmimage_png_cat.debug()
252 <<
"PNG_COLOR_TYPE_GRAY_ALPHA\n";
256 case PNG_COLOR_TYPE_RGB:
257 pnmimage_png_cat.debug()
258 <<
"PNG_COLOR_TYPE_RGB\n";
262 case PNG_COLOR_TYPE_RGB_ALPHA:
263 pnmimage_png_cat.debug()
264 <<
"PNG_COLOR_TYPE_RGB_ALPHA\n";
268 case PNG_COLOR_TYPE_PALETTE:
269 pnmimage_png_cat.debug()
270 <<
"PNG_COLOR_TYPE_PALETTE\n";
271 png_set_palette_to_rgb(_png);
277 pnmimage_png_cat.error()
278 <<
"Unsupported color type: " << color_type <<
"\n";
283 if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
284 png_set_tRNS_to_alpha(_png);
290 png_read_update_info(_png, _info);
296 PNMFileTypePNG::Reader::
310 int PNMFileTypePNG::Reader::
311 read_data(
xel *array, xelval *alpha_data) {
316 if (setjmp(_jmpbuf)) {
324 int row_byte_length = _x_size * _num_channels;
326 row_byte_length *= 2;
329 int num_rows = _y_size;
331 if (pnmimage_png_cat.is_debug()) {
332 pnmimage_png_cat.debug()
333 <<
"Allocating " << num_rows <<
" rows of " << row_byte_length
341 png_bytep *rows = (png_bytep *)alloca(num_rows *
sizeof(png_bytep));
344 png_byte *alloc = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length *
sizeof(png_byte) * num_rows);
345 for (yi = 0; yi < num_rows; yi++) {
346 rows[yi] = alloc + (row_byte_length *
sizeof(png_byte)) * yi;
349 png_read_image(_png, rows);
351 bool get_color = !is_grayscale();
352 bool get_alpha = has_alpha();
355 for (yi = 0; yi < num_rows; yi++) {
356 png_bytep source = rows[yi];
357 for (
int xi = 0; xi < _x_size; xi++) {
365 red = (source[0] << 8) | source[1];
368 green = (source[0] << 8) | source[1];
372 blue = (source[0] << 8) | source[1];
376 alpha = (source[0] << 8) | source[1];
398 PPM_ASSIGN(array[pi], red, green, blue);
400 alpha_data[pi] = alpha;
405 nassertr(source <= rows[yi] + row_byte_length, yi);
408 png_read_end(_png,
nullptr);
409 PANDA_FREE_ARRAY(alloc);
417 void PNMFileTypePNG::Reader::
420 png_destroy_read_struct(&_png, &_info,
nullptr);
428 void PNMFileTypePNG::Reader::
429 png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
430 Reader *
self = (Reader *)png_get_io_ptr(png_ptr);
431 self->_file->read((
char *)data, length);
432 if (length != (png_size_t)self->_file->gcount()) {
433 pnmimage_png_cat.error()
434 <<
"Didn't read enough bytes.\n";
444 void PNMFileTypePNG::Reader::
445 png_warning(png_structp, png_const_charp warning_msg) {
446 pnmimage_png_cat.warning()
447 << warning_msg <<
"\n";
454 void PNMFileTypePNG::Reader::
455 png_error(png_structp png_ptr, png_const_charp error_msg) {
456 pnmimage_png_cat.error()
457 << error_msg <<
"\n";
461 Reader *
self = (Reader *)png_get_io_ptr(png_ptr);
462 if (
self ==
nullptr) {
465 pnmimage_png_cat.error()
466 <<
"Returning before opening file.\n";
470 longjmp(self->_jmpbuf,
true);
476 PNMFileTypePNG::Writer::
477 Writer(
PNMFileType *type, ostream *file,
bool owns_file) :
484 _png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
485 png_error, png_warning);
486 if (_png ==
nullptr) {
490 _info = png_create_info_struct(_png);
491 if (_info ==
nullptr) {
492 png_destroy_write_struct(&_png,
nullptr);
502 PNMFileTypePNG::Writer::
516 int PNMFileTypePNG::Writer::
517 write_data(
xel *array, xelval *alpha_data) {
522 if (setjmp(_jmpbuf)) {
530 png_set_write_fn(_png, (
void *)
this, png_write_data, png_flush_data);
534 png_set_compression_level(_png, png_compression_level);
539 int png_bit_depth = make_png_bit_depth(true_bit_depth);
542 sig_bit.red = true_bit_depth;
543 sig_bit.green = true_bit_depth;
544 sig_bit.blue = true_bit_depth;
545 sig_bit.gray = true_bit_depth;
546 sig_bit.alpha = true_bit_depth;
550 if (!is_grayscale()) {
551 color_type |= PNG_COLOR_MASK_COLOR;
554 color_type |= PNG_COLOR_MASK_ALPHA;
562 HistMap palette_lookup;
563 png_color png_palette_table[png_max_palette];
564 png_byte png_trans[png_max_palette];
567 if (png_bit_depth <= 8) {
568 if (compute_palette(palette, array, alpha_data, png_max_palette)) {
569 pnmimage_png_cat.debug()
570 << palette.size() <<
" colors found.\n";
572 int palette_bit_depth = make_png_bit_depth(
pm_maxvaltobits(palette.size() - 1));
574 int total_bits = png_bit_depth;
575 if (!is_grayscale()) {
579 total_bits += png_bit_depth;
582 if (palette_bit_depth < total_bits ||
583 _maxval != (1 << true_bit_depth) - 1) {
584 pnmimage_png_cat.debug()
585 <<
"palette bit depth of " << palette_bit_depth
586 <<
" improves on bit depth of " << total_bits
587 <<
"; making a palette image.\n";
589 color_type = PNG_COLOR_TYPE_PALETTE;
593 sort(palette.begin(), palette.end(), LowAlphaCompare());
595 double palette_scale = 255.0 / _maxval;
598 for (
int i = 0; i < (int)palette.size(); i++) {
599 png_palette_table[i].red = (int)(palette[i]._red * palette_scale + 0.5);
600 png_palette_table[i].green = (int)(palette[i]._green * palette_scale + 0.5);
601 png_palette_table[i].blue = (int)(palette[i]._blue * palette_scale + 0.5);
602 png_trans[i] = (int)(palette[i]._alpha * palette_scale + 0.5);
603 if (palette[i]._alpha != _maxval) {
609 palette_lookup[palette[i]] = i;
612 png_set_PLTE(_png, _info, png_palette_table, palette.size());
614 pnmimage_png_cat.debug()
615 <<
"palette contains " << num_alpha <<
" transparent entries.\n";
616 png_set_tRNS(_png, _info, png_trans, num_alpha,
nullptr);
619 pnmimage_png_cat.debug()
620 <<
"palette bit depth of " << palette_bit_depth
621 <<
" does not improve on bit depth of " << total_bits
622 <<
"; not making a palette image.\n";
626 pnmimage_png_cat.debug()
627 <<
"more than " << png_max_palette
628 <<
" colors found; not making a palette image.\n";
631 pnmimage_png_cat.debug()
632 <<
"maxval exceeds 255; not making a palette image.\n";
635 pnmimage_png_cat.debug()
636 <<
"palette images are not enabled.\n";
639 pnmimage_png_cat.debug()
640 <<
"width = " << _x_size <<
" height = " << _y_size
641 <<
" maxval = " << _maxval <<
" bit_depth = " 642 << png_bit_depth <<
" color_type = " << color_type
643 <<
" color_space = " << _color_space <<
"\n";
645 png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth,
646 color_type, PNG_INTERLACE_NONE,
647 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
650 if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
651 png_set_sBIT(_png, _info, &sig_bit);
655 switch (_color_space) {
657 png_set_gAMA(_png, _info, 1.0);
662 png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
669 png_write_info(_png, _info);
673 if (png_bit_depth < 8) {
674 png_set_packing(_png);
677 double val_scale = 1.0;
679 if (color_type != PNG_COLOR_TYPE_PALETTE) {
680 if (png_bit_depth != true_bit_depth) {
681 png_set_shift(_png, &sig_bit);
685 int png_maxval = (1 << png_bit_depth) - 1;
686 val_scale = (double)png_maxval / (
double)_maxval;
689 int row_byte_length = _x_size * _num_channels;
690 if (png_bit_depth > 8) {
691 row_byte_length *= 2;
694 int num_rows = _y_size;
696 if (pnmimage_png_cat.is_debug()) {
697 pnmimage_png_cat.debug()
698 <<
"Allocating one row of " << row_byte_length
706 png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length *
sizeof(png_byte));
708 bool save_color = !is_grayscale();
709 bool save_alpha = has_alpha();
711 if (val_scale == 1.0) {
714 for (
int yi = 0; yi < num_rows; yi++) {
715 png_bytep dest = row;
717 if (color_type == PNG_COLOR_TYPE_PALETTE) {
718 for (
int xi = 0; xi < _x_size; xi++) {
723 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
725 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
729 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
731 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
739 }
else if (png_bit_depth > 8) {
740 for (
int xi = 0; xi < _x_size; xi++) {
742 xelval red = PPM_GETR(array[pi]);
743 *dest++ = (red >> 8) & 0xff;
744 *dest++ = red & 0xff;
745 xelval green = PPM_GETG(array[pi]);
746 *dest++ = (green >> 8) & 0xff;
747 *dest++ = green & 0xff;
749 xelval blue = PPM_GETB(array[pi]);
750 *dest++ = (blue >> 8) & 0xff;
751 *dest++ = blue & 0xff;
754 xelval alpha = alpha_data[pi];
755 *dest++ = (alpha >> 8) & 0xff;
756 *dest++ = alpha & 0xff;
762 for (
int xi = 0; xi < _x_size; xi++) {
764 *dest++ = PPM_GETR(array[pi]);
765 *dest++ = PPM_GETG(array[pi]);
768 *dest++ = PPM_GETB(array[pi]);
771 *dest++ = alpha_data[pi];
777 nassertr(dest <= row + row_byte_length, yi);
778 png_write_row(_png, row);
784 nassertr(color_type != PNG_COLOR_TYPE_PALETTE, 0);
786 for (
int yi = 0; yi < num_rows; yi++) {
787 png_bytep dest = row;
789 if (png_bit_depth > 8) {
790 for (
int xi = 0; xi < _x_size; xi++) {
792 xelval red = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
793 *dest++ = (red >> 8) & 0xff;
794 *dest++ = red & 0xff;
795 xelval green = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
796 *dest++ = (green >> 8) & 0xff;
797 *dest++ = green & 0xff;
799 xelval blue = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
800 *dest++ = (blue >> 8) & 0xff;
801 *dest++ = blue & 0xff;
804 xelval alpha = (xelval)(alpha_data[pi] * val_scale + 0.5);
805 *dest++ = (alpha >> 8) & 0xff;
806 *dest++ = alpha & 0xff;
812 for (
int xi = 0; xi < _x_size; xi++) {
814 *dest++ = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
815 *dest++ = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
818 *dest++ = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
821 *dest++ = (xelval)(alpha_data[pi] * val_scale + 0.5);
827 nassertr(dest <= row + row_byte_length, yi);
828 png_write_row(_png, row);
832 PANDA_FREE_ARRAY(row);
834 png_write_end(_png,
nullptr);
842 void PNMFileTypePNG::Writer::
845 png_destroy_write_struct(&_png, &_info);
854 int PNMFileTypePNG::Writer::
855 make_png_bit_depth(
int bit_depth) {
882 void PNMFileTypePNG::Writer::
883 png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
884 Writer *
self = (Writer *)png_get_io_ptr(png_ptr);
885 self->_file->write((
char *)data, length);
886 if (self->_file->fail()) {
887 pnmimage_png_cat.error()
888 <<
"Unable to write to the iostream.\n";
896 void PNMFileTypePNG::Writer::
897 png_flush_data(png_structp png_ptr) {
898 Writer *
self = (Writer *)png_get_io_ptr(png_ptr);
899 self->_file->flush();
906 void PNMFileTypePNG::Writer::
907 png_warning(png_structp, png_const_charp warning_msg) {
908 pnmimage_png_cat.warning()
909 << warning_msg <<
"\n";
916 void PNMFileTypePNG::Writer::
917 png_error(png_structp png_ptr, png_const_charp error_msg) {
918 pnmimage_png_cat.error()
919 << error_msg <<
"\n";
923 Writer *
self = (Writer *)png_get_io_ptr(png_ptr);
924 if (
self ==
nullptr) {
927 pnmimage_png_cat.error()
928 <<
"Returning before opening file.\n";
932 longjmp(self->_jmpbuf,
true);
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.
Base class for objects that can be written to and read from Bam files.
This is the base class of a family of classes that represent particular image file types that PNMImag...
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e....
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is an abstract base class that defines the interface for reading image files of various types.
This is an abstract base class that defines the interface for writing image files of various types.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
TypeHandle is the identifier used to differentiate C++ class types.