28static const char *
const extensions_png[] = {
31static const int num_extensions_png =
sizeof(extensions_png) /
sizeof(
const char *);
35static const int png_max_palette = 256;
39class 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";
66string PNMFileTypePNG::
76get_num_extensions()
const {
77 return num_extensions_png;
84string PNMFileTypePNG::
85get_extension(
int n)
const {
86 nassertr(n >= 0 && n < num_extensions_png,
string());
87 return extensions_png[n];
94string PNMFileTypePNG::
95get_suggested_extension()
const {
104has_magic_number()
const {
114matches_magic_number(
const string &magic_number)
const {
115 return png_sig_cmp((png_bytep)magic_number.data(), 0, magic_number.length()) == 0;
124make_reader(istream *file,
bool owns_file,
const string &magic_number) {
126 return new Reader(
this, file, owns_file, magic_number);
135make_writer(ostream *file,
bool owns_file) {
137 return new Writer(
this, file, owns_file);
144register_with_read_factory() {
146 register_factory(get_class_type(), make_PNMFileTypePNG);
165PNMFileTypePNG::Reader::
166Reader(
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);
308PNMFileTypePNG::Reader::
322int PNMFileTypePNG::Reader::
323read_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);
429void PNMFileTypePNG::Reader::
432 png_destroy_read_struct(&_png, &_info,
nullptr);
440void PNMFileTypePNG::Reader::
441png_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";
456void (PNMFileTypePNG::Reader::
457png_warning)(png_structp, png_const_charp warning_msg) {
458 pnmimage_png_cat.warning()
459 << warning_msg <<
"\n";
466void (PNMFileTypePNG::Reader::
467png_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);
488PNMFileTypePNG::Writer::
489Writer(
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);
514PNMFileTypePNG::Writer::
528int PNMFileTypePNG::Writer::
529write_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);
862void PNMFileTypePNG::Writer::
865 png_destroy_write_struct(&_png, &_info);
874int PNMFileTypePNG::Writer::
875make_png_bit_depth(
int bit_depth) {
902void PNMFileTypePNG::Writer::
903png_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";
916void PNMFileTypePNG::Writer::
917png_flush_data(png_structp png_ptr) {
918 Writer *self = (Writer *)png_get_io_ptr(png_ptr);
919 self->_file->flush();
926void (PNMFileTypePNG::Writer::
927png_warning)(png_structp, png_const_charp warning_msg) {
928 pnmimage_png_cat.warning()
929 << warning_msg <<
"\n";
936void (PNMFileTypePNG::Writer::
937png_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);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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...
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 void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
TypeHandle is the identifier used to differentiate C++ class types.
Base class for objects that can be written to and read from Bam files.
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.