Panda3D

paletteImage.cxx

00001 // Filename: paletteImage.cxx
00002 // Created by:  drose (01Dec00)
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 "paletteImage.h"
00016 #include "palettePage.h"
00017 #include "paletteGroup.h"
00018 #include "texturePlacement.h"
00019 #include "palettizer.h"
00020 #include "textureImage.h"
00021 #include "sourceTextureImage.h"
00022 #include "filenameUnifier.h"
00023 
00024 #include "indent.h"
00025 #include "datagram.h"
00026 #include "datagramIterator.h"
00027 #include "bamReader.h"
00028 #include "bamWriter.h"
00029 #include "string_utils.h"
00030 
00031 #include <algorithm>
00032 
00033 TypeHandle PaletteImage::_type_handle;
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: PaletteImage::ClearedRegion::Default Constructor
00037 //       Access: Public
00038 //  Description: The default constructor is only for the convenience
00039 //               of the bam reader.
00040 ////////////////////////////////////////////////////////////////////
00041 PaletteImage::ClearedRegion::
00042 ClearedRegion() {
00043   _x = 0;
00044   _y = 0;
00045   _x_size = 0;
00046   _y_size = 0;
00047 }
00048 
00049 ////////////////////////////////////////////////////////////////////
00050 //     Function: PaletteImage::ClearedRegion::Constructor
00051 //       Access: Public
00052 //  Description:
00053 ////////////////////////////////////////////////////////////////////
00054 PaletteImage::ClearedRegion::
00055 ClearedRegion(TexturePlacement *placement) {
00056   _x = placement->get_placed_x();
00057   _y = placement->get_placed_y();
00058   _x_size = placement->get_placed_x_size();
00059   _y_size = placement->get_placed_y_size();
00060 }
00061 
00062 ////////////////////////////////////////////////////////////////////
00063 //     Function: PaletteImage::ClearedRegion::Copy Constructor
00064 //       Access: Public
00065 //  Description:
00066 ////////////////////////////////////////////////////////////////////
00067 PaletteImage::ClearedRegion::
00068 ClearedRegion(const PaletteImage::ClearedRegion &copy) :
00069   _x(copy._x),
00070   _y(copy._y),
00071   _x_size(copy._x_size),
00072   _y_size(copy._y_size)
00073 {
00074 }
00075 
00076 ////////////////////////////////////////////////////////////////////
00077 //     Function: PaletteImage::ClearedRegion::Copy Assignment Operator
00078 //       Access: Public
00079 //  Description:
00080 ////////////////////////////////////////////////////////////////////
00081 void PaletteImage::ClearedRegion::
00082 operator = (const PaletteImage::ClearedRegion &copy) {
00083   _x = copy._x;
00084   _y = copy._y;
00085   _x_size = copy._x_size;
00086   _y_size = copy._y_size;
00087 }
00088 
00089 ////////////////////////////////////////////////////////////////////
00090 //     Function: PaletteImage::ClearedRegion::clear
00091 //       Access: Public
00092 //  Description: Sets the appropriate region of the image to black.
00093 ////////////////////////////////////////////////////////////////////
00094 void PaletteImage::ClearedRegion::
00095 clear(PNMImage &image) {
00096   LRGBColord rgb(pal->_background[0], pal->_background[1], pal->_background[2]);
00097   double alpha = pal->_background[3];
00098 
00099   for (int y = _y; y < _y + _y_size; y++) {
00100     for (int x = _x; x < _x + _x_size; x++) {
00101       image.set_xel(x, y, rgb);
00102     }
00103   }
00104   if (image.has_alpha()) {
00105     for (int y = _y; y < _y + _y_size; y++) {
00106       for (int x = _x; x < _x + _x_size; x++) {
00107         image.set_alpha(x, y, alpha);
00108       }
00109     }
00110   }
00111 }
00112 
00113 ////////////////////////////////////////////////////////////////////
00114 //     Function: PaletteImage::ClearedRegion::write_datagram
00115 //       Access: Public
00116 //  Description: Writes the contents of the ClearedRegion to the
00117 //               indicated datagram.
00118 ////////////////////////////////////////////////////////////////////
00119 void PaletteImage::ClearedRegion::
00120 write_datagram(Datagram &datagram) const {
00121   datagram.add_int32(_x);
00122   datagram.add_int32(_y);
00123   datagram.add_int32(_x_size);
00124   datagram.add_int32(_y_size);
00125 }
00126 
00127 ////////////////////////////////////////////////////////////////////
00128 //     Function: PaletteImage::ClearedRegion::write_datagram
00129 //       Access: Public
00130 //  Description: Extracts the contents of the ClearedRegion from the
00131 //               indicated datagram.
00132 ////////////////////////////////////////////////////////////////////
00133 void PaletteImage::ClearedRegion::
00134 fillin(DatagramIterator &scan) {
00135   _x = scan.get_int32();
00136   _y = scan.get_int32();
00137   _x_size = scan.get_int32();
00138   _y_size = scan.get_int32();
00139 }
00140 
00141 
00142 
00143 
00144 
00145 
00146 ////////////////////////////////////////////////////////////////////
00147 //     Function: PaletteImage::Default Constructor
00148 //       Access: Private
00149 //  Description: The default constructor is only for the convenience
00150 //               of the Bam reader.
00151 ////////////////////////////////////////////////////////////////////
00152 PaletteImage::
00153 PaletteImage() {
00154   _page = (PalettePage *)NULL;
00155   _index = 0;
00156   _new_image = false;
00157   _got_image = false;
00158 
00159   _swapped_image = 0;
00160 }
00161 
00162 ////////////////////////////////////////////////////////////////////
00163 //     Function: PaletteImage::Constructor
00164 //       Access: Public
00165 //  Description:
00166 ////////////////////////////////////////////////////////////////////
00167 PaletteImage::
00168 PaletteImage(PalettePage *page, int index) :
00169   _page(page),
00170   _index(index)
00171 {
00172   _properties = page->get_properties();
00173   _size_known = true;
00174   _x_size = pal->_pal_x_size;
00175   _y_size = pal->_pal_y_size;
00176   _new_image = true;
00177   _got_image = false;
00178   _swapped_image = 0;
00179 
00180   setup_filename();
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: PaletteImage::Constructor
00185 //       Access: Public
00186 //  Description:
00187 ////////////////////////////////////////////////////////////////////
00188 PaletteImage::
00189 PaletteImage(PalettePage *page, int index, unsigned swapIndex) :
00190   _page(page),
00191   _index(index),
00192   _swapped_image(swapIndex)
00193 {
00194   _properties = page->get_properties();
00195   _size_known = true;
00196   _x_size = pal->_pal_x_size;
00197   _y_size = pal->_pal_y_size;
00198   _new_image = true;
00199   _got_image = false;
00200 
00201   setup_filename();
00202 }
00203 
00204 
00205 ////////////////////////////////////////////////////////////////////
00206 //     Function: PaletteImage::get_page
00207 //       Access: Public
00208 //  Description: Returns the particular PalettePage this image is
00209 //               associated with.
00210 ////////////////////////////////////////////////////////////////////
00211 PalettePage *PaletteImage::
00212 get_page() const {
00213   return _page;
00214 }
00215 
00216 ////////////////////////////////////////////////////////////////////
00217 //     Function: PaletteImage::is_empty
00218 //       Access: Public
00219 //  Description: Returns true if there are no textures, or only one
00220 //               "solitary" texture, placed on the image.  In either
00221 //               case, the PaletteImage need not be generated.
00222 ////////////////////////////////////////////////////////////////////
00223 bool PaletteImage::
00224 is_empty() const {
00225   if (_placements.empty()) {
00226     // The image is genuinely empty.
00227     return true;
00228 
00229   } else if (_placements.size() == 1) {
00230     // If the image has exactly one texture, we consider the image
00231     // empty only if the texture is actually flagged as 'solitary'.
00232     return (_placements[0]->get_omit_reason() == OR_solitary);
00233 
00234   } else {
00235     // The image has more than one texture, so it's definitely not
00236     // empty.
00237     return false;
00238   }
00239 }
00240 
00241 ////////////////////////////////////////////////////////////////////
00242 //     Function: PaletteImage::count_utilization
00243 //       Access: Public
00244 //  Description: Returns the fraction of the PaletteImage that is
00245 //               actually used by any textures.  This is 1.0 if every
00246 //               pixel in the PaletteImage is used, or 0.0 if none
00247 //               are.  Normally it will be somewhere in between.
00248 ////////////////////////////////////////////////////////////////////
00249 double PaletteImage::
00250 count_utilization() const {
00251   int used_pixels = 0;
00252 
00253   Placements::const_iterator pi;
00254   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00255     TexturePlacement *placement = (*pi);
00256 
00257     int texture_pixels =
00258       placement->get_placed_x_size() *
00259       placement->get_placed_y_size();
00260     used_pixels += texture_pixels;
00261   }
00262 
00263   int total_pixels = get_x_size() * get_y_size();
00264 
00265   return (double)used_pixels / (double)total_pixels;
00266 }
00267 
00268 ////////////////////////////////////////////////////////////////////
00269 //     Function: PaletteImage::count_coverage
00270 //       Access: Public
00271 //  Description: Returns the a weighted average of the fraction of
00272 //               coverage represented by all of the textures placed on
00273 //               the palette.  This number represents the fraction of
00274 //               wasted pixels in the palette image consumed by
00275 //               copying the same pixels multiple times into the
00276 //               palette, or if the number is negative, it represents
00277 //               the fraction of pixels saved by not having to copy
00278 //               the entire texture into the palette.
00279 ////////////////////////////////////////////////////////////////////
00280 double PaletteImage::
00281 count_coverage() const {
00282   int coverage_pixels = 0;
00283 
00284   Placements::const_iterator pi;
00285   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00286     TexturePlacement *placement = (*pi);
00287     TextureImage *texture = placement->get_texture();
00288     nassertr(texture != (TextureImage *)NULL, 0.0);
00289 
00290     int orig_pixels =
00291       texture->get_x_size() *
00292       texture->get_y_size();
00293     int placed_pixels =
00294       placement->get_placed_x_size() *
00295       placement->get_placed_y_size();
00296 
00297     coverage_pixels += placed_pixels - orig_pixels;
00298   }
00299 
00300   int total_pixels = get_x_size() * get_y_size();
00301 
00302   return (double)coverage_pixels / (double)total_pixels;
00303 }
00304 
00305 ////////////////////////////////////////////////////////////////////
00306 //     Function: PaletteImage::place
00307 //       Access: Public
00308 //  Description: Attempts to place the indicated texture on the image.
00309 //               Returns true if successful, or false if there was no
00310 //               available space.
00311 ////////////////////////////////////////////////////////////////////
00312 bool PaletteImage::
00313 place(TexturePlacement *placement) {
00314   nassertr(placement->is_size_known(), true);
00315   nassertr(!placement->is_placed(), true);
00316 
00317   int x, y;
00318   if (find_hole(x, y, placement->get_x_size(), placement->get_y_size())) {
00319     placement->place_at(this, x, y);
00320     _placements.push_back(placement);
00321 
00322     // [gjeon] create swappedImages
00323     TexturePlacement::TextureSwaps::iterator tsi;
00324     for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
00325       if ((tsi - placement->_textureSwaps.begin()) >= _swappedImages.size()) {
00326         PaletteImage *swappedImage = new PaletteImage(_page, _swappedImages.size(), tsi - placement->_textureSwaps.begin() + 1);
00327         swappedImage->_masterPlacements = &_placements;
00328         _swappedImages.push_back(swappedImage);
00329       }
00330     }
00331 
00332     return true;
00333   }
00334 
00335   return false;
00336 }
00337 
00338 ////////////////////////////////////////////////////////////////////
00339 //     Function: PaletteImage::unplace
00340 //       Access: Public
00341 //  Description: Removes the texture from the image.
00342 ////////////////////////////////////////////////////////////////////
00343 void PaletteImage::
00344 unplace(TexturePlacement *placement) {
00345   nassertv(placement->is_placed() && placement->get_image() == this);
00346 
00347   Placements::iterator pi;
00348   pi = find(_placements.begin(), _placements.end(), placement);
00349   while (pi != _placements.end()) {
00350     _placements.erase(pi);
00351     pi = find(_placements.begin(), _placements.end(), placement);
00352   }
00353   _cleared_regions.push_back(ClearedRegion(placement));
00354 }
00355 
00356 ////////////////////////////////////////////////////////////////////
00357 //     Function: PaletteImage::check_solitary
00358 //       Access: Public
00359 //  Description: To be called after all textures have been placed on
00360 //               the image, this checks to see if there is only one
00361 //               texture on the image.  If there is, it is flagged as
00362 //               'solitary' so that the egg files will not needlessly
00363 //               reference the palettized image.
00364 //
00365 //               However, if pal->_omit_solitary is false, we
00366 //               generally don't change textures to solitary state.
00367 ////////////////////////////////////////////////////////////////////
00368 void PaletteImage::
00369 check_solitary() {
00370   if (_placements.size() == 1) {
00371     // How sad, only one.
00372     TexturePlacement *placement = *_placements.begin();
00373     nassertv(placement->get_omit_reason() == OR_none ||
00374              placement->get_omit_reason() == OR_solitary);
00375 
00376     if (pal->_omit_solitary || placement->get_omit_reason() == OR_solitary) {
00377       // We only omit the solitary texture if (a) we have
00378       // omit_solitary in effect, or (b) we don't have omit_solitary
00379       // in effect now, but we did before, and the texture is still
00380       // flagged as solitary from that previous pass.
00381       placement->omit_solitary();
00382     }
00383 
00384   } else {
00385     // Zero or multiple.
00386     Placements::const_iterator pi;
00387     for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00388       TexturePlacement *placement = (*pi);
00389       /*
00390       if (!(placement->get_omit_reason() == OR_none ||
00391             placement->get_omit_reason() == OR_solitary)) {
00392         nout << "texture " << *placement->get_texture() << " is omitted for "
00393              << placement->get_omit_reason() << "\n";
00394       }
00395       */
00396       nassertv(placement->get_omit_reason() == OR_none ||
00397                placement->get_omit_reason() == OR_solitary);
00398       placement->not_solitary();
00399     }
00400   }
00401 }
00402 
00403 ////////////////////////////////////////////////////////////////////
00404 //     Function: PaletteImage::optimal_resize
00405 //       Access: Public
00406 //  Description: Attempts to resize the palette image to as small as
00407 //               it can go.
00408 ////////////////////////////////////////////////////////////////////
00409 void PaletteImage::
00410 optimal_resize() {
00411   if (is_empty()) { // && (_swapped_image == 0)) {
00412     return;
00413   }
00414 
00415   bool resized_any = false;
00416   bool success;
00417   do {
00418     success = false;
00419     nassertv(_x_size > 0 && _y_size > 0);
00420 
00421     // Try to cut it in half in both dimensions, one at a time.
00422     if (resize_image(_x_size, _y_size / 2)) {
00423       success = true;
00424       resized_any = true;
00425     }
00426     if (resize_image(_x_size / 2, _y_size)) {
00427       success = true;
00428       resized_any = true;
00429     }
00430 
00431   } while (success);
00432 
00433   if (resized_any) {
00434     nout << "Resizing "
00435          << FilenameUnifier::make_user_filename(get_filename()) << " to "
00436          << _x_size << " " << _y_size << "\n";
00437 
00438     // [gjeon] resize swapped images, also
00439     SwappedImages::iterator si;
00440     for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
00441       PaletteImage *swappedImage = (*si);
00442       swappedImage->resize_swapped_image(_x_size, _y_size);
00443     }  
00444   }
00445 }
00446 
00447 ////////////////////////////////////////////////////////////////////
00448 //     Function: PaletteImage::resize_image
00449 //       Access: Public
00450 //  Description: Attempts to resize the palette image, and repack all
00451 //               of the textures within the new size.  Returns true if
00452 //               successful, false otherwise.  If this fails, it will
00453 //               still result in repacking all the textures in the
00454 //               original size.
00455 ////////////////////////////////////////////////////////////////////
00456 bool PaletteImage::
00457 resize_image(int x_size, int y_size) {
00458   // We already know we're going to be generating a new image from
00459   // scratch after this.
00460   _cleared_regions.clear();
00461   remove_image();
00462 
00463   // First, Save the current placement list, while simultaneously
00464   // clearing it.
00465   Placements saved;
00466   saved.swap(_placements);
00467 
00468   // Also save our current size.
00469   int saved_x_size = _x_size;
00470   int saved_y_size = _y_size;
00471 
00472   // Then, sort the textures to in order from biggest to smallest, as
00473   // an aid to optimal packing.
00474   sort(saved.begin(), saved.end(), SortPlacementBySize());
00475 
00476   // And while we're at it, we need to officially unplace each of
00477   // these.
00478   Placements::iterator pi;
00479   for (pi = saved.begin(); pi != saved.end(); ++pi) {
00480     (*pi)->force_replace();
00481   }
00482 
00483   // Finally, apply the new size and try to fit all the textures.
00484   _x_size = x_size;
00485   _y_size = y_size;
00486 
00487   bool packed = true;
00488   for (pi = saved.begin(); pi != saved.end() && packed; ++pi) {
00489     if (!place(*pi)) {
00490       packed = false;
00491     }
00492   }
00493 
00494   if (!packed) {
00495     // If it didn't work, phooey.  Put 'em all back.
00496     _x_size = saved_x_size;
00497     _y_size = saved_y_size;
00498 
00499     Placements remove;
00500     remove.swap(_placements);
00501     for (pi = remove.begin(); pi != remove.end(); ++pi) {
00502       (*pi)->force_replace();
00503     }
00504 
00505     bool all_packed = true;
00506     for (pi = saved.begin(); pi != saved.end(); ++pi) {
00507       if (!place(*pi)) {
00508         all_packed = false;
00509       }
00510     }
00511     nassertr(all_packed, false);
00512   }
00513 
00514   return packed;
00515 }
00516 
00517 ////////////////////////////////////////////////////////////////////
00518 //     Function: PaletteImage::resize_swapped_image
00519 //       Access: Public
00520 //  Description: Attempts to resize the palette image, and repack all
00521 //               of the textures within the new size.  Returns true if
00522 //               successful, false otherwise.  If this fails, it will
00523 //               still result in repacking all the textures in the
00524 //               original size.
00525 ////////////////////////////////////////////////////////////////////
00526 void PaletteImage::
00527 resize_swapped_image(int x_size, int y_size) {
00528   // Finally, apply the new size and try to fit all the textures.
00529   _x_size = x_size;
00530   _y_size = y_size;
00531 }
00532 
00533 ////////////////////////////////////////////////////////////////////
00534 //     Function: PaletteImage::write_placements
00535 //       Access: Public
00536 //  Description: Writes a list of the textures that have been placed
00537 //               on this image to the indicated output stream, one per
00538 //               line.
00539 ////////////////////////////////////////////////////////////////////
00540 void PaletteImage::
00541 write_placements(ostream &out, int indent_level) const {
00542   Placements::const_iterator pi;
00543   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00544     TexturePlacement *placement = (*pi);
00545     placement->write_placed(out, indent_level);
00546   }
00547 }
00548 
00549 ////////////////////////////////////////////////////////////////////
00550 //     Function: PaletteImage::reset_image
00551 //       Access: Public
00552 //  Description: Unpacks each texture that has been placed on this
00553 //               image, resetting the image to empty.
00554 ////////////////////////////////////////////////////////////////////
00555 void PaletteImage::
00556 reset_image() {
00557   // We need a copy so we can modify this list as we traverse it.
00558   Placements copy_placements = _placements;
00559   Placements::const_iterator pi;
00560   for (pi = copy_placements.begin(); pi != copy_placements.end(); ++pi) {
00561     TexturePlacement *placement = (*pi);
00562     placement->force_replace();
00563   }
00564 
00565   _placements.clear();
00566   _cleared_regions.clear();
00567   remove_image();
00568 }
00569 
00570 ////////////////////////////////////////////////////////////////////
00571 //     Function: PaletteImage::setup_shadow_image
00572 //       Access: Public
00573 //  Description: Ensures the _shadow_image has the correct filename
00574 //               and image types, based on what was supplied on the
00575 //               command line and in the .txa file.
00576 ////////////////////////////////////////////////////////////////////
00577 void PaletteImage::
00578 setup_shadow_image() {
00579   _shadow_image.make_shadow_image(_basename);
00580 
00581   // [gjeon] setup shadoe_image of swappedImages
00582   SwappedImages::iterator si;
00583   for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
00584     PaletteImage *swappedImage = (*si);
00585     swappedImage->setup_shadow_image();
00586   }  
00587 }
00588 
00589 ////////////////////////////////////////////////////////////////////
00590 //     Function: PaletteImage::update_image
00591 //       Access: Public
00592 //  Description: If the palette has changed since it was last written
00593 //               out, updates the image and writes out a new one.  If
00594 //               redo_all is true, regenerates the image from scratch
00595 //               and writes it out again, whether it needed it or not.
00596 ////////////////////////////////////////////////////////////////////
00597 void PaletteImage::
00598 update_image(bool redo_all) {
00599   if (is_empty() && pal->_aggressively_clean_mapdir) {
00600     // If the palette image is 'empty', ensure that it doesn't exist.
00601     // No need to clutter up the map directory.
00602     remove_image();
00603     return;
00604   }
00605 
00606   if (redo_all) {
00607     // If we're redoing everything, throw out the old image anyway.
00608     remove_image();
00609   }
00610 
00611   // Check the filename too.
00612   update_filename();
00613 
00614   // Do we need to update?
00615   bool needs_update =
00616     _new_image || !exists() ||
00617     !_cleared_regions.empty();
00618 
00619   Placements::iterator pi;
00620   // We must continue to walk through all of the textures on the
00621   // palette, even after we discover the palette requires an update,
00622   // so we can determine which source images need to be recopied.
00623   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00624     TexturePlacement *placement = (*pi);
00625 
00626     if (!placement->is_filled()) {
00627       needs_update = true;
00628 
00629     } else {
00630       TextureImage *texture = placement->get_texture();
00631 
00632       // Only check the timestamps on textures that are named
00633       // (indirectly) on the command line.
00634       if (texture->is_texture_named()) {
00635         SourceTextureImage *source = texture->get_preferred_source();
00636 
00637         if (source != (SourceTextureImage *)NULL &&
00638             source->get_filename().compare_timestamps(get_filename()) > 0) {
00639           // The source image is newer than the palette image; we need to
00640           // regenerate.
00641           placement->mark_unfilled();
00642           needs_update = true;
00643         }
00644       }
00645 
00646       // [gjeon] to find out all of the swappable textures is up to date
00647       TexturePlacement::TextureSwaps::iterator tsi;
00648       for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
00649         TextureImage *swapTexture = (*tsi);
00650 
00651         if (swapTexture->is_texture_named()) {
00652           SourceTextureImage *sourceSwapTexture = swapTexture->get_preferred_source();
00653 
00654           if (sourceSwapTexture != (SourceTextureImage *)NULL &&
00655               sourceSwapTexture->get_filename().compare_timestamps(get_filename()) > 0) {
00656             // The source image is newer than the palette image; we need to
00657             // regenerate.
00658             placement->mark_unfilled();
00659             needs_update = true;
00660           }
00661         }
00662       }
00663 
00664     }
00665   }
00666 
00667   if (!needs_update) {
00668     // No sweat; nothing has changed.
00669     return;
00670   }
00671 
00672   get_image();
00673   // [gjeon] get swapped images, too
00674   get_swapped_images();
00675 
00676   // Set to black any parts of the image that we recently unplaced.
00677   ClearedRegions::iterator ci;
00678   for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
00679     ClearedRegion &region = (*ci);
00680     region.clear(_image);
00681 
00682     // [gjeon] clear swapped images also
00683     SwappedImages::iterator si;
00684     for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
00685       PaletteImage *swappedImage = (*si);
00686       region.clear(swappedImage->_image);
00687     }
00688   }
00689   _cleared_regions.clear();
00690 
00691   // Now add the recent additions to the image.
00692   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00693     TexturePlacement *placement = (*pi);
00694     if (!placement->is_filled()) {
00695       placement->fill_image(_image);
00696 
00697       // [gjeon] fill swapped images
00698       SwappedImages::iterator si;
00699       for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
00700         PaletteImage *swappedImage = (*si);
00701         swappedImage->update_filename();
00702         placement->fill_swapped_image(swappedImage->_image, si - _swappedImages.begin());
00703       }
00704     }
00705   }
00706 
00707   write(_image);
00708 
00709   if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00710     _shadow_image.write(_image);
00711   }
00712 
00713   release_image();
00714 
00715   // [gjeon] write and release swapped images
00716   SwappedImages::iterator si;
00717   for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
00718     PaletteImage *swappedImage = (*si);
00719     swappedImage->write(swappedImage->_image);
00720     if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00721       swappedImage->_shadow_image.write(swappedImage->_image);
00722     }
00723     swappedImage->release_image();
00724   }
00725 }
00726 
00727 ////////////////////////////////////////////////////////////////////
00728 //     Function: PaletteImage::update_filename
00729 //       Access: Public
00730 //  Description: Changes the image filename to match the current
00731 //               naming scheme, assuming something has changed since
00732 //               the image was created.  Returns true if the image
00733 //               filename changes (which means update_image() should
00734 //               be called).
00735 ////////////////////////////////////////////////////////////////////
00736 bool PaletteImage::
00737 update_filename() {
00738   Filename orig_filename = _filename;
00739   Filename orig_alpha_filename = _alpha_filename;
00740   Filename orig_shadow_filename = _shadow_image.get_filename();
00741 
00742   if (setup_filename()) {
00743     nout << "Renaming " << FilenameUnifier::make_user_filename(orig_filename) 
00744          << " to " << FilenameUnifier::make_user_filename(_filename) << "\n";
00745 
00746     if (!orig_filename.empty() && orig_filename.exists()) {
00747       nout << "Deleting " << FilenameUnifier::make_user_filename(orig_filename) << "\n";
00748       orig_filename.unlink();
00749     }
00750     if (!orig_alpha_filename.empty() && orig_alpha_filename.exists()) {
00751       nout << "Deleting " << FilenameUnifier::make_user_filename(orig_alpha_filename) << "\n";
00752       orig_alpha_filename.unlink();
00753     }
00754     if (!orig_shadow_filename.empty() && orig_shadow_filename.exists()) {
00755       nout << "Deleting " << FilenameUnifier::make_user_filename(orig_shadow_filename) << "\n";
00756       orig_shadow_filename.unlink();
00757     }
00758     _new_image = true;
00759 
00760     // Since the palette filename has changed, we need to mark all of
00761     // the egg files that referenced the old filename as stale.
00762 
00763     // Marking egg files stale at this late point can cause minor
00764     // problems; because we might do this, it's necessary for
00765     // eggPalettize.cxx to call read_stale_eggs() twice.
00766     Placements::iterator pi;
00767     for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00768       TexturePlacement *placement = (*pi);
00769       placement->mark_eggs_stale();
00770     }
00771 
00772     return true;
00773   }
00774 
00775   return false;
00776 }
00777 
00778 ////////////////////////////////////////////////////////////////////
00779 //     Function: PaletteImage::setup_filename
00780 //       Access: Private
00781 //  Description: Sets up the image's filename (and that of the
00782 //               _shadow_pal) according to the specified properties.
00783 //
00784 //               Returns true if the filename changes from what it was
00785 //               previously, false otherwise.
00786 ////////////////////////////////////////////////////////////////////
00787 bool PaletteImage::
00788 setup_filename() {
00789   // Build up the basename for the palette image, based on the
00790   // supplied image pattern.
00791   _basename = string();
00792 
00793   string::iterator si = pal->_generated_image_pattern.begin();
00794   while (si != pal->_generated_image_pattern.end()) {
00795     if ((*si) == '%') {
00796       // Some keycode.
00797       ++si;
00798       if (si != pal->_generated_image_pattern.end()) {
00799         switch (*si) {
00800         case '%':
00801           _basename += '%';
00802           break;
00803 
00804         case 'g':
00805           _basename += _page->get_group()->get_name();
00806           break;
00807 
00808         case 'p':
00809           _basename += _page->get_name();
00810           break;
00811 
00812         case 'i':
00813           _basename += format_string(_index + 1);
00814           break;
00815 
00816         default:
00817           _basename += '%';
00818           _basename += (*si);
00819         }
00820         ++si;
00821       }
00822     } else {
00823       // A literal character.
00824       _basename += (*si);
00825       ++si;
00826     }
00827   }
00828 
00829   if (_swapped_image > 0) {
00830     _basename += "_swp_";
00831     _basename += format_string(_swapped_image);
00832   }
00833     
00834   // We must end the basename with a dot, so that it does not appear
00835   // to have a filename extension.  Otherwise, an embedded dot in the
00836   // group's name would make everything following appear to be an
00837   // extension, which would get lost in the set_filename() call.
00838   if (_basename.empty() || _basename[_basename.length() - 1] != '.') {
00839     _basename += '.';
00840   }
00841 
00842   bool any_changed = false;
00843 
00844   if (set_filename(_page->get_group(), _basename)) {
00845     any_changed = true;
00846   }
00847 
00848   if (_shadow_image.make_shadow_image(_basename)) {
00849     any_changed = true;
00850   }
00851   
00852   return any_changed;
00853 }
00854 
00855 ////////////////////////////////////////////////////////////////////
00856 //     Function: PaletteImage::find_hole
00857 //       Access: Private
00858 //  Description: Searches for a hole of at least x_size by y_size
00859 //               pixels somewhere within the PaletteImage.  If a
00860 //               suitable hole is found, sets x and y to the top left
00861 //               corner and returns true; otherwise, returns false.
00862 ////////////////////////////////////////////////////////////////////
00863 bool PaletteImage::
00864 find_hole(int &x, int &y, int x_size, int y_size) const {
00865   y = 0;
00866   while (y + y_size <= _y_size) {
00867     int next_y = _y_size;
00868     // Scan along the row at 'y'.
00869     x = 0;
00870     while (x + x_size <= _x_size) {
00871       int next_x = x;
00872 
00873       // Consider the spot at x, y.
00874       TexturePlacement *overlap = find_overlap(x, y, x_size, y_size);
00875 
00876       if (overlap == (TexturePlacement *)NULL) {
00877         // Hooray!
00878         return true;
00879       }
00880 
00881       next_x = overlap->get_placed_x() + overlap->get_placed_x_size();
00882       next_y = min(next_y, overlap->get_placed_y() + overlap->get_placed_y_size());
00883       nassertr(next_x > x, false);
00884       x = next_x;
00885     }
00886 
00887     nassertr(next_y > y, false);
00888     y = next_y;
00889   }
00890 
00891   // Nope, wouldn't fit anywhere.
00892   return false;
00893 }
00894 
00895 ////////////////////////////////////////////////////////////////////
00896 //     Function: PaletteImage::find_overlap
00897 //       Access: Private
00898 //  Description: If the rectangle whose top left corner is x, y and
00899 //               whose size is x_size, y_size describes an empty hole
00900 //               that does not overlap any placed images, returns
00901 //               NULL; otherwise, returns the first placed texture
00902 //               that the image does overlap.  It is assumed the
00903 //               rectangle lies completely within the boundaries of
00904 //               the image itself.
00905 ////////////////////////////////////////////////////////////////////
00906 TexturePlacement *PaletteImage::
00907 find_overlap(int x, int y, int x_size, int y_size) const {
00908   Placements::const_iterator pi;
00909   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00910     TexturePlacement *placement = (*pi);
00911     if (placement->is_placed() &&
00912         placement->intersects(x, y, x_size, y_size)) {
00913       return placement;
00914     }
00915   }
00916 
00917   return (TexturePlacement *)NULL;
00918 }
00919 
00920 ////////////////////////////////////////////////////////////////////
00921 //     Function: PaletteImage::get_image
00922 //       Access: Public
00923 //  Description: Reads or generates the PNMImage that corresponds to
00924 //               the palette as it is known so far.
00925 ////////////////////////////////////////////////////////////////////
00926 void PaletteImage::
00927 get_image() {
00928   if (_got_image) {
00929     return;
00930   }
00931 
00932   if (!_new_image) {
00933     if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00934       if (_shadow_image.get_filename().exists() && _shadow_image.read(_image)) {
00935         _got_image = true;
00936         return;
00937       }
00938     } else {
00939       if (get_filename().exists() && read(_image)) {
00940         _got_image = true;
00941         return;
00942       }
00943     }
00944   }
00945 
00946   nout << "Generating new "
00947        << FilenameUnifier::make_user_filename(get_filename()) << "\n";
00948 
00949   // We won't be using this any more.
00950   _cleared_regions.clear();
00951 
00952   _image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
00953   _image.fill(pal->_background[0], pal->_background[1], pal->_background[2]);
00954   if (_image.has_alpha()) {
00955     _image.alpha_fill(pal->_background[3]);
00956   }
00957 
00958   _new_image = false;
00959   _got_image = true;
00960 
00961   // Now fill up the image.
00962   Placements::iterator pi;
00963   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
00964     TexturePlacement *placement = (*pi);
00965     placement->fill_image(_image);
00966   }
00967 }
00968 
00969 ////////////////////////////////////////////////////////////////////
00970 //     Function: PaletteImage::get_swapped_image
00971 //       Access: Public
00972 //  Description: Reads or generates the PNMImage for swapped textures
00973 ////////////////////////////////////////////////////////////////////
00974 void PaletteImage::
00975 get_swapped_image(int index) {
00976   if (_got_image) {
00977     return;
00978   }
00979 
00980   if (!_new_image) {
00981     if (pal->_shadow_color_type != (PNMFileType *)NULL) {
00982       if (_shadow_image.get_filename().exists() && _shadow_image.read(_image)) {
00983         _got_image = true;
00984         return;
00985       }
00986     } else {
00987       if (get_filename().exists() && read(_image)) {
00988         _got_image = true;
00989         return;
00990       }
00991     }
00992   }
00993 
00994   nout << "Generating new "
00995        << FilenameUnifier::make_user_filename(get_filename()) << "\n";
00996 
00997   // We won't be using this any more.
00998   _cleared_regions.clear();
00999 
01000   _image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
01001   _image.fill(pal->_background[0], pal->_background[1], pal->_background[2]);
01002   if (_image.has_alpha()) {
01003     _image.alpha_fill(pal->_background[3]);
01004   }
01005 
01006   _new_image = false;
01007   _got_image = true;
01008 
01009   // Now fill up the image.
01010   Placements::iterator pi;
01011   for (pi = _masterPlacements->begin(); pi != _masterPlacements->end(); ++pi) {
01012     TexturePlacement *placement = (*pi);
01013     if (placement->_textureSwaps.size() > index)
01014       placement->fill_swapped_image(_image, index);
01015     else
01016       placement->fill_image(_image);
01017   }
01018 }
01019 
01020 ////////////////////////////////////////////////////////////////////
01021 //     Function: PaletteImage::get_swapped_images
01022 //       Access: Public
01023 //  Description: Reads or generates the PNMImage that corresponds to
01024 //               the palette as it is known so far.
01025 ////////////////////////////////////////////////////////////////////
01026 void PaletteImage::
01027 get_swapped_images() {
01028   SwappedImages::iterator si;
01029   for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
01030     PaletteImage *swappedImage = (*si);
01031     swappedImage->get_swapped_image(si - _swappedImages.begin());
01032   }
01033 }
01034 
01035 ////////////////////////////////////////////////////////////////////
01036 //     Function: PaletteImage::release_image
01037 //       Access: Public
01038 //  Description: Deallocates the memory allocated by a previous call to
01039 //               get_image().
01040 ////////////////////////////////////////////////////////////////////
01041 void PaletteImage::
01042 release_image() {
01043   _image.clear();
01044   _got_image = false;
01045 }
01046 
01047 ////////////////////////////////////////////////////////////////////
01048 //     Function: PaletteImage::remove_image
01049 //       Access: Private
01050 //  Description: Deletes the image file.
01051 ////////////////////////////////////////////////////////////////////
01052 void PaletteImage::
01053 remove_image() {
01054   unlink();
01055   if (pal->_shadow_color_type != (PNMFileType *)NULL) {
01056     _shadow_image.unlink();
01057   }
01058   _new_image = true;
01059 }
01060 
01061 ////////////////////////////////////////////////////////////////////
01062 //     Function: PaletteImage::register_with_read_factory
01063 //       Access: Public, Static
01064 //  Description: Registers the current object as something that can be
01065 //               read from a Bam file.
01066 ////////////////////////////////////////////////////////////////////
01067 void PaletteImage::
01068 register_with_read_factory() {
01069   BamReader::get_factory()->
01070     register_factory(get_class_type(), make_PaletteImage);
01071 }
01072 
01073 ////////////////////////////////////////////////////////////////////
01074 //     Function: PaletteImage::write_datagram
01075 //       Access: Public, Virtual
01076 //  Description: Fills the indicated datagram up with a binary
01077 //               representation of the current object, in preparation
01078 //               for writing to a Bam file.
01079 ////////////////////////////////////////////////////////////////////
01080 void PaletteImage::
01081 write_datagram(BamWriter *writer, Datagram &datagram) {
01082   ImageFile::write_datagram(writer, datagram);
01083 
01084   datagram.add_uint32(_cleared_regions.size());
01085   ClearedRegions::const_iterator ci;
01086   for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
01087     (*ci).write_datagram(datagram);
01088   }
01089 
01090   datagram.add_uint32(_placements.size());
01091   Placements::const_iterator pi;
01092   for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
01093     writer->write_pointer(datagram, (*pi));
01094   }
01095 
01096   writer->write_pointer(datagram, _page);
01097   datagram.add_uint32(_index);
01098   datagram.add_string(_basename);
01099   datagram.add_bool(_new_image);
01100 
01101   // We don't write _got_image or _image.  These are loaded
01102   // per-session.
01103 
01104   // We don't write _shadow_image.  This is just a runtime convenience
01105   // for specifying the name of the shadow file, and we redefine this
01106   // per-session (which allows us to pick up a new
01107   // pal->_shadow_dirname if it changes).
01108 }
01109 
01110 ////////////////////////////////////////////////////////////////////
01111 //     Function: PaletteImage::complete_pointers
01112 //       Access: Public, Virtual
01113 //  Description: Called after the object is otherwise completely read
01114 //               from a Bam file, this function's job is to store the
01115 //               pointers that were retrieved from the Bam file for
01116 //               each pointer object written.  The return value is the
01117 //               number of pointers processed from the list.
01118 ////////////////////////////////////////////////////////////////////
01119 int PaletteImage::
01120 complete_pointers(TypedWritable **p_list, BamReader *manager) {
01121   int index = ImageFile::complete_pointers(p_list, manager);
01122 
01123   int i;
01124   _placements.reserve(_num_placements);
01125   for (i = 0; i < _num_placements; i++) {
01126     TexturePlacement *placement;
01127     DCAST_INTO_R(placement, p_list[index], index);
01128     _placements.push_back(placement);
01129     index++;
01130   }
01131 
01132   if (p_list[index] != (TypedWritable *)NULL) {
01133     DCAST_INTO_R(_page, p_list[index], index);
01134   }
01135   index++;
01136 
01137   return index;
01138 }
01139 
01140 ////////////////////////////////////////////////////////////////////
01141 //     Function: PaletteImage::make_PaletteImage
01142 //       Access: Protected
01143 //  Description: This method is called by the BamReader when an object
01144 //               of this type is encountered in a Bam file; it should
01145 //               allocate and return a new object with all the data
01146 //               read.
01147 ////////////////////////////////////////////////////////////////////
01148 TypedWritable *PaletteImage::
01149 make_PaletteImage(const FactoryParams &params) {
01150   PaletteImage *me = new PaletteImage;
01151   DatagramIterator scan;
01152   BamReader *manager;
01153 
01154   parse_params(params, scan, manager);
01155   me->fillin(scan, manager);
01156   return me;
01157 }
01158 
01159 ////////////////////////////////////////////////////////////////////
01160 //     Function: PaletteImage::fillin
01161 //       Access: Protected
01162 //  Description: Reads the binary data from the given datagram
01163 //               iterator, which was written by a previous call to
01164 //               write_datagram().
01165 ////////////////////////////////////////////////////////////////////
01166 void PaletteImage::
01167 fillin(DatagramIterator &scan, BamReader *manager) {
01168   ImageFile::fillin(scan, manager);
01169 
01170   int num_cleared_regions = scan.get_uint32();
01171   _cleared_regions.reserve(num_cleared_regions);
01172   for (int i = 0; i < num_cleared_regions; i++) {
01173     _cleared_regions.push_back(ClearedRegion());
01174     _cleared_regions.back().fillin(scan);
01175   }
01176 
01177   _num_placements = scan.get_uint32();
01178   manager->read_pointers(scan, _num_placements);
01179 
01180   manager->read_pointer(scan);  // _page
01181 
01182   _index = scan.get_uint32();
01183   _basename = scan.get_string();
01184   _new_image = scan.get_bool();
01185 }
 All Classes Functions Variables Enumerations