15 #include "pnmFileTypePNG.h"
19 #include "config_pnmimagetypes.h"
21 #include "pnmFileTypeRegistry.h"
22 #include "bamReader.h"
25 static const char *
const extensions_png[] = {
28 static const int num_extensions_png =
sizeof(extensions_png) /
sizeof(
const char *);
32 static const int png_max_palette = 256;
36 class LowAlphaCompare {
40 if (a._alpha != b._alpha) {
41 return a._alpha < b._alpha;
56 if (pnmimage_png_cat->is_debug()) {
57 pnmimage_png_cat->debug()
58 <<
"PNG version " << PNG_LIBPNG_VER <<
"\n";
67 string PNMFileTypePNG::
79 get_num_extensions()
const {
80 return num_extensions_png;
90 string PNMFileTypePNG::
91 get_extension(
int n)
const {
92 nassertr(n >= 0 && n < num_extensions_png,
string());
93 return extensions_png[n];
103 string PNMFileTypePNG::
104 get_suggested_extension()
const {
114 bool PNMFileTypePNG::
115 has_magic_number()
const {
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;
139 make_reader(istream *file,
bool owns_file,
const string &magic_number) {
141 return new Reader(
this, file, owns_file, magic_number);
152 make_writer(ostream *file,
bool owns_file) {
154 return new Writer(
this, file, owns_file);
163 void PNMFileTypePNG::
164 register_with_read_factory() {
166 register_factory(get_class_type(), make_PNMFileTypePNG);
191 PNMFileTypePNG::Reader::
192 Reader(
PNMFileType *type, istream *file,
bool owns_file,
string magic_number) :
199 _png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
200 png_error, png_warning);
205 _info = png_create_info_struct(_png);
207 png_destroy_read_struct(&_png, NULL, NULL);
213 if (setjmp(_jmpbuf)) {
221 png_set_read_fn(_png, (
void *)
this, png_read_data);
222 png_set_sig_bytes(_png, magic_number.length());
224 png_read_info(_png, _info);
233 png_get_IHDR(_png, _info, &width, &height,
234 &bit_depth, &color_type, NULL, NULL, NULL);
237 if (png_get_sRGB(_png, _info, &srgb_intent) == PNG_INFO_sRGB) {
238 _color_space = CS_sRGB;
240 }
else if (png_get_gAMA(_png, _info, &gamma) == PNG_INFO_gAMA) {
242 if (gamma >= 0.99 && gamma <= 1.01) {
243 _color_space = CS_linear;
245 }
else if (gamma >= 0.44999 && gamma <= 0.455001) {
247 _color_space = CS_sRGB;
250 pnmimage_png_cat.warning()
251 <<
"Unsupported image gamma " << gamma <<
", "
252 <<
"please re-export image as sRGB or linear.\n";
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";
263 _maxval = (1 << bit_depth) - 1;
266 png_set_packing(_png);
269 switch (color_type) {
270 case PNG_COLOR_TYPE_GRAY:
271 pnmimage_png_cat.debug()
272 <<
"PNG_COLOR_TYPE_GRAY\n";
276 case PNG_COLOR_TYPE_GRAY_ALPHA:
277 pnmimage_png_cat.debug()
278 <<
"PNG_COLOR_TYPE_GRAY_ALPHA\n";
282 case PNG_COLOR_TYPE_RGB:
283 pnmimage_png_cat.debug()
284 <<
"PNG_COLOR_TYPE_RGB\n";
288 case PNG_COLOR_TYPE_RGB_ALPHA:
289 pnmimage_png_cat.debug()
290 <<
"PNG_COLOR_TYPE_RGB_ALPHA\n";
294 case PNG_COLOR_TYPE_PALETTE:
295 pnmimage_png_cat.debug()
296 <<
"PNG_COLOR_TYPE_PALETTE\n";
297 png_set_palette_to_rgb(_png);
303 pnmimage_png_cat.error()
304 <<
"Unsupported color type: " << color_type <<
"\n";
309 if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
310 png_set_tRNS_to_alpha(_png);
316 png_read_update_info(_png, _info);
324 PNMFileTypePNG::Reader::
342 int PNMFileTypePNG::Reader::
343 read_data(
xel *array, xelval *alpha_data) {
348 if (setjmp(_jmpbuf)) {
356 int row_byte_length = _x_size * _num_channels;
358 row_byte_length *= 2;
361 int num_rows = _y_size;
363 if (pnmimage_png_cat.is_debug()) {
364 pnmimage_png_cat.debug()
365 <<
"Allocating " << num_rows <<
" rows of " << row_byte_length
373 png_bytep *rows = (png_bytep *)PANDA_MALLOC_ARRAY(num_rows *
sizeof(png_bytep));
376 for (yi = 0; yi < num_rows; yi++) {
377 rows[yi] = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length *
sizeof(png_byte));
380 png_read_image(_png, rows);
382 bool get_color = !is_grayscale();
383 bool get_alpha = has_alpha();
386 for (yi = 0; yi < num_rows; yi++) {
387 png_bytep source = rows[yi];
388 for (
int xi = 0; xi < _x_size; xi++) {
396 red = (source[0] << 8) | source[1];
399 green = (source[0] << 8) | source[1];
403 blue = (source[0] << 8) | source[1];
407 alpha = (source[0] << 8) | source[1];
429 PPM_ASSIGN(array[pi], red, green, blue);
431 alpha_data[pi] = alpha;
436 nassertr(source <= rows[yi] + row_byte_length, yi);
437 PANDA_FREE_ARRAY(rows[yi]);
440 PANDA_FREE_ARRAY(rows);
442 png_read_end(_png, NULL);
453 void PNMFileTypePNG::Reader::
456 png_destroy_read_struct(&_png, &_info, NULL);
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";
485 void PNMFileTypePNG::Reader::
486 png_warning(png_structp, png_const_charp warning_msg) {
487 pnmimage_png_cat.warning()
488 << warning_msg <<
"\n";
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";
504 Reader *
self = (Reader *)png_get_io_ptr(png_ptr);
505 if (
self == (Reader *)NULL) {
508 pnmimage_png_cat.error()
509 <<
"Returning before opening file.\n";
513 longjmp(self->_jmpbuf,
true);
521 PNMFileTypePNG::Writer::
522 Writer(
PNMFileType *type, ostream *file,
bool owns_file) :
529 _png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
530 png_error, png_warning);
535 _info = png_create_info_struct(_png);
537 png_destroy_write_struct(&_png, NULL);
549 PNMFileTypePNG::Writer::
567 int PNMFileTypePNG::Writer::
568 write_data(
xel *array, xelval *alpha_data) {
573 if (setjmp(_jmpbuf)) {
581 png_set_write_fn(_png, (
void *)
this, png_write_data, png_flush_data);
585 int true_bit_depth = pm_maxvaltobits(_maxval);
586 int png_bit_depth = make_png_bit_depth(true_bit_depth);
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;
597 if (!is_grayscale()) {
598 color_type |= PNG_COLOR_MASK_COLOR;
601 color_type |= PNG_COLOR_MASK_ALPHA;
610 HistMap palette_lookup;
611 png_color png_palette_table[png_max_palette];
612 png_byte png_trans[png_max_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";
620 int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1));
622 int total_bits = png_bit_depth;
623 if (!is_grayscale()) {
627 total_bits += png_bit_depth;
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";
637 color_type = PNG_COLOR_TYPE_PALETTE;
641 sort(palette.begin(), palette.end(), LowAlphaCompare());
643 double palette_scale = 255.0 / _maxval;
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) {
657 palette_lookup[palette[i]] = i;
660 png_set_PLTE(_png, _info, png_palette_table, palette.size());
662 pnmimage_png_cat.debug()
663 <<
"palette contains " << num_alpha <<
" transparent entries.\n";
664 png_set_tRNS(_png, _info, png_trans, num_alpha, NULL);
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";
674 pnmimage_png_cat.debug()
675 <<
"more than " << png_max_palette
676 <<
" colors found; not making a palette image.\n";
679 pnmimage_png_cat.debug()
680 <<
"maxval exceeds 255; not making a palette image.\n";
683 pnmimage_png_cat.debug()
684 <<
"palette images are not enabled.\n";
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";
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);
698 if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
699 png_set_sBIT(_png, _info, &sig_bit);
703 switch (_color_space) {
705 png_set_gAMA(_png, _info, 1.0);
710 png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
717 png_write_info(_png, _info);
721 if (png_bit_depth < 8) {
722 png_set_packing(_png);
725 double val_scale = 1.0;
727 if (color_type != PNG_COLOR_TYPE_PALETTE) {
728 if (png_bit_depth != true_bit_depth) {
729 png_set_shift(_png, &sig_bit);
733 int png_maxval = (1 << png_bit_depth) - 1;
734 val_scale = (double)png_maxval / (
double)_maxval;
737 int row_byte_length = _x_size * _num_channels;
738 if (png_bit_depth > 8) {
739 row_byte_length *= 2;
742 int num_rows = _y_size;
744 if (pnmimage_png_cat.is_debug()) {
745 pnmimage_png_cat.debug()
746 <<
"Allocating one row of " << row_byte_length
755 png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length *
sizeof(png_byte));
757 bool save_color = !is_grayscale();
758 bool save_alpha = has_alpha();
760 if (val_scale == 1.0) {
763 for (
int yi = 0; yi < num_rows; yi++) {
764 png_bytep dest = row;
766 if (color_type == PNG_COLOR_TYPE_PALETTE) {
767 for (
int xi = 0; xi < _x_size; xi++) {
772 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
774 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
778 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
780 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
788 }
else if (png_bit_depth > 8) {
789 for (
int xi = 0; xi < _x_size; xi++) {
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;
798 xelval blue = PPM_GETB(array[pi]);
799 *dest++ = (blue >> 8) & 0xff;
800 *dest++ = blue & 0xff;
803 xelval alpha = alpha_data[pi];
804 *dest++ = (alpha >> 8) & 0xff;
805 *dest++ = alpha & 0xff;
811 for (
int xi = 0; xi < _x_size; xi++) {
813 *dest++ = PPM_GETR(array[pi]);
814 *dest++ = PPM_GETG(array[pi]);
817 *dest++ = PPM_GETB(array[pi]);
820 *dest++ = alpha_data[pi];
826 nassertr(dest <= row + row_byte_length, yi);
827 png_write_row(_png, row);
833 nassertr(color_type != PNG_COLOR_TYPE_PALETTE, 0);
835 for (
int yi = 0; yi < num_rows; yi++) {
836 png_bytep dest = row;
838 if (png_bit_depth > 8) {
839 for (
int xi = 0; xi < _x_size; xi++) {
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;
848 xelval blue = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
849 *dest++ = (blue >> 8) & 0xff;
850 *dest++ = blue & 0xff;
853 xelval alpha = (xelval)(alpha_data[pi] * val_scale + 0.5);
854 *dest++ = (alpha >> 8) & 0xff;
855 *dest++ = alpha & 0xff;
861 for (
int xi = 0; xi < _x_size; xi++) {
863 *dest++ = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
864 *dest++ = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
867 *dest++ = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
870 *dest++ = (xelval)(alpha_data[pi] * val_scale + 0.5);
876 nassertr(dest <= row + row_byte_length, yi);
877 png_write_row(_png, row);
881 PANDA_FREE_ARRAY(row);
883 png_write_end(_png, NULL);
894 void PNMFileTypePNG::Writer::
897 png_destroy_write_struct(&_png, &_info);
908 int PNMFileTypePNG::Writer::
909 make_png_bit_depth(
int bit_depth) {
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";
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();
968 void PNMFileTypePNG::Writer::
969 png_warning(png_structp, png_const_charp warning_msg) {
970 pnmimage_png_cat.warning()
971 << warning_msg <<
"\n";
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";
987 Writer *
self = (Writer *)png_get_io_ptr(png_ptr);
988 if (
self == (Writer *)NULL) {
991 pnmimage_png_cat.error()
992 <<
"Returning before opening file.\n";
996 longjmp(self->_jmpbuf,
true);
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e...
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...
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
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...
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.