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