Panda3D
|
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 ©) : 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 ©) { 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 ®ion = (*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 ¶ms) { 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 }