00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
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
00035
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
00049
00050
00051
00052 PNMFileTypePNG::
00053 PNMFileTypePNG() {
00054
00055
00056 if (pnmimage_png_cat->is_debug()) {
00057 pnmimage_png_cat->debug()
00058 << "PNG version " << PNG_LIBPNG_VER << "\n";
00059 }
00060 }
00061
00062
00063
00064
00065
00066
00067 string PNMFileTypePNG::
00068 get_name() const {
00069 return "PNG";
00070 }
00071
00072
00073
00074
00075
00076
00077
00078 int PNMFileTypePNG::
00079 get_num_extensions() const {
00080 return num_extensions_png;
00081 }
00082
00083
00084
00085
00086
00087
00088
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
00098
00099
00100
00101
00102
00103 string PNMFileTypePNG::
00104 get_suggested_extension() const {
00105 return "png";
00106 }
00107
00108
00109
00110
00111
00112
00113
00114 bool PNMFileTypePNG::
00115 has_magic_number() const {
00116 return true;
00117 }
00118
00119
00120
00121
00122
00123
00124
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
00133
00134
00135
00136
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
00146
00147
00148
00149
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
00159
00160
00161
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
00171
00172
00173
00174
00175
00176
00177
00178
00179
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
00188
00189
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
00215
00216
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
00298
00299
00300
00301 PNMFileTypePNG::Reader::
00302 ~Reader() {
00303 free_png();
00304 }
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
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
00327
00328
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
00347
00348
00349
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
00426
00427
00428
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
00440
00441
00442
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
00452 }
00453 Thread::consider_yield();
00454 }
00455
00456
00457
00458
00459
00460
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
00470
00471
00472
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
00480
00481 Reader *self = (Reader *)png_get_io_ptr(png_ptr);
00482 if (self == (Reader *)NULL) {
00483
00484
00485 pnmimage_png_cat.error()
00486 << "Returning before opening file.\n";
00487 return;
00488 }
00489
00490 longjmp(self->_jmpbuf, true);
00491 }
00492
00493
00494
00495
00496
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
00523
00524
00525
00526 PNMFileTypePNG::Writer::
00527 ~Writer() {
00528 free_png();
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
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
00552
00553
00554 free_png();
00555 return 0;
00556 }
00557
00558 png_set_write_fn(_png, (void *)this, png_write_data, png_flush_data);
00559
00560
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
00582
00583
00584
00585
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
00615
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
00629
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
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
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
00681
00682
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
00700
00701
00702
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
00787
00788
00789
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
00801
00802
00803
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
00832
00833
00834
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
00844 }
00845 }
00846
00847
00848
00849
00850
00851
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
00861
00862
00863
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
00873
00874
00875
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
00883
00884 Writer *self = (Writer *)png_get_io_ptr(png_ptr);
00885 if (self == (Writer *)NULL) {
00886
00887
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