Panda3D

textureImage.cxx

00001 // Filename: textureImage.cxx
00002 // Created by:  drose (29Nov00)
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 "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 TypeHandle TextureImage::_type_handle;
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: TextureImage::Constructor
00037 //       Access: Public
00038 //  Description:
00039 ////////////////////////////////////////////////////////////////////
00040 TextureImage::
00041 TextureImage() {
00042   _preferred_source = (SourceTextureImage *)NULL;
00043   _read_source_image = false;
00044   _allow_release_source_image = true;
00045   _is_surprise = true;
00046   _ever_read_image = false;
00047   _forced_grayscale = false;
00048   _alpha_bits = 0;
00049   _mid_pixel_ratio = 0.0;
00050   _is_cutout = false;
00051   _alpha_mode = EggRenderMode::AM_unspecified;
00052   _txa_wrap_u = EggTexture::WM_unspecified;
00053   _txa_wrap_v = EggTexture::WM_unspecified;
00054   _texture_named = false;
00055   _got_txa_file = false;
00056 }
00057 
00058 ////////////////////////////////////////////////////////////////////
00059 //     Function: TextureImage::note_egg_file
00060 //       Access: Public
00061 //  Description: Records that a particular egg file references this
00062 //               texture.  This is essential to know when deciding how
00063 //               to assign the TextureImage to the various
00064 //               PaletteGroups.
00065 ////////////////////////////////////////////////////////////////////
00066 void TextureImage::
00067 note_egg_file(EggFile *egg_file) {
00068   nassertv(!egg_file->get_complete_groups().empty());
00069   _egg_files.insert(egg_file);
00070 }
00071 
00072 ////////////////////////////////////////////////////////////////////
00073 //     Function: TextureImage::assign_groups
00074 //       Access: Public
00075 //  Description: Assigns the texture to all of the PaletteGroups the
00076 //               various egg files that use it need.  Attempts to
00077 //               choose the minimum set of PaletteGroups that
00078 //               satisfies all of the egg files.
00079 ////////////////////////////////////////////////////////////////////
00080 void TextureImage::
00081 assign_groups() {
00082   if (_egg_files.empty()) {
00083     // If we're not referenced by any egg files any more, assign us to
00084     // no groups.
00085     PaletteGroups empty;
00086     assign_to_groups(empty);
00087     return;
00088   }
00089 
00090   PaletteGroups definitely_in;
00091 
00092   // First, we need to eliminate from consideration all the egg files
00093   // that are already taken care of by the user's explicit group
00094   // assignments for this texture.
00095   WorkingEggs needed_eggs;
00096 
00097   if (_explicitly_assigned_groups.empty()) {
00098     // If we have no explicit group assignments, we must consider all
00099     // the egg files.
00100     copy(_egg_files.begin(), _egg_files.end(), back_inserter(needed_eggs));
00101 
00102   } else {
00103     // Otherwise, we only need to consider the egg files that don't
00104     // have any groups in common with our explicit assignments.
00105 
00106     EggFiles::const_iterator ei;
00107     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00108       PaletteGroups intersect;
00109       intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups());
00110       if (!intersect.empty()) {
00111         // This egg file is satisfied by one of the texture's explicit
00112         // assignments.
00113 
00114         // We must use at least one of the explicitly-assigned groups
00115         // that satisfied the egg file.  We don't need to use all of
00116         // them, however, and we choose the first one arbitrarily.
00117         definitely_in.insert(*intersect.begin());
00118 
00119       } else {
00120         // This egg file was not satisfied by any of the texture's
00121         // explicit assignments.  Therefore, we'll need to choose some
00122         // additional group to assign the texture to, to make the egg
00123         // file happy.  Defer this a bit.
00124         needed_eggs.push_back(*ei);
00125       }
00126     }
00127   }
00128 
00129   while (!needed_eggs.empty()) {
00130     // We need to know the complete set of groups that we need to
00131     // consider adding the texture to.  This is the union of all the egg
00132     // files' requested groups.
00133     PaletteGroups total;
00134     WorkingEggs::const_iterator ei;
00135     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00136       total.make_union(total, (*ei)->get_complete_groups());
00137     }
00138 
00139     // We don't count the "null" group for texture assignment.
00140     total.remove_null();
00141     if (total.empty()) {
00142       break;
00143     }
00144 
00145     // Now, find the group that will satisfy the most egg files.  If
00146     // two groups satisfy the same number of egg files, choose (a) the
00147     // most specific one, i.e. with the lowest dirname_level, or the
00148     // lowest dependency_level if the dirname_levels are equal, and
00149     // (b) the one that has the fewest egg files sharing it.
00150     PaletteGroups::iterator gi = total.begin();
00151     PaletteGroup *best = (*gi);
00152     int best_egg_count = compute_egg_count(best, needed_eggs);
00153     ++gi;
00154     while (gi != total.end()) {
00155       PaletteGroup *group = (*gi);
00156 
00157       // Do we prefer this group to our current 'best'?
00158       bool prefer_group = false;
00159       int group_egg_count = compute_egg_count(group, needed_eggs);
00160       if (group_egg_count != best_egg_count) {
00161         prefer_group = (group_egg_count > best_egg_count);
00162 
00163       } else {
00164         prefer_group = group->is_preferred_over(*best);
00165       }
00166 
00167       if (prefer_group) {
00168         best = group;
00169         best_egg_count = group_egg_count;
00170       }
00171       ++gi;
00172     }
00173 
00174     // Okay, now we've picked the best group.  Eliminate all the eggs
00175     // from consideration that are satisfied by this group, and repeat.
00176     definitely_in.insert(best);
00177 
00178     WorkingEggs next_needed_eggs;
00179     for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
00180       if ((*ei)->get_complete_groups().count(best) == 0) {
00181         // This one wasn't eliminated.
00182         next_needed_eggs.push_back(*ei);
00183       }
00184     }
00185     needed_eggs.swap(next_needed_eggs);
00186   }
00187 
00188   // Finally, now that we've computed the set of groups we need to
00189   // assign the texture to, we need to reconcile this with the set of
00190   // groups we've assigned the texture to previously.
00191   assign_to_groups(definitely_in);
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: TextureImage::get_groups
00196 //       Access: Public
00197 //  Description: Once assign_groups() has been called, this returns
00198 //               the actual set of groups the TextureImage has been
00199 //               assigned to.
00200 ////////////////////////////////////////////////////////////////////
00201 const PaletteGroups &TextureImage::
00202 get_groups() const {
00203   return _actual_assigned_groups;
00204 }
00205 
00206 ////////////////////////////////////////////////////////////////////
00207 //     Function: TextureImage::get_placement
00208 //       Access: Public
00209 //  Description: Gets the TexturePlacement object which represents the
00210 //               assignment of this texture to the indicated group.
00211 //               If the texture has not been assigned to the indicated
00212 //               group, returns NULL.
00213 ////////////////////////////////////////////////////////////////////
00214 TexturePlacement *TextureImage::
00215 get_placement(PaletteGroup *group) const {
00216   Placement::const_iterator pi;
00217   pi = _placement.find(group);
00218   if (pi == _placement.end()) {
00219     return (TexturePlacement *)NULL;
00220   }
00221 
00222   return (*pi).second;
00223 }
00224 
00225 ////////////////////////////////////////////////////////////////////
00226 //     Function: TextureImage::force_replace
00227 //       Access: Public
00228 //  Description: Removes the texture from any PaletteImages it is
00229 //               assigned to, but does not remove it from the groups.
00230 //               It will be re-placed within each group when
00231 //               PaletteGroup::place_all() is called.
00232 ////////////////////////////////////////////////////////////////////
00233 void TextureImage::
00234 force_replace() {
00235   Placement::iterator pi;
00236   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00237     (*pi).second->force_replace();
00238   }
00239 }
00240 
00241 ////////////////////////////////////////////////////////////////////
00242 //     Function: TextureImage::mark_eggs_stale
00243 //       Access: Public
00244 //  Description: Marks all the egg files that reference this texture
00245 //               stale.  Should be called only when the texture
00246 //               properties change in some catastrophic way that will
00247 //               require every egg file referencing it to be
00248 //               regenerated, even if it is not palettized.
00249 ////////////////////////////////////////////////////////////////////
00250 void TextureImage::
00251 mark_eggs_stale() {
00252   Placement::iterator pi;
00253   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00254     (*pi).second->mark_eggs_stale();
00255   }
00256 }
00257 
00258 ////////////////////////////////////////////////////////////////////
00259 //     Function: TextureImage::mark_texture_named
00260 //       Access: Public
00261 //  Description: Indicates that this particular texture has been named
00262 //               by the user for processing this session, normally by
00263 //               listing an egg file on the command line that
00264 //               references it.
00265 ////////////////////////////////////////////////////////////////////
00266 void TextureImage::
00267 mark_texture_named() {
00268   _texture_named = true;
00269 }
00270 
00271 ////////////////////////////////////////////////////////////////////
00272 //     Function: TextureImage::is_texture_named
00273 //       Access: Public
00274 //  Description: Returns true if this particular texture has been
00275 //               named by the user for procession this session, for
00276 //               instance by listing an egg file on the command line
00277 //               that references it.
00278 ////////////////////////////////////////////////////////////////////
00279 bool TextureImage::
00280 is_texture_named() const {
00281   return _texture_named;
00282 }
00283 
00284 ////////////////////////////////////////////////////////////////////
00285 //     Function: TextureImage::pre_txa_file
00286 //       Access: Public
00287 //  Description: Updates any internal state prior to reading the .txa
00288 //               file.
00289 ////////////////////////////////////////////////////////////////////
00290 void TextureImage::
00291 pre_txa_file() {
00292   // Save our current properties, so we can note if they change.
00293   _pre_txa_properties = _properties;
00294 
00295   // Get our properties from the actual image for this texture.  It's
00296   // possible the .txa file will update them further.
00297   SourceTextureImage *source = get_preferred_source();
00298   if (source != (SourceTextureImage *)NULL) {
00299     _properties = source->get_properties();
00300   }
00301 
00302   _pre_txa_alpha_mode = _alpha_mode;
00303   _alpha_mode = EggRenderMode::AM_unspecified;
00304 
00305   _request.pre_txa_file();
00306   _is_surprise = true;
00307 }
00308 
00309 ////////////////////////////////////////////////////////////////////
00310 //     Function: TextureImage::post_txa_file
00311 //       Access: Public
00312 //  Description: Once the .txa file has been read and the TextureImage
00313 //               matched against it, considers applying the requested
00314 //               size change.  Updates the TextureImage's size with
00315 //               the size the texture ought to be, if this can be
00316 //               determined.
00317 ////////////////////////////////////////////////////////////////////
00318 void TextureImage::
00319 post_txa_file() {
00320   _got_txa_file = true;
00321 
00322   // First, get the actual size of the texture.
00323   SourceTextureImage *source = get_preferred_source();
00324   if (source != (SourceTextureImage *)NULL) {
00325     if (source->get_size()) {
00326       _size_known = true;
00327       _x_size = source->get_x_size();
00328       _y_size = source->get_y_size();
00329       _properties.set_num_channels(source->get_num_channels());
00330     }
00331   }
00332 
00333   // Now update this with a particularly requested size.
00334   if (_request._got_size) {
00335     _size_known = true;
00336     _x_size = _request._x_size;
00337     _y_size = _request._y_size;
00338   }
00339 
00340   if (_txa_wrap_u != _request._wrap_u ||
00341       _txa_wrap_v != _request._wrap_v) {
00342     _txa_wrap_u = _request._wrap_u;
00343     _txa_wrap_v = _request._wrap_v;
00344 
00345     // If the explicit wrap mode changes, we may need to regenerate
00346     // the egg files, and/or refill the palettes.
00347     mark_eggs_stale();
00348 
00349     Placement::iterator pi;
00350     for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00351       TexturePlacement *placement = (*pi).second;
00352       placement->mark_unfilled();
00353     }
00354   }
00355 
00356   if (_properties.has_num_channels() && !_request._keep_format) {
00357     int num_channels = _properties.get_num_channels();
00358     // Examine the image to determine if we can downgrade the number
00359     // of channels, for instance from color to grayscale.
00360     if (num_channels == 3 || num_channels == 4) {
00361       consider_grayscale();
00362     }
00363     
00364     // Also consider the alpha properties, and whether we should
00365     // downgrade from alpha to non-alpha.
00366     if (num_channels == 2 || num_channels == 4) {
00367       consider_alpha();
00368     }
00369   }
00370 
00371   // However, if we got an explicit request for channels, honor that.
00372   if (_request._got_num_channels) {
00373     _properties.set_num_channels(_request._num_channels);
00374   }
00375 
00376   _properties._generic_format = _request._generic_format;
00377   _properties._keep_format = _request._keep_format;
00378 
00379   if (_request._format != EggTexture::F_unspecified) {
00380     _properties._format = _request._format;
00381     _properties._force_format = _request._force_format;
00382   }
00383 
00384   if (_request._minfilter != EggTexture::FT_unspecified) {
00385     _properties._minfilter = _request._minfilter;
00386   }
00387   if (_request._magfilter != EggTexture::FT_unspecified) {
00388     _properties._magfilter = _request._magfilter;
00389   }
00390 
00391   _properties._anisotropic_degree = _request._anisotropic_degree;
00392 
00393   if (_properties._color_type == (PNMFileType *)NULL) {
00394     _properties._color_type = _request._properties._color_type;
00395     _properties._alpha_type = _request._properties._alpha_type;
00396   }
00397 
00398   // Finally, make sure our properties are fully defined.
00399   _properties.fully_define();
00400 
00401   // Now, if our properties have changed in all that from our previous
00402   // session, we need to re-place ourself in all palette groups.
00403   if (_properties != _pre_txa_properties) {
00404     force_replace();
00405 
00406     // The above will mark the egg files stale when the texture is
00407     // palettized (since the UV's will certainly need to be
00408     // recomputed), but sometimes we need to mark the egg files stale
00409     // even when the texture is not palettized (if a critical property
00410     // has changed).  The following accomplishes this:
00411     if (!_properties.egg_properties_match(_pre_txa_properties)) {
00412       mark_eggs_stale();
00413     }
00414   }
00415 
00416   // The alpha mode isn't stored in the properties, because it doesn't
00417   // affect which textures may be associated into a common palette.
00418   if (_request._alpha_mode != EggRenderMode::AM_unspecified) {
00419     _alpha_mode = _request._alpha_mode;
00420   }
00421 
00422   // On the other hand, if we don't have an alpha channel, we
00423   // shouldn't have an alpha mode.
00424   if (_properties.has_num_channels()) {
00425     int num_channels = _properties.get_num_channels();
00426     if (num_channels == 1 || num_channels == 3) {
00427       _alpha_mode = EggRenderMode::AM_unspecified;
00428     }
00429   }
00430 
00431   // If we've changed the alpha mode, we should also mark the eggs
00432   // stale.
00433   if (_pre_txa_alpha_mode != _alpha_mode) {
00434     mark_eggs_stale();
00435   }
00436 }
00437 
00438 ////////////////////////////////////////////////////////////////////
00439 //     Function: TextureImage::got_txa_file
00440 //       Access: Public
00441 //  Description: Returns true if this TextureImage has been looked up
00442 //               in the .txa file this session, false otherwise.
00443 ////////////////////////////////////////////////////////////////////
00444 bool TextureImage::
00445 got_txa_file() const {
00446   return _got_txa_file;
00447 }
00448 
00449 ////////////////////////////////////////////////////////////////////
00450 //     Function: TextureImage::determine_placement_size
00451 //       Access: Public
00452 //  Description: Calls determine_size() on each TexturePlacement for
00453 //               the texture, to ensure that each TexturePlacement is
00454 //               still requesting the best possible size for the
00455 //               texture.
00456 ////////////////////////////////////////////////////////////////////
00457 void TextureImage::
00458 determine_placement_size() {
00459   Placement::iterator pi;
00460   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00461     TexturePlacement *placement = (*pi).second;
00462     placement->determine_size();
00463   }
00464 }
00465 
00466 ////////////////////////////////////////////////////////////////////
00467 //     Function: TextureImage::get_omit
00468 //       Access: Public
00469 //  Description: Returns true if the user specifically requested to
00470 //               omit this texture via the "omit" keyword in the .txa
00471 //               file, or false otherwise.
00472 ////////////////////////////////////////////////////////////////////
00473 bool TextureImage::
00474 get_omit() const {
00475   return _request._omit;
00476 }
00477 
00478 ////////////////////////////////////////////////////////////////////
00479 //     Function: TextureImage::get_coverage_threshold
00480 //       Access: Public
00481 //  Description: Returns the appropriate coverage threshold for this
00482 //               texture.  This is either the
00483 //               Palettizer::_coverage_threshold parameter, given
00484 //               globally via -r, or a particular value for this
00485 //               texture as supplied by the "coverage" keyword in the
00486 //               .txa file.
00487 ////////////////////////////////////////////////////////////////////
00488 double TextureImage::
00489 get_coverage_threshold() const {
00490   return _request._coverage_threshold;
00491 }
00492 
00493 ////////////////////////////////////////////////////////////////////
00494 //     Function: TextureImage::get_margin
00495 //       Access: Public
00496 //  Description: Returns the appropriate margin for this texture.
00497 //               This is either the Palettizer::_margin parameter, or
00498 //               a particular value for this texture as supplied by
00499 //               the "margin" keyword in the .txa file.
00500 ////////////////////////////////////////////////////////////////////
00501 int TextureImage::
00502 get_margin() const {
00503   return _request._margin;
00504 }
00505 
00506 ////////////////////////////////////////////////////////////////////
00507 //     Function: TextureImage::is_surprise
00508 //       Access: Public
00509 //  Description: Returns true if this particular texture is a
00510 //               'surprise', i.e. it wasn't matched by a line in the
00511 //               .txa file that didn't include the keyword 'cont'.
00512 ////////////////////////////////////////////////////////////////////
00513 bool TextureImage::
00514 is_surprise() const {
00515   if (_placement.empty()) {
00516     // A texture that is not actually placed anywhere is not
00517     // considered a surprise.
00518     return false;
00519   }
00520 
00521   return _is_surprise;
00522 }
00523 
00524 ////////////////////////////////////////////////////////////////////
00525 //     Function: TextureImage::is_used
00526 //       Access: Public
00527 //  Description: Returns true if this particular texture has been
00528 //               placed somewhere, anywhere, or false if it is not
00529 //               used.
00530 ////////////////////////////////////////////////////////////////////
00531 bool TextureImage::
00532 is_used() const {
00533   return !_placement.empty();
00534 }
00535 
00536 ////////////////////////////////////////////////////////////////////
00537 //     Function: TextureImage::get_alpha_mode
00538 //       Access: Public
00539 //  Description: Returns the alpha mode that should be used to render
00540 //               objects with this texture, as specified by the user
00541 //               or as determined from examining the texture's alpha
00542 //               channel.
00543 ////////////////////////////////////////////////////////////////////
00544 EggRenderMode::AlphaMode TextureImage::
00545 get_alpha_mode() const {
00546   return _alpha_mode;
00547 }
00548 
00549 ////////////////////////////////////////////////////////////////////
00550 //     Function: TextureImage::get_txa_wrap_u
00551 //       Access: Public
00552 //  Description: Returns the wrap mode specified in the u direction in
00553 //               the txa file, or WM_unspecified.
00554 ////////////////////////////////////////////////////////////////////
00555 EggTexture::WrapMode TextureImage::
00556 get_txa_wrap_u() const {
00557   return _txa_wrap_u;
00558 }
00559 
00560 ////////////////////////////////////////////////////////////////////
00561 //     Function: TextureImage::get_txa_wrap_v
00562 //       Access: Public
00563 //  Description: Returns the wrap mode specified in the v direction in
00564 //               the txa file, or WM_unspecified.
00565 ////////////////////////////////////////////////////////////////////
00566 EggTexture::WrapMode TextureImage::
00567 get_txa_wrap_v() const {
00568   return _txa_wrap_v;
00569 }
00570 
00571 
00572 ////////////////////////////////////////////////////////////////////
00573 //     Function: TextureImage::get_source
00574 //       Access: Public
00575 //  Description: Returns the SourceTextureImage corresponding to the
00576 //               given filename(s).  If the given filename has never
00577 //               been used as a SourceTexture for this particular
00578 //               texture, creates a new SourceTextureImage and returns
00579 //               that.
00580 ////////////////////////////////////////////////////////////////////
00581 SourceTextureImage *TextureImage::
00582 get_source(const Filename &filename, const Filename &alpha_filename,
00583            int alpha_file_channel) {
00584   string key = get_source_key(filename, alpha_filename, alpha_file_channel);
00585 
00586   Sources::iterator si;
00587   si = _sources.find(key);
00588   if (si != _sources.end()) {
00589     return (*si).second;
00590   }
00591 
00592   SourceTextureImage *source =
00593     new SourceTextureImage(this, filename, alpha_filename, alpha_file_channel);
00594   _sources.insert(Sources::value_type(key, source));
00595 
00596   // Clear out the preferred source image to force us to rederive this
00597   // next time someone asks.
00598   _preferred_source = (SourceTextureImage *)NULL;
00599   _read_source_image = false;
00600 
00601   return source;
00602 }
00603 
00604 ////////////////////////////////////////////////////////////////////
00605 //     Function: TextureImage::get_preferred_source
00606 //       Access: Public
00607 //  Description: Determines the preferred source image for examining
00608 //               size and reading pixels, etc.  This is the largest
00609 //               and most recent of all the available source images.
00610 ////////////////////////////////////////////////////////////////////
00611 SourceTextureImage *TextureImage::
00612 get_preferred_source() {
00613   if (_preferred_source != (SourceTextureImage *)NULL) {
00614     return _preferred_source;
00615   }
00616 
00617   // Now examine all of the various source images available to us and
00618   // pick the most suitable.  We base this on the following criteria:
00619 
00620   // (1) A suitable source image must be referenced by at least one
00621   // egg file, unless no source images are referenced by any egg file.
00622 
00623   // (2) A larger source image is preferable to a smaller one.
00624 
00625   // (3) Given two source images of the same size, the more recent one
00626   // is preferable.
00627 
00628   // Are any source images referenced by an egg file?
00629 
00630   bool any_referenced = false;
00631   Sources::iterator si;
00632   for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) {
00633     SourceTextureImage *source = (*si).second;
00634     if (source->get_egg_count() > 0) {
00635       any_referenced = true;
00636     }
00637   }
00638 
00639   SourceTextureImage *best = (SourceTextureImage *)NULL;
00640   int best_size = 0;
00641 
00642   for (si = _sources.begin(); si != _sources.end(); ++si) {
00643     SourceTextureImage *source = (*si).second;
00644 
00645     if (source->get_egg_count() > 0 || !any_referenced) {
00646       // Rule (1) passes.
00647 
00648       if (source->exists() && source->get_size()) {
00649         int source_size = source->get_x_size() * source->get_y_size();
00650         if (best == (SourceTextureImage *)NULL) {
00651           best = source;
00652           best_size = source_size;
00653 
00654         } else if (source_size > best_size) {
00655           // Rule (2) passes.
00656           best = source;
00657           best_size = source_size;
00658 
00659         } else if (source_size == best_size &&
00660                    source->get_filename().compare_timestamps(best->get_filename()) > 0) {
00661           // Rule (3) passes.
00662           best = source;
00663           best_size = source_size;
00664         }
00665       }
00666     }
00667   }
00668 
00669   if (best == (SourceTextureImage *)NULL && !_sources.empty()) {
00670     // If we didn't pick any that pass, it must be that all of them
00671     // are unreadable.  In this case, it really doesn't matter which
00672     // one we pick, but we should at least pick one that has an egg
00673     // reference, if any of them do.
00674     if (any_referenced) {
00675       for (si = _sources.begin();
00676            si != _sources.end() && best == (SourceTextureImage *)NULL;
00677            ++si) {
00678         SourceTextureImage *source = (*si).second;
00679         if (source->get_egg_count() > 0) {
00680           best = source;
00681         }
00682       }
00683     } else {
00684       best = (*_sources.begin()).second;
00685     }
00686   }
00687 
00688   _preferred_source = best;
00689   return _preferred_source;
00690 }
00691 
00692 ////////////////////////////////////////////////////////////////////
00693 //     Function: TextureImage::clear_source_basic_properties
00694 //       Access: Public
00695 //  Description: Calls clear_basic_properties() on each source texture
00696 //               image used by this texture, to reset the properties
00697 //               in preparation for re-applying them from the set of
00698 //               all known egg files.
00699 ////////////////////////////////////////////////////////////////////
00700 void TextureImage::
00701 clear_source_basic_properties() {
00702   Sources::iterator si;
00703   for (si = _sources.begin(); si != _sources.end(); ++si) {
00704     SourceTextureImage *source = (*si).second;
00705     source->clear_basic_properties();
00706   }
00707 }
00708 
00709 ////////////////////////////////////////////////////////////////////
00710 //     Function: TextureImage::copy_unplaced
00711 //       Access: Public
00712 //  Description: Copies the texture to whichever destination
00713 //               directories are appropriate for the groups in which
00714 //               it has been unplaced.  Also removes the old filenames
00715 //               for previous sessions where it was unplaced, but is
00716 //               no longer.
00717 //
00718 //               If redo_all is true, this recopies the texture
00719 //               whether it needed to or not.
00720 ////////////////////////////////////////////////////////////////////
00721 void TextureImage::
00722 copy_unplaced(bool redo_all) {
00723   // First, we need to build up the set of DestTextureImages that
00724   // represents the files we need to generate.
00725   Dests generate;
00726 
00727   // Go through all the TexturePlacements and note the ones for which
00728   // we're unplaced.  We check get_omit_reason() and not is_placed(),
00729   // because we want to consider solitary images to be unplaced in
00730   // this case.
00731   Placement::iterator pi;
00732   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00733     TexturePlacement *placement = (*pi).second;
00734     if (placement->get_omit_reason() != OR_none &&
00735         placement->get_omit_reason() != OR_unknown) {
00736       DestTextureImage *dest = new DestTextureImage(placement);
00737       Filename filename = dest->get_filename();
00738       FilenameUnifier::make_canonical(filename);
00739 
00740       pair<Dests::iterator, bool> insert_result = generate.insert
00741         (Dests::value_type(filename, dest));
00742       if (!insert_result.second) {
00743         // At least two DestTextureImages map to the same filename, no
00744         // sweat.
00745         delete dest;
00746         dest = (*insert_result.first).second;
00747       }
00748 
00749       placement->set_dest(dest);
00750 
00751     } else {
00752       placement->set_dest((DestTextureImage *)NULL);
00753     }
00754   }
00755 
00756   if (redo_all) {
00757     // If we're redoing everything, we remove everything first and
00758     // then recopy it again.
00759     Dests empty;
00760     remove_old_dests(empty, _dests);
00761     copy_new_dests(generate, empty);
00762 
00763   } else {
00764     // Otherwise, we only remove and recopy the things that changed
00765     // between this time and last time.
00766     remove_old_dests(generate, _dests);
00767     copy_new_dests(generate, _dests);
00768   }
00769 
00770   // Clean up the old set.
00771   Dests::iterator di;
00772   for (di = _dests.begin(); di != _dests.end(); ++di) {
00773     delete (*di).second;
00774   }
00775 
00776   _dests.swap(generate);
00777 }
00778 
00779 ////////////////////////////////////////////////////////////////////
00780 //     Function: TextureImage::read_source_image
00781 //       Access: Public
00782 //  Description: Reads in the original image, if it has not already
00783 //               been read, and returns it.
00784 ////////////////////////////////////////////////////////////////////
00785 const PNMImage &TextureImage::
00786 read_source_image() {
00787   if (!_read_source_image) {
00788     SourceTextureImage *source = get_preferred_source();
00789     if (source != (SourceTextureImage *)NULL) {
00790       source->read(_source_image);
00791     }
00792     _read_source_image = true;
00793     _allow_release_source_image = true;
00794     _ever_read_image = true;
00795   }
00796 
00797   return _source_image;
00798 }
00799 
00800 ////////////////////////////////////////////////////////////////////
00801 //     Function: TextureImage::release_source_image
00802 //       Access: Public
00803 //  Description: Frees the memory that was allocated by a previous
00804 //               call to read_source_image().  The next time
00805 //               read_source_image() is called, it will have to read
00806 //               the disk again.
00807 ////////////////////////////////////////////////////////////////////
00808 void TextureImage::
00809 release_source_image() {
00810   if (_read_source_image && _allow_release_source_image) {
00811     _source_image.clear();
00812     _read_source_image = false;
00813   }
00814 }
00815 
00816 ////////////////////////////////////////////////////////////////////
00817 //     Function: TextureImage::set_source_image
00818 //       Access: Public
00819 //  Description: Accepts the indicated source image as if it had been
00820 //               read from disk.  This image is copied into the
00821 //               structure, and will be returned by future calls to
00822 //               read_source_image().
00823 ////////////////////////////////////////////////////////////////////
00824 void TextureImage::
00825 set_source_image(const PNMImage &image) {
00826   _source_image = image;
00827   _allow_release_source_image = false;
00828   _read_source_image = true;
00829   _ever_read_image = true;
00830 }
00831 
00832 ////////////////////////////////////////////////////////////////////
00833 //     Function: TextureImage::read_header
00834 //       Access: Public
00835 //  Description: Causes the header part of the image to be reread,
00836 //               usually to confirm that its image properties (size,
00837 //               number of channels, etc.) haven't changed.
00838 ////////////////////////////////////////////////////////////////////
00839 void TextureImage::
00840 read_header() {
00841   if (!_read_source_image) {
00842     SourceTextureImage *source = get_preferred_source();
00843     if (source != (SourceTextureImage *)NULL) {
00844       source->read_header();
00845     }
00846   }
00847 }
00848 
00849 ////////////////////////////////////////////////////////////////////
00850 //     Function: TextureImage::is_newer_than
00851 //       Access: Public
00852 //  Description: Returns true if the source image is newer than the
00853 //               indicated file, false otherwise.  If the image has
00854 //               already been read, this always returns false.
00855 ////////////////////////////////////////////////////////////////////
00856 bool TextureImage::
00857 is_newer_than(const Filename &reference_filename) {
00858   if (!_read_source_image) {
00859     SourceTextureImage *source = get_preferred_source();
00860     if (source != (SourceTextureImage *)NULL) {
00861       const Filename &source_filename = source->get_filename();
00862       return source_filename.compare_timestamps(reference_filename) >= 0;
00863     }
00864   }
00865 
00866   return false;
00867 }
00868 
00869 ////////////////////////////////////////////////////////////////////
00870 //     Function: TextureImage::write_source_pathnames
00871 //       Access: Public
00872 //  Description: Writes the list of source pathnames that might
00873 //               contribute to this texture to the indicated output
00874 //               stream, one per line.
00875 ////////////////////////////////////////////////////////////////////
00876 void TextureImage::
00877 write_source_pathnames(ostream &out, int indent_level) const {
00878   Sources::const_iterator si;
00879   for (si = _sources.begin(); si != _sources.end(); ++si) {
00880     SourceTextureImage *source = (*si).second;
00881 
00882     if (source->get_egg_count() > 0) {
00883       indent(out, indent_level);
00884       source->output_filename(out);
00885       if (!source->is_size_known()) {
00886         out << " (unknown size)";
00887 
00888       } else {
00889         out << " " << source->get_x_size() << " "
00890             << source->get_y_size();
00891 
00892         if (source->get_properties().has_num_channels()) {
00893           out << " " << source->get_properties().get_num_channels();
00894         }
00895       }
00896       out << "\n";
00897     }
00898   }
00899 
00900   if (_is_cutout) {
00901     indent(out, indent_level)
00902       << "Cutout image (ratio " << (float)_mid_pixel_ratio << ")\n";
00903   }
00904 
00905   // Now write out the group assignments.
00906   if (!_egg_files.empty()) {
00907     // Sort the egg files into order by name for output.
00908     pvector<EggFile *> egg_vector;
00909     egg_vector.reserve(_egg_files.size());
00910     EggFiles::const_iterator ei;
00911     for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
00912       egg_vector.push_back(*ei);
00913     }
00914     sort(egg_vector.begin(), egg_vector.end(),
00915          IndirectCompareNames<EggFile>());
00916 
00917     indent(out, indent_level)
00918       << "Used by:\n";
00919     pvector<EggFile *>::const_iterator evi;
00920     for (evi = egg_vector.begin(); evi != egg_vector.end(); ++evi) {
00921       EggFile *egg = (*evi);
00922       indent(out, indent_level + 2)
00923         << egg->get_name() << " (";
00924       if (egg->get_explicit_groups().empty()) {
00925         out << *egg->get_default_group();
00926       } else {
00927         out << egg->get_explicit_groups();
00928       }
00929       out << ")\n";
00930     }
00931   }
00932   if (!_explicitly_assigned_groups.empty()) {
00933     indent(out, indent_level)
00934       << "Explicitly assigned to " << _explicitly_assigned_groups << " in .txa\n";
00935   }
00936 
00937   if (_placement.empty()) {
00938     indent(out, indent_level)
00939       << "Not used.\n";
00940   } else {
00941     indent(out, indent_level)
00942       << "Assigned to " << _actual_assigned_groups << "\n";
00943   }
00944 }
00945 
00946 ////////////////////////////////////////////////////////////////////
00947 //     Function: TextureImage::write_scale_info
00948 //       Access: Public
00949 //  Description: Writes the information about the texture's size and
00950 //               placement.
00951 ////////////////////////////////////////////////////////////////////
00952 void TextureImage::
00953 write_scale_info(ostream &out, int indent_level) {
00954   SourceTextureImage *source = get_preferred_source();
00955   indent(out, indent_level) << get_name();
00956 
00957   // Write the list of groups we're placed in.
00958   if (_placement.empty()) {
00959     out << " (not used)";
00960   } else {
00961     Placement::const_iterator pi;
00962     pi = _placement.begin();
00963     out << " (" << (*pi).second->get_group()->get_name();
00964     ++pi;
00965     while (pi != _placement.end()) {
00966       out << " " << (*pi).second->get_group()->get_name();
00967       ++pi;
00968     }
00969     out << ")";
00970   }
00971 
00972   out << " orig ";
00973 
00974   if (source == (SourceTextureImage *)NULL ||
00975       !source->is_size_known()) {
00976     out << "unknown";
00977   } else {
00978     out << source->get_x_size() << " " << source->get_y_size()
00979         << " " << source->get_num_channels();
00980   }
00981 
00982   if (!_placement.empty() && is_size_known()) {
00983     out << " new " << get_x_size() << " " << get_y_size()
00984         << " " << get_num_channels();
00985 
00986     if (source != (SourceTextureImage *)NULL &&
00987         source->is_size_known()) {
00988       double scale =
00989         100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
00990                  ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
00991       out << " scale " << scale << "%";
00992     }
00993   }
00994   out << "\n";
00995 
00996   // Also cross-reference the placed and unplaced information.
00997   Placement::iterator pi;
00998   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
00999     TexturePlacement *placement = (*pi).second;
01000     if (placement->get_omit_reason() == OR_none) {
01001       PaletteImage *image = placement->get_image();
01002       nassertv(image != (PaletteImage *)NULL);
01003       indent(out, indent_level + 2)
01004         << "placed on "
01005         << FilenameUnifier::make_user_filename(image->get_filename())
01006         << "\n";
01007 
01008     } else if (placement->get_omit_reason() == OR_unknown) {
01009       indent(out, indent_level + 2)
01010         << "not placed because unknown.\n";
01011 
01012     } else {
01013       DestTextureImage *image = placement->get_dest();
01014       nassertv(image != (DestTextureImage *)NULL);
01015       indent(out, indent_level + 2)
01016         << "copied to "
01017         << FilenameUnifier::make_user_filename(image->get_filename());
01018       if (image->is_size_known() && is_size_known() &&
01019           (image->get_x_size() != get_x_size() ||
01020            image->get_y_size() != get_y_size())) {
01021         out << " at size " << image->get_x_size() << " "
01022             << image->get_y_size();
01023         if (source != (SourceTextureImage *)NULL &&
01024             source->is_size_known()) {
01025           double scale =
01026             100.0 * (((double)image->get_x_size() / (double)source->get_x_size()) +
01027                      ((double)image->get_y_size() / (double)source->get_y_size())) / 2.0;
01028           out << " scale " << scale << "%";
01029         }
01030       }
01031       out << "\n";
01032     }
01033   }
01034 }
01035 
01036 ////////////////////////////////////////////////////////////////////
01037 //     Function: TextureImage::compute_egg_count
01038 //       Access: Private
01039 //  Description: Counts the number of egg files in the indicated set
01040 //               that will be satisfied if a texture is assigned to
01041 //               the indicated group.
01042 ////////////////////////////////////////////////////////////////////
01043 int TextureImage::
01044 compute_egg_count(PaletteGroup *group,
01045                   const TextureImage::WorkingEggs &egg_files) {
01046   int count = 0;
01047 
01048   WorkingEggs::const_iterator ei;
01049   for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
01050     if ((*ei)->get_complete_groups().count(group) != 0) {
01051       count++;
01052     }
01053   }
01054 
01055   return count;
01056 }
01057 
01058 ////////////////////////////////////////////////////////////////////
01059 //     Function: TextureImage::assign_to_groups
01060 //       Access: Private
01061 //  Description: Assigns the texture to the indicated set of groups.
01062 //               If the texture was previously assigned to any of
01063 //               these groups, keeps the same TexturePlacement object
01064 //               for the assignment; at the same time, deletes any
01065 //               TexturePlacement objects that represent groups we are
01066 //               no longer assigned to.
01067 ////////////////////////////////////////////////////////////////////
01068 void TextureImage::
01069 assign_to_groups(const PaletteGroups &groups) {
01070   PaletteGroups::const_iterator gi;
01071   Placement::const_iterator pi;
01072 
01073   Placement new_placement;
01074 
01075   gi = groups.begin();
01076   pi = _placement.begin();
01077 
01078   while (gi != groups.end() && pi != _placement.end()) {
01079     PaletteGroup *a = (*gi);
01080     PaletteGroup *b = (*pi).first;
01081 
01082     if (a < b) {
01083       // Here's a group we're now assigned to that we weren't assigned
01084       // to previously.
01085       TexturePlacement *place = a->prepare(this);
01086       new_placement.insert
01087         (new_placement.end(), Placement::value_type(a, place));
01088       ++gi;
01089 
01090     } else if (b < a) {
01091       // Here's a group we're no longer assigned to.
01092       TexturePlacement *place = (*pi).second;
01093       delete place;
01094       ++pi;
01095 
01096     } else { // b == a
01097       // Here's a group we're still assigned to.
01098       TexturePlacement *place = (*pi).second;
01099       new_placement.insert
01100         (new_placement.end(), Placement::value_type(a, place));
01101       ++gi;
01102       ++pi;
01103     }
01104   }
01105 
01106   while (gi != groups.end()) {
01107     // Here's a group we're now assigned to that we weren't assigned
01108     // to previously.
01109     PaletteGroup *a = (*gi);
01110     TexturePlacement *place = a->prepare(this);
01111     new_placement.insert
01112       (new_placement.end(), Placement::value_type(a, place));
01113     ++gi;
01114   }
01115 
01116   while (pi != _placement.end()) {
01117     // Here's a group we're no longer assigned to.
01118     TexturePlacement *place = (*pi).second;
01119     delete place;
01120     ++pi;
01121   }
01122 
01123   _placement.swap(new_placement);
01124   _actual_assigned_groups = groups;
01125 }
01126 
01127 ////////////////////////////////////////////////////////////////////
01128 //     Function: TextureImage::consider_grayscale
01129 //       Access: Private
01130 //  Description: Examines the actual contents of the image to
01131 //               determine if it should maybe be considered a
01132 //               grayscale image (even though it has separate rgb
01133 //               components).
01134 ////////////////////////////////////////////////////////////////////
01135 void TextureImage::
01136 consider_grayscale() {
01137   // Since this isn't likely to change for a particular texture after
01138   // its creation, we save a bit of time by not performing this check
01139   // unless this is the first time we've ever seen this texture.  This
01140   // will save us from having to load the texture images each time we
01141   // look at them.  On the other hand, if we've already loaded up the
01142   // image, then go ahead.
01143   if (!_read_source_image && _ever_read_image) {
01144     if (_forced_grayscale) {
01145       _properties.force_grayscale();
01146     }
01147     return;
01148   }
01149 
01150   const PNMImage &source = read_source_image();
01151   if (!source.is_valid()) {
01152     return;
01153   }
01154 
01155   for (int y = 0; y < source.get_y_size(); y++) {
01156     for (int x = 0; x < source.get_x_size(); x++) {
01157       const xel &v = source.get_xel_val(x, y);
01158       if (PPM_GETR(v) != PPM_GETG(v) || PPM_GETR(v) != PPM_GETB(v)) {
01159         // Here's a colored pixel.  We can't go grayscale.
01160         _forced_grayscale = false;
01161         return;
01162       }
01163     }
01164   }
01165 
01166   // All pixels in the image were grayscale!
01167   _properties.force_grayscale();
01168   _forced_grayscale = true;
01169 }
01170 
01171 ////////////////////////////////////////////////////////////////////
01172 //     Function: TextureImage::consider_alpha
01173 //       Access: Private
01174 //  Description: Examines the actual contents of the image to
01175 //               determine what alpha properties it has.
01176 ////////////////////////////////////////////////////////////////////
01177 void TextureImage::
01178 consider_alpha() {
01179   // As above, we don't bother doing this if we've already done this
01180   // in a previous session.
01181 
01182   // _alpha_bits == -1 indicates we have read an older textures.boo
01183   // file that didn't define these bits.
01184   if (_read_source_image || !_ever_read_image || _alpha_bits == -1) {
01185     _alpha_bits = 0;
01186     int num_mid_pixels = 0;
01187 
01188     const PNMImage &source = read_source_image();
01189     if (source.is_valid() && source.has_alpha()) {
01190       xelval maxval = source.get_maxval();
01191       for (int y = 0; y < source.get_y_size(); y++) {
01192         for (int x = 0; x < source.get_x_size(); x++) {
01193           xelval alpha_val = source.get_alpha_val(x, y);
01194           if (alpha_val == 0) {
01195             _alpha_bits |= AB_zero;
01196           } else if (alpha_val == maxval) {
01197             _alpha_bits |= AB_one;
01198           } else {
01199             _alpha_bits |= AB_mid;
01200             ++num_mid_pixels;
01201           }
01202         }
01203       }
01204     }
01205 
01206     int num_pixels = source.get_x_size() * source.get_y_size();
01207     _mid_pixel_ratio = 0.0;
01208     if (num_pixels != 0) {
01209       _mid_pixel_ratio = (double)num_mid_pixels / (double)num_pixels;
01210     }
01211   }
01212 
01213   _is_cutout = false;
01214 
01215   if (_alpha_bits != 0) {
01216     if (_alpha_bits == AB_one) {
01217       // All alpha pixels are white; drop the alpha channel.
01218       _properties.force_nonalpha();
01219 
01220     } else if (_alpha_bits == AB_zero) {
01221       // All alpha pixels are invisible; this is probably a mistake.
01222       // Drop the alpha channel and complain.
01223       _properties.force_nonalpha();
01224       if (_read_source_image) {
01225         nout << *this << " has an all-zero alpha channel; dropping alpha.\n";
01226       }
01227 
01228     } else if (_alpha_mode == EggRenderMode::AM_unspecified) {
01229       // Consider fiddling with the alpha mode, if the user hasn't
01230       // specified a particular alpha mode in the txa file.
01231       if ((_alpha_bits & AB_mid) == 0) {
01232         // No middle range bits: a binary alpha image.
01233         _alpha_mode = EggRenderMode::AM_binary;
01234 
01235       } else if ((_alpha_bits & AB_one) != 0 && _mid_pixel_ratio < pal->_cutout_ratio) {
01236         // At least some opaque bits, and relatively few middle range
01237         // bits: a cutout image.
01238         _alpha_mode = pal->_cutout_mode;
01239         _is_cutout = true;
01240 
01241       } else {
01242         // No opaque bits; just use regular alpha blending.
01243         _alpha_mode = EggRenderMode::AM_blend;
01244       }
01245     }
01246   }
01247 }
01248 
01249 ////////////////////////////////////////////////////////////////////
01250 //     Function: TextureImage::remove_old_dests
01251 //       Access: Private
01252 //  Description: Removes all of the filenames named in b that are not
01253 //               also named in a.
01254 ////////////////////////////////////////////////////////////////////
01255 void TextureImage::
01256 remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01257   Dests::const_iterator ai = a.begin();
01258   Dests::const_iterator bi = b.begin();
01259 
01260   while (ai != a.end() && bi != b.end()) {
01261     const string &astr = (*ai).first;
01262     const string &bstr = (*bi).first;
01263 
01264     if (astr < bstr) {
01265       // Here's a filename in a, not in b.
01266       ++ai;
01267 
01268     } else if (bstr < astr) {
01269       // Here's a filename in b, not in a.
01270       (*bi).second->unlink();
01271       ++bi;
01272 
01273     } else { // bstr == astr
01274       // Here's a filename in both a and b.
01275       ++ai;
01276       ++bi;
01277     }
01278   }
01279 
01280   while (bi != b.end()) {
01281     // Here's a filename in b, not in a.
01282     (*bi).second->unlink();
01283     ++bi;
01284   }
01285 
01286   while (ai != a.end()) {
01287     ++ai;
01288   }
01289 }
01290 
01291 ////////////////////////////////////////////////////////////////////
01292 //     Function: TextureImage::copy_new_dests
01293 //       Access: Private
01294 //  Description: Copies a resized texture into each filename named in
01295 //               a that is not also listed in b, or whose
01296 //               corresponding listing in b is out of date.
01297 ////////////////////////////////////////////////////////////////////
01298 void TextureImage::
01299 copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
01300   Dests::const_iterator ai = a.begin();
01301   Dests::const_iterator bi = b.begin();
01302 
01303   while (ai != a.end() && bi != b.end()) {
01304     const string &astr = (*ai).first;
01305     const string &bstr = (*bi).first;
01306 
01307     if (astr < bstr) {
01308       // Here's a filename in a, not in b.
01309       (*ai).second->copy(this);
01310       ++ai;
01311 
01312     } else if (bstr < astr) {
01313       // Here's a filename in b, not in a.
01314       ++bi;
01315 
01316     } else { // bstr == astr
01317       // Here's a filename in both a and b.
01318       (*ai).second->copy_if_stale((*bi).second, this);
01319       ++ai;
01320       ++bi;
01321     }
01322   }
01323 
01324   while (ai != a.end()) {
01325     // Here's a filename in a, not in b.
01326     (*ai).second->copy(this);
01327     ++ai;
01328   }
01329 }
01330 
01331 ////////////////////////////////////////////////////////////////////
01332 //     Function: TextureImage::get_source_key
01333 //       Access: Private
01334 //  Description: Returns the key that a SourceTextureImage should be
01335 //               stored in, given its one or two filenames.
01336 ////////////////////////////////////////////////////////////////////
01337 string TextureImage::
01338 get_source_key(const Filename &filename, const Filename &alpha_filename,
01339                int alpha_file_channel) {
01340   Filename f = FilenameUnifier::make_bam_filename(filename);
01341   Filename a = FilenameUnifier::make_bam_filename(alpha_filename);
01342 
01343   return f.get_fullpath() + ":" + a.get_fullpath() + ":" +
01344     format_string(alpha_file_channel);
01345 }
01346 
01347 ////////////////////////////////////////////////////////////////////
01348 //     Function: TextureImage::register_with_read_factory
01349 //       Access: Public, Static
01350 //  Description: Registers the current object as something that can be
01351 //               read from a Bam file.
01352 ////////////////////////////////////////////////////////////////////
01353 void TextureImage::
01354 register_with_read_factory() {
01355   BamReader::get_factory()->
01356     register_factory(get_class_type(), make_TextureImage);
01357 }
01358 
01359 ////////////////////////////////////////////////////////////////////
01360 //     Function: TextureImage::write_datagram
01361 //       Access: Public, Virtual
01362 //  Description: Fills the indicated datagram up with a binary
01363 //               representation of the current object, in preparation
01364 //               for writing to a Bam file.
01365 ////////////////////////////////////////////////////////////////////
01366 void TextureImage::
01367 write_datagram(BamWriter *writer, Datagram &datagram) {
01368   ImageFile::write_datagram(writer, datagram);
01369   datagram.add_string(get_name());
01370 
01371   // We don't write out _request; this is re-read from the .txa file
01372   // each time.
01373 
01374   // We don't write out _pre_txa_properties; this is transitional.
01375 
01376   // We don't write out _preferred_source; this is redetermined each
01377   // session.
01378 
01379   datagram.add_bool(_is_surprise);
01380   datagram.add_bool(_ever_read_image);
01381   datagram.add_bool(_forced_grayscale);
01382   datagram.add_uint8(_alpha_bits);
01383   datagram.add_int16((int)_alpha_mode);
01384   datagram.add_float64(_mid_pixel_ratio);
01385   datagram.add_bool(_is_cutout);
01386   datagram.add_uint8((int)_txa_wrap_u);
01387   datagram.add_uint8((int)_txa_wrap_v);
01388 
01389   // We don't write out _explicitly_assigned_groups; this is re-read
01390   // from the .txa file each time.
01391 
01392   _actual_assigned_groups.write_datagram(writer, datagram);
01393 
01394   // We don't write out _egg_files; this is redetermined each session.
01395 
01396   datagram.add_uint32(_placement.size());
01397   Placement::const_iterator pi;
01398   for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
01399     writer->write_pointer(datagram, (*pi).first);
01400     writer->write_pointer(datagram, (*pi).second);
01401   }
01402 
01403   datagram.add_uint32(_sources.size());
01404   Sources::const_iterator si;
01405   for (si = _sources.begin(); si != _sources.end(); ++si) {
01406     writer->write_pointer(datagram, (*si).second);
01407   }
01408 
01409   datagram.add_uint32(_dests.size());
01410   Dests::const_iterator di;
01411   for (di = _dests.begin(); di != _dests.end(); ++di) {
01412     writer->write_pointer(datagram, (*di).second);
01413   }
01414 }
01415 
01416 ////////////////////////////////////////////////////////////////////
01417 //     Function: TextureImage::complete_pointers
01418 //       Access: Public, Virtual
01419 //  Description: Called after the object is otherwise completely read
01420 //               from a Bam file, this function's job is to store the
01421 //               pointers that were retrieved from the Bam file for
01422 //               each pointer object written.  The return value is the
01423 //               number of pointers processed from the list.
01424 ////////////////////////////////////////////////////////////////////
01425 int TextureImage::
01426 complete_pointers(TypedWritable **p_list, BamReader *manager) {
01427   int pi = ImageFile::complete_pointers(p_list, manager);
01428 
01429   pi += _actual_assigned_groups.complete_pointers(p_list + pi, manager);
01430 
01431   int i;
01432   for (i = 0; i < _num_placement; i++) {
01433     PaletteGroup *group;
01434     TexturePlacement *placement;
01435     DCAST_INTO_R(group, p_list[pi++], pi);
01436     DCAST_INTO_R(placement, p_list[pi++], pi);
01437     _placement.insert(Placement::value_type(group, placement));
01438   }
01439 
01440   for (i = 0; i < _num_sources; i++) {
01441     SourceTextureImage *source;
01442     DCAST_INTO_R(source, p_list[pi++], pi);
01443     string key = get_source_key(source->get_filename(),
01444                                 source->get_alpha_filename(),
01445                                 source->get_alpha_file_channel());
01446 
01447     bool inserted = _sources.insert(Sources::value_type(key, source)).second;
01448     if (!inserted) {
01449       nout << "Warning: texture key " << key
01450            << " is nonunique; texture lost.\n";
01451     }
01452   }
01453 
01454   for (i = 0; i < _num_dests; i++) {
01455     DestTextureImage *dest;
01456     DCAST_INTO_R(dest, p_list[pi++], pi);
01457     bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second;
01458     if (!inserted) {
01459       nout << "Warning: dest filename " << dest->get_filename()
01460            << " is nonunique; texture lost.\n";
01461     }
01462   }
01463 
01464   return pi;
01465 }
01466 
01467 ////////////////////////////////////////////////////////////////////
01468 //     Function: TextureImage::make_TextureImage
01469 //       Access: Protected
01470 //  Description: This method is called by the BamReader when an object
01471 //               of this type is encountered in a Bam file; it should
01472 //               allocate and return a new object with all the data
01473 //               read.
01474 ////////////////////////////////////////////////////////////////////
01475 TypedWritable *TextureImage::
01476 make_TextureImage(const FactoryParams &params) {
01477   TextureImage *me = new TextureImage;
01478   DatagramIterator scan;
01479   BamReader *manager;
01480 
01481   parse_params(params, scan, manager);
01482   me->fillin(scan, manager);
01483   return me;
01484 }
01485 
01486 ////////////////////////////////////////////////////////////////////
01487 //     Function: TextureImage::fillin
01488 //       Access: Protected
01489 //  Description: Reads the binary data from the given datagram
01490 //               iterator, which was written by a previous call to
01491 //               write_datagram().
01492 ////////////////////////////////////////////////////////////////////
01493 void TextureImage::
01494 fillin(DatagramIterator &scan, BamReader *manager) {
01495   ImageFile::fillin(scan, manager);
01496   set_name(scan.get_string());
01497 
01498   _is_surprise = scan.get_bool();
01499   _ever_read_image = scan.get_bool();
01500   _forced_grayscale = scan.get_bool();
01501   _alpha_bits = scan.get_uint8();
01502   _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16();
01503   if (pal->_read_pi_version >= 16) {
01504     _mid_pixel_ratio = scan.get_float64();
01505     _is_cutout = scan.get_bool();
01506   } else {
01507     // Force a re-read of the image if we are upgrading to pi version 16.
01508     _ever_read_image = false;
01509     _mid_pixel_ratio = 0.0;
01510     _is_cutout = false;
01511   }
01512   if (pal->_read_pi_version >= 17) {
01513     _txa_wrap_u = (EggTexture::WrapMode)scan.get_uint8();
01514     _txa_wrap_v = (EggTexture::WrapMode)scan.get_uint8();
01515   }
01516 
01517   _actual_assigned_groups.fillin(scan, manager);
01518 
01519   _num_placement = scan.get_uint32();
01520   manager->read_pointers(scan, _num_placement * 2);
01521 
01522   _num_sources = scan.get_uint32();
01523   manager->read_pointers(scan, _num_sources);
01524   _num_dests = scan.get_uint32();
01525   manager->read_pointers(scan, _num_dests);
01526 }
 All Classes Functions Variables Enumerations