Panda3D
 All Classes Functions Variables Enumerations
paletteImage.cxx
1 // Filename: paletteImage.cxx
2 // Created by: drose (01Dec00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "paletteImage.h"
16 #include "palettePage.h"
17 #include "paletteGroup.h"
18 #include "texturePlacement.h"
19 #include "palettizer.h"
20 #include "textureImage.h"
21 #include "sourceTextureImage.h"
22 #include "filenameUnifier.h"
23 
24 #include "indent.h"
25 #include "datagram.h"
26 #include "datagramIterator.h"
27 #include "bamReader.h"
28 #include "bamWriter.h"
29 #include "string_utils.h"
30 
31 #include <algorithm>
32 
33 TypeHandle PaletteImage::_type_handle;
34 
35 ////////////////////////////////////////////////////////////////////
36 // Function: PaletteImage::ClearedRegion::Default Constructor
37 // Access: Public
38 // Description: The default constructor is only for the convenience
39 // of the bam reader.
40 ////////////////////////////////////////////////////////////////////
41 PaletteImage::ClearedRegion::
42 ClearedRegion() {
43  _x = 0;
44  _y = 0;
45  _x_size = 0;
46  _y_size = 0;
47 }
48 
49 ////////////////////////////////////////////////////////////////////
50 // Function: PaletteImage::ClearedRegion::Constructor
51 // Access: Public
52 // Description:
53 ////////////////////////////////////////////////////////////////////
54 PaletteImage::ClearedRegion::
55 ClearedRegion(TexturePlacement *placement) {
56  _x = placement->get_placed_x();
57  _y = placement->get_placed_y();
58  _x_size = placement->get_placed_x_size();
59  _y_size = placement->get_placed_y_size();
60 }
61 
62 ////////////////////////////////////////////////////////////////////
63 // Function: PaletteImage::ClearedRegion::Copy Constructor
64 // Access: Public
65 // Description:
66 ////////////////////////////////////////////////////////////////////
67 PaletteImage::ClearedRegion::
68 ClearedRegion(const PaletteImage::ClearedRegion &copy) :
69  _x(copy._x),
70  _y(copy._y),
71  _x_size(copy._x_size),
72  _y_size(copy._y_size)
73 {
74 }
75 
76 ////////////////////////////////////////////////////////////////////
77 // Function: PaletteImage::ClearedRegion::Copy Assignment Operator
78 // Access: Public
79 // Description:
80 ////////////////////////////////////////////////////////////////////
81 void PaletteImage::ClearedRegion::
82 operator = (const PaletteImage::ClearedRegion &copy) {
83  _x = copy._x;
84  _y = copy._y;
85  _x_size = copy._x_size;
86  _y_size = copy._y_size;
87 }
88 
89 ////////////////////////////////////////////////////////////////////
90 // Function: PaletteImage::ClearedRegion::clear
91 // Access: Public
92 // Description: Sets the appropriate region of the image to black.
93 ////////////////////////////////////////////////////////////////////
94 void PaletteImage::ClearedRegion::
95 clear(PNMImage &image) {
96  LRGBColorf rgb(pal->_background[0], pal->_background[1], pal->_background[2]);
97  float alpha = pal->_background[3];
98 
99  for (int y = _y; y < _y + _y_size; y++) {
100  for (int x = _x; x < _x + _x_size; x++) {
101  image.set_xel(x, y, rgb);
102  }
103  }
104  if (image.has_alpha()) {
105  for (int y = _y; y < _y + _y_size; y++) {
106  for (int x = _x; x < _x + _x_size; x++) {
107  image.set_alpha(x, y, alpha);
108  }
109  }
110  }
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: PaletteImage::ClearedRegion::write_datagram
115 // Access: Public
116 // Description: Writes the contents of the ClearedRegion to the
117 // indicated datagram.
118 ////////////////////////////////////////////////////////////////////
119 void PaletteImage::ClearedRegion::
120 write_datagram(Datagram &datagram) const {
121  datagram.add_int32(_x);
122  datagram.add_int32(_y);
123  datagram.add_int32(_x_size);
124  datagram.add_int32(_y_size);
125 }
126 
127 ////////////////////////////////////////////////////////////////////
128 // Function: PaletteImage::ClearedRegion::write_datagram
129 // Access: Public
130 // Description: Extracts the contents of the ClearedRegion from the
131 // indicated datagram.
132 ////////////////////////////////////////////////////////////////////
133 void PaletteImage::ClearedRegion::
134 fillin(DatagramIterator &scan) {
135  _x = scan.get_int32();
136  _y = scan.get_int32();
137  _x_size = scan.get_int32();
138  _y_size = scan.get_int32();
139 }
140 
141 
142 
143 
144 
145 
146 ////////////////////////////////////////////////////////////////////
147 // Function: PaletteImage::Default Constructor
148 // Access: Private
149 // Description: The default constructor is only for the convenience
150 // of the Bam reader.
151 ////////////////////////////////////////////////////////////////////
152 PaletteImage::
153 PaletteImage() {
154  _page = (PalettePage *)NULL;
155  _index = 0;
156  _new_image = false;
157  _got_image = false;
158 
159  _swapped_image = 0;
160 }
161 
162 ////////////////////////////////////////////////////////////////////
163 // Function: PaletteImage::Constructor
164 // Access: Public
165 // Description:
166 ////////////////////////////////////////////////////////////////////
167 PaletteImage::
168 PaletteImage(PalettePage *page, int index) :
169  _page(page),
170  _index(index)
171 {
172  _properties = page->get_properties();
173  _size_known = true;
174  _x_size = pal->_pal_x_size;
175  _y_size = pal->_pal_y_size;
176  _new_image = true;
177  _got_image = false;
178  _swapped_image = 0;
179 
180  setup_filename();
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: PaletteImage::Constructor
185 // Access: Public
186 // Description:
187 ////////////////////////////////////////////////////////////////////
188 PaletteImage::
189 PaletteImage(PalettePage *page, int index, unsigned swapIndex) :
190  _page(page),
191  _index(index),
192  _swapped_image(swapIndex)
193 {
194  _properties = page->get_properties();
195  _size_known = true;
196  _x_size = pal->_pal_x_size;
197  _y_size = pal->_pal_y_size;
198  _new_image = true;
199  _got_image = false;
200 
201  setup_filename();
202 }
203 
204 
205 ////////////////////////////////////////////////////////////////////
206 // Function: PaletteImage::get_page
207 // Access: Public
208 // Description: Returns the particular PalettePage this image is
209 // associated with.
210 ////////////////////////////////////////////////////////////////////
212 get_page() const {
213  return _page;
214 }
215 
216 ////////////////////////////////////////////////////////////////////
217 // Function: PaletteImage::is_empty
218 // Access: Public
219 // Description: Returns true if there are no textures, or only one
220 // "solitary" texture, placed on the image. In either
221 // case, the PaletteImage need not be generated.
222 ////////////////////////////////////////////////////////////////////
223 bool PaletteImage::
224 is_empty() const {
225  if (_placements.empty()) {
226  // The image is genuinely empty.
227  return true;
228 
229  } else if (_placements.size() == 1) {
230  // If the image has exactly one texture, we consider the image
231  // empty only if the texture is actually flagged as 'solitary'.
232  return (_placements[0]->get_omit_reason() == OR_solitary);
233 
234  } else {
235  // The image has more than one texture, so it's definitely not
236  // empty.
237  return false;
238  }
239 }
240 
241 ////////////////////////////////////////////////////////////////////
242 // Function: PaletteImage::count_utilization
243 // Access: Public
244 // Description: Returns the fraction of the PaletteImage that is
245 // actually used by any textures. This is 1.0 if every
246 // pixel in the PaletteImage is used, or 0.0 if none
247 // are. Normally it will be somewhere in between.
248 ////////////////////////////////////////////////////////////////////
249 double PaletteImage::
251  int used_pixels = 0;
252 
253  Placements::const_iterator pi;
254  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
255  TexturePlacement *placement = (*pi);
256 
257  int texture_pixels =
258  placement->get_placed_x_size() *
259  placement->get_placed_y_size();
260  used_pixels += texture_pixels;
261  }
262 
263  int total_pixels = get_x_size() * get_y_size();
264 
265  return (double)used_pixels / (double)total_pixels;
266 }
267 
268 ////////////////////////////////////////////////////////////////////
269 // Function: PaletteImage::count_coverage
270 // Access: Public
271 // Description: Returns the a weighted average of the fraction of
272 // coverage represented by all of the textures placed on
273 // the palette. This number represents the fraction of
274 // wasted pixels in the palette image consumed by
275 // copying the same pixels multiple times into the
276 // palette, or if the number is negative, it represents
277 // the fraction of pixels saved by not having to copy
278 // the entire texture into the palette.
279 ////////////////////////////////////////////////////////////////////
280 double PaletteImage::
281 count_coverage() const {
282  int coverage_pixels = 0;
283 
284  Placements::const_iterator pi;
285  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
286  TexturePlacement *placement = (*pi);
287  TextureImage *texture = placement->get_texture();
288  nassertr(texture != (TextureImage *)NULL, 0.0);
289 
290  int orig_pixels =
291  texture->get_x_size() *
292  texture->get_y_size();
293  int placed_pixels =
294  placement->get_placed_x_size() *
295  placement->get_placed_y_size();
296 
297  coverage_pixels += placed_pixels - orig_pixels;
298  }
299 
300  int total_pixels = get_x_size() * get_y_size();
301 
302  return (double)coverage_pixels / (double)total_pixels;
303 }
304 
305 ////////////////////////////////////////////////////////////////////
306 // Function: PaletteImage::place
307 // Access: Public
308 // Description: Attempts to place the indicated texture on the image.
309 // Returns true if successful, or false if there was no
310 // available space.
311 ////////////////////////////////////////////////////////////////////
312 bool PaletteImage::
313 place(TexturePlacement *placement) {
314  nassertr(placement->is_size_known(), true);
315  nassertr(!placement->is_placed(), true);
316 
317  int x, y;
318  if (find_hole(x, y, placement->get_x_size(), placement->get_y_size())) {
319  placement->place_at(this, x, y);
320  _placements.push_back(placement);
321 
322  // [gjeon] create swappedImages
323  TexturePlacement::TextureSwaps::iterator tsi;
324  for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
325  if ((tsi - placement->_textureSwaps.begin()) >= (int)_swappedImages.size()) {
326  PaletteImage *swappedImage = new PaletteImage(_page, _swappedImages.size(), tsi - placement->_textureSwaps.begin() + 1);
327  swappedImage->_masterPlacements = &_placements;
328  _swappedImages.push_back(swappedImage);
329  }
330  }
331 
332  return true;
333  }
334 
335  return false;
336 }
337 
338 ////////////////////////////////////////////////////////////////////
339 // Function: PaletteImage::unplace
340 // Access: Public
341 // Description: Removes the texture from the image.
342 ////////////////////////////////////////////////////////////////////
343 void PaletteImage::
345  nassertv(placement->is_placed() && placement->get_image() == this);
346 
347  Placements::iterator pi;
348  pi = find(_placements.begin(), _placements.end(), placement);
349  while (pi != _placements.end()) {
350  _placements.erase(pi);
351  pi = find(_placements.begin(), _placements.end(), placement);
352  }
353  _cleared_regions.push_back(ClearedRegion(placement));
354 }
355 
356 ////////////////////////////////////////////////////////////////////
357 // Function: PaletteImage::check_solitary
358 // Access: Public
359 // Description: To be called after all textures have been placed on
360 // the image, this checks to see if there is only one
361 // texture on the image. If there is, it is flagged as
362 // 'solitary' so that the egg files will not needlessly
363 // reference the palettized image.
364 //
365 // However, if pal->_omit_solitary is false, we
366 // generally don't change textures to solitary state.
367 ////////////////////////////////////////////////////////////////////
368 void PaletteImage::
370  if (_placements.size() == 1) {
371  // How sad, only one.
372  TexturePlacement *placement = *_placements.begin();
373  nassertv(placement->get_omit_reason() == OR_none ||
374  placement->get_omit_reason() == OR_solitary);
375 
376  if (pal->_omit_solitary || placement->get_omit_reason() == OR_solitary) {
377  // We only omit the solitary texture if (a) we have
378  // omit_solitary in effect, or (b) we don't have omit_solitary
379  // in effect now, but we did before, and the texture is still
380  // flagged as solitary from that previous pass.
381  placement->omit_solitary();
382  }
383 
384  } else {
385  // Zero or multiple.
386  Placements::const_iterator pi;
387  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
388  TexturePlacement *placement = (*pi);
389  /*
390  if (!(placement->get_omit_reason() == OR_none ||
391  placement->get_omit_reason() == OR_solitary)) {
392  nout << "texture " << *placement->get_texture() << " is omitted for "
393  << placement->get_omit_reason() << "\n";
394  }
395  */
396  nassertv(placement->get_omit_reason() == OR_none ||
397  placement->get_omit_reason() == OR_solitary);
398  placement->not_solitary();
399  }
400  }
401 }
402 
403 ////////////////////////////////////////////////////////////////////
404 // Function: PaletteImage::optimal_resize
405 // Access: Public
406 // Description: Attempts to resize the palette image to as small as
407 // it can go.
408 ////////////////////////////////////////////////////////////////////
409 void PaletteImage::
411  if (is_empty()) { // && (_swapped_image == 0)) {
412  return;
413  }
414 
415  bool resized_any = false;
416  bool success;
417  do {
418  success = false;
419  nassertv(_x_size > 0 && _y_size > 0);
420 
421  // Try to cut it in half in both dimensions, one at a time.
422  if (resize_image(_x_size, _y_size / 2)) {
423  success = true;
424  resized_any = true;
425  }
426  if (resize_image(_x_size / 2, _y_size)) {
427  success = true;
428  resized_any = true;
429  }
430 
431  } while (success);
432 
433  if (resized_any) {
434  nout << "Resizing "
436  << _x_size << " " << _y_size << "\n";
437 
438  // [gjeon] resize swapped images, also
439  SwappedImages::iterator si;
440  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
441  PaletteImage *swappedImage = (*si);
442  swappedImage->resize_swapped_image(_x_size, _y_size);
443  }
444  }
445 }
446 
447 ////////////////////////////////////////////////////////////////////
448 // Function: PaletteImage::resize_image
449 // Access: Public
450 // Description: Attempts to resize the palette image, and repack all
451 // of the textures within the new size. Returns true if
452 // successful, false otherwise. If this fails, it will
453 // still result in repacking all the textures in the
454 // original size.
455 ////////////////////////////////////////////////////////////////////
456 bool PaletteImage::
457 resize_image(int x_size, int y_size) {
458  // We already know we're going to be generating a new image from
459  // scratch after this.
460  _cleared_regions.clear();
461  remove_image();
462 
463  // First, Save the current placement list, while simultaneously
464  // clearing it.
465  Placements saved;
466  saved.swap(_placements);
467 
468  // Also save our current size.
469  int saved_x_size = _x_size;
470  int saved_y_size = _y_size;
471 
472  // Then, sort the textures to in order from biggest to smallest, as
473  // an aid to optimal packing.
474  sort(saved.begin(), saved.end(), SortPlacementBySize());
475 
476  // And while we're at it, we need to officially unplace each of
477  // these.
478  Placements::iterator pi;
479  for (pi = saved.begin(); pi != saved.end(); ++pi) {
480  (*pi)->force_replace();
481  }
482 
483  // Finally, apply the new size and try to fit all the textures.
484  _x_size = x_size;
485  _y_size = y_size;
486 
487  bool packed = true;
488  for (pi = saved.begin(); pi != saved.end() && packed; ++pi) {
489  if (!place(*pi)) {
490  packed = false;
491  }
492  }
493 
494  if (!packed) {
495  // If it didn't work, phooey. Put 'em all back.
496  _x_size = saved_x_size;
497  _y_size = saved_y_size;
498 
499  Placements remove;
500  remove.swap(_placements);
501  for (pi = remove.begin(); pi != remove.end(); ++pi) {
502  (*pi)->force_replace();
503  }
504 
505  bool all_packed = true;
506  for (pi = saved.begin(); pi != saved.end(); ++pi) {
507  if (!place(*pi)) {
508  all_packed = false;
509  }
510  }
511  nassertr(all_packed, false);
512  }
513 
514  return packed;
515 }
516 
517 ////////////////////////////////////////////////////////////////////
518 // Function: PaletteImage::resize_swapped_image
519 // Access: Public
520 // Description: Attempts to resize the palette image, and repack all
521 // of the textures within the new size. Returns true if
522 // successful, false otherwise. If this fails, it will
523 // still result in repacking all the textures in the
524 // original size.
525 ////////////////////////////////////////////////////////////////////
526 void PaletteImage::
527 resize_swapped_image(int x_size, int y_size) {
528  // Finally, apply the new size and try to fit all the textures.
529  _x_size = x_size;
530  _y_size = y_size;
531 }
532 
533 ////////////////////////////////////////////////////////////////////
534 // Function: PaletteImage::write_placements
535 // Access: Public
536 // Description: Writes a list of the textures that have been placed
537 // on this image to the indicated output stream, one per
538 // line.
539 ////////////////////////////////////////////////////////////////////
540 void PaletteImage::
541 write_placements(ostream &out, int indent_level) const {
542  Placements::const_iterator pi;
543  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
544  TexturePlacement *placement = (*pi);
545  placement->write_placed(out, indent_level);
546  }
547 }
548 
549 ////////////////////////////////////////////////////////////////////
550 // Function: PaletteImage::reset_image
551 // Access: Public
552 // Description: Unpacks each texture that has been placed on this
553 // image, resetting the image to empty.
554 ////////////////////////////////////////////////////////////////////
555 void PaletteImage::
557  // We need a copy so we can modify this list as we traverse it.
558  Placements copy_placements = _placements;
559  Placements::const_iterator pi;
560  for (pi = copy_placements.begin(); pi != copy_placements.end(); ++pi) {
561  TexturePlacement *placement = (*pi);
562  placement->force_replace();
563  }
564 
565  _placements.clear();
566  _cleared_regions.clear();
567  remove_image();
568 }
569 
570 ////////////////////////////////////////////////////////////////////
571 // Function: PaletteImage::setup_shadow_image
572 // Access: Public
573 // Description: Ensures the _shadow_image has the correct filename
574 // and image types, based on what was supplied on the
575 // command line and in the .txa file.
576 ////////////////////////////////////////////////////////////////////
577 void PaletteImage::
579  _shadow_image.make_shadow_image(_basename);
580 
581  // [gjeon] setup shadoe_image of swappedImages
582  SwappedImages::iterator si;
583  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
584  PaletteImage *swappedImage = (*si);
585  swappedImage->setup_shadow_image();
586  }
587 }
588 
589 ////////////////////////////////////////////////////////////////////
590 // Function: PaletteImage::update_image
591 // Access: Public
592 // Description: If the palette has changed since it was last written
593 // out, updates the image and writes out a new one. If
594 // redo_all is true, regenerates the image from scratch
595 // and writes it out again, whether it needed it or not.
596 ////////////////////////////////////////////////////////////////////
597 void PaletteImage::
598 update_image(bool redo_all) {
599  if (is_empty() && pal->_aggressively_clean_mapdir) {
600  // If the palette image is 'empty', ensure that it doesn't exist.
601  // No need to clutter up the map directory.
602  remove_image();
603  return;
604  }
605 
606  if (redo_all) {
607  // If we're redoing everything, throw out the old image anyway.
608  remove_image();
609  }
610 
611  // Check the filename too.
612  update_filename();
613 
614  // Do we need to update?
615  bool needs_update =
616  _new_image || !exists() ||
617  !_cleared_regions.empty();
618 
619  Placements::iterator pi;
620  // We must continue to walk through all of the textures on the
621  // palette, even after we discover the palette requires an update,
622  // so we can determine which source images need to be recopied.
623  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
624  TexturePlacement *placement = (*pi);
625 
626  if (!placement->is_filled()) {
627  needs_update = true;
628 
629  } else {
630  TextureImage *texture = placement->get_texture();
631 
632  // Only check the timestamps on textures that are named
633  // (indirectly) on the command line.
634  if (texture->is_texture_named()) {
635  SourceTextureImage *source = texture->get_preferred_source();
636 
637  if (source != (SourceTextureImage *)NULL &&
638  source->get_filename().compare_timestamps(get_filename()) > 0) {
639  // The source image is newer than the palette image; we need to
640  // regenerate.
641  placement->mark_unfilled();
642  needs_update = true;
643  }
644  }
645 
646  // [gjeon] to find out all of the swappable textures is up to date
647  TexturePlacement::TextureSwaps::iterator tsi;
648  for (tsi = placement->_textureSwaps.begin(); tsi != placement->_textureSwaps.end(); ++tsi) {
649  TextureImage *swapTexture = (*tsi);
650 
651  if (swapTexture->is_texture_named()) {
652  SourceTextureImage *sourceSwapTexture = swapTexture->get_preferred_source();
653 
654  if (sourceSwapTexture != (SourceTextureImage *)NULL &&
655  sourceSwapTexture->get_filename().compare_timestamps(get_filename()) > 0) {
656  // The source image is newer than the palette image; we need to
657  // regenerate.
658  placement->mark_unfilled();
659  needs_update = true;
660  }
661  }
662  }
663 
664  }
665  }
666 
667  if (!needs_update) {
668  // No sweat; nothing has changed.
669  return;
670  }
671 
672  get_image();
673  // [gjeon] get swapped images, too
674  get_swapped_images();
675 
676  // Set to black any parts of the image that we recently unplaced.
677  ClearedRegions::iterator ci;
678  for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
679  ClearedRegion &region = (*ci);
680  region.clear(_image);
681 
682  // [gjeon] clear swapped images also
683  SwappedImages::iterator si;
684  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
685  PaletteImage *swappedImage = (*si);
686  region.clear(swappedImage->_image);
687  }
688  }
689  _cleared_regions.clear();
690 
691  // Now add the recent additions to the image.
692  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
693  TexturePlacement *placement = (*pi);
694  if (!placement->is_filled()) {
695  placement->fill_image(_image);
696 
697  // [gjeon] fill swapped images
698  SwappedImages::iterator si;
699  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
700  PaletteImage *swappedImage = (*si);
701  swappedImage->update_filename();
702  placement->fill_swapped_image(swappedImage->_image, si - _swappedImages.begin());
703  }
704  }
705  }
706 
707  write(_image);
708 
709  if (pal->_shadow_color_type != (PNMFileType *)NULL) {
710  _shadow_image.write(_image);
711  }
712 
713  release_image();
714 
715  // [gjeon] write and release swapped images
716  SwappedImages::iterator si;
717  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
718  PaletteImage *swappedImage = (*si);
719  swappedImage->write(swappedImage->_image);
720  if (pal->_shadow_color_type != (PNMFileType *)NULL) {
721  swappedImage->_shadow_image.write(swappedImage->_image);
722  }
723  swappedImage->release_image();
724  }
725 }
726 
727 ////////////////////////////////////////////////////////////////////
728 // Function: PaletteImage::update_filename
729 // Access: Public
730 // Description: Changes the image filename to match the current
731 // naming scheme, assuming something has changed since
732 // the image was created. Returns true if the image
733 // filename changes (which means update_image() should
734 // be called).
735 ////////////////////////////////////////////////////////////////////
736 bool PaletteImage::
738  Filename orig_filename = _filename;
739  Filename orig_alpha_filename = _alpha_filename;
740  Filename orig_shadow_filename = _shadow_image.get_filename();
741 
742  if (setup_filename()) {
743  nout << "Renaming " << FilenameUnifier::make_user_filename(orig_filename)
744  << " to " << FilenameUnifier::make_user_filename(_filename) << "\n";
745 
746  if (!orig_filename.empty() && orig_filename.exists()) {
747  nout << "Deleting " << FilenameUnifier::make_user_filename(orig_filename) << "\n";
748  orig_filename.unlink();
749  }
750  if (!orig_alpha_filename.empty() && orig_alpha_filename.exists()) {
751  nout << "Deleting " << FilenameUnifier::make_user_filename(orig_alpha_filename) << "\n";
752  orig_alpha_filename.unlink();
753  }
754  if (!orig_shadow_filename.empty() && orig_shadow_filename.exists()) {
755  nout << "Deleting " << FilenameUnifier::make_user_filename(orig_shadow_filename) << "\n";
756  orig_shadow_filename.unlink();
757  }
758  _new_image = true;
759 
760  // Since the palette filename has changed, we need to mark all of
761  // the egg files that referenced the old filename as stale.
762 
763  // Marking egg files stale at this late point can cause minor
764  // problems; because we might do this, it's necessary for
765  // eggPalettize.cxx to call read_stale_eggs() twice.
766  Placements::iterator pi;
767  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
768  TexturePlacement *placement = (*pi);
769  placement->mark_eggs_stale();
770  }
771 
772  return true;
773  }
774 
775  return false;
776 }
777 
778 ////////////////////////////////////////////////////////////////////
779 // Function: PaletteImage::setup_filename
780 // Access: Private
781 // Description: Sets up the image's filename (and that of the
782 // _shadow_pal) according to the specified properties.
783 //
784 // Returns true if the filename changes from what it was
785 // previously, false otherwise.
786 ////////////////////////////////////////////////////////////////////
787 bool PaletteImage::
788 setup_filename() {
789  // Build up the basename for the palette image, based on the
790  // supplied image pattern.
791  _basename = string();
792 
793  string::iterator si = pal->_generated_image_pattern.begin();
794  while (si != pal->_generated_image_pattern.end()) {
795  if ((*si) == '%') {
796  // Some keycode.
797  ++si;
798  if (si != pal->_generated_image_pattern.end()) {
799  switch (*si) {
800  case '%':
801  _basename += '%';
802  break;
803 
804  case 'g':
805  _basename += _page->get_group()->get_name();
806  break;
807 
808  case 'p':
809  _basename += _page->get_name();
810  break;
811 
812  case 'i':
813  _basename += format_string(_index + 1);
814  break;
815 
816  default:
817  _basename += '%';
818  _basename += (*si);
819  }
820  ++si;
821  }
822  } else {
823  // A literal character.
824  _basename += (*si);
825  ++si;
826  }
827  }
828 
829  if (_swapped_image > 0) {
830  _basename += "_swp_";
831  _basename += format_string(_swapped_image);
832  }
833 
834  // We must end the basename with a dot, so that it does not appear
835  // to have a filename extension. Otherwise, an embedded dot in the
836  // group's name would make everything following appear to be an
837  // extension, which would get lost in the set_filename() call.
838  if (_basename.empty() || _basename[_basename.length() - 1] != '.') {
839  _basename += '.';
840  }
841 
842  bool any_changed = false;
843 
844  if (set_filename(_page->get_group(), _basename)) {
845  any_changed = true;
846  }
847 
848  if (_shadow_image.make_shadow_image(_basename)) {
849  any_changed = true;
850  }
851 
852  return any_changed;
853 }
854 
855 ////////////////////////////////////////////////////////////////////
856 // Function: PaletteImage::find_hole
857 // Access: Private
858 // Description: Searches for a hole of at least x_size by y_size
859 // pixels somewhere within the PaletteImage. If a
860 // suitable hole is found, sets x and y to the top left
861 // corner and returns true; otherwise, returns false.
862 ////////////////////////////////////////////////////////////////////
863 bool PaletteImage::
864 find_hole(int &x, int &y, int x_size, int y_size) const {
865  y = 0;
866  while (y + y_size <= _y_size) {
867  int next_y = _y_size;
868  // Scan along the row at 'y'.
869  x = 0;
870  while (x + x_size <= _x_size) {
871  int next_x = x;
872 
873  // Consider the spot at x, y.
874  TexturePlacement *overlap = find_overlap(x, y, x_size, y_size);
875 
876  if (overlap == (TexturePlacement *)NULL) {
877  // Hooray!
878  return true;
879  }
880 
881  next_x = overlap->get_placed_x() + overlap->get_placed_x_size();
882  next_y = min(next_y, overlap->get_placed_y() + overlap->get_placed_y_size());
883  nassertr(next_x > x, false);
884  x = next_x;
885  }
886 
887  nassertr(next_y > y, false);
888  y = next_y;
889  }
890 
891  // Nope, wouldn't fit anywhere.
892  return false;
893 }
894 
895 ////////////////////////////////////////////////////////////////////
896 // Function: PaletteImage::find_overlap
897 // Access: Private
898 // Description: If the rectangle whose top left corner is x, y and
899 // whose size is x_size, y_size describes an empty hole
900 // that does not overlap any placed images, returns
901 // NULL; otherwise, returns the first placed texture
902 // that the image does overlap. It is assumed the
903 // rectangle lies completely within the boundaries of
904 // the image itself.
905 ////////////////////////////////////////////////////////////////////
906 TexturePlacement *PaletteImage::
907 find_overlap(int x, int y, int x_size, int y_size) const {
908  Placements::const_iterator pi;
909  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
910  TexturePlacement *placement = (*pi);
911  if (placement->is_placed() &&
912  placement->intersects(x, y, x_size, y_size)) {
913  return placement;
914  }
915  }
916 
917  return (TexturePlacement *)NULL;
918 }
919 
920 ////////////////////////////////////////////////////////////////////
921 // Function: PaletteImage::get_image
922 // Access: Public
923 // Description: Reads or generates the PNMImage that corresponds to
924 // the palette as it is known so far.
925 ////////////////////////////////////////////////////////////////////
926 void PaletteImage::
927 get_image() {
928  if (_got_image) {
929  return;
930  }
931 
932  if (!_new_image) {
933  if (pal->_shadow_color_type != (PNMFileType *)NULL) {
934  if (_shadow_image.get_filename().exists() && _shadow_image.read(_image)) {
935  _got_image = true;
936  return;
937  }
938  } else {
939  if (get_filename().exists() && read(_image)) {
940  _got_image = true;
941  return;
942  }
943  }
944  }
945 
946  nout << "Generating new "
948 
949  // We won't be using this any more.
950  _cleared_regions.clear();
951 
952  _image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
953  _image.fill(pal->_background[0], pal->_background[1], pal->_background[2]);
954  if (_image.has_alpha()) {
955  _image.alpha_fill(pal->_background[3]);
956  }
957 
958  _new_image = false;
959  _got_image = true;
960 
961  // Now fill up the image.
962  Placements::iterator pi;
963  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
964  TexturePlacement *placement = (*pi);
965  placement->fill_image(_image);
966  }
967 }
968 
969 ////////////////////////////////////////////////////////////////////
970 // Function: PaletteImage::get_swapped_image
971 // Access: Public
972 // Description: Reads or generates the PNMImage for swapped textures
973 ////////////////////////////////////////////////////////////////////
974 void PaletteImage::
975 get_swapped_image(int index) {
976  if (_got_image) {
977  return;
978  }
979 
980  if (!_new_image) {
981  if (pal->_shadow_color_type != (PNMFileType *)NULL) {
982  if (_shadow_image.get_filename().exists() && _shadow_image.read(_image)) {
983  _got_image = true;
984  return;
985  }
986  } else {
987  if (get_filename().exists() && read(_image)) {
988  _got_image = true;
989  return;
990  }
991  }
992  }
993 
994  nout << "Generating new "
996 
997  // We won't be using this any more.
998  _cleared_regions.clear();
999 
1000  _image.clear(get_x_size(), get_y_size(), _properties.get_num_channels());
1001  _image.fill(pal->_background[0], pal->_background[1], pal->_background[2]);
1002  if (_image.has_alpha()) {
1003  _image.alpha_fill(pal->_background[3]);
1004  }
1005 
1006  _new_image = false;
1007  _got_image = true;
1008 
1009  // Now fill up the image.
1010  Placements::iterator pi;
1011  for (pi = _masterPlacements->begin(); pi != _masterPlacements->end(); ++pi) {
1012  TexturePlacement *placement = (*pi);
1013  if ((int)placement->_textureSwaps.size() > index) {
1014  placement->fill_swapped_image(_image, index);
1015  } else {
1016  placement->fill_image(_image);
1017  }
1018  }
1019 }
1020 
1021 ////////////////////////////////////////////////////////////////////
1022 // Function: PaletteImage::get_swapped_images
1023 // Access: Public
1024 // Description: Reads or generates the PNMImage that corresponds to
1025 // the palette as it is known so far.
1026 ////////////////////////////////////////////////////////////////////
1027 void PaletteImage::
1028 get_swapped_images() {
1029  SwappedImages::iterator si;
1030  for (si = _swappedImages.begin(); si != _swappedImages.end(); ++si) {
1031  PaletteImage *swappedImage = (*si);
1032  swappedImage->get_swapped_image(si - _swappedImages.begin());
1033  }
1034 }
1035 
1036 ////////////////////////////////////////////////////////////////////
1037 // Function: PaletteImage::release_image
1038 // Access: Public
1039 // Description: Deallocates the memory allocated by a previous call to
1040 // get_image().
1041 ////////////////////////////////////////////////////////////////////
1042 void PaletteImage::
1043 release_image() {
1044  _image.clear();
1045  _got_image = false;
1046 }
1047 
1048 ////////////////////////////////////////////////////////////////////
1049 // Function: PaletteImage::remove_image
1050 // Access: Private
1051 // Description: Deletes the image file.
1052 ////////////////////////////////////////////////////////////////////
1053 void PaletteImage::
1054 remove_image() {
1055  unlink();
1056  if (pal->_shadow_color_type != (PNMFileType *)NULL) {
1057  _shadow_image.unlink();
1058  }
1059  _new_image = true;
1060 }
1061 
1062 ////////////////////////////////////////////////////////////////////
1063 // Function: PaletteImage::register_with_read_factory
1064 // Access: Public, Static
1065 // Description: Registers the current object as something that can be
1066 // read from a Bam file.
1067 ////////////////////////////////////////////////////////////////////
1068 void PaletteImage::
1071  register_factory(get_class_type(), make_PaletteImage);
1072 }
1073 
1074 ////////////////////////////////////////////////////////////////////
1075 // Function: PaletteImage::write_datagram
1076 // Access: Public, Virtual
1077 // Description: Fills the indicated datagram up with a binary
1078 // representation of the current object, in preparation
1079 // for writing to a Bam file.
1080 ////////////////////////////////////////////////////////////////////
1081 void PaletteImage::
1082 write_datagram(BamWriter *writer, Datagram &datagram) {
1083  ImageFile::write_datagram(writer, datagram);
1084 
1085  datagram.add_uint32(_cleared_regions.size());
1086  ClearedRegions::const_iterator ci;
1087  for (ci = _cleared_regions.begin(); ci != _cleared_regions.end(); ++ci) {
1088  (*ci).write_datagram(datagram);
1089  }
1090 
1091  datagram.add_uint32(_placements.size());
1092  Placements::const_iterator pi;
1093  for (pi = _placements.begin(); pi != _placements.end(); ++pi) {
1094  writer->write_pointer(datagram, (*pi));
1095  }
1096 
1097  writer->write_pointer(datagram, _page);
1098  datagram.add_uint32(_index);
1099  datagram.add_string(_basename);
1100  datagram.add_bool(_new_image);
1101 
1102  // We don't write _got_image or _image. These are loaded
1103  // per-session.
1104 
1105  // We don't write _shadow_image. This is just a runtime convenience
1106  // for specifying the name of the shadow file, and we redefine this
1107  // per-session (which allows us to pick up a new
1108  // pal->_shadow_dirname if it changes).
1109 }
1110 
1111 ////////////////////////////////////////////////////////////////////
1112 // Function: PaletteImage::complete_pointers
1113 // Access: Public, Virtual
1114 // Description: Called after the object is otherwise completely read
1115 // from a Bam file, this function's job is to store the
1116 // pointers that were retrieved from the Bam file for
1117 // each pointer object written. The return value is the
1118 // number of pointers processed from the list.
1119 ////////////////////////////////////////////////////////////////////
1120 int PaletteImage::
1122  int index = ImageFile::complete_pointers(p_list, manager);
1123 
1124  int i;
1125  _placements.reserve(_num_placements);
1126  for (i = 0; i < _num_placements; i++) {
1127  TexturePlacement *placement;
1128  DCAST_INTO_R(placement, p_list[index], index);
1129  _placements.push_back(placement);
1130  index++;
1131  }
1132 
1133  if (p_list[index] != (TypedWritable *)NULL) {
1134  DCAST_INTO_R(_page, p_list[index], index);
1135  }
1136  index++;
1137 
1138  return index;
1139 }
1140 
1141 ////////////////////////////////////////////////////////////////////
1142 // Function: PaletteImage::make_PaletteImage
1143 // Access: Protected
1144 // Description: This method is called by the BamReader when an object
1145 // of this type is encountered in a Bam file; it should
1146 // allocate and return a new object with all the data
1147 // read.
1148 ////////////////////////////////////////////////////////////////////
1149 TypedWritable *PaletteImage::
1150 make_PaletteImage(const FactoryParams &params) {
1151  PaletteImage *me = new PaletteImage;
1152  DatagramIterator scan;
1153  BamReader *manager;
1154 
1155  parse_params(params, scan, manager);
1156  me->fillin(scan, manager);
1157  return me;
1158 }
1159 
1160 ////////////////////////////////////////////////////////////////////
1161 // Function: PaletteImage::fillin
1162 // Access: Protected
1163 // Description: Reads the binary data from the given datagram
1164 // iterator, which was written by a previous call to
1165 // write_datagram().
1166 ////////////////////////////////////////////////////////////////////
1167 void PaletteImage::
1168 fillin(DatagramIterator &scan, BamReader *manager) {
1169  ImageFile::fillin(scan, manager);
1170 
1171  int num_cleared_regions = scan.get_uint32();
1172  _cleared_regions.reserve(num_cleared_regions);
1173  for (int i = 0; i < num_cleared_regions; i++) {
1174  _cleared_regions.push_back(ClearedRegion());
1175  _cleared_regions.back().fillin(scan);
1176  }
1177 
1178  _num_placements = scan.get_uint32();
1179  manager->read_pointers(scan, _num_placements);
1180 
1181  manager->read_pointer(scan); // _page
1182 
1183  _index = scan.get_uint32();
1184  _basename = scan.get_string();
1185  _new_image = scan.get_bool();
1186 }
int get_x_size() const
Returns the size in the X dimension, in pixels, of the texture image as it must appear in the palette...
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
void not_solitary()
Indicates that the texture, formerly indicated as solitary, is now no longer.
bool unlink() const
Permanently deletes the file associated with the filename, if possible.
Definition: filename.cxx:2554
static void register_with_read_factory()
Registers the current object as something that can be read from a Bam file.
bool get_bool()
Extracts a boolean value.
void add_string(const string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:351
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
PalettePage * get_page() const
Returns the particular PalettePage this image is associated with.
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function&#39;s job is to store...
Definition: imageFile.cxx:519
void alpha_fill(float alpha=0.0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.I:218
bool set_filename(PaletteGroup *group, const string &basename)
Sets the filename, and if applicable, the alpha_filename, from the indicated basename.
Definition: imageFile.cxx:181
bool is_placed() const
Returns true if the texture has been placed on a palette image, false otherwise.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:35
void write_placements(ostream &out, int indent_level=0) const
Writes a list of the textures that have been placed on this image to the indicated output stream...
int get_placed_y() const
Returns the Y pixel at which the texture has been placed within its PaletteImage. ...
int get_placed_x_size() const
Returns the size in the X dimension, in pixels, of the texture image as it has been placed within the...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool make_shadow_image(const string &basename)
Sets up the ImageFile as a &quot;shadow image&quot; of a particular PaletteImage.
Definition: imageFile.cxx:56
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object, in preparation for writing to a Bam file.
PN_int32 get_int32()
Extracts a signed 32-bit integer.
This is a particular collection of textures, within a PaletteGroup, that all share the same TexturePr...
Definition: palettePage.h:37
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
bool is_texture_named() const
Returns true if this particular texture has been named by the user for procession this session...
void place_at(PaletteImage *image, int x, int y)
Assigns the texture to a particular position within the indicated PaletteImage.
int get_y_size() const
Returns the size of the image file in pixels in the Y direction.
Definition: imageFile.cxx:106
int get_y_size() const
Returns the size in the Y dimension, in pixels, of the texture image as it must appear in the palette...
void omit_solitary()
Sets the omit reason (returned by get_omit()) to OR_solitary, indicating that the palettized version ...
string get_string()
Extracts a variable-length string.
SourceTextureImage * get_preferred_source()
Determines the preferred source image for examining size and reading pixels, etc. ...
PaletteGroup * get_group() const
Returns the group this particular PalettePage belongs to.
Definition: palettePage.cxx:62
bool place(TexturePlacement *placement)
Attempts to place the indicated texture on the image.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object, in preparation for writing to a Bam file.
Definition: imageFile.cxx:498
bool exists() const
Returns true if the file or files named by the image file exist, false otherwise. ...
Definition: imageFile.cxx:303
void optimal_resize()
Attempts to resize the palette image to as small as it can go.
void force_replace()
Removes the texture from its particular PaletteImage, but does not remove it from the PaletteGroup...
void mark_unfilled()
Marks the texture as unfilled, so that it will need to be copied into the palette image again...
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:118
void check_solitary()
To be called after all textures have been placed on the image, this checks to see if there is only on...
static Filename make_user_filename(Filename filename)
Returns a new filename that&#39;s made relative to the current directory, suitable for reporting to the u...
bool is_empty() const
Returns true if there are no textures, or only one &quot;solitary&quot; texture, placed on the image...
OmitReason get_omit_reason() const
Returns the reason the texture has been omitted from a palette image, or OR_none if it has not...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
bool is_filled() const
Returns true if the texture has been filled (i.e.
const Filename & get_filename() const
Returns the primary filename of the image file.
Definition: imageFile.cxx:263
void reset_image()
Unpacks each texture that has been placed on this image, resetting the image to empty.
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
Definition: bamReader.cxx:694
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function&#39;s job is to store...
bool write(const PNMImage &image) const
Writes out the image in the indicated PNMImage to the _filename and/or _alpha_filename.
Definition: imageFile.cxx:391
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
This corresponds to a particular assignment of a TextureImage with a PaletteGroup, and specifically describes which PaletteImage (if any), and where on the PaletteImage, the TextureImage has been assigned to.
This is a texture image reference as it appears in an egg file: the source image of the texture...
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
const TextureProperties & get_properties() const
Returns the texture grouping properties that all textures in this page share.
Definition: palettePage.cxx:73
int get_x_size() const
Returns the size of the image file in pixels in the X direction.
Definition: imageFile.cxx:93
int compare_timestamps(const Filename &other, bool this_missing_is_old=true, bool other_missing_is_old=true) const
Returns a number less than zero if the file named by this object is older than the given file...
Definition: filename.cxx:1521
double count_utilization() const
Returns the fraction of the PaletteImage that is actually used by any textures.
void unlink()
Deletes the image file or files.
Definition: imageFile.cxx:442
int get_placed_x() const
Returns the X pixel at which the texture has been placed within its PaletteImage. ...
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color, type, etc).
Definition: pnmImage.cxx:50
int get_placed_y_size() const
Returns the size in the Y dimension, in pixels, of the texture image as it has been placed within the...
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:1007
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
PaletteImage * get_image() const
Returns the particular PaletteImage on which the texture has been placed.
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
bool is_size_known() const
Returns true if the texture&#39;s size is known, false otherwise.
void setup_shadow_image()
Ensures the _shadow_image has the correct filename and image types, based on what was supplied on the...
void fill_swapped_image(PNMImage &image, int index)
Fills in the rectangle of the swapped palette image represented by the texture placement with the ima...
double count_coverage() const
Returns the a weighted average of the fraction of coverage represented by all of the textures placed ...
This is a single palette image, one of several within a PalettePage, which is in turn one of several ...
Definition: paletteImage.h:36
void unplace(TexturePlacement *placement)
Removes the texture from the image.
bool read(PNMImage &image) const
Reads in the image (or images, if the alpha_filename is separate) and stores it in the indicated PNMI...
Definition: imageFile.cxx:326
bool intersects(int x, int y, int x_size, int y_size)
Returns true if the particular position this texture has been assigned to overlaps the rectangle whos...
void add_int32(PN_int32 value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:159
A class to retrieve the individual data elements previously stored in a Datagram. ...
bool resize_image(int x_size, int y_size)
Attempts to resize the palette image, and repack all of the textures within the new size...
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:51
void update_image(bool redo_all)
If the palette has changed since it was last written out, updates the image and writes out a new one...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
TextureImage * get_texture() const
Returns the texture that this placement represents.
void fill(float red, float green, float blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.I:186
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
void resize_swapped_image(int x_size, int y_size)
Attempts to resize the palette image, and repack all of the textures within the new size...
void set_xel(int x, int y, const LRGBColorf &value)
Changes the RGB color at the indicated pixel.
Definition: pnmImage.I:641
void mark_eggs_stale()
Marks all the egg files that reference this placement stale.
void write_placed(ostream &out, int indent_level=0)
Writes the placement position information on a line by itself.
bool update_filename()
Changes the image filename to match the current naming scheme, assuming something has changed since t...
void fill_image(PNMImage &image)
Fills in the rectangle of the palette image represented by the texture placement with the image pixel...
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:279
void read_pointer(DatagramIterator &scan)
The interface for reading a pointer to another object from a Bam file.
Definition: bamReader.cxx:652