Panda3D

pnmFileTypePNG.cxx

00001 // Filename: pnmFileTypePNG.cxx
00002 // Created by:  drose (16Mar04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "pnmFileTypePNG.h"
00016 
00017 #ifdef HAVE_PNG
00018 
00019 #include "config_pnmimagetypes.h"
00020 
00021 #include "pnmFileTypeRegistry.h"
00022 #include "bamReader.h"
00023 #include "thread.h"
00024 
00025 static const char * const extensions_png[] = {
00026   "png"
00027 };
00028 static const int num_extensions_png = sizeof(extensions_png) / sizeof(const char *);
00029 
00030 TypeHandle PNMFileTypePNG::_type_handle;
00031 
00032 static const int png_max_palette = 256;
00033 
00034 // This STL comparison functor is used in write_data(), below.  It
00035 // sorts the non-maxval alpha pixels to the front of the list.
00036 class LowAlphaCompare {
00037 public:
00038   bool operator() (const PNMImageHeader::PixelSpec &a, 
00039                    const PNMImageHeader::PixelSpec &b) {
00040     if (a._alpha != b._alpha) {
00041       return a._alpha < b._alpha;
00042     }
00043     return a < b;
00044   }
00045 };
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: PNMFileTypePNG::Constructor
00049 //       Access: Public
00050 //  Description:
00051 ////////////////////////////////////////////////////////////////////
00052 PNMFileTypePNG::
00053 PNMFileTypePNG() {
00054   // This constructor may run at static init time, so we use the ->
00055   // dereferencing convention on the notify category.
00056   if (pnmimage_png_cat->is_debug()) {
00057     pnmimage_png_cat->debug()
00058       << "PNG version " << PNG_LIBPNG_VER << "\n";
00059   }
00060 }
00061 
00062 ////////////////////////////////////////////////////////////////////
00063 //     Function: PNMFileTypePNG::get_name
00064 //       Access: Public, Virtual
00065 //  Description: Returns a few words describing the file type.
00066 ////////////////////////////////////////////////////////////////////
00067 string PNMFileTypePNG::
00068 get_name() const {
00069   return "PNG";
00070 }
00071 
00072 ////////////////////////////////////////////////////////////////////
00073 //     Function: PNMFileTypePNG::get_num_extensions
00074 //       Access: Public, Virtual
00075 //  Description: Returns the number of different possible filename
00076 //               extensions associated with this particular file type.
00077 ////////////////////////////////////////////////////////////////////
00078 int PNMFileTypePNG::
00079 get_num_extensions() const {
00080   return num_extensions_png;
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: PNMFileTypePNG::get_extension
00085 //       Access: Public, Virtual
00086 //  Description: Returns the nth possible filename extension
00087 //               associated with this particular file type, without a
00088 //               leading dot.
00089 ////////////////////////////////////////////////////////////////////
00090 string PNMFileTypePNG::
00091 get_extension(int n) const {
00092   nassertr(n >= 0 && n < num_extensions_png, string());
00093   return extensions_png[n];
00094 }
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: PNMFileTypePNG::get_suggested_extension
00098 //       Access: Public, Virtual
00099 //  Description: Returns a suitable filename extension (without a
00100 //               leading dot) to suggest for files of this type, or
00101 //               empty string if no suggestions are available.
00102 ////////////////////////////////////////////////////////////////////
00103 string PNMFileTypePNG::
00104 get_suggested_extension() const {
00105   return "png";
00106 }
00107 
00108 ////////////////////////////////////////////////////////////////////
00109 //     Function: PNMFileTypePNG::has_magic_number
00110 //       Access: Public, Virtual
00111 //  Description: Returns true if this particular file type uses a
00112 //               magic number to identify it, false otherwise.
00113 ////////////////////////////////////////////////////////////////////
00114 bool PNMFileTypePNG::
00115 has_magic_number() const {
00116   return true;
00117 }
00118 
00119 ////////////////////////////////////////////////////////////////////
00120 //     Function: PNMFileTypePNG::matches_magic_number
00121 //       Access: Public, Virtual
00122 //  Description: Returns true if the indicated "magic number" byte
00123 //               stream (the initial few bytes read from the file)
00124 //               matches this particular file type, false otherwise.
00125 ////////////////////////////////////////////////////////////////////
00126 bool PNMFileTypePNG::
00127 matches_magic_number(const string &magic_number) const {
00128   return png_sig_cmp((png_bytep)magic_number.data(), 0, magic_number.length()) == 0;
00129 }
00130 
00131 ////////////////////////////////////////////////////////////////////
00132 //     Function: PNMFileTypePNG::make_reader
00133 //       Access: Public, Virtual
00134 //  Description: Allocates and returns a new PNMReader suitable for
00135 //               reading from this file type, if possible.  If reading
00136 //               from this file type is not supported, returns NULL.
00137 ////////////////////////////////////////////////////////////////////
00138 PNMReader *PNMFileTypePNG::
00139 make_reader(istream *file, bool owns_file, const string &magic_number) {
00140   init_pnm();
00141   return new Reader(this, file, owns_file, magic_number);
00142 }
00143 
00144 ////////////////////////////////////////////////////////////////////
00145 //     Function: PNMFileTypePNG::make_writer
00146 //       Access: Public, Virtual
00147 //  Description: Allocates and returns a new PNMWriter suitable for
00148 //               reading from this file type, if possible.  If writing
00149 //               files of this type is not supported, returns NULL.
00150 ////////////////////////////////////////////////////////////////////
00151 PNMWriter *PNMFileTypePNG::
00152 make_writer(ostream *file, bool owns_file) {
00153   init_pnm();
00154   return new Writer(this, file, owns_file);
00155 }
00156 
00157 ////////////////////////////////////////////////////////////////////
00158 //     Function: PNMFileTypePNG::register_with_read_factory
00159 //       Access: Public, Static
00160 //  Description: Registers the current object as something that can be
00161 //               read from a Bam file.
00162 ////////////////////////////////////////////////////////////////////
00163 void PNMFileTypePNG::
00164 register_with_read_factory() {
00165   BamReader::get_factory()->
00166     register_factory(get_class_type(), make_PNMFileTypePNG);
00167 }
00168 
00169 ////////////////////////////////////////////////////////////////////
00170 //     Function: PNMFileTypePNG::make_PNMFileTypePNG
00171 //       Access: Protected, Static
00172 //  Description: This method is called by the BamReader when an object
00173 //               of this type is encountered in a Bam file; it should
00174 //               allocate and return a new object with all the data
00175 //               read.
00176 //
00177 //               In the case of the PNMFileType objects, since these
00178 //               objects are all shared, we just pull the object from
00179 //               the registry.
00180 ////////////////////////////////////////////////////////////////////
00181 TypedWritable *PNMFileTypePNG::
00182 make_PNMFileTypePNG(const FactoryParams &params) {
00183   return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
00184 }
00185 
00186 ////////////////////////////////////////////////////////////////////
00187 //     Function: PNMFileTypePNG::Reader::Constructor
00188 //       Access: Public
00189 //  Description:
00190 ////////////////////////////////////////////////////////////////////
00191 PNMFileTypePNG::Reader::
00192 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
00193   PNMReader(type, file, owns_file)
00194 {
00195   _png = NULL;
00196   _info = NULL;
00197   _is_valid = false;
00198 
00199   _png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
00200                                 png_error, png_warning);
00201   if (_png == NULL) {
00202     return;
00203   }
00204 
00205   _info = png_create_info_struct(_png);
00206   if (_info == NULL) {
00207     png_destroy_read_struct(&_png, NULL, NULL);
00208     return;
00209   }
00210 
00211   _is_valid = true;
00212 
00213   if (setjmp(_jmpbuf)) {
00214     // This is the ANSI C way to handle exceptions.  If setjmp(),
00215     // above, returns true, it means that libpng detected an exception
00216     // while executing the code that reads the header info, below.
00217     free_png();
00218     return;
00219   }
00220 
00221   png_set_read_fn(_png, (void *)this, png_read_data);
00222   png_set_sig_bytes(_png, magic_number.length());
00223 
00224   png_read_info(_png, _info);
00225 
00226   png_uint_32 width;
00227   png_uint_32 height;
00228   int bit_depth;
00229   int color_type;
00230 
00231   png_get_IHDR(_png, _info, &width, &height,
00232                &bit_depth, &color_type, NULL, NULL, NULL);
00233 
00234   pnmimage_png_cat.debug()
00235     << "width = " << width << " height = " << height << " bit_depth = "
00236     << bit_depth << " color_type = " << color_type << "\n";
00237 
00238   _x_size = width;
00239   _y_size = height;
00240   _maxval = ( 1 << bit_depth ) - 1;
00241 
00242   if (bit_depth < 8) {
00243     png_set_packing(_png);
00244   }
00245 
00246   switch (color_type) {
00247   case PNG_COLOR_TYPE_GRAY:
00248     pnmimage_png_cat.debug()
00249       << "PNG_COLOR_TYPE_GRAY\n";
00250     _num_channels = 1;
00251     break;
00252 
00253   case PNG_COLOR_TYPE_GRAY_ALPHA:
00254     pnmimage_png_cat.debug()
00255       << "PNG_COLOR_TYPE_GRAY_ALPHA\n";
00256     _num_channels = 2;
00257     break;
00258 
00259   case PNG_COLOR_TYPE_RGB:
00260     pnmimage_png_cat.debug()
00261       << "PNG_COLOR_TYPE_RGB\n";
00262     _num_channels = 3;
00263     break;
00264 
00265   case PNG_COLOR_TYPE_RGB_ALPHA:
00266     pnmimage_png_cat.debug()
00267       << "PNG_COLOR_TYPE_RGB_ALPHA\n";
00268     _num_channels = 4;
00269     break;
00270 
00271   case PNG_COLOR_TYPE_PALETTE:
00272     pnmimage_png_cat.debug()
00273       << "PNG_COLOR_TYPE_PALETTE\n";
00274     png_set_palette_to_rgb(_png);
00275     _maxval = 255;
00276     _num_channels = 3;
00277     break;
00278 
00279   default:
00280     pnmimage_png_cat.error()
00281       << "Unsupported color type: " << color_type << "\n";
00282     free_png();
00283     return;
00284   }
00285 
00286   if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
00287     png_set_tRNS_to_alpha(_png);
00288     if (!has_alpha()) {
00289       _num_channels++;
00290     }
00291   }
00292 
00293   png_read_update_info(_png, _info);
00294 }
00295 
00296 ////////////////////////////////////////////////////////////////////
00297 //     Function: PNMFileTypePNG::Reader::Destructor
00298 //       Access: Public, Virtual
00299 //  Description:
00300 ////////////////////////////////////////////////////////////////////
00301 PNMFileTypePNG::Reader::
00302 ~Reader() {
00303   free_png();
00304 }
00305 
00306 ////////////////////////////////////////////////////////////////////
00307 //     Function: PNMFileTypePNG::Reader::read_data
00308 //       Access: Public, Virtual
00309 //  Description: Reads in an entire image all at once, storing it in
00310 //               the pre-allocated _x_size * _y_size array and alpha
00311 //               pointers.  (If the image type has no alpha channel,
00312 //               alpha is ignored.)  Returns the number of rows
00313 //               correctly read.
00314 //
00315 //               Derived classes need not override this if they
00316 //               instead provide supports_read_row() and read_row(),
00317 //               below.
00318 ////////////////////////////////////////////////////////////////////
00319 int PNMFileTypePNG::Reader::
00320 read_data(xel *array, xelval *alpha_data) {
00321   if (!is_valid()) {
00322     return 0;
00323   }
00324 
00325   if (setjmp(_jmpbuf)) {
00326     // This is the ANSI C way to handle exceptions.  If setjmp(),
00327     // above, returns true, it means that libpng detected an exception
00328     // while executing the code that reads the image, below.
00329     free_png();
00330     return 0;
00331   }
00332 
00333   int row_byte_length = _x_size * _num_channels;
00334   if (_maxval > 255) {
00335     row_byte_length *= 2;
00336   }
00337 
00338   int num_rows = _y_size;
00339 
00340   if (pnmimage_png_cat.is_debug()) {
00341     pnmimage_png_cat.debug()
00342       << "Allocating " << num_rows << " rows of " << row_byte_length
00343       << " bytes each.\n";
00344   }
00345     
00346   // We need to read a full copy of the image in first, in libpng's
00347   // 2-d array format, mainly because we keep array and alpha data
00348   // separately, and there doesn't appear to be good support to get
00349   // this stuff out row-at-a-time for interlaced files.
00350   png_bytep *rows = (png_bytep *)PANDA_MALLOC_ARRAY(num_rows * sizeof(png_bytep));
00351   int yi;
00352 
00353   for (yi = 0; yi < num_rows; yi++) {
00354     rows[yi] = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte));
00355   }
00356 
00357   png_read_image(_png, rows);
00358 
00359   bool get_color = !is_grayscale();
00360   bool get_alpha = has_alpha();
00361 
00362   int pi = 0;
00363   for (yi = 0; yi < num_rows; yi++) {
00364     png_bytep source = rows[yi];
00365     for (int xi = 0; xi < _x_size; xi++) {
00366       int red = 0;
00367       int green = 0;
00368       int blue = 0;
00369       int alpha = 0;
00370 
00371       if (_maxval > 255) {
00372         if (get_color) {
00373           red = (source[0] << 8) | source[1];
00374           source += 2;
00375 
00376           green = (source[0] << 8) | source[1];
00377           source += 2;
00378         }
00379 
00380         blue = (source[0] << 8) | source[1];
00381         source += 2;
00382 
00383         if (get_alpha) {
00384           alpha = (source[0] << 8) | source[1];
00385           source += 2;
00386         }
00387         
00388       } else {
00389         if (get_color) {
00390           red = *source;
00391           source++;
00392 
00393           green = *source;
00394           source++;
00395         }
00396 
00397         blue = *source;
00398         source++;
00399 
00400         if (get_alpha) {
00401           alpha = *source;
00402           source++;
00403         }
00404       }
00405       
00406       PPM_ASSIGN(array[pi], red, green, blue);
00407       if (get_alpha) {
00408         alpha_data[pi] = alpha;
00409       }
00410       pi++;
00411     }
00412 
00413     nassertr(source <= rows[yi] + row_byte_length, yi);
00414     PANDA_FREE_ARRAY(rows[yi]);
00415   }
00416 
00417   PANDA_FREE_ARRAY(rows);
00418 
00419   png_read_end(_png, NULL);
00420 
00421   return _y_size;
00422 }
00423 
00424 ////////////////////////////////////////////////////////////////////
00425 //     Function: PNMFileTypePNG::Reader::free_png
00426 //       Access: Private
00427 //  Description: Releases the internal PNG structures and marks the
00428 //               reader invalid.
00429 ////////////////////////////////////////////////////////////////////
00430 void PNMFileTypePNG::Reader::
00431 free_png() {
00432   if (_is_valid) {
00433     png_destroy_read_struct(&_png, &_info, NULL);
00434     _is_valid = false;
00435   }
00436 }
00437 
00438 ////////////////////////////////////////////////////////////////////
00439 //     Function: PNMFileTypePNG::Reader::png_read_data
00440 //       Access: Private, Static
00441 //  Description: A callback handler that PNG uses to read data from
00442 //               the iostream.
00443 ////////////////////////////////////////////////////////////////////
00444 void PNMFileTypePNG::Reader::
00445 png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00446   Reader *self = (Reader *)png_get_io_ptr(png_ptr);
00447   self->_file->read((char *)data, length);
00448   if (length != (png_size_t)self->_file->gcount()) {
00449     pnmimage_png_cat.error()
00450       << "Didn't read enough bytes.\n";
00451     // Is there no way to indicate a read failure to libpng?
00452   }
00453   Thread::consider_yield();
00454 }
00455 
00456 ////////////////////////////////////////////////////////////////////
00457 //     Function: PNMFileTypePNG::Reader::png_warning
00458 //       Access: Private, Static
00459 //  Description: This is our own warning handler.  It is called by the
00460 //               png library to issue a warning message.
00461 ////////////////////////////////////////////////////////////////////
00462 void PNMFileTypePNG::Reader::
00463 png_warning(png_structp, png_const_charp warning_msg) {
00464   pnmimage_png_cat.warning()
00465     << warning_msg << "\n";
00466 }
00467 
00468 ////////////////////////////////////////////////////////////////////
00469 //     Function: PNMFileTypePNG::Reader::png_error
00470 //       Access: Private, Static
00471 //  Description: This is our own error handler.  It is called by the
00472 //               png library to issue a fatal error message.
00473 ////////////////////////////////////////////////////////////////////
00474 void PNMFileTypePNG::Reader::
00475 png_error(png_structp png_ptr, png_const_charp error_msg) {
00476   pnmimage_png_cat.error()
00477     << error_msg << "\n";
00478 
00479   // The PNG library insists we should not return, so instead of
00480   // returning, we will do a longjmp out of the png code.
00481   Reader *self = (Reader *)png_get_io_ptr(png_ptr);
00482   if (self == (Reader *)NULL) {
00483     // Oops, we haven't got a self pointer yet.  Return anyway and
00484     // hope we'll be ok.
00485     pnmimage_png_cat.error()
00486       << "Returning before opening file.\n";
00487     return;
00488   }
00489 
00490   longjmp(self->_jmpbuf, true);
00491 }
00492 
00493 ////////////////////////////////////////////////////////////////////
00494 //     Function: PNMFileTypePNG::Writer::Constructor
00495 //       Access: Public
00496 //  Description:
00497 ////////////////////////////////////////////////////////////////////
00498 PNMFileTypePNG::Writer::
00499 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00500   PNMWriter(type, file, owns_file)
00501 {
00502   _png = NULL;
00503   _info = NULL;
00504   _is_valid = false;
00505 
00506   _png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
00507                                  png_error, png_warning);
00508   if (_png == NULL) {
00509     return;
00510   }
00511 
00512   _info = png_create_info_struct(_png);
00513   if (_info == NULL) {
00514     png_destroy_write_struct(&_png, NULL);
00515     return;
00516   }
00517 
00518   _is_valid = true;
00519 }
00520 
00521 ////////////////////////////////////////////////////////////////////
00522 //     Function: PNMFileTypePNG::Writer::Destructor
00523 //       Access: Public, Virtual
00524 //  Description:
00525 ////////////////////////////////////////////////////////////////////
00526 PNMFileTypePNG::Writer::
00527 ~Writer() {
00528   free_png();
00529 }
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: PNMFileTypePNG::Writer::write_data
00533 //       Access: Public, Virtual
00534 //  Description: Writes in an entire image all at once, storing it in
00535 //               the pre-allocated _x_size * _y_size array and alpha
00536 //               pointers.  (If the image type has no alpha channel,
00537 //               alpha is ignored.)  Returns the number of rows
00538 //               correctly write.
00539 //
00540 //               Derived classes need not override this if they
00541 //               instead provide supports_write_row() and write_row(),
00542 //               below.
00543 ////////////////////////////////////////////////////////////////////
00544 int PNMFileTypePNG::Writer::
00545 write_data(xel *array, xelval *alpha_data) {
00546   if (!is_valid()) {
00547     return 0;
00548   }
00549 
00550   if (setjmp(_jmpbuf)) {
00551     // This is the ANSI C way to handle exceptions.  If setjmp(),
00552     // above, returns true, it means that libpng detected an exception
00553     // while executing the code that writes the image, below.
00554     free_png();
00555     return 0;
00556   }
00557 
00558   png_set_write_fn(_png, (void *)this, png_write_data, png_flush_data);
00559 
00560   // First, write the header.
00561 
00562   int true_bit_depth = pm_maxvaltobits(_maxval);
00563   int png_bit_depth = make_png_bit_depth(true_bit_depth);
00564 
00565   png_color_8 sig_bit;
00566   sig_bit.red = true_bit_depth;
00567   sig_bit.green = true_bit_depth;
00568   sig_bit.blue = true_bit_depth;
00569   sig_bit.gray = true_bit_depth;
00570   sig_bit.alpha = true_bit_depth;
00571 
00572   int color_type = 0;
00573 
00574   if (!is_grayscale()) {
00575     color_type |= PNG_COLOR_MASK_COLOR;
00576   }
00577   if (has_alpha()) {
00578     color_type |= PNG_COLOR_MASK_ALPHA;
00579   }
00580 
00581   // Determine if we should make a palettized image out of this.  In
00582   // order for this to be possible and effective, we must have no more
00583   // than 256 unique color/alpha combinations for a color image, and
00584   // the resulting bitdepth should be smaller than what we would have
00585   // otherwise.
00586   Palette palette;
00587   HistMap palette_lookup;
00588   png_color png_palette[png_max_palette];
00589   png_byte png_trans[png_max_palette];
00590 
00591   if (png_bit_depth <= 8) {
00592     if (compute_palette(palette, array, alpha_data, png_max_palette)) {
00593       pnmimage_png_cat.debug()
00594         << palette.size() << " colors found.\n";
00595 
00596       int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1));
00597 
00598       int total_bits = png_bit_depth;
00599       if (!is_grayscale()) {
00600         total_bits *= 3;
00601       }
00602       if (has_alpha()) {
00603         total_bits += png_bit_depth;
00604       }
00605 
00606       if (palette_bit_depth < total_bits) {
00607         pnmimage_png_cat.debug()
00608           << "palette bit depth of " << palette_bit_depth
00609           << " improves on bit depth of " << total_bits 
00610           << "; making a palette image.\n";
00611 
00612         color_type = PNG_COLOR_TYPE_PALETTE;
00613 
00614         // Re-sort the palette to put the semitransparent pixels at the
00615         // beginning.
00616         sort(palette.begin(), palette.end(), LowAlphaCompare());
00617         
00618         int num_alpha = 0;
00619         for (int i = 0; i < (int)palette.size(); i++) {
00620           png_palette[i].red = palette[i]._red;
00621           png_palette[i].green = palette[i]._green;
00622           png_palette[i].blue = palette[i]._blue;
00623           png_trans[i] = palette[i]._alpha;
00624           if (palette[i]._alpha != _maxval) {
00625             num_alpha = i + 1;
00626           }
00627           
00628           // Also build a reverse-lookup from color to palette index in
00629           // the "histogram" structure.
00630           palette_lookup[palette[i]] = i;
00631         }
00632 
00633         png_set_PLTE(_png, _info, png_palette, palette.size());
00634         if (has_alpha()) {
00635           pnmimage_png_cat.debug()
00636             << "palette contains " << num_alpha << " transparent entries.\n";
00637           png_set_tRNS(_png, _info, png_trans, num_alpha, NULL);
00638         }
00639       } else {
00640         pnmimage_png_cat.debug()
00641           << "palette bit depth of " << palette_bit_depth
00642           << " does not improve on bit depth of " << total_bits 
00643           << "; not making a palette image.\n";
00644       }
00645 
00646     } else {
00647       pnmimage_png_cat.debug()
00648         << "more than " << png_max_palette
00649         << " colors found; not making a palette image.\n";
00650     }
00651   } else {
00652     pnmimage_png_cat.debug()
00653       << "maxval exceeds 255; not making a palette image.\n";
00654   }
00655   
00656 
00657   pnmimage_png_cat.debug()
00658     << "width = " << _x_size << " height = " << _y_size
00659     << " maxval = " << _maxval << " bit_depth = "
00660     << png_bit_depth << " color_type = " << color_type << "\n";
00661 
00662   png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth,
00663                color_type, PNG_INTERLACE_NONE,
00664                PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00665 
00666   // Set the true bit depth of the image data.
00667   if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
00668     png_set_sBIT(_png, _info, &sig_bit);
00669   }
00670 
00671   png_write_info(_png, _info);
00672 
00673 
00674   // Now set up the transformations to write the image data.
00675   if (png_bit_depth < 8) {
00676     png_set_packing(_png);
00677   }
00678 
00679   if (png_bit_depth != true_bit_depth && color_type != PNG_COLOR_TYPE_PALETTE) {
00680     // This does assume that _maxval is one less than a power of 2.
00681     // If it is not, the PNG image will be written as if it were the
00682     // next-higher power of 2, darkening the image.
00683     png_set_shift(_png, &sig_bit);
00684   }
00685 
00686   int row_byte_length = _x_size * _num_channels;
00687   if (png_bit_depth > 8) {
00688     row_byte_length *= 2;
00689   }
00690 
00691   int num_rows = _y_size;
00692 
00693   if (pnmimage_png_cat.is_debug()) {
00694     pnmimage_png_cat.debug()
00695       << "Allocating one row of " << row_byte_length
00696       << " bytes.\n";
00697   }
00698 
00699   // When writing, we only need to copy the image out one row at a
00700   // time, because we don't mess around with writing interlaced files.
00701   // If we were writing an interlaced file, we'd have to copy the
00702   // whole image first.
00703 
00704   png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte));
00705 
00706   bool save_color = !is_grayscale();
00707   bool save_alpha = has_alpha();
00708 
00709   int pi = 0;
00710   for (int yi = 0; yi < num_rows; yi++) {
00711     png_bytep dest = row;
00712 
00713     if (color_type == PNG_COLOR_TYPE_PALETTE) {
00714       for (int xi = 0; xi < _x_size; xi++) {
00715         int index;
00716 
00717         if (save_color) {
00718           if (save_alpha) {
00719             index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
00720           } else {
00721             index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
00722           }
00723         } else {
00724           if (save_alpha) {
00725             index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
00726           } else {
00727             index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
00728           }
00729         }
00730 
00731         *dest++ = index;
00732         pi++;
00733       }
00734 
00735     } else if (png_bit_depth > 8) {
00736       for (int xi = 0; xi < _x_size; xi++) {
00737         if (save_color) {
00738           xelval red = PPM_GETR(array[pi]);
00739           *dest++ = (red >> 8) & 0xff;
00740           *dest++ = red & 0xff;
00741           xelval green = PPM_GETG(array[pi]);
00742           *dest++ = (green >> 8) & 0xff;
00743           *dest++ = green & 0xff;
00744         }
00745         xelval blue = PPM_GETB(array[pi]);
00746         *dest++ = (blue >> 8) & 0xff;
00747         *dest++ = blue & 0xff;
00748 
00749         if (save_alpha) {
00750           xelval alpha = alpha_data[pi];
00751           *dest++ = (alpha >> 8) & 0xff;
00752           *dest++ = alpha & 0xff;
00753         }
00754         pi++;
00755       }
00756 
00757     } else {
00758       for (int xi = 0; xi < _x_size; xi++) {
00759         if (save_color) {
00760           *dest++ = PPM_GETR(array[pi]);
00761           *dest++ = PPM_GETG(array[pi]);
00762         }
00763 
00764         *dest++ = PPM_GETB(array[pi]);
00765 
00766         if (save_alpha) {
00767           *dest++ = alpha_data[pi];
00768         }
00769         pi++;
00770       }
00771     }
00772 
00773     nassertr(dest <= row + row_byte_length, yi);
00774     png_write_row(_png, row);
00775     Thread::consider_yield();
00776   }
00777 
00778   PANDA_FREE_ARRAY(row);
00779 
00780   png_write_end(_png, NULL);
00781 
00782   return _y_size;
00783 }
00784 
00785 ////////////////////////////////////////////////////////////////////
00786 //     Function: PNMFileTypePNG::Writer::free_png
00787 //       Access: Private
00788 //  Description: Releases the internal PNG structures and marks the
00789 //               writer invalid.
00790 ////////////////////////////////////////////////////////////////////
00791 void PNMFileTypePNG::Writer::
00792 free_png() {
00793   if (_is_valid) {
00794     png_destroy_write_struct(&_png, &_info);
00795     _is_valid = false;
00796   }
00797 }
00798 
00799 ////////////////////////////////////////////////////////////////////
00800 //     Function: PNMFileTypePNG::Writer::make_png_bit_depth
00801 //       Access: Private, Static
00802 //  Description: Elevates the indicated bit depth to one of the legal
00803 //               PNG bit depths: 1, 2, 4, 8, or 16.
00804 ////////////////////////////////////////////////////////////////////
00805 int PNMFileTypePNG::Writer::
00806 make_png_bit_depth(int bit_depth) {
00807   switch (bit_depth) {
00808   case 0:
00809   case 1:
00810     return 1;
00811 
00812   case 2:
00813     return 2;
00814 
00815   case 3:
00816   case 4:
00817     return 4;
00818 
00819   case 5:
00820   case 6:
00821   case 7:
00822   case 8:
00823     return 8;
00824 
00825   default:
00826     return 16;
00827   }
00828 }
00829 
00830 ////////////////////////////////////////////////////////////////////
00831 //     Function: PNMFileTypePNG::Writer::png_write_data
00832 //       Access: Private, Static
00833 //  Description: A callback handler that PNG uses to write data to
00834 //               the iostream.
00835 ////////////////////////////////////////////////////////////////////
00836 void PNMFileTypePNG::Writer::
00837 png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
00838   Writer *self = (Writer *)png_get_io_ptr(png_ptr);
00839   self->_file->write((char *)data, length);
00840   if (self->_file->fail()) {
00841     pnmimage_png_cat.error()
00842       << "Unable to write to the iostream.\n";
00843     // Is there no way to indicate a write failure to libpng?
00844   }
00845 }
00846 
00847 ////////////////////////////////////////////////////////////////////
00848 //     Function: PNMFileTypePNG::Writer::png_flush_data
00849 //       Access: Private, Static
00850 //  Description: A callback handler that PNG uses to write data to
00851 //               the iostream.
00852 ////////////////////////////////////////////////////////////////////
00853 void PNMFileTypePNG::Writer::
00854 png_flush_data(png_structp png_ptr) {
00855   Writer *self = (Writer *)png_get_io_ptr(png_ptr);
00856   self->_file->flush();
00857 }
00858 
00859 ////////////////////////////////////////////////////////////////////
00860 //     Function: PNMFileTypePNG::Writer::png_warning
00861 //       Access: Private, Static
00862 //  Description: This is our own warning handler.  It is called by the
00863 //               png library to issue a warning message.
00864 ////////////////////////////////////////////////////////////////////
00865 void PNMFileTypePNG::Writer::
00866 png_warning(png_structp, png_const_charp warning_msg) {
00867   pnmimage_png_cat.warning()
00868     << warning_msg << "\n";
00869 }
00870 
00871 ////////////////////////////////////////////////////////////////////
00872 //     Function: PNMFileTypePNG::Writer::png_error
00873 //       Access: Private, Static
00874 //  Description: This is our own error handler.  It is called by the
00875 //               png library to issue a fatal error message.
00876 ////////////////////////////////////////////////////////////////////
00877 void PNMFileTypePNG::Writer::
00878 png_error(png_structp png_ptr, png_const_charp error_msg) {
00879   pnmimage_png_cat.error()
00880     << error_msg << "\n";
00881 
00882   // The PNG library insists we should not return, so instead of
00883   // returning, we will do a longjmp out of the png code.
00884   Writer *self = (Writer *)png_get_io_ptr(png_ptr);
00885   if (self == (Writer *)NULL) {
00886     // Oops, we haven't got a self pointer yet.  Return anyway and
00887     // hope we'll be ok.
00888     pnmimage_png_cat.error()
00889       << "Returning before opening file.\n";
00890     return;
00891   }
00892 
00893   longjmp(self->_jmpbuf, true);
00894 }
00895 
00896 
00897 #endif  // HAVE_PNG
 All Classes Functions Variables Enumerations