Panda3D
textureImage.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file textureImage.cxx
10  * @author drose
11  * @date 2000-11-29
12  */
13 
14 #include "textureImage.h"
15 #include "sourceTextureImage.h"
16 #include "destTextureImage.h"
17 #include "eggFile.h"
18 #include "paletteGroup.h"
19 #include "paletteImage.h"
20 #include "texturePlacement.h"
21 #include "filenameUnifier.h"
22 #include "string_utils.h"
23 #include "indent.h"
24 #include "datagram.h"
25 #include "datagramIterator.h"
26 #include "bamReader.h"
27 #include "bamWriter.h"
28 #include "pnmFileType.h"
29 #include "indirectCompareNames.h"
30 #include "pvector.h"
31 
32 #include <iterator>
33 
34 using std::string;
35 
36 TypeHandle TextureImage::_type_handle;
37 
38 /**
39  *
40  */
41 TextureImage::
42 TextureImage() {
43  _preferred_source = nullptr;
44  _read_source_image = false;
45  _allow_release_source_image = true;
46  _is_surprise = true;
47  _ever_read_image = false;
48  _forced_grayscale = false;
49  _alpha_bits = 0;
50  _mid_pixel_ratio = 0.0;
51  _is_cutout = false;
52  _alpha_mode = EggRenderMode::AM_unspecified;
53  _txa_wrap_u = EggTexture::WM_unspecified;
54  _txa_wrap_v = EggTexture::WM_unspecified;
55  _texture_named = false;
56  _got_txa_file = false;
57 }
58 
59 /**
60  * Records that a particular egg file references this texture. This is
61  * essential to know when deciding how to assign the TextureImage to the
62  * various PaletteGroups.
63  */
64 void TextureImage::
65 note_egg_file(EggFile *egg_file) {
66  nassertv(!egg_file->get_complete_groups().empty());
67  _egg_files.insert(egg_file);
68 }
69 
70 /**
71  * Assigns the texture to all of the PaletteGroups the various egg files that
72  * use it need. Attempts to choose the minimum set of PaletteGroups that
73  * satisfies all of the egg files.
74  */
75 void TextureImage::
77  if (_egg_files.empty()) {
78  // If we're not referenced by any egg files any more, assign us to no
79  // groups.
80  PaletteGroups empty;
81  assign_to_groups(empty);
82  return;
83  }
84 
85  PaletteGroups definitely_in;
86 
87  // First, we need to eliminate from consideration all the egg files that are
88  // already taken care of by the user's explicit group assignments for this
89  // texture.
90  WorkingEggs needed_eggs;
91 
92  if (_explicitly_assigned_groups.empty()) {
93  // If we have no explicit group assignments, we must consider all the egg
94  // files.
95  std::copy(_egg_files.begin(), _egg_files.end(), std::back_inserter(needed_eggs));
96 
97  } else {
98  // Otherwise, we only need to consider the egg files that don't have any
99  // groups in common with our explicit assignments.
100 
101  EggFiles::const_iterator ei;
102  for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
103  PaletteGroups intersect;
104  intersect.make_intersection(_explicitly_assigned_groups, (*ei)->get_complete_groups());
105  if (!intersect.empty()) {
106  // This egg file is satisfied by one of the texture's explicit
107  // assignments.
108 
109  // We must use at least one of the explicitly-assigned groups that
110  // satisfied the egg file. We don't need to use all of them, however,
111  // and we choose the first one arbitrarily.
112  definitely_in.insert(*intersect.begin());
113 
114  } else {
115  // This egg file was not satisfied by any of the texture's explicit
116  // assignments. Therefore, we'll need to choose some additional group
117  // to assign the texture to, to make the egg file happy. Defer this a
118  // bit.
119  needed_eggs.push_back(*ei);
120  }
121  }
122  }
123 
124  while (!needed_eggs.empty()) {
125  // We need to know the complete set of groups that we need to consider
126  // adding the texture to. This is the union of all the egg files'
127  // requested groups.
128  PaletteGroups total;
129  WorkingEggs::const_iterator ei;
130  for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
131  total.make_union(total, (*ei)->get_complete_groups());
132  }
133 
134  // We don't count the "null" group for texture assignment.
135  total.remove_null();
136  if (total.empty()) {
137  break;
138  }
139 
140  // Now, find the group that will satisfy the most egg files. If two
141  // groups satisfy the same number of egg files, choose (a) the most
142  // specific one, i.e. with the lowest dirname_level, or the lowest
143  // dependency_level if the dirname_levels are equal, and (b) the one that
144  // has the fewest egg files sharing it.
145  PaletteGroups::iterator gi = total.begin();
146  PaletteGroup *best = (*gi);
147  int best_egg_count = compute_egg_count(best, needed_eggs);
148  ++gi;
149  while (gi != total.end()) {
150  PaletteGroup *group = (*gi);
151 
152  // Do we prefer this group to our current 'best'?
153  bool prefer_group = false;
154  int group_egg_count = compute_egg_count(group, needed_eggs);
155  if (group_egg_count != best_egg_count) {
156  prefer_group = (group_egg_count > best_egg_count);
157 
158  } else {
159  prefer_group = group->is_preferred_over(*best);
160  }
161 
162  if (prefer_group) {
163  best = group;
164  best_egg_count = group_egg_count;
165  }
166  ++gi;
167  }
168 
169  // Okay, now we've picked the best group. Eliminate all the eggs from
170  // consideration that are satisfied by this group, and repeat.
171  definitely_in.insert(best);
172 
173  WorkingEggs next_needed_eggs;
174  for (ei = needed_eggs.begin(); ei != needed_eggs.end(); ++ei) {
175  if ((*ei)->get_complete_groups().count(best) == 0) {
176  // This one wasn't eliminated.
177  next_needed_eggs.push_back(*ei);
178  }
179  }
180  needed_eggs.swap(next_needed_eggs);
181  }
182 
183  // Finally, now that we've computed the set of groups we need to assign the
184  // texture to, we need to reconcile this with the set of groups we've
185  // assigned the texture to previously.
186  assign_to_groups(definitely_in);
187 }
188 
189 /**
190  * Once assign_groups() has been called, this returns the actual set of groups
191  * the TextureImage has been assigned to.
192  */
194 get_groups() const {
195  return _actual_assigned_groups;
196 }
197 
198 /**
199  * Gets the TexturePlacement object which represents the assignment of this
200  * texture to the indicated group. If the texture has not been assigned to
201  * the indicated group, returns NULL.
202  */
205  Placement::const_iterator pi;
206  pi = _placement.find(group);
207  if (pi == _placement.end()) {
208  return nullptr;
209  }
210 
211  return (*pi).second;
212 }
213 
214 /**
215  * Removes the texture from any PaletteImages it is assigned to, but does not
216  * remove it from the groups. It will be re-placed within each group when
217  * PaletteGroup::place_all() is called.
218  */
219 void TextureImage::
221  Placement::iterator pi;
222  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
223  (*pi).second->force_replace();
224  }
225 }
226 
227 /**
228  * Marks all the egg files that reference this texture stale. Should be
229  * called only when the texture properties change in some catastrophic way
230  * that will require every egg file referencing it to be regenerated, even if
231  * it is not palettized.
232  */
233 void TextureImage::
235  Placement::iterator pi;
236  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
237  (*pi).second->mark_eggs_stale();
238  }
239 }
240 
241 /**
242  * Indicates that this particular texture has been named by the user for
243  * processing this session, normally by listing an egg file on the command
244  * line that references it.
245  */
246 void TextureImage::
248  _texture_named = true;
249 }
250 
251 /**
252  * Returns true if this particular texture has been named by the user for
253  * procession this session, for instance by listing an egg file on the command
254  * line that references it.
255  */
256 bool TextureImage::
258  return _texture_named;
259 }
260 
261 /**
262  * Updates any internal state prior to reading the .txa file.
263  */
264 void TextureImage::
266  // Save our current properties, so we can note if they change.
267  _pre_txa_properties = _properties;
268 
269  // Get our properties from the actual image for this texture. It's possible
270  // the .txa file will update them further.
272  if (source != nullptr) {
273  _properties = source->get_properties();
274  }
275 
276  _pre_txa_alpha_mode = _alpha_mode;
277  _alpha_mode = EggRenderMode::AM_unspecified;
278 
279  _request.pre_txa_file();
280  _is_surprise = true;
281 }
282 
283 /**
284  * Once the .txa file has been read and the TextureImage matched against it,
285  * considers applying the requested size change. Updates the TextureImage's
286  * size with the size the texture ought to be, if this can be determined.
287  */
288 void TextureImage::
290  _got_txa_file = true;
291 
292  // First, get the actual size of the texture.
294  if (source != nullptr) {
295  if (source->get_size()) {
296  _size_known = true;
297  _x_size = source->get_x_size();
298  _y_size = source->get_y_size();
299  _properties.set_num_channels(source->get_num_channels());
300  }
301  }
302 
303  // Now update this with a particularly requested size.
304  if (_request._got_size) {
305  _size_known = true;
306  _x_size = _request._x_size;
307  _y_size = _request._y_size;
308  }
309 
310  if (_txa_wrap_u != _request._wrap_u ||
311  _txa_wrap_v != _request._wrap_v) {
312  _txa_wrap_u = _request._wrap_u;
313  _txa_wrap_v = _request._wrap_v;
314 
315  // If the explicit wrap mode changes, we may need to regenerate the egg
316  // files, andor refill the palettes.
317  mark_eggs_stale();
318 
319  Placement::iterator pi;
320  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
321  TexturePlacement *placement = (*pi).second;
322  placement->mark_unfilled();
323  }
324  }
325 
326  if (_properties.has_num_channels() && !_request._keep_format) {
327  int num_channels = _properties.get_num_channels();
328  // Examine the image to determine if we can downgrade the number of
329  // channels, for instance from color to grayscale.
330  if (num_channels == 3 || num_channels == 4) {
331  consider_grayscale();
332  }
333 
334  // Also consider the alpha properties, and whether we should downgrade
335  // from alpha to non-alpha.
336  if (num_channels == 2 || num_channels == 4) {
337  consider_alpha();
338  }
339  }
340 
341  // However, if we got an explicit request for channels, honor that.
342  if (_request._got_num_channels) {
343  _properties.set_num_channels(_request._num_channels);
344  }
345 
346  _properties._generic_format = _request._generic_format;
347  _properties._keep_format = _request._keep_format;
348 
349  if (_request._format != EggTexture::F_unspecified) {
350  _properties._format = _request._format;
351  _properties._force_format = _request._force_format;
352  }
353 
354  if (_request._minfilter != EggTexture::FT_unspecified) {
355  _properties._minfilter = _request._minfilter;
356  }
357  if (_request._magfilter != EggTexture::FT_unspecified) {
358  _properties._magfilter = _request._magfilter;
359  }
360 
361  _properties._anisotropic_degree = _request._anisotropic_degree;
362 
363  if (_properties._color_type == nullptr) {
364  _properties._color_type = _request._properties._color_type;
365  _properties._alpha_type = _request._properties._alpha_type;
366  }
367 
368  // Finally, make sure our properties are fully defined.
369  _properties.fully_define();
370 
371  // Now, if our properties have changed in all that from our previous
372  // session, we need to re-place ourself in all palette groups.
373  if (_properties != _pre_txa_properties) {
374  force_replace();
375 
376  // The above will mark the egg files stale when the texture is palettized
377  // (since the UV's will certainly need to be recomputed), but sometimes we
378  // need to mark the egg files stale even when the texture is not
379  // palettized (if a critical property has changed). The following
380  // accomplishes this:
381  if (!_properties.egg_properties_match(_pre_txa_properties)) {
382  mark_eggs_stale();
383  }
384  }
385 
386  // The alpha mode isn't stored in the properties, because it doesn't affect
387  // which textures may be associated into a common palette.
388  if (_request._alpha_mode != EggRenderMode::AM_unspecified) {
389  _alpha_mode = _request._alpha_mode;
390  }
391 
392  // On the other hand, if we don't have an alpha channel, we shouldn't have
393  // an alpha mode.
394  if (_properties.has_num_channels()) {
395  int num_channels = _properties.get_num_channels();
396  if (num_channels == 1 || num_channels == 3) {
397  _alpha_mode = EggRenderMode::AM_unspecified;
398  }
399  }
400 
401  // If we've changed the alpha mode, we should also mark the eggs stale.
402  if (_pre_txa_alpha_mode != _alpha_mode) {
403  mark_eggs_stale();
404  }
405 }
406 
407 /**
408  * Returns true if this TextureImage has been looked up in the .txa file this
409  * session, false otherwise.
410  */
411 bool TextureImage::
412 got_txa_file() const {
413  return _got_txa_file;
414 }
415 
416 /**
417  * Calls determine_size() on each TexturePlacement for the texture, to ensure
418  * that each TexturePlacement is still requesting the best possible size for
419  * the texture.
420  */
421 void TextureImage::
423  Placement::iterator pi;
424  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
425  TexturePlacement *placement = (*pi).second;
426  placement->determine_size();
427  }
428 }
429 
430 /**
431  * Returns true if the user specifically requested to omit this texture via
432  * the "omit" keyword in the .txa file, or false otherwise.
433  */
434 bool TextureImage::
435 get_omit() const {
436  return _request._omit;
437 }
438 
439 /**
440  * Returns the appropriate coverage threshold for this texture. This is
441  * either the Palettizer::_coverage_threshold parameter, given globally via
442  * -r, or a particular value for this texture as supplied by the "coverage"
443  * keyword in the .txa file.
444  */
445 double TextureImage::
447  return _request._coverage_threshold;
448 }
449 
450 /**
451  * Returns the appropriate margin for this texture. This is either the
452  * Palettizer::_margin parameter, or a particular value for this texture as
453  * supplied by the "margin" keyword in the .txa file.
454  */
455 int TextureImage::
456 get_margin() const {
457  return _request._margin;
458 }
459 
460 /**
461  * Returns true if this particular texture is a 'surprise', i.e. it wasn't
462  * matched by a line in the .txa file that didn't include the keyword 'cont'.
463  */
464 bool TextureImage::
465 is_surprise() const {
466  if (_placement.empty()) {
467  // A texture that is not actually placed anywhere is not considered a
468  // surprise.
469  return false;
470  }
471 
472  return _is_surprise;
473 }
474 
475 /**
476  * Returns true if this particular texture has been placed somewhere,
477  * anywhere, or false if it is not used.
478  */
479 bool TextureImage::
480 is_used() const {
481  return !_placement.empty();
482 }
483 
484 /**
485  * Returns the alpha mode that should be used to render objects with this
486  * texture, as specified by the user or as determined from examining the
487  * texture's alpha channel.
488  */
489 EggRenderMode::AlphaMode TextureImage::
490 get_alpha_mode() const {
491  return _alpha_mode;
492 }
493 
494 /**
495  * Returns the wrap mode specified in the u direction in the txa file, or
496  * WM_unspecified.
497  */
498 EggTexture::WrapMode TextureImage::
499 get_txa_wrap_u() const {
500  return _txa_wrap_u;
501 }
502 
503 /**
504  * Returns the wrap mode specified in the v direction in the txa file, or
505  * WM_unspecified.
506  */
507 EggTexture::WrapMode TextureImage::
508 get_txa_wrap_v() const {
509  return _txa_wrap_v;
510 }
511 
512 
513 /**
514  * Returns the SourceTextureImage corresponding to the given filename(s). If
515  * the given filename has never been used as a SourceTexture for this
516  * particular texture, creates a new SourceTextureImage and returns that.
517  */
519 get_source(const Filename &filename, const Filename &alpha_filename,
520  int alpha_file_channel) {
521  string key = get_source_key(filename, alpha_filename, alpha_file_channel);
522 
523  Sources::iterator si;
524  si = _sources.find(key);
525  if (si != _sources.end()) {
526  return (*si).second;
527  }
528 
529  SourceTextureImage *source =
530  new SourceTextureImage(this, filename, alpha_filename, alpha_file_channel);
531  _sources.insert(Sources::value_type(key, source));
532 
533  // Clear out the preferred source image to force us to rederive this next
534  // time someone asks.
535  _preferred_source = nullptr;
536  _read_source_image = false;
537 
538  return source;
539 }
540 
541 /**
542  * Determines the preferred source image for examining size and reading
543  * pixels, etc. This is the largest and most recent of all the available
544  * source images.
545  */
548  if (_preferred_source != nullptr) {
549  return _preferred_source;
550  }
551 
552  // Now examine all of the various source images available to us and pick the
553  // most suitable. We base this on the following criteria:
554 
555  // (1) A suitable source image must be referenced by at least one egg file,
556  // unless no source images are referenced by any egg file.
557 
558  // (2) A larger source image is preferable to a smaller one.
559 
560  // (3) Given two source images of the same size, the more recent one is
561  // preferable.
562 
563  // Are any source images referenced by an egg file?
564 
565  bool any_referenced = false;
566  Sources::iterator si;
567  for (si = _sources.begin(); si != _sources.end() && !any_referenced; ++si) {
568  SourceTextureImage *source = (*si).second;
569  if (source->get_egg_count() > 0) {
570  any_referenced = true;
571  }
572  }
573 
574  SourceTextureImage *best = nullptr;
575  int best_size = 0;
576 
577  for (si = _sources.begin(); si != _sources.end(); ++si) {
578  SourceTextureImage *source = (*si).second;
579 
580  if (source->get_egg_count() > 0 || !any_referenced) {
581  // Rule (1) passes.
582 
583  if (source->exists() && source->get_size()) {
584  int source_size = source->get_x_size() * source->get_y_size();
585  if (best == nullptr) {
586  best = source;
587  best_size = source_size;
588 
589  } else if (source_size > best_size) {
590  // Rule (2) passes.
591  best = source;
592  best_size = source_size;
593 
594  } else if (source_size == best_size &&
595  source->get_filename().compare_timestamps(best->get_filename()) > 0) {
596  // Rule (3) passes.
597  best = source;
598  best_size = source_size;
599  }
600  }
601  }
602  }
603 
604  if (best == nullptr && !_sources.empty()) {
605  // If we didn't pick any that pass, it must be that all of them are
606  // unreadable. In this case, it really doesn't matter which one we pick,
607  // but we should at least pick one that has an egg reference, if any of
608  // them do.
609  if (any_referenced) {
610  for (si = _sources.begin();
611  si != _sources.end() && best == nullptr;
612  ++si) {
613  SourceTextureImage *source = (*si).second;
614  if (source->get_egg_count() > 0) {
615  best = source;
616  }
617  }
618  } else {
619  best = (*_sources.begin()).second;
620  }
621  }
622 
623  _preferred_source = best;
624  return _preferred_source;
625 }
626 
627 /**
628  * Calls clear_basic_properties() on each source texture image used by this
629  * texture, to reset the properties in preparation for re-applying them from
630  * the set of all known egg files.
631  */
632 void TextureImage::
634  Sources::iterator si;
635  for (si = _sources.begin(); si != _sources.end(); ++si) {
636  SourceTextureImage *source = (*si).second;
637  source->clear_basic_properties();
638  }
639 }
640 
641 /**
642  * Copies the texture to whichever destination directories are appropriate for
643  * the groups in which it has been unplaced. Also removes the old filenames
644  * for previous sessions where it was unplaced, but is no longer.
645  *
646  * If redo_all is true, this recopies the texture whether it needed to or not.
647  */
648 void TextureImage::
649 copy_unplaced(bool redo_all) {
650  // First, we need to build up the set of DestTextureImages that represents
651  // the files we need to generate.
652  Dests generate;
653 
654  // Go through all the TexturePlacements and note the ones for which we're
655  // unplaced. We check get_omit_reason() and not is_placed(), because we
656  // want to consider solitary images to be unplaced in this case.
657  Placement::iterator pi;
658  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
659  TexturePlacement *placement = (*pi).second;
660  if (placement->get_omit_reason() != OR_none &&
661  placement->get_omit_reason() != OR_unknown) {
662  DestTextureImage *dest = new DestTextureImage(placement);
663  Filename filename = dest->get_filename();
665 
666  std::pair<Dests::iterator, bool> insert_result = generate.insert
667  (Dests::value_type(filename, dest));
668  if (!insert_result.second) {
669  // At least two DestTextureImages map to the same filename, no sweat.
670  delete dest;
671  dest = (*insert_result.first).second;
672  }
673 
674  placement->set_dest(dest);
675 
676  } else {
677  placement->set_dest(nullptr);
678  }
679  }
680 
681  if (redo_all) {
682  // If we're redoing everything, we remove everything first and then recopy
683  // it again.
684  Dests empty;
685  remove_old_dests(empty, _dests);
686  copy_new_dests(generate, empty);
687 
688  } else {
689  // Otherwise, we only remove and recopy the things that changed between
690  // this time and last time.
691  remove_old_dests(generate, _dests);
692  copy_new_dests(generate, _dests);
693  }
694 
695  // Clean up the old set.
696  Dests::iterator di;
697  for (di = _dests.begin(); di != _dests.end(); ++di) {
698  delete (*di).second;
699  }
700 
701  _dests.swap(generate);
702 }
703 
704 /**
705  * Reads in the original image, if it has not already been read, and returns
706  * it.
707  */
710  if (!_read_source_image) {
712  if (source != nullptr) {
713  source->read(_source_image);
714  }
715  _read_source_image = true;
716  _allow_release_source_image = true;
717  _ever_read_image = true;
718  }
719 
720  return _source_image;
721 }
722 
723 /**
724  * Frees the memory that was allocated by a previous call to
725  * read_source_image(). The next time read_source_image() is called, it will
726  * have to read the disk again.
727  */
728 void TextureImage::
730  if (_read_source_image && _allow_release_source_image) {
731  _source_image.clear();
732  _read_source_image = false;
733  }
734 }
735 
736 /**
737  * Accepts the indicated source image as if it had been read from disk. This
738  * image is copied into the structure, and will be returned by future calls to
739  * read_source_image().
740  */
741 void TextureImage::
742 set_source_image(const PNMImage &image) {
743  _source_image = image;
744  _allow_release_source_image = false;
745  _read_source_image = true;
746  _ever_read_image = true;
747 }
748 
749 /**
750  * Causes the header part of the image to be reread, usually to confirm that
751  * its image properties (size, number of channels, etc.) haven't changed.
752  */
753 void TextureImage::
755  if (!_read_source_image) {
757  if (source != nullptr) {
758  source->read_header();
759  }
760  }
761 }
762 
763 /**
764  * Returns true if the source image is newer than the indicated file, false
765  * otherwise. If the image has already been read, this always returns false.
766  */
767 bool TextureImage::
768 is_newer_than(const Filename &reference_filename) {
769  if (!_read_source_image) {
771  if (source != nullptr) {
772  const Filename &source_filename = source->get_filename();
773  return source_filename.compare_timestamps(reference_filename) >= 0;
774  }
775  }
776 
777  return false;
778 }
779 
780 /**
781  * Writes the list of source pathnames that might contribute to this texture
782  * to the indicated output stream, one per line.
783  */
784 void TextureImage::
785 write_source_pathnames(std::ostream &out, int indent_level) const {
786  Sources::const_iterator si;
787  for (si = _sources.begin(); si != _sources.end(); ++si) {
788  SourceTextureImage *source = (*si).second;
789 
790  if (source->get_egg_count() > 0) {
791  indent(out, indent_level);
792  source->output_filename(out);
793  if (!source->is_size_known()) {
794  out << " (unknown size)";
795 
796  } else {
797  out << " " << source->get_x_size() << " "
798  << source->get_y_size();
799 
800  if (source->get_properties().has_num_channels()) {
801  out << " " << source->get_properties().get_num_channels();
802  }
803  }
804  out << "\n";
805  }
806  }
807 
808  if (_is_cutout) {
809  indent(out, indent_level)
810  << "Cutout image (ratio " << (PN_stdfloat)_mid_pixel_ratio << ")\n";
811  }
812 
813  // Now write out the group assignments.
814  if (!_egg_files.empty()) {
815  // Sort the egg files into order by name for output.
816  pvector<EggFile *> egg_vector;
817  egg_vector.reserve(_egg_files.size());
818  EggFiles::const_iterator ei;
819  for (ei = _egg_files.begin(); ei != _egg_files.end(); ++ei) {
820  egg_vector.push_back(*ei);
821  }
822  sort(egg_vector.begin(), egg_vector.end(),
824 
825  indent(out, indent_level)
826  << "Used by:\n";
828  for (evi = egg_vector.begin(); evi != egg_vector.end(); ++evi) {
829  EggFile *egg = (*evi);
830  indent(out, indent_level + 2)
831  << egg->get_name() << " (";
832  if (egg->get_explicit_groups().empty()) {
833  out << *egg->get_default_group();
834  } else {
835  out << egg->get_explicit_groups();
836  }
837  out << ")\n";
838  }
839  }
840  if (!_explicitly_assigned_groups.empty()) {
841  indent(out, indent_level)
842  << "Explicitly assigned to " << _explicitly_assigned_groups << " in .txa\n";
843  }
844 
845  if (_placement.empty()) {
846  indent(out, indent_level)
847  << "Not used.\n";
848  } else {
849  indent(out, indent_level)
850  << "Assigned to " << _actual_assigned_groups << "\n";
851  }
852 }
853 
854 /**
855  * Writes the information about the texture's size and placement.
856  */
857 void TextureImage::
858 write_scale_info(std::ostream &out, int indent_level) {
860  indent(out, indent_level) << get_name();
861 
862  // Write the list of groups we're placed in.
863  if (_placement.empty()) {
864  out << " (not used)";
865  } else {
866  Placement::const_iterator pi;
867  pi = _placement.begin();
868  out << " (" << (*pi).second->get_group()->get_name();
869  ++pi;
870  while (pi != _placement.end()) {
871  out << " " << (*pi).second->get_group()->get_name();
872  ++pi;
873  }
874  out << ")";
875  }
876 
877  out << " orig ";
878 
879  if (source == nullptr ||
880  !source->is_size_known()) {
881  out << "unknown";
882  } else {
883  out << source->get_x_size() << " " << source->get_y_size()
884  << " " << source->get_num_channels();
885  }
886 
887  if (!_placement.empty() && is_size_known()) {
888  out << " new " << get_x_size() << " " << get_y_size()
889  << " " << get_num_channels();
890 
891  if (source != nullptr &&
892  source->is_size_known()) {
893  double scale =
894  100.0 * (((double)get_x_size() / (double)source->get_x_size()) +
895  ((double)get_y_size() / (double)source->get_y_size())) / 2.0;
896  out << " scale " << scale << "%";
897  }
898  }
899  out << "\n";
900 
901  // Also cross-reference the placed and unplaced information.
902  Placement::iterator pi;
903  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
904  TexturePlacement *placement = (*pi).second;
905  if (placement->get_omit_reason() == OR_none) {
906  PaletteImage *image = placement->get_image();
907  nassertv(image != nullptr);
908  indent(out, indent_level + 2)
909  << "placed on "
911  << "\n";
912 
913  } else if (placement->get_omit_reason() == OR_unknown) {
914  indent(out, indent_level + 2)
915  << "not placed because unknown.\n";
916 
917  } else {
918  DestTextureImage *image = placement->get_dest();
919  nassertv(image != nullptr);
920  indent(out, indent_level + 2)
921  << "copied to "
923  if (image->is_size_known() && is_size_known() &&
924  (image->get_x_size() != get_x_size() ||
925  image->get_y_size() != get_y_size())) {
926  out << " at size " << image->get_x_size() << " "
927  << image->get_y_size();
928  if (source != nullptr &&
929  source->is_size_known()) {
930  double scale =
931  100.0 * (((double)image->get_x_size() / (double)source->get_x_size()) +
932  ((double)image->get_y_size() / (double)source->get_y_size())) / 2.0;
933  out << " scale " << scale << "%";
934  }
935  }
936  out << "\n";
937  }
938  }
939 }
940 
941 /**
942  * Counts the number of egg files in the indicated set that will be satisfied
943  * if a texture is assigned to the indicated group.
944  */
945 int TextureImage::
946 compute_egg_count(PaletteGroup *group,
947  const TextureImage::WorkingEggs &egg_files) {
948  int count = 0;
949 
950  WorkingEggs::const_iterator ei;
951  for (ei = egg_files.begin(); ei != egg_files.end(); ++ei) {
952  if ((*ei)->get_complete_groups().count(group) != 0) {
953  count++;
954  }
955  }
956 
957  return count;
958 }
959 
960 /**
961  * Assigns the texture to the indicated set of groups. If the texture was
962  * previously assigned to any of these groups, keeps the same TexturePlacement
963  * object for the assignment; at the same time, deletes any TexturePlacement
964  * objects that represent groups we are no longer assigned to.
965  */
966 void TextureImage::
967 assign_to_groups(const PaletteGroups &groups) {
968  PaletteGroups::const_iterator gi;
969  Placement::const_iterator pi;
970 
971  Placement new_placement;
972 
973  gi = groups.begin();
974  pi = _placement.begin();
975 
976  while (gi != groups.end() && pi != _placement.end()) {
977  PaletteGroup *a = (*gi);
978  PaletteGroup *b = (*pi).first;
979 
980  if (a < b) {
981  // Here's a group we're now assigned to that we weren't assigned to
982  // previously.
983  TexturePlacement *place = a->prepare(this);
984  new_placement.insert
985  (new_placement.end(), Placement::value_type(a, place));
986  ++gi;
987 
988  } else if (b < a) {
989  // Here's a group we're no longer assigned to.
990  TexturePlacement *place = (*pi).second;
991  delete place;
992  ++pi;
993 
994  } else { // b == a
995  // Here's a group we're still assigned to.
996  TexturePlacement *place = (*pi).second;
997  new_placement.insert
998  (new_placement.end(), Placement::value_type(a, place));
999  ++gi;
1000  ++pi;
1001  }
1002  }
1003 
1004  while (gi != groups.end()) {
1005  // Here's a group we're now assigned to that we weren't assigned to
1006  // previously.
1007  PaletteGroup *a = (*gi);
1008  TexturePlacement *place = a->prepare(this);
1009  new_placement.insert
1010  (new_placement.end(), Placement::value_type(a, place));
1011  ++gi;
1012  }
1013 
1014  while (pi != _placement.end()) {
1015  // Here's a group we're no longer assigned to.
1016  TexturePlacement *place = (*pi).second;
1017  delete place;
1018  ++pi;
1019  }
1020 
1021  _placement.swap(new_placement);
1022  _actual_assigned_groups = groups;
1023 }
1024 
1025 /**
1026  * Examines the actual contents of the image to determine if it should maybe
1027  * be considered a grayscale image (even though it has separate rgb
1028  * components).
1029  */
1030 void TextureImage::
1031 consider_grayscale() {
1032  // Since this isn't likely to change for a particular texture after its
1033  // creation, we save a bit of time by not performing this check unless this
1034  // is the first time we've ever seen this texture. This will save us from
1035  // having to load the texture images each time we look at them. On the
1036  // other hand, if we've already loaded up the image, then go ahead.
1037  if (!_read_source_image && _ever_read_image) {
1038  if (_forced_grayscale) {
1039  _properties.force_grayscale();
1040  }
1041  return;
1042  }
1043 
1044  const PNMImage &source = read_source_image();
1045  if (!source.is_valid()) {
1046  return;
1047  }
1048 
1049  for (int y = 0; y < source.get_y_size(); y++) {
1050  for (int x = 0; x < source.get_x_size(); x++) {
1051  const xel &v = source.get_xel_val(x, y);
1052  if (PPM_GETR(v) != PPM_GETG(v) || PPM_GETR(v) != PPM_GETB(v)) {
1053  // Here's a colored pixel. We can't go grayscale.
1054  _forced_grayscale = false;
1055  return;
1056  }
1057  }
1058  }
1059 
1060  // All pixels in the image were grayscale!
1061  _properties.force_grayscale();
1062  _forced_grayscale = true;
1063 }
1064 
1065 /**
1066  * Examines the actual contents of the image to determine what alpha
1067  * properties it has.
1068  */
1069 void TextureImage::
1070 consider_alpha() {
1071  // As above, we don't bother doing this if we've already done this in a
1072  // previous session.
1073 
1074  // _alpha_bits == -1 indicates we have read an older textures.boo file that
1075  // didn't define these bits.
1076  if (_read_source_image || !_ever_read_image || _alpha_bits == -1) {
1077  _alpha_bits = 0;
1078  int num_mid_pixels = 0;
1079 
1080  const PNMImage &source = read_source_image();
1081  if (source.is_valid() && source.has_alpha()) {
1082  xelval maxval = source.get_maxval();
1083  for (int y = 0; y < source.get_y_size(); y++) {
1084  for (int x = 0; x < source.get_x_size(); x++) {
1085  xelval alpha_val = source.get_alpha_val(x, y);
1086  if (alpha_val == 0) {
1087  _alpha_bits |= AB_zero;
1088  } else if (alpha_val == maxval) {
1089  _alpha_bits |= AB_one;
1090  } else {
1091  _alpha_bits |= AB_mid;
1092  ++num_mid_pixels;
1093  }
1094  }
1095  }
1096  }
1097 
1098  int num_pixels = source.get_x_size() * source.get_y_size();
1099  _mid_pixel_ratio = 0.0;
1100  if (num_pixels != 0) {
1101  _mid_pixel_ratio = (double)num_mid_pixels / (double)num_pixels;
1102  }
1103  }
1104 
1105  _is_cutout = false;
1106 
1107  if (_alpha_bits != 0) {
1108  if (_alpha_bits == AB_one) {
1109  // All alpha pixels are white; drop the alpha channel.
1110  _properties.force_nonalpha();
1111 
1112  } else if (_alpha_bits == AB_zero) {
1113  // All alpha pixels are invisible; this is probably a mistake. Drop the
1114  // alpha channel and complain.
1115  _properties.force_nonalpha();
1116  if (_read_source_image) {
1117  nout << *this << " has an all-zero alpha channel; dropping alpha.\n";
1118  }
1119 
1120  } else if (_alpha_mode == EggRenderMode::AM_unspecified) {
1121  // Consider fiddling with the alpha mode, if the user hasn't specified a
1122  // particular alpha mode in the txa file.
1123  if ((_alpha_bits & AB_mid) == 0) {
1124  // No middle range bits: a binary alpha image.
1125  _alpha_mode = EggRenderMode::AM_binary;
1126 
1127  } else if ((_alpha_bits & AB_one) != 0 && _mid_pixel_ratio < pal->_cutout_ratio) {
1128  // At least some opaque bits, and relatively few middle range bits: a
1129  // cutout image.
1130  _alpha_mode = pal->_cutout_mode;
1131  _is_cutout = true;
1132 
1133  } else {
1134  // No opaque bits; just use regular alpha blending.
1135  _alpha_mode = EggRenderMode::AM_blend;
1136  }
1137  }
1138  }
1139 }
1140 
1141 /**
1142  * Removes all of the filenames named in b that are not also named in a.
1143  */
1144 void TextureImage::
1145 remove_old_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
1146  Dests::const_iterator ai = a.begin();
1147  Dests::const_iterator bi = b.begin();
1148 
1149  while (ai != a.end() && bi != b.end()) {
1150  const string &astr = (*ai).first;
1151  const string &bstr = (*bi).first;
1152 
1153  if (astr < bstr) {
1154  // Here's a filename in a, not in b.
1155  ++ai;
1156 
1157  } else if (bstr < astr) {
1158  // Here's a filename in b, not in a.
1159  (*bi).second->unlink();
1160  ++bi;
1161 
1162  } else { // bstr == astr
1163  // Here's a filename in both a and b.
1164  ++ai;
1165  ++bi;
1166  }
1167  }
1168 
1169  while (bi != b.end()) {
1170  // Here's a filename in b, not in a.
1171  (*bi).second->unlink();
1172  ++bi;
1173  }
1174 
1175  while (ai != a.end()) {
1176  ++ai;
1177  }
1178 }
1179 
1180 /**
1181  * Copies a resized texture into each filename named in a that is not also
1182  * listed in b, or whose corresponding listing in b is out of date.
1183  */
1184 void TextureImage::
1185 copy_new_dests(const TextureImage::Dests &a, const TextureImage::Dests &b) {
1186  Dests::const_iterator ai = a.begin();
1187  Dests::const_iterator bi = b.begin();
1188 
1189  while (ai != a.end() && bi != b.end()) {
1190  const string &astr = (*ai).first;
1191  const string &bstr = (*bi).first;
1192 
1193  if (astr < bstr) {
1194  // Here's a filename in a, not in b.
1195  (*ai).second->copy(this);
1196  ++ai;
1197 
1198  } else if (bstr < astr) {
1199  // Here's a filename in b, not in a.
1200  ++bi;
1201 
1202  } else { // bstr == astr
1203  // Here's a filename in both a and b.
1204  (*ai).second->copy_if_stale((*bi).second, this);
1205  ++ai;
1206  ++bi;
1207  }
1208  }
1209 
1210  while (ai != a.end()) {
1211  // Here's a filename in a, not in b.
1212  (*ai).second->copy(this);
1213  ++ai;
1214  }
1215 }
1216 
1217 /**
1218  * Returns the key that a SourceTextureImage should be stored in, given its
1219  * one or two filenames.
1220  */
1221 string TextureImage::
1222 get_source_key(const Filename &filename, const Filename &alpha_filename,
1223  int alpha_file_channel) {
1225  Filename a = FilenameUnifier::make_bam_filename(alpha_filename);
1226 
1227  return f.get_fullpath() + ":" + a.get_fullpath() + ":" +
1228  format_string(alpha_file_channel);
1229 }
1230 
1231 /**
1232  * Registers the current object as something that can be read from a Bam file.
1233  */
1234 void TextureImage::
1237  register_factory(get_class_type(), make_TextureImage);
1238 }
1239 
1240 /**
1241  * Fills the indicated datagram up with a binary representation of the current
1242  * object, in preparation for writing to a Bam file.
1243  */
1244 void TextureImage::
1245 write_datagram(BamWriter *writer, Datagram &datagram) {
1246  ImageFile::write_datagram(writer, datagram);
1247  datagram.add_string(get_name());
1248 
1249  // We don't write out _request; this is re-read from the .txa file each
1250  // time.
1251 
1252  // We don't write out _pre_txa_properties; this is transitional.
1253 
1254  // We don't write out _preferred_source; this is redetermined each session.
1255 
1256  datagram.add_bool(_is_surprise);
1257  datagram.add_bool(_ever_read_image);
1258  datagram.add_bool(_forced_grayscale);
1259  datagram.add_uint8(_alpha_bits);
1260  datagram.add_int16((int)_alpha_mode);
1261  datagram.add_float64(_mid_pixel_ratio);
1262  datagram.add_bool(_is_cutout);
1263  datagram.add_uint8((int)_txa_wrap_u);
1264  datagram.add_uint8((int)_txa_wrap_v);
1265 
1266  // We don't write out _explicitly_assigned_groups; this is re-read from the
1267  // .txa file each time.
1268 
1269  _actual_assigned_groups.write_datagram(writer, datagram);
1270 
1271  // We don't write out _egg_files; this is redetermined each session.
1272 
1273  datagram.add_uint32(_placement.size());
1274  Placement::const_iterator pi;
1275  for (pi = _placement.begin(); pi != _placement.end(); ++pi) {
1276  writer->write_pointer(datagram, (*pi).first);
1277  writer->write_pointer(datagram, (*pi).second);
1278  }
1279 
1280  datagram.add_uint32(_sources.size());
1281  Sources::const_iterator si;
1282  for (si = _sources.begin(); si != _sources.end(); ++si) {
1283  writer->write_pointer(datagram, (*si).second);
1284  }
1285 
1286  datagram.add_uint32(_dests.size());
1287  Dests::const_iterator di;
1288  for (di = _dests.begin(); di != _dests.end(); ++di) {
1289  writer->write_pointer(datagram, (*di).second);
1290  }
1291 }
1292 
1293 /**
1294  * Called after the object is otherwise completely read from a Bam file, this
1295  * function's job is to store the pointers that were retrieved from the Bam
1296  * file for each pointer object written. The return value is the number of
1297  * pointers processed from the list.
1298  */
1299 int TextureImage::
1301  int pi = ImageFile::complete_pointers(p_list, manager);
1302 
1303  pi += _actual_assigned_groups.complete_pointers(p_list + pi, manager);
1304 
1305  int i;
1306  for (i = 0; i < _num_placement; i++) {
1307  PaletteGroup *group;
1308  TexturePlacement *placement;
1309  DCAST_INTO_R(group, p_list[pi++], pi);
1310  DCAST_INTO_R(placement, p_list[pi++], pi);
1311  _placement.insert(Placement::value_type(group, placement));
1312  }
1313 
1314  for (i = 0; i < _num_sources; i++) {
1315  SourceTextureImage *source;
1316  DCAST_INTO_R(source, p_list[pi++], pi);
1317  string key = get_source_key(source->get_filename(),
1318  source->get_alpha_filename(),
1319  source->get_alpha_file_channel());
1320 
1321  bool inserted = _sources.insert(Sources::value_type(key, source)).second;
1322  if (!inserted) {
1323  nout << "Warning: texture key " << key
1324  << " is nonunique; texture lost.\n";
1325  }
1326  }
1327 
1328  for (i = 0; i < _num_dests; i++) {
1329  DestTextureImage *dest;
1330  DCAST_INTO_R(dest, p_list[pi++], pi);
1331  bool inserted = _dests.insert(Dests::value_type(dest->get_filename(), dest)).second;
1332  if (!inserted) {
1333  nout << "Warning: dest filename " << dest->get_filename()
1334  << " is nonunique; texture lost.\n";
1335  }
1336  }
1337 
1338  return pi;
1339 }
1340 
1341 /**
1342  * This method is called by the BamReader when an object of this type is
1343  * encountered in a Bam file; it should allocate and return a new object with
1344  * all the data read.
1345  */
1346 TypedWritable *TextureImage::
1347 make_TextureImage(const FactoryParams &params) {
1348  TextureImage *me = new TextureImage;
1349  DatagramIterator scan;
1350  BamReader *manager;
1351 
1352  parse_params(params, scan, manager);
1353  me->fillin(scan, manager);
1354  return me;
1355 }
1356 
1357 /**
1358  * Reads the binary data from the given datagram iterator, which was written
1359  * by a previous call to write_datagram().
1360  */
1361 void TextureImage::
1362 fillin(DatagramIterator &scan, BamReader *manager) {
1363  ImageFile::fillin(scan, manager);
1364  set_name(scan.get_string());
1365 
1366  _is_surprise = scan.get_bool();
1367  _ever_read_image = scan.get_bool();
1368  _forced_grayscale = scan.get_bool();
1369  _alpha_bits = scan.get_uint8();
1370  _alpha_mode = (EggRenderMode::AlphaMode)scan.get_int16();
1371  if (pal->_read_pi_version >= 16) {
1372  _mid_pixel_ratio = scan.get_float64();
1373  _is_cutout = scan.get_bool();
1374  } else {
1375  // Force a re-read of the image if we are upgrading to pi version 16.
1376  _ever_read_image = false;
1377  _mid_pixel_ratio = 0.0;
1378  _is_cutout = false;
1379  }
1380  if (pal->_read_pi_version >= 17) {
1381  _txa_wrap_u = (EggTexture::WrapMode)scan.get_uint8();
1382  _txa_wrap_v = (EggTexture::WrapMode)scan.get_uint8();
1383  }
1384 
1385  _actual_assigned_groups.fillin(scan, manager);
1386 
1387  _num_placement = scan.get_uint32();
1388  manager->read_pointers(scan, _num_placement * 2);
1389 
1390  _num_sources = scan.get_uint32();
1391  manager->read_pointers(scan, _num_sources);
1392  _num_dests = scan.get_uint32();
1393  manager->read_pointers(scan, _num_dests);
1394 }
void mark_eggs_stale()
Marks all the egg files that reference this texture stale.
void make_intersection(const PaletteGroups &a, const PaletteGroups &b)
Computes the intersection of PaletteGroups a and b, and stores the result in this object.
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
Definition: datagram.I:58
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Filename make_bam_filename(Filename filename)
Returns a new filename that's made relative to the bam file itself, suitable for writing to the bam f...
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:277
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function's job is to store...
This represents a texture filename as it has been resized and copied to the map directory (e....
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_num_channels() const
Returns the number of channels (1 through 4) associated with the image.
iterator end() const
Returns an iterator suitable for traversing the set.
bool determine_size()
Attempts to determine the appropriate size of the texture for the given placement.
bool get_bool()
Extracts a boolean value.
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:58
void pre_txa_file()
Updates any internal state prior to reading the .txa file.
bool exists() const
Returns true if the file or files named by the image file exist, false otherwise.
Definition: imageFile.cxx:256
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition: datagram.I:123
void remove_null()
Removes the special "null" group from the set.
int get_alpha_file_channel() const
Returns the particular channel number of the alpha image file from which the alpha channel should be ...
Definition: imageFile.cxx:246
bool is_newer_than(const Filename &reference_filename)
Returns true if the source image is newer than the indicated file, false otherwise.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:110
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function's job is to store...
Definition: imageFile.cxx:454
void clear_source_basic_properties()
Calls clear_basic_properties() on each source texture image used by this texture, to reset the proper...
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
int get_egg_count() const
Returns the number of egg files that share this SourceTextureImage.
void write_scale_info(std::ostream &out, int indent_level=0)
Writes the information about the texture's size and placement.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SourceTextureImage * get_source(const Filename &filename, const Filename &alpha_filename, int alpha_file_channel)
Returns the SourceTextureImage corresponding to the given filename(s).
int get_y_size() const
Returns the size of the image file in pixels in the Y direction.
Definition: imageFile.cxx:92
PaletteImage * get_image() const
Returns the particular PaletteImage on which the texture has been placed.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
iterator begin() const
Returns an iterator suitable for traversing the set.
void determine_placement_size()
Calls determine_size() on each TexturePlacement for the texture, to ensure that each TexturePlacement...
This is the highest level of grouping for TextureImages.
Definition: paletteGroup.h:43
static void register_with_read_factory()
Registers the current object as something that can be read from a Bam file.
void fillin(DatagramIterator &scan, BamReader *manager)
Reads the binary data from the given datagram iterator, which was written by a previous call to write...
void output_filename(std::ostream &out) const
Writes the filename (or pair of filenames) to the indicated output stream.
Definition: imageFile.cxx:424
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read_header()
Reads the actual image header to determine the image properties, like its size.
TexturePlacement * get_placement(PaletteGroup *group) const
Gets the TexturePlacement object which represents the assignment of this texture to the indicated gro...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
EggTexture::WrapMode get_txa_wrap_v() const
Returns the wrap mode specified in the v direction in the txa file, or WM_unspecified.
const PaletteGroups & get_explicit_groups() const
Returns the set of PaletteGroups that the egg file has been explicitly assigned to in the ....
Definition: eggFile.cxx:257
const PaletteGroups & get_complete_groups() const
Returns the complete set of PaletteGroups that the egg file is assigned to.
Definition: eggFile.cxx:276
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void pre_txa_file()
Sets some state up that must be set prior to reading the .txa file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_string()
Extracts a variable-length string.
int get_y_size() const
Returns the number of pixels in the Y direction.
void note_egg_file(EggFile *egg_file)
Records that a particular egg file references this texture.
int get_x_size() const
Returns the number of pixels in the X direction.
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:94
SourceTextureImage * get_preferred_source()
Determines the preferred source image for examining size and reading pixels, etc.
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width.
Definition: pnmImage.I:253
xel & get_xel_val(int x, int y)
Returns the RGB color at the indicated pixel.
Definition: pnmImage.I:309
bool is_used() const
Returns true if this particular texture has been placed somewhere, anywhere, or false if it is not us...
void force_replace()
Removes the texture from any PaletteImages it is assigned to, but does not remove it from the groups.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
Definition: imageFile.cxx:436
const TextureProperties & get_properties() const
Returns the grouping properties of the image.
Definition: imageFile.cxx:119
void parse_params(const FactoryParams &params, DatagramIterator &scan, BamReader *&manager)
Takes in a FactoryParams, passed from a WritableFactory into any TypedWritable's make function,...
Definition: bamReader.I:275
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:34
static Filename make_user_filename(Filename filename)
Returns a new filename that's made relative to the current directory, suitable for reporting to the u...
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
bool is_size_known() const
Returns true if the size of the image file is known, false otherwise.
Definition: imageFile.cxx:73
void read_header()
Causes the header part of the image to be reread, usually to confirm that its image properties (size,...
EggRenderMode::AlphaMode get_alpha_mode() const
Returns the alpha mode that should be used to render objects with this texture, as specified by the u...
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_x_size() const
Returns the size of the image file in pixels in the X direction.
Definition: imageFile.cxx:82
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
bool get_omit() const
Returns true if the user specifically requested to omit this texture via the "omit" keyword in the ....
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
Definition: bamReader.cxx:653
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
static void make_canonical(Filename &filename)
Does the same thing as Filename::make_canonical()–it converts the filename to its canonical form–but ...
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
void copy_unplaced(bool redo_all)
Copies the texture to whichever destination directories are appropriate for the groups in which it ha...
int get_num_channels() const
Returns the number of channels of the image.
Definition: imageFile.cxx:111
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool empty() const
Returns true if the set is empty, false otherwise.
void clear_basic_properties()
Resets the properties to a neutral state, for instance in preparation for calling update_properties()...
Definition: imageFile.cxx:128
bool get_size()
Determines the size of the SourceTextureImage, if it is not already known.
This corresponds to a particular assignment of a TextureImage with a PaletteGroup,...
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
This is a texture image reference as it appears in an egg file: the source image of the texture.
void assign_groups()
Assigns the texture to all of the PaletteGroups the various egg files that use it need.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double get_coverage_threshold() const
Returns the appropriate coverage threshold for this texture.
bool has_num_channels() const
Returns true if the number of channels is known.
OmitReason get_omit_reason() const
Returns the reason the texture has been omitted from a palette image, or OR_none if it has not.
int get_margin() const
Returns the appropriate margin for this texture.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool is_surprise() const
Returns true if this particular texture is a 'surprise', i.e.
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:1417
void write_source_pathnames(std::ostream &out, int indent_level=0) const
Writes the list of source pathnames that might contribute to this texture to the indicated output str...
const Filename & get_alpha_filename() const
Returns the alpha filename of the image file.
Definition: imageFile.cxx:235
void mark_texture_named()
Indicates that this particular texture has been named by the user for processing this session,...
void make_union(const PaletteGroups &a, const PaletteGroups &b)
Computes the union of PaletteGroups a and b, and stores the result in this object.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition: pnmImage.cxx:48
void insert(PaletteGroup *group)
Inserts a new group to the set, if it is not already there.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
void set_source_image(const PNMImage &image)
Accepts the indicated source image as if it had been read from disk.
const PNMImage & read_source_image()
Reads in the original image, if it has not already been read, and returns it.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A set of PaletteGroups.
Definition: paletteGroups.h:28
DestTextureImage * get_dest() const
Returns the DestTextureImage that corresponds to this texture as it was copied to the install directo...
bool is_texture_named() const
Returns true if this particular texture has been named by the user for procession this session,...
This is a single palette image, one of several within a PalettePage, which is in turn one of several ...
Definition: paletteImage.h:32
bool is_preferred_over(const PaletteGroup &other) const
Returns true if this group should be preferred for adding textures over the other group,...
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
TexturePlacement * prepare(TextureImage *texture)
Marks the indicated Texture as ready for placing somewhere within this group, and returns a placehold...
int16_t get_int16()
Extracts a signed 16-bit integer.
A class to retrieve the individual data elements previously stored in a Datagram.
This represents a single source texture that is referenced by one or more egg files.
Definition: textureImage.h:46
void set_dest(DestTextureImage *dest)
Sets the DestTextureImage that corresponds to this texture as it was copied to the install directory.
bool got_txa_file() const
Returns true if this TextureImage has been looked up in the .txa file this session,...
void post_txa_file()
Once the .txa file has been read and the TextureImage matched against it, considers applying the requ...
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:405
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const Filename & get_filename() const
Returns the primary filename of the image file.
Definition: imageFile.cxx:225
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
EggTexture::WrapMode get_txa_wrap_u() const
Returns the wrap mode specified in the u direction in the txa file, or WM_unspecified.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This represents a single egg file known to the palettizer.
Definition: eggFile.h:36
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Called after the object is otherwise completely read from a Bam file, this function's job is to store...
PaletteGroup * get_default_group() const
Returns the PaletteGroup that was specified as the default group on the command line at the time the ...
Definition: eggFile.cxx:266
const PaletteGroups & get_groups() const
Once assign_groups() has been called, this returns the actual set of groups the TextureImage has been...
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
void release_source_image()
Frees the memory that was allocated by a previous call to read_source_image().