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.