Panda3D
|
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 ¶ms) { 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