00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "textureImage.h"
00020 #include "sourceTextureImage.h"
00021 #include "destTextureImage.h"
00022 #include "eggFile.h"
00023 #include "paletteGroup.h"
00024 #include "paletteImage.h"
00025 #include "texturePlacement.h"
00026 #include "filenameUnifier.h"
00027
00028 #include "indent.h"
00029 #include "datagram.h"
00030 #include "datagramIterator.h"
00031 #include "bamReader.h"
00032 #include "bamWriter.h"
00033 #include "pnmFileType.h"
00034 #include "indirectCompareNames.h"
00035 #include "pvector.h"
00036
00037 TypeHandle TextureImage::_type_handle;
00038
00039
00040
00041
00042
00043
00044 TextureImage::
00045 TextureImage() {
00046 _preferred_source = (SourceTextureImage *)NULL;
00047 _read_source_image = false;
00048 _is_surprise = true;
00049 _ever_read_image = false;
00050 _forced_grayscale = false;
00051 _alpha_bits = 0;
00052 _alpha_mode = EggRenderMode::AM_unspecified;
00053 _got_txa_file = false;
00054 }
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064 void TextureImage::
00065 note_egg_file(EggFile *egg_file) {
00066 nassertv(!egg_file->get_complete_groups().empty());
00067 _egg_files.insert(egg_file);
00068 }
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078 void TextureImage::
00079 assign_groups() {
00080 if (_egg_files.empty()) {
00081
00082
00083 PaletteGroups empty;
00084 assign_to_groups(empty);
00085 return;
00086 }
00087
00088 PaletteGroups definitely_in;
00089
00090
00091
00092
00093 WorkingEggs needed_eggs;
00094
00095 if (_explicitly_assigned_groups.empty()) {
00096
00097
00098 copy(_egg_files.begin(), _egg_files.end(), back_inserter(needed_eggs));
00099
00100 } else {
00101
00102
00103
00104 EggFiles::const_iterator ei;
00105 for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00106 PaletteGroups intersect;
00107 intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups());
00108 if (!intersect.empty()) {
00109
00110
00111
00112
00113
00114
00115 definitely_in.insert(*intersect.begin());
00116
00117 } else {
00118
00119
00120
00121
00122 needed_eggs.push_back(*ei);
00123 }
00124 }
00125 }
00126
00127 while (!needed_eggs.empty()) {
00128
00129
00130
00131 PaletteGroups total;
00132 WorkingEggs::const_iterator ei;
00133 for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00134 total.make_union(total, (*ei)->get_complete_groups());
00135 }
00136
00137
00138
00139
00140
00141
00142 nassertv(!total.empty());
00143 PaletteGroups::iterator gi = total.begin();
00144 PaletteGroup *best = (*gi);
00145 int best_egg_count = compute_egg_count(best, needed_eggs);
00146 ++gi;
00147 while (gi != total.end()) {
00148 PaletteGroup *group = (*gi);
00149
00150
00151 bool prefer_group = false;
00152 int group_egg_count = compute_egg_count(group, needed_eggs);
00153 if (group_egg_count != best_egg_count) {
00154 prefer_group = (group_egg_count > best_egg_count);
00155
00156 } else {
00157 prefer_group = group->is_preferred_over(*best);
00158 }
00159
00160 if (prefer_group) {
00161 best = group;
00162 best_egg_count = group_egg_count;
00163 }
00164 ++gi;
00165 }
00166
00167
00168
00169 definitely_in.insert(best);
00170
00171 WorkingEggs next_needed_eggs;
00172 for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00173 if ((*ei)->get_complete_groups().count(best) == 0) {
00174
00175 next_needed_eggs.push_back(*ei);
00176 }
00177 }
00178 needed_eggs.swap(next_needed_eggs);
00179 }
00180
00181
00182
00183
00184 assign_to_groups(definitely_in);
00185 }
00186
00187
00188
00189
00190
00191
00192
00193
00194 const PaletteGroups &TextureImage::
00195 get_groups() const {
00196 return _actual_assigned_groups;
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207 TexturePlacement *TextureImage::
00208 get_placement(PaletteGroup *group) const {
00209 Placement::const_iterator pi;
00210 pi = _placement.find(group);
00211 if (pi == _placement.end()) {
00212 return (TexturePlacement *)NULL;
00213 }
00214
00215 return (*pi).second;
00216 }
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226 void TextureImage::
00227 force_replace() {
00228 Placement::iterator pi;
00229 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00230 (*pi).second->force_replace();
00231 }
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243 void TextureImage::
00244 mark_eggs_stale() {
00245 Placement::iterator pi;
00246 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00247 (*pi).second->mark_eggs_stale();
00248 }
00249 }
00250
00251
00252
00253
00254
00255
00256
00257 void TextureImage::
00258 pre_txa_file() {
00259
00260 _pre_txa_properties = _properties;
00261
00262
00263
00264 SourceTextureImage *source = get_preferred_source();
00265 if (source != (SourceTextureImage *)NULL) {
00266 _properties = source->get_properties();
00267 }
00268
00269 _pre_txa_alpha_mode = _alpha_mode;
00270 _alpha_mode = EggRenderMode::AM_unspecified;
00271
00272 _request.pre_txa_file();
00273 _is_surprise = true;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285 void TextureImage::
00286 post_txa_file() {
00287 _got_txa_file = true;
00288
00289
00290 SourceTextureImage *source = get_preferred_source();
00291 if (source != (SourceTextureImage *)NULL) {
00292 if (source->get_size()) {
00293 _size_known = true;
00294 _x_size = source->get_x_size();
00295 _y_size = source->get_y_size();
00296 _properties.set_num_channels(source->get_num_channels());
00297 }
00298 }
00299
00300
00301 if (_request._got_size) {
00302 _size_known = true;
00303 _x_size = _request._x_size;
00304 _y_size = _request._y_size;
00305 }
00306
00307 if (_properties.has_num_channels()) {
00308 int num_channels = _properties.get_num_channels();
00309
00310
00311 if (num_channels == 3 || num_channels == 4) {
00312 consider_grayscale();
00313 }
00314
00315
00316
00317 if (num_channels == 2 || num_channels == 4) {
00318 consider_alpha();
00319 }
00320 }
00321
00322
00323 if (_request._got_num_channels) {
00324 _properties.set_num_channels(_request._num_channels);
00325 }
00326
00327 _properties._generic_format = _request._generic_format;
00328
00329 if (_request._format != EggTexture::F_unspecified) {
00330 _properties._format = _request._format;
00331 _properties._force_format = _request._force_format;
00332 }
00333 if (_request._minfilter != EggTexture::FT_unspecified) {
00334 _properties._minfilter = _request._minfilter;
00335 }
00336 if (_request._magfilter != EggTexture::FT_unspecified) {
00337 _properties._magfilter = _request._magfilter;
00338 }
00339
00340 _properties._anisotropic_degree = _request._anisotropic_degree;
00341
00342 if (_properties._color_type == (PNMFileType *)NULL) {
00343 _properties._color_type = _request._properties._color_type;
00344 _properties._alpha_type = _request._properties._alpha_type;
00345 }
00346
00347
00348 _properties.fully_define();
00349
00350
00351
00352 if (_properties != _pre_txa_properties) {
00353 force_replace();
00354
00355
00356
00357
00358
00359
00360 if (!_properties.egg_properties_match(_pre_txa_properties)) {
00361 mark_eggs_stale();
00362 }
00363 }
00364
00365
00366
00367 if (_request._alpha_mode != EggRenderMode::AM_unspecified) {
00368 _alpha_mode = _request._alpha_mode;
00369 }
00370
00371
00372
00373 if (_properties.has_num_channels()) {
00374 int num_channels = _properties.get_num_channels();
00375 if (num_channels == 1 || num_channels == 3) {
00376 _alpha_mode = EggRenderMode::AM_unspecified;
00377 }
00378 }
00379
00380
00381
00382 if (_pre_txa_alpha_mode != _alpha_mode) {
00383 mark_eggs_stale();
00384 }
00385 }
00386
00387
00388
00389
00390
00391
00392
00393 bool TextureImage::
00394 got_txa_file() const {
00395 return _got_txa_file;
00396 }
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406 void TextureImage::
00407 determine_placement_size() {
00408 Placement::iterator pi;
00409 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00410 TexturePlacement *placement = (*pi).second;
00411 placement->determine_size();
00412 }
00413 }
00414
00415
00416
00417
00418
00419
00420
00421
00422 bool TextureImage::
00423 get_omit() const {
00424 return _request._omit;
00425 }
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437 double TextureImage::
00438 get_coverage_threshold() const {
00439 return _request._coverage_threshold;
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450 int TextureImage::
00451 get_margin() const {
00452 return _request._margin;
00453 }
00454
00455
00456
00457
00458
00459
00460
00461
00462 bool TextureImage::
00463 is_surprise() const {
00464 if (_placement.empty()) {
00465
00466
00467 return false;
00468 }
00469
00470 return _is_surprise;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479
00480 bool TextureImage::
00481 is_used() const {
00482 return !_placement.empty();
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493 EggRenderMode::AlphaMode TextureImage::
00494 get_alpha_mode() const {
00495 return _alpha_mode;
00496 }
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508 SourceTextureImage *TextureImage::
00509 get_source(const Filename &filename, const Filename &alpha_filename,
00510 int alpha_file_channel) {
00511 string key = get_source_key(filename, alpha_filename, alpha_file_channel);
00512
00513 Sources::iterator si;
00514 si = _sources.find(key);
00515 if (si != _sources.end()) {
00516 return (*si).second;
00517 }
00518
00519 SourceTextureImage *source =
00520 new SourceTextureImage(this, filename, alpha_filename, alpha_file_channel);
00521 _sources.insert(Sources::value_type(key, source));
00522
00523
00524
00525 _preferred_source = (SourceTextureImage *)NULL;
00526 _read_source_image = false;
00527
00528 return source;
00529 }
00530
00531
00532
00533
00534
00535
00536
00537
00538 SourceTextureImage *TextureImage::
00539 get_preferred_source() {
00540 if (_preferred_source != (SourceTextureImage *)NULL) {
00541 return _preferred_source;
00542 }
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557 bool any_referenced = false;
00558 Sources::iterator si;
00559 for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) {
00560 SourceTextureImage *source = (*si).second;
00561 if (source->get_egg_count() > 0) {
00562 any_referenced = true;
00563 }
00564 }
00565
00566 SourceTextureImage *best = (SourceTextureImage *)NULL;
00567 int best_size = 0;
00568
00569 for (si = _sources.begin(); si != _sources.end(); ++si) {
00570 SourceTextureImage *source = (*si).second;
00571
00572 if (source->get_egg_count() > 0 || !any_referenced) {
00573
00574
00575 if (source->exists() && source->get_size()) {
00576 int source_size = source->get_x_size() * source->get_y_size();
00577 if (best == (SourceTextureImage *)NULL) {
00578 best = source;
00579 best_size = source_size;
00580
00581 } else if (source_size > best_size) {
00582
00583 best = source;
00584 best_size = source_size;
00585
00586 } else if (source_size == best_size &&
00587 source->get_filename().compare_timestamps(best->get_filename()) > 0) {
00588
00589 best = source;
00590 best_size = source_size;
00591 }
00592 }
00593 }
00594 }
00595
00596 if (best == (SourceTextureImage *)NULL && !_sources.empty()) {
00597
00598
00599
00600
00601 if (any_referenced) {
00602 for (si = _sources.begin();
00603 si != _sources.end() && best == (SourceTextureImage *)NULL;
00604 ++si) {
00605 SourceTextureImage *source = (*si).second;
00606 if (source->get_egg_count() > 0) {
00607 best = source;
00608 }
00609 }
00610 } else {
00611 best = (*_sources.begin()).second;
00612 }
00613 }
00614
00615 _preferred_source = best;
00616 return _preferred_source;
00617 }
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 void TextureImage::
00628 clear_source_basic_properties() {
00629 Sources::iterator si;
00630 for (si = _sources.begin(); si != _sources.end(); ++si) {
00631 SourceTextureImage *source = (*si).second;
00632 source->clear_basic_properties();
00633 }
00634 }
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648 void TextureImage::
00649 copy_unplaced(bool redo_all) {
00650
00651
00652 Dests generate;
00653
00654
00655
00656
00657
00658 Placement::iterator pi;
00659 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00660 TexturePlacement *placement = (*pi).second;
00661 if (placement->get_omit_reason() != OR_none &&
00662 placement->get_omit_reason() != OR_unknown) {
00663 DestTextureImage *dest = new DestTextureImage(placement);
00664 Filename filename = dest->get_filename();
00665 filename.make_canonical();
00666
00667 pair<Dests::iterator, bool> insert_result = generate.insert
00668 (Dests::value_type(filename, dest));
00669 if (!insert_result.second) {
00670
00671
00672 delete dest;
00673 dest = (*insert_result.first).second;
00674 }
00675
00676 placement->set_dest(dest);
00677
00678 } else {
00679 placement->set_dest((DestTextureImage *)NULL);
00680 }
00681 }
00682
00683 if (redo_all) {
00684
00685
00686 Dests empty;
00687 remove_old_dests(empty, _dests);
00688 copy_new_dests(generate, empty);
00689
00690 } else {
00691
00692
00693 remove_old_dests(generate, _dests);
00694 copy_new_dests(generate, _dests);
00695 }
00696
00697
00698 Dests::iterator di;
00699 for (di = _dests.begin(); di != _dests.end(); ++di) {
00700 delete (*di).second;
00701 }
00702
00703 _dests.swap(generate);
00704 }
00705
00706
00707
00708
00709
00710
00711
00712 const PNMImage &TextureImage::
00713 read_source_image() {
00714 if (!_read_source_image) {
00715 SourceTextureImage *source = get_preferred_source();
00716 if (source != (SourceTextureImage *)NULL) {
00717 source->read(_source_image);
00718 }
00719 _read_source_image = true;
00720 _ever_read_image = true;
00721 }
00722
00723 return _source_image;
00724 }
00725
00726
00727
00728
00729
00730
00731
00732
00733 void TextureImage::
00734 read_header() {
00735 if (!_read_source_image) {
00736 SourceTextureImage *source = get_preferred_source();
00737 if (source != (SourceTextureImage *)NULL) {
00738 source->read_header();
00739 }
00740 }
00741 }
00742
00743
00744
00745
00746
00747
00748
00749
00750 bool TextureImage::
00751 is_newer_than(const Filename &reference_filename) {
00752 if (!_read_source_image) {
00753 SourceTextureImage *source = get_preferred_source();
00754 if (source != (SourceTextureImage *)NULL) {
00755 const Filename &source_filename = source->get_filename();
00756 return source_filename.compare_timestamps(reference_filename) >= 0;
00757 }
00758 }
00759
00760 return false;
00761 }
00762
00763
00764
00765
00766
00767
00768
00769
00770 void TextureImage::
00771 write_source_pathnames(ostream &out, int indent_level) const {
00772 Sources::const_iterator si;
00773 for (si = _sources.begin(); si != _sources.end(); ++si) {
00774 SourceTextureImage *source = (*si).second;
00775
00776 if (source->get_egg_count() > 0) {
00777 indent(out, indent_level);
00778 source->output_filename(out);
00779 if (!source->is_size_known()) {
00780 out << " (unknown size)";
00781
00782 } else {
00783 out << " " << source->get_x_size() << " "
00784 << source->get_y_size();
00785
00786 if (source->get_properties().has_num_channels()) {
00787 out << " " << source->get_properties().get_num_channels();
00788 }
00789 }
00790 out << "\n";
00791 }
00792 }
00793
00794
00795 if (!_egg_files.empty()) {
00796
00797 pvector<EggFile *> egg_vector;
00798 egg_vector.reserve(_egg_files.size());
00799 EggFiles::const_iterator ei;
00800 for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00801 egg_vector.push_back(*ei);
00802 }
00803 sort(egg_vector.begin(), egg_vector.end(),
00804 IndirectCompareNames<EggFile>());
00805
00806 indent(out, indent_level)
00807 << "Used by:\n";
00808 pvector<EggFile *>::const_iterator evi;
00809 for (evi = egg_vector.begin(); evi != egg_vector.end(); ++evi) {
00810 EggFile *egg = (*evi);
00811 indent(out, indent_level + 2)
00812 << egg->get_name() << " (";
00813 if (egg->get_explicit_groups().empty()) {
00814 out << *egg->get_default_group();
00815 } else {
00816 out << egg->get_explicit_groups();
00817 }
00818 out << ")\n";
00819 }
00820 }
00821 if (!_explicitly_assigned_groups.empty()) {
00822 indent(out, indent_level)
00823 << "Explicitly assigned to " << _explicitly_assigned_groups << " in .txa\n";
00824 }
00825
00826 if (_placement.empty()) {
00827 indent(out, indent_level)
00828 << "Not used.\n";
00829 } else {
00830 indent(out, indent_level)
00831 << "Assigned to " << _actual_assigned_groups << "\n";
00832 }
00833 }
00834
00835
00836
00837
00838
00839
00840
00841
00842 void TextureImage::
00843 write_scale_info(ostream &out, int indent_level) {
00844 SourceTextureImage *source = get_preferred_source();
00845 indent(out, indent_level) << get_name();
00846
00847
00848 if (_placement.empty()) {
00849 out << " (not used)";
00850 } else {
00851 Placement::const_iterator pi;
00852 pi = _placement.begin();
00853 out << " (" << (*pi).second->get_group()->get_name();
00854 ++pi;
00855 while (pi != _placement.end()) {
00856 out << " " << (*pi).second->get_group()->get_name();
00857 ++pi;
00858 }
00859 out << ")";
00860 }
00861
00862 out << " orig ";
00863
00864 if (source == (SourceTextureImage *)NULL ||
00865 !source->is_size_known()) {
00866 out << "unknown";
00867 } else {
00868 out << source->get_x_size() << " " << source->get_y_size()
00869 << " " << source->get_num_channels();
00870 }
00871
00872 if (!_placement.empty() && is_size_known()) {
00873 out << " new " << get_x_size() << " " << get_y_size()
00874 << " " << get_num_channels();
00875
00876 if (source != (SourceTextureImage *)NULL &&
00877 source->is_size_known()) {
00878 double scale =
00879 100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
00880 ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
00881 out << " scale " << scale << "%";
00882 }
00883 }
00884 out << "\n";
00885
00886
00887 Placement::iterator pi;
00888 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00889 TexturePlacement *placement = (*pi).second;
00890 if (placement->get_omit_reason() == OR_none) {
00891 PaletteImage *image = placement->get_image();
00892 nassertv(image != (PaletteImage *)NULL);
00893 indent(out, indent_level + 2)
00894 << "placed on "
00895 << FilenameUnifier::make_user_filename(image->get_filename())
00896 << "\n";
00897
00898 } else if (placement->get_omit_reason() == OR_unknown) {
00899 indent(out, indent_level + 2)
00900 << "not placed because unknown.\n";
00901
00902 } else {
00903 DestTextureImage *image = placement->get_dest();
00904 nassertv(image != (DestTextureImage *)NULL);
00905 indent(out, indent_level + 2)
00906 << "copied to "
00907 << FilenameUnifier::make_user_filename(image->get_filename());
00908 if (image->is_size_known() && is_size_known() &&
00909 (image->get_x_size() != get_x_size() ||
00910 image->get_y_size() != get_y_size())) {
00911 out << " at size " << image->get_x_size() << " "
00912 << image->get_y_size();
00913 if (source != (SourceTextureImage *)NULL &&
00914 source->is_size_known()) {
00915 double scale =
00916 100.0 * (((double)image->get_x_size() / (double)source->get_x_size()) +
00917 ((double)image->get_y_size() / (double)source->get_y_size())) / 2.0;
00918 out << " scale " << scale << "%";
00919 }
00920 }
00921 out << "\n";
00922 }
00923 }
00924 }
00925
00926
00927
00928
00929
00930
00931
00932
00933 int TextureImage::
00934 compute_egg_count(PaletteGroup *group,
00935 const TextureImage::WorkingEggs &egg_files) {
00936 int count = 0;
00937
00938 WorkingEggs::const_iterator ei;
00939 for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
00940 if ((*ei)->get_complete_groups().count(group) != 0) {
00941 count++;
00942 }
00943 }
00944
00945 return count;
00946 }
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958 void TextureImage::
00959 assign_to_groups(const PaletteGroups &groups) {
00960 PaletteGroups::const_iterator gi;
00961 Placement::const_iterator pi;
00962
00963 Placement new_placement;
00964
00965 gi = groups.begin();
00966 pi = _placement.begin();
00967
00968 while (gi != groups.end() && pi != _placement.end()) {
00969 PaletteGroup *a = (*gi);
00970 PaletteGroup *b = (*pi).first;
00971
00972 if (a < b) {
00973
00974
00975 TexturePlacement *place = a->prepare(this);
00976 new_placement.insert
00977 (new_placement.end(), Placement::value_type(a, place));
00978 ++gi;
00979
00980 } else if (b < a) {
00981
00982 TexturePlacement *place = (*pi).second;
00983 delete place;
00984 ++pi;
00985
00986 } else {
00987
00988 TexturePlacement *place = (*pi).second;
00989 new_placement.insert
00990 (new_placement.end(), Placement::value_type(a, place));
00991 ++gi;
00992 ++pi;
00993 }
00994 }
00995
00996 while (gi != groups.end()) {
00997
00998
00999 PaletteGroup *a = (*gi);
01000 TexturePlacement *place = a->prepare(this);
01001 new_placement.insert
01002 (new_placement.end(), Placement::value_type(a, place));
01003 ++gi;
01004 }
01005
01006 while (pi != _placement.end()) {
01007
01008 TexturePlacement *place = (*pi).second;
01009 delete place;
01010 ++pi;
01011 }
01012
01013 _placement.swap(new_placement);
01014 _actual_assigned_groups = groups;
01015 }
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025 void TextureImage::
01026 consider_grayscale() {
01027
01028
01029
01030
01031
01032
01033 if (!_read_source_image && _ever_read_image) {
01034 if (_forced_grayscale) {
01035 _properties.force_grayscale();
01036 }
01037 return;
01038 }
01039
01040 const PNMImage &source = read_source_image();
01041 if (!source.is_valid()) {
01042 return;
01043 }
01044
01045 for (int y = 0; y < source.get_y_size(); y++) {
01046 for (int x = 0; x < source.get_x_size(); x++) {
01047 const xel &v = source.get_xel_val(x, y);
01048 if (PPM_GETR(v) != PPM_GETG(v) || PPM_GETR(v) != PPM_GETB(v)) {
01049
01050 _forced_grayscale = false;
01051 return;
01052 }
01053 }
01054 }
01055
01056
01057 _properties.force_grayscale();
01058 _forced_grayscale = true;
01059 }
01060
01061
01062
01063
01064
01065
01066
01067 void TextureImage::
01068 consider_alpha() {
01069
01070
01071
01072
01073
01074 if (_read_source_image || !_ever_read_image || _alpha_bits == -1) {
01075 _alpha_bits = 0;
01076
01077 const PNMImage &source = read_source_image();
01078 if (source.is_valid() && source.has_alpha()) {
01079 xelval maxval = source.get_maxval();
01080 for (int y = 0; y < source.get_y_size(); y++) {
01081 for (int x = 0; x < source.get_x_size(); x++) {
01082 xelval alpha_val = source.get_alpha_val(x, y);
01083 if (alpha_val == 0) {
01084 _alpha_bits |= AB_zero;
01085 } else if (alpha_val == maxval) {
01086 _alpha_bits |= AB_one;
01087 } else {
01088 _alpha_bits |= AB_mid;
01089 }
01090 if (_alpha_bits == AB_all) {
01091
01092
01093 break;
01094 }
01095 }
01096 }
01097 }
01098 }
01099
01100 if (_alpha_bits != 0) {
01101 if (_alpha_bits == AB_one) {
01102
01103 _properties.force_nonalpha();
01104
01105 } else if (_alpha_bits == AB_zero) {
01106
01107
01108 _properties.force_nonalpha();
01109 if (_read_source_image) {
01110 nout << *this << " has an all-zero alpha channel; dropping alpha.\n";
01111 }
01112
01113 } else if (_alpha_mode == EggRenderMode::AM_unspecified) {
01114
01115
01116 if ((_alpha_bits & AB_mid) == 0) {
01117
01118 _alpha_mode = EggRenderMode::AM_binary;
01119
01120 } else if ((_alpha_bits & AB_one) != 0) {
01121
01122 _alpha_mode = EggRenderMode::AM_dual;
01123
01124 } else {
01125
01126 _alpha_mode = EggRenderMode::AM_blend;
01127 }
01128 }
01129 }
01130 }
01131
01132
01133
01134
01135
01136
01137
01138 void TextureImage::
01139 remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01140 Dests::const_iterator ai = a.begin();
01141 Dests::const_iterator bi = b.begin();
01142
01143 while (ai != a.end() && bi != b.end()) {
01144 const string &astr = (*ai).first;
01145 const string &bstr = (*bi).first;
01146
01147 if (astr < bstr) {
01148
01149 ++ai;
01150
01151 } else if (bstr < astr) {
01152
01153 (*bi).second->unlink();
01154 ++bi;
01155
01156 } else {
01157
01158 ++ai;
01159 ++bi;
01160 }
01161 }
01162
01163 while (bi != b.end()) {
01164
01165 (*bi).second->unlink();
01166 ++bi;
01167 }
01168
01169 while (ai != a.end()) {
01170 ++ai;
01171 }
01172 }
01173
01174
01175
01176
01177
01178
01179
01180
01181 void TextureImage::
01182 copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01183 Dests::const_iterator ai = a.begin();
01184 Dests::const_iterator bi = b.begin();
01185
01186 while (ai != a.end() && bi != b.end()) {
01187 const string &astr = (*ai).first;
01188 const string &bstr = (*bi).first;
01189
01190 if (astr < bstr) {
01191
01192 (*ai).second->copy(this);
01193 ++ai;
01194
01195 } else if (bstr < astr) {
01196
01197 ++bi;
01198
01199 } else {
01200
01201 (*ai).second->copy_if_stale((*bi).second, this);
01202 ++ai;
01203 ++bi;
01204 }
01205 }
01206
01207 while (ai != a.end()) {
01208
01209 (*ai).second->copy(this);
01210 ++ai;
01211 }
01212 }
01213
01214
01215
01216
01217
01218
01219
01220 string TextureImage::
01221 get_source_key(const Filename &filename, const Filename &alpha_filename,
01222 int alpha_file_channel) {
01223 Filename f = FilenameUnifier::make_bam_filename(filename);
01224 Filename a = FilenameUnifier::make_bam_filename(alpha_filename);
01225
01226 return f.get_fullpath() + ":" + a.get_fullpath() + ":" +
01227 format_string(alpha_file_channel);
01228 }
01229
01230
01231
01232
01233
01234
01235
01236 void TextureImage::
01237 register_with_read_factory() {
01238 BamReader::get_factory()->
01239 register_factory(get_class_type(), make_TextureImage);
01240 }
01241
01242
01243
01244
01245
01246
01247
01248
01249 void TextureImage::
01250 write_datagram(BamWriter *writer, Datagram &datagram) {
01251 ImageFile::write_datagram(writer, datagram);
01252 datagram.add_string(get_name());
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262 datagram.add_bool(_is_surprise);
01263 datagram.add_bool(_ever_read_image);
01264 datagram.add_bool(_forced_grayscale);
01265 datagram.add_uint8(_alpha_bits);
01266 datagram.add_int16((int)_alpha_mode);
01267
01268
01269
01270
01271 _actual_assigned_groups.write_datagram(writer, datagram);
01272
01273
01274
01275 datagram.add_uint32(_placement.size());
01276 Placement::const_iterator pi;
01277 for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
01278 writer->write_pointer(datagram, (*pi).first);
01279 writer->write_pointer(datagram, (*pi).second);
01280 }
01281
01282 datagram.add_uint32(_sources.size());
01283 Sources::const_iterator si;
01284 for (si = _sources.begin(); si != _sources.end(); ++si) {
01285 writer->write_pointer(datagram, (*si).second);
01286 }
01287
01288 datagram.add_uint32(_dests.size());
01289 Dests::const_iterator di;
01290 for (di = _dests.begin(); di != _dests.end(); ++di) {
01291 writer->write_pointer(datagram, (*di).second);
01292 }
01293 }
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304 int TextureImage::
01305 complete_pointers(TypedWritable **p_list, BamReader *manager) {
01306 int pi = ImageFile::complete_pointers(p_list, manager);
01307
01308 pi += _actual_assigned_groups.complete_pointers(p_list + pi, manager);
01309
01310 int i;
01311 for (i = 0; i < _num_placement; i++) {
01312 PaletteGroup *group;
01313 TexturePlacement *placement;
01314 DCAST_INTO_R(group, p_list[pi++], pi);
01315 DCAST_INTO_R(placement, p_list[pi++], pi);
01316 _placement.insert(Placement::value_type(group, placement));
01317 }
01318
01319 for (i = 0; i < _num_sources; i++) {
01320 SourceTextureImage *source;
01321 DCAST_INTO_R(source, p_list[pi++], pi);
01322 string key = get_source_key(source->get_filename(),
01323 source->get_alpha_filename(),
01324 source->get_alpha_file_channel());
01325
01326 bool inserted = _sources.insert(Sources::value_type(key, source)).second;
01327 if (!inserted) {
01328 nout << "Warning: texture key " << key
01329 << " is nonunique; texture lost.\n";
01330 }
01331 }
01332
01333 for (i = 0; i < _num_dests; i++) {
01334 DestTextureImage *dest;
01335 DCAST_INTO_R(dest, p_list[pi++], pi);
01336 bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second;
01337 if (!inserted) {
01338 nout << "Warning: dest filename " << dest->get_filename()
01339 << " is nonunique; texture lost.\n";
01340 }
01341 }
01342
01343 return pi;
01344 }
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354 TypedWritable *TextureImage::
01355 make_TextureImage(const FactoryParams ¶ms) {
01356 TextureImage *me = new TextureImage;
01357 DatagramIterator scan;
01358 BamReader *manager;
01359
01360 parse_params(params, scan, manager);
01361 me->fillin(scan, manager);
01362 return me;
01363 }
01364
01365
01366
01367
01368
01369
01370
01371
01372 void TextureImage::
01373 fillin(DatagramIterator &scan, BamReader *manager) {
01374 ImageFile::fillin(scan, manager);
01375 set_name(scan.get_string());
01376
01377 _is_surprise = scan.get_bool();
01378 _ever_read_image = scan.get_bool();
01379 _forced_grayscale = scan.get_bool();
01380 _alpha_bits = scan.get_uint8();
01381 _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16();
01382
01383 _actual_assigned_groups.fillin(scan, manager);
01384
01385 _num_placement = scan.get_uint32();
01386 manager->read_pointers(scan, _num_placement * 2);
01387
01388 _num_sources = scan.get_uint32();
01389 manager->read_pointers(scan, _num_sources);
01390 _num_dests = scan.get_uint32();
01391 manager->read_pointers(scan, _num_dests);
01392 }