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 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";
239 _maxval = (1 << bit_depth) - 1;
242 png_set_packing(_png);
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";
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";
262 case PNG_COLOR_TYPE_RGB:
263 if (pnmimage_png_cat.is_debug()) {
264 pnmimage_png_cat.debug()
265 <<
"PNG_COLOR_TYPE_RGB\n";
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";
278 case PNG_COLOR_TYPE_PALETTE:
279 if (pnmimage_png_cat.is_debug()) {
280 pnmimage_png_cat.debug()
281 <<
"PNG_COLOR_TYPE_PALETTE\n";
283 png_set_palette_to_rgb(_png);
289 pnmimage_png_cat.error()
290 <<
"Unsupported color type: " << color_type <<
"\n";
295 if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
296 png_set_tRNS_to_alpha(_png);
302 png_read_update_info(_png, _info);
308 PNMFileTypePNG::Reader::
322 int PNMFileTypePNG::Reader::
323 read_data(
xel *array, xelval *alpha_data) {
328 if (setjmp(_jmpbuf)) {
336 int row_byte_length = _x_size * _num_channels;
338 row_byte_length *= 2;
341 int num_rows = _y_size;
343 if (pnmimage_png_cat.is_debug()) {
344 pnmimage_png_cat.debug()
345 <<
"Allocating " << num_rows <<
" rows of " << row_byte_length
353 png_bytep *rows = (png_bytep *)alloca(num_rows *
sizeof(png_bytep));
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;
361 png_read_image(_png, rows);
363 bool get_color = !is_grayscale();
364 bool get_alpha = has_alpha();
367 for (yi = 0; yi < num_rows; yi++) {
368 png_bytep source = rows[yi];
369 for (
int xi = 0; xi < _x_size; xi++) {
377 red = (source[0] << 8) | source[1];
380 green = (source[0] << 8) | source[1];
384 blue = (source[0] << 8) | source[1];
388 alpha = (source[0] << 8) | source[1];
410 PPM_ASSIGN(array[pi], red, green, blue);
412 alpha_data[pi] = alpha;
417 nassertr(source <= rows[yi] + row_byte_length, yi);
420 png_read_end(_png,
nullptr);
421 PANDA_FREE_ARRAY(alloc);
429 void PNMFileTypePNG::Reader::
432 png_destroy_read_struct(&_png, &_info,
nullptr);
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";
456 void PNMFileTypePNG::Reader::
457 png_warning(png_structp, png_const_charp warning_msg) {
458 pnmimage_png_cat.warning()
459 << warning_msg <<
"\n";
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";
473 Reader *
self = (Reader *)png_get_io_ptr(png_ptr);
474 if (
self ==
nullptr) {
477 pnmimage_png_cat.error()
478 <<
"Returning before opening file.\n";
482 longjmp(self->_jmpbuf,
true);
488 PNMFileTypePNG::Writer::
489 Writer(
PNMFileType *type, ostream *file,
bool owns_file) :
496 _png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
nullptr,
497 png_error, png_warning);
498 if (_png ==
nullptr) {
502 _info = png_create_info_struct(_png);
503 if (_info ==
nullptr) {
504 png_destroy_write_struct(&_png,
nullptr);
514 PNMFileTypePNG::Writer::
528 int PNMFileTypePNG::Writer::
529 write_data(
xel *array, xelval *alpha_data) {
534 if (setjmp(_jmpbuf)) {
542 png_set_write_fn(_png, (
void *)
this, png_write_data, png_flush_data);
546 png_set_compression_level(_png, png_compression_level);
551 int png_bit_depth = make_png_bit_depth(true_bit_depth);
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;
562 if (!is_grayscale()) {
563 color_type |= PNG_COLOR_MASK_COLOR;
566 color_type |= PNG_COLOR_MASK_ALPHA;
574 HistMap palette_lookup;
575 png_color png_palette_table[png_max_palette];
576 png_byte png_trans[png_max_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";
586 int palette_bit_depth = make_png_bit_depth(
pm_maxvaltobits(palette.size() - 1));
588 int total_bits = png_bit_depth;
589 if (!is_grayscale()) {
593 total_bits += png_bit_depth;
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";
605 color_type = PNG_COLOR_TYPE_PALETTE;
609 sort(palette.begin(), palette.end(), LowAlphaCompare());
611 double palette_scale = 255.0 / _maxval;
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) {
625 palette_lookup[palette[i]] = i;
628 png_set_PLTE(_png, _info, png_palette_table, palette.size());
630 if (pnmimage_png_cat.is_debug()) {
631 pnmimage_png_cat.debug()
632 <<
"palette contains " << num_alpha <<
" transparent entries.\n";
634 png_set_tRNS(_png, _info, png_trans, num_alpha,
nullptr);
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";
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";
648 }
else if (pnmimage_png_cat.is_debug()) {
649 pnmimage_png_cat.debug()
650 <<
"maxval exceeds 255; not making a palette image.\n";
652 }
else if (pnmimage_png_cat.is_debug()) {
653 pnmimage_png_cat.debug()
654 <<
"palette images are not enabled.\n";
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";
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);
670 if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
671 png_set_sBIT(_png, _info, &sig_bit);
675 switch (_color_space) {
677 png_set_gAMA(_png, _info, 1.0);
682 png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
689 png_write_info(_png, _info);
693 if (png_bit_depth < 8) {
694 png_set_packing(_png);
697 double val_scale = 1.0;
699 if (color_type != PNG_COLOR_TYPE_PALETTE) {
700 if (png_bit_depth != true_bit_depth) {
701 png_set_shift(_png, &sig_bit);
705 int png_maxval = (1 << png_bit_depth) - 1;
706 val_scale = (double)png_maxval / (
double)_maxval;
709 int row_byte_length = _x_size * _num_channels;
710 if (png_bit_depth > 8) {
711 row_byte_length *= 2;
714 int num_rows = _y_size;
716 if (pnmimage_png_cat.is_debug()) {
717 pnmimage_png_cat.debug()
718 <<
"Allocating one row of " << row_byte_length
726 png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length *
sizeof(png_byte));
728 bool save_color = !is_grayscale();
729 bool save_alpha = has_alpha();
731 if (val_scale == 1.0) {
734 for (
int yi = 0; yi < num_rows; yi++) {
735 png_bytep dest = row;
737 if (color_type == PNG_COLOR_TYPE_PALETTE) {
738 for (
int xi = 0; xi < _x_size; xi++) {
743 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
745 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
749 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
751 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
759 }
else if (png_bit_depth > 8) {
760 for (
int xi = 0; xi < _x_size; xi++) {
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;
769 xelval blue = PPM_GETB(array[pi]);
770 *dest++ = (blue >> 8) & 0xff;
771 *dest++ = blue & 0xff;
774 xelval alpha = alpha_data[pi];
775 *dest++ = (alpha >> 8) & 0xff;
776 *dest++ = alpha & 0xff;
782 for (
int xi = 0; xi < _x_size; xi++) {
784 *dest++ = PPM_GETR(array[pi]);
785 *dest++ = PPM_GETG(array[pi]);
788 *dest++ = PPM_GETB(array[pi]);
791 *dest++ = alpha_data[pi];
797 nassertr(dest <= row + row_byte_length, yi);
798 png_write_row(_png, row);
804 nassertr(color_type != PNG_COLOR_TYPE_PALETTE, 0);
806 for (
int yi = 0; yi < num_rows; yi++) {
807 png_bytep dest = row;
809 if (png_bit_depth > 8) {
810 for (
int xi = 0; xi < _x_size; xi++) {
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;
819 xelval blue = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
820 *dest++ = (blue >> 8) & 0xff;
821 *dest++ = blue & 0xff;
824 xelval alpha = (xelval)(alpha_data[pi] * val_scale + 0.5);
825 *dest++ = (alpha >> 8) & 0xff;
826 *dest++ = alpha & 0xff;
832 for (
int xi = 0; xi < _x_size; xi++) {
834 *dest++ = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
835 *dest++ = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
838 *dest++ = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
841 *dest++ = (xelval)(alpha_data[pi] * val_scale + 0.5);
847 nassertr(dest <= row + row_byte_length, yi);
848 png_write_row(_png, row);
852 PANDA_FREE_ARRAY(row);
854 png_write_end(_png,
nullptr);
862 void PNMFileTypePNG::Writer::
865 png_destroy_write_struct(&_png, &_info);
874 int PNMFileTypePNG::Writer::
875 make_png_bit_depth(
int bit_depth) {
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";
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();
926 void PNMFileTypePNG::Writer::
927 png_warning(png_structp, png_const_charp warning_msg) {
928 pnmimage_png_cat.warning()
929 << warning_msg <<
"\n";
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";
943 Writer *
self = (Writer *)png_get_io_ptr(png_ptr);
944 if (
self ==
nullptr) {
947 pnmimage_png_cat.error()
948 <<
"Returning before opening file.\n";
952 longjmp(self->_jmpbuf,
true);