Panda3D
Loading...
Searching...
No Matches
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"
30#include "pvector.h"
31
32#include <iterator>
33
34using std::string;
35
36TypeHandle TextureImage::_type_handle;
37
38/**
39 *
40 */
41TextureImage::
42TextureImage() {
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 */
65note_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 */
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 */
194get_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 */
204get_placement(PaletteGroup *group) const {
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 */
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 */
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 */
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 */
257is_texture_named() const {
258 return _texture_named;
259}
260
261/**
262 * Updates any internal state prior to reading the .txa file.
263 */
265pre_txa_file() {
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 */
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.
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) {
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)) {
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) {
404 }
405}
406
407/**
408 * Returns true if this TextureImage has been looked up in the .txa file this
409 * session, false otherwise.
410 */
412got_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 */
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 */
435get_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 */
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 */
456get_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 */
465is_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 */
480is_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 */
489EggRenderMode::AlphaMode TextureImage::
490get_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 */
498EggTexture::WrapMode TextureImage::
499get_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 */
507EggTexture::WrapMode TextureImage::
508get_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 */
519get_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 */
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 */
649copy_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 */
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 */
742set_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 */
754read_header() {
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 */
768is_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 */
785write_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 */
858write_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 */
945int TextureImage::
946compute_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 */
966void TextureImage::
967assign_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 */
1030void TextureImage::
1031consider_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 */
1069void TextureImage::
1070consider_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 */
1144void TextureImage::
1145remove_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 */
1184void TextureImage::
1185copy_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 */
1221string TextureImage::
1222get_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 */
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 */
1245write_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 */
1300complete_pointers(TypedWritable **p_list, BamReader *manager) {
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 */
1346TypedWritable *TextureImage::
1347make_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 */
1361void TextureImage::
1362fillin(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}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition bamReader.h:110
void read_pointers(DatagramIterator &scan, int count)
A convenience function to read a contiguous list of pointers.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition bamWriter.h:63
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
A class to retrieve the individual data elements previously stored in a Datagram.
uint8_t get_uint8()
Extracts an unsigned 8-bit integer.
int16_t get_int16()
Extracts a signed 16-bit integer.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
PN_float64 get_float64()
Extracts a 64-bit floating-point number.
bool get_bool()
Extracts a boolean value.
std::string get_string()
Extracts a variable-length string.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition datagram.I:94
void add_int16(int16_t value)
Adds a signed 16-bit integer to the datagram.
Definition datagram.I:58
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition datagram.I:50
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition datagram.I:34
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition datagram.I:219
void add_float64(PN_float64 value)
Adds a 64-bit floating-point number to the datagram.
Definition datagram.I:123
This represents a texture filename as it has been resized and copied to the map directory (e....
This represents a single egg file known to the palettizer.
Definition eggFile.h:36
const PaletteGroups & get_complete_groups() const
Returns the complete set of PaletteGroups that the egg file is assigned to.
Definition eggFile.cxx:276
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_explicit_groups() const
Returns the set of PaletteGroups that the egg file has been explicitly assigned to in the ....
Definition eggFile.cxx:257
An instance of this class is passed to the Factory when requesting it to do its business and construc...
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...
static void make_canonical(Filename &filename)
Does the same thing as Filename::make_canonical()–it converts the filename to its canonical form–but ...
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...
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
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,...
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition filename.I:338
int get_num_channels() const
Returns the number of channels of the image.
int get_x_size() const
Returns the size of the image file in pixels in the X direction.
Definition imageFile.cxx:82
void output_filename(std::ostream &out) const
Writes the filename (or pair of filenames) to the indicated output stream.
bool exists() const
Returns true if the file or files named by the image file exist, false otherwise.
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...
const TextureProperties & get_properties() const
Returns the grouping properties of the image.
const Filename & get_filename() const
Returns the primary filename of the image file.
bool read(PNMImage &image) const
Reads in the image (or images, if the alpha_filename is separate) and stores it in the indicated PNMI...
const Filename & get_alpha_filename() const
Returns the alpha filename of the image file.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
bool is_size_known() const
Returns true if the size of the image file is known, false otherwise.
Definition imageFile.cxx:73
int get_y_size() const
Returns the size of the image file in pixels in the Y direction.
Definition imageFile.cxx:92
int get_alpha_file_channel() const
Returns the particular channel number of the alpha image file from which the alpha channel should be ...
void clear_basic_properties()
Resets the properties to a neutral state, for instance in preparation for calling update_properties()...
An STL function object class, this is intended to be used on any ordered collection of pointers to cl...
get_maxval
Returns the maximum channel value allowable for any pixel in this image; for instance,...
int get_x_size() const
Returns the number of pixels in the X direction.
static bool has_alpha(ColorType color_type)
This static variant of has_alpha() returns true if the indicated image type includes an alpha channel...
int get_y_size() const
Returns the number of pixels in the Y direction.
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 clear()
Frees all memory allocated for the image, and clears all its parameters (size, color,...
Definition pnmImage.cxx:48
xel & get_xel_val(int x, int y)
Returns the RGB color at the indicated pixel.
Definition pnmImage.I:398
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition pnmImage.I:494
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width.
Definition pnmImage.I:342
This is the highest level of grouping for TextureImages.
bool is_preferred_over(const PaletteGroup &other) const
Returns true if this group should be preferred for adding textures over the other group,...
TexturePlacement * prepare(TextureImage *texture)
Marks the indicated Texture as ready for placing somewhere within this group, and returns a placehold...
A set of PaletteGroups.
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...
iterator begin() const
Returns an iterator suitable for traversing the set.
void insert(PaletteGroup *group)
Inserts a new group to the set, if it is not already there.
void remove_null()
Removes the special "null" group from the set.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
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 make_union(const PaletteGroups &a, const PaletteGroups &b)
Computes the union of PaletteGroups a and b, and stores the result in this object.
iterator end() const
Returns an iterator suitable for traversing the set.
bool empty() const
Returns true if the set is empty, false otherwise.
void fillin(DatagramIterator &scan, BamReader *manager)
Reads the binary data from the given datagram iterator, which was written by a previous call to write...
This is a single palette image, one of several within a PalettePage, which is in turn one of several ...
This is a texture image reference as it appears in an egg file: the source image of the texture.
bool get_size()
Determines the size of the SourceTextureImage, if it is not already known.
bool read_header()
Reads the actual image header to determine the image properties, like its size.
int get_egg_count() const
Returns the number of egg files that share this SourceTextureImage.
This represents a single source texture that is referenced by one or more egg files.
bool got_txa_file() const
Returns true if this TextureImage has been looked up in the .txa file this session,...
void pre_txa_file()
Updates any internal state prior to reading the .txa file.
void note_egg_file(EggFile *egg_file)
Records that a particular egg file references this texture.
void clear_source_basic_properties()
Calls clear_basic_properties() on each source texture image used by this texture, to reset the proper...
const PaletteGroups & get_groups() const
Once assign_groups() has been called, this returns the actual set of groups the TextureImage has been...
void mark_eggs_stale()
Marks all the egg files that reference this texture stale.
virtual void write_datagram(BamWriter *writer, Datagram &datagram)
Fills the indicated datagram up with a binary representation of the current object,...
EggTexture::WrapMode get_txa_wrap_v() const
Returns the wrap mode specified in the v direction in the txa file, or WM_unspecified.
void read_header()
Causes the header part of the image to be reread, usually to confirm that its image properties (size,...
void copy_unplaced(bool redo_all)
Copies the texture to whichever destination directories are appropriate for the groups in which it ha...
SourceTextureImage * get_preferred_source()
Determines the preferred source image for examining size and reading pixels, etc.
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...
int get_margin() const
Returns the appropriate margin for this texture.
bool is_used() const
Returns true if this particular texture has been placed somewhere, anywhere, or false if it is not us...
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...
void write_scale_info(std::ostream &out, int indent_level=0)
Writes the information about the texture's size and placement.
bool get_omit() const
Returns true if the user specifically requested to omit this texture via the "omit" keyword in the ....
bool is_surprise() const
Returns true if this particular texture is a 'surprise', i.e.
TexturePlacement * get_placement(PaletteGroup *group) const
Gets the TexturePlacement object which represents the assignment of this texture to the indicated gro...
void mark_texture_named()
Indicates that this particular texture has been named by the user for processing this session,...
void release_source_image()
Frees the memory that was allocated by a previous call to read_source_image().
void assign_groups()
Assigns the texture to all of the PaletteGroups the various egg files that use it need.
void set_source_image(const PNMImage &image)
Accepts the indicated source image as if it had been read from disk.
bool is_newer_than(const Filename &reference_filename)
Returns true if the source image is newer than the indicated file, false otherwise.
static void register_with_read_factory()
Registers the current object as something that can be read from a Bam file.
void force_replace()
Removes the texture from any PaletteImages it is assigned to, but does not remove it from the groups.
void post_txa_file()
Once the .txa file has been read and the TextureImage matched against it, considers applying the requ...
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...
bool is_texture_named() const
Returns true if this particular texture has been named by the user for procession this session,...
double get_coverage_threshold() const
Returns the appropriate coverage threshold for this texture.
SourceTextureImage * get_source(const Filename &filename, const Filename &alpha_filename, int alpha_file_channel)
Returns the SourceTextureImage corresponding to the given filename(s).
void determine_placement_size()
Calls determine_size() on each TexturePlacement for the texture, to ensure that each TexturePlacement...
const PNMImage & read_source_image()
Reads in the original image, if it has not already been read, and returns it.
EggTexture::WrapMode get_txa_wrap_u() const
Returns the wrap mode specified in the u direction in the txa file, or WM_unspecified.
This corresponds to a particular assignment of a TextureImage with a PaletteGroup,...
OmitReason get_omit_reason() const
Returns the reason the texture has been omitted from a palette image, or OR_none if it has not.
DestTextureImage * get_dest() const
Returns the DestTextureImage that corresponds to this texture as it was copied to the install directo...
PaletteImage * get_image() const
Returns the particular PaletteImage on which the texture has been placed.
void set_dest(DestTextureImage *dest)
Sets the DestTextureImage that corresponds to this texture as it was copied to the install directory.
void mark_unfilled()
Marks the texture as unfilled, so that it will need to be copied into the palette image again.
bool determine_size()
Attempts to determine the appropriate size of the texture for the given placement.
int get_num_channels() const
Returns the number of channels (1 through 4) associated with the image.
bool has_num_channels() const
Returns true if the number of channels is known.
void pre_txa_file()
Sets some state up that must be set prior to reading the .txa file.
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.