Panda3D
texture.cxx
1 // Filename: texture.cxx
2 // Created by: mike (09Jan97)
3 // Updated by: fperazzi, PandaSE(29Apr10) (added TT_2d_texture_array)
4 //
5 ////////////////////////////////////////////////////////////////////
6 //
7 // PANDA 3D SOFTWARE
8 // Copyright (c) Carnegie Mellon University. All rights reserved.
9 //
10 // All use of this software is subject to the terms of the revised BSD
11 // license. You should have received a copy of this license along
12 // with this source code in a file named "LICENSE."
13 //
14 ////////////////////////////////////////////////////////////////////
15 
16 #include "pandabase.h"
17 #include "texture.h"
18 #include "config_gobj.h"
19 #include "config_util.h"
20 #include "texturePool.h"
21 #include "textureContext.h"
22 #include "bamCache.h"
23 #include "bamCacheRecord.h"
24 #include "datagram.h"
25 #include "datagramIterator.h"
26 #include "bamReader.h"
27 #include "bamWriter.h"
28 #include "string_utils.h"
29 #include "preparedGraphicsObjects.h"
30 #include "pnmImage.h"
31 #include "pnmReader.h"
32 #include "pfmFile.h"
33 #include "virtualFileSystem.h"
34 #include "datagramInputFile.h"
35 #include "datagramOutputFile.h"
36 #include "bam.h"
37 #include "zStream.h"
38 #include "indent.h"
39 #include "cmath.h"
40 #include "pStatTimer.h"
41 #include "pbitops.h"
42 #include "streamReader.h"
43 #include "texturePeeker.h"
44 #include "convert_srgb.h"
45 
46 #ifdef HAVE_SQUISH
47 #include <squish.h>
48 #endif // HAVE_SQUISH
49 
50 #include <stddef.h>
51 
53 ("texture-quality-level", Texture::QL_normal,
54  PRC_DESC("This specifies a global quality level for all textures. You "
55  "may specify either fastest, normal, or best. This actually "
56  "affects the meaning of Texture::set_quality_level(QL_default), "
57  "so it may be overridden on a per-texture basis. This generally "
58  "only has an effect when using the tinydisplay software renderer; "
59  "it has little or no effect on normal, hardware-accelerated "
60  "renderers. See Texture::set_quality_level()."));
61 
62 PStatCollector Texture::_texture_read_pcollector("*:Texture:Read");
63 TypeHandle Texture::_type_handle;
64 TypeHandle Texture::CData::_type_handle;
65 AutoTextureScale Texture::_textures_power_2 = ATS_unspecified;
66 
67 // Stuff to read and write DDS files.
68 
69 // little-endian, of course
70 #define DDS_MAGIC 0x20534444
71 
72 
73 // DDS_header.dwFlags
74 #define DDSD_CAPS 0x00000001
75 #define DDSD_HEIGHT 0x00000002
76 #define DDSD_WIDTH 0x00000004
77 #define DDSD_PITCH 0x00000008
78 #define DDSD_PIXELFORMAT 0x00001000
79 #define DDSD_MIPMAPCOUNT 0x00020000
80 #define DDSD_LINEARSIZE 0x00080000
81 #define DDSD_DEPTH 0x00800000
82 
83 // DDS_header.sPixelFormat.dwFlags
84 #define DDPF_ALPHAPIXELS 0x00000001
85 #define DDPF_FOURCC 0x00000004
86 #define DDPF_INDEXED 0x00000020
87 #define DDPF_RGB 0x00000040
88 
89 // DDS_header.sCaps.dwCaps1
90 #define DDSCAPS_COMPLEX 0x00000008
91 #define DDSCAPS_TEXTURE 0x00001000
92 #define DDSCAPS_MIPMAP 0x00400000
93 
94 // DDS_header.sCaps.dwCaps2
95 #define DDSCAPS2_CUBEMAP 0x00000200
96 #define DDSCAPS2_CUBEMAP_POSITIVEX 0x00000400
97 #define DDSCAPS2_CUBEMAP_NEGATIVEX 0x00000800
98 #define DDSCAPS2_CUBEMAP_POSITIVEY 0x00001000
99 #define DDSCAPS2_CUBEMAP_NEGATIVEY 0x00002000
100 #define DDSCAPS2_CUBEMAP_POSITIVEZ 0x00004000
101 #define DDSCAPS2_CUBEMAP_NEGATIVEZ 0x00008000
102 #define DDSCAPS2_VOLUME 0x00200000
103 
105  unsigned int pf_size;
106  unsigned int pf_flags;
107  unsigned int four_cc;
108  unsigned int rgb_bitcount;
109  unsigned int r_mask;
110  unsigned int g_mask;
111  unsigned int b_mask;
112  unsigned int a_mask;
113 };
114 
115 struct DDSCaps2 {
116  unsigned int caps1;
117  unsigned int caps2;
118  unsigned int ddsx;
119 };
120 
121 struct DDSHeader {
122  unsigned int dds_magic;
123  unsigned int dds_size;
124  unsigned int dds_flags;
125  unsigned int height;
126  unsigned int width;
127  unsigned int pitch;
128  unsigned int depth;
129  unsigned int num_levels;
130 
131  DDSPixelFormat pf;
132  DDSCaps2 caps;
133 };
134 
135 ////////////////////////////////////////////////////////////////////
136 // Function: Texture::Constructor
137 // Access: Published
138 // Description: Constructs an empty texture. The default is to set
139 // up the texture as an empty 2-d texture; follow up
140 // with one of the variants of setup_texture() if this
141 // is not what you want.
142 ////////////////////////////////////////////////////////////////////
143 Texture::
144 Texture(const string &name) :
145  Namable(name),
146  _lock(name),
147  _cvar(_lock)
148 {
149  _reloading = false;
150 
151  CDWriter cdata(_cycler, true);
152  do_set_format(cdata, F_rgb);
153  do_set_component_type(cdata, T_unsigned_byte);
154 }
155 
156 ////////////////////////////////////////////////////////////////////
157 // Function: Texture::Copy Constructor
158 // Access: Protected
159 // Description: Use Texture::make_copy() to make a duplicate copy of
160 // an existing Texture.
161 ////////////////////////////////////////////////////////////////////
162 Texture::
163 Texture(const Texture &copy) :
164  Namable(copy),
165  _cycler(copy._cycler),
166  _lock(copy.get_name()),
167  _cvar(_lock)
168 {
169  _reloading = false;
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: Texture::Copy Assignment Operator
174 // Access: Protected
175 // Description: Use Texture::make_copy() to make a duplicate copy of
176 // an existing Texture.
177 ////////////////////////////////////////////////////////////////////
178 void Texture::
179 operator = (const Texture &copy) {
180  Namable::operator = (copy);
181  _cycler = copy._cycler;
182 }
183 
184 ////////////////////////////////////////////////////////////////////
185 // Function: Texture::Destructor
186 // Access: Published, Virtual
187 // Description:
188 ////////////////////////////////////////////////////////////////////
189 Texture::
190 ~Texture() {
191  release_all();
192  nassertv(!_reloading);
193 }
194 
195 ////////////////////////////////////////////////////////////////////
196 // Function: Texture::generate_normalization_cube_map
197 // Access: Published
198 // Description: Generates a special cube map image in the texture
199 // that can be used to apply bump mapping effects: for
200 // each texel in the cube map that is indexed by the 3-d
201 // texture coordinates (x, y, z), the resulting value is
202 // the normalized vector (x, y, z) (compressed from
203 // -1..1 into 0..1).
204 ////////////////////////////////////////////////////////////////////
205 void Texture::
207  CDWriter cdata(_cycler, true);
208  do_setup_texture(cdata, TT_cube_map, size, size, 6, T_unsigned_byte, F_rgb);
209  PTA_uchar image = do_make_ram_image(cdata);
210  cdata->_keep_ram_image = true;
211 
212  cdata->inc_image_modified();
213  cdata->inc_properties_modified();
214 
215  PN_stdfloat half_size = (PN_stdfloat)size * 0.5f;
216  PN_stdfloat center = half_size - 0.5f;
217 
218  LMatrix4 scale
219  (127.5f, 0.0f, 0.0f, 0.0f,
220  0.0f, 127.5f, 0.0f, 0.0f,
221  0.0f, 0.0f, 127.5f, 0.0f,
222  127.5f, 127.5f, 127.5f, 1.0f);
223 
224  unsigned char *p = image;
225  int xi, yi;
226 
227  // Page 0: positive X.
228  for (yi = 0; yi < size; ++yi) {
229  for (xi = 0; xi < size; ++xi) {
230  LVector3 vec(half_size, center - yi, center - xi);
231  vec.normalize();
232  vec = scale.xform_point(vec);
233 
234  *p++ = (unsigned char)vec[2];
235  *p++ = (unsigned char)vec[1];
236  *p++ = (unsigned char)vec[0];
237  }
238  }
239 
240  // Page 1: negative X.
241  for (yi = 0; yi < size; ++yi) {
242  for (xi = 0; xi < size; ++xi) {
243  LVector3 vec(-half_size, center - yi, xi - center);
244  vec.normalize();
245  vec = scale.xform_point(vec);
246  *p++ = (unsigned char)vec[2];
247  *p++ = (unsigned char)vec[1];
248  *p++ = (unsigned char)vec[0];
249  }
250  }
251 
252  // Page 2: positive Y.
253  for (yi = 0; yi < size; ++yi) {
254  for (xi = 0; xi < size; ++xi) {
255  LVector3 vec(xi - center, half_size, yi - center);
256  vec.normalize();
257  vec = scale.xform_point(vec);
258  *p++ = (unsigned char)vec[2];
259  *p++ = (unsigned char)vec[1];
260  *p++ = (unsigned char)vec[0];
261  }
262  }
263 
264  // Page 3: negative Y.
265  for (yi = 0; yi < size; ++yi) {
266  for (xi = 0; xi < size; ++xi) {
267  LVector3 vec(xi - center, -half_size, center - yi);
268  vec.normalize();
269  vec = scale.xform_point(vec);
270  *p++ = (unsigned char)vec[2];
271  *p++ = (unsigned char)vec[1];
272  *p++ = (unsigned char)vec[0];
273  }
274  }
275 
276  // Page 4: positive Z.
277  for (yi = 0; yi < size; ++yi) {
278  for (xi = 0; xi < size; ++xi) {
279  LVector3 vec(xi - center, center - yi, half_size);
280  vec.normalize();
281  vec = scale.xform_point(vec);
282  *p++ = (unsigned char)vec[2];
283  *p++ = (unsigned char)vec[1];
284  *p++ = (unsigned char)vec[0];
285  }
286  }
287 
288  // Page 5: negative Z.
289  for (yi = 0; yi < size; ++yi) {
290  for (xi = 0; xi < size; ++xi) {
291  LVector3 vec(center - xi, center - yi, -half_size);
292  vec.normalize();
293  vec = scale.xform_point(vec);
294  *p++ = (unsigned char)vec[2];
295  *p++ = (unsigned char)vec[1];
296  *p++ = (unsigned char)vec[0];
297  }
298  }
299 }
300 
301 ////////////////////////////////////////////////////////////////////
302 // Function: Texture::generate_alpha_scale_map
303 // Access: Published
304 // Description: Generates a special 256x1 1-d texture that can be
305 // used to apply an arbitrary alpha scale to objects by
306 // judicious use of texture matrix. The texture is a
307 // gradient, with an alpha of 0 on the left (U = 0), and
308 // 255 on the right (U = 1).
309 ////////////////////////////////////////////////////////////////////
310 void Texture::
312  CDWriter cdata(_cycler, true);
313  do_setup_texture(cdata, TT_1d_texture, 256, 1, 1, T_unsigned_byte, F_alpha);
314  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
315  cdata->_default_sampler.set_minfilter(SamplerState::FT_nearest);
316  cdata->_default_sampler.set_magfilter(SamplerState::FT_nearest);
317 
318  cdata->_compression = CM_off;
319 
320  cdata->inc_image_modified();
321  cdata->inc_properties_modified();
322 
323  PTA_uchar image = do_make_ram_image(cdata);
324  cdata->_keep_ram_image = true;
325 
326  unsigned char *p = image;
327  for (int xi = 0; xi < 256; ++xi) {
328  *p++ = xi;
329  }
330 }
331 
332 ////////////////////////////////////////////////////////////////////
333 // Function: Texture::read
334 // Access: Published
335 // Description: Reads the named filename into the texture.
336 ////////////////////////////////////////////////////////////////////
337 bool Texture::
338 read(const Filename &fullpath, const LoaderOptions &options) {
339  CDWriter cdata(_cycler, true);
340  do_clear(cdata);
341  cdata->inc_properties_modified();
342  cdata->inc_image_modified();
343  return do_read(cdata, fullpath, Filename(), 0, 0, 0, 0, false, false,
344  options, NULL);
345 }
346 
347 ////////////////////////////////////////////////////////////////////
348 // Function: Texture::read
349 // Access: Published
350 // Description: Combine a 3-component image with a grayscale image
351 // to get a 4-component image.
352 //
353 // See the description of the full-parameter read()
354 // method for the meaning of the
355 // primary_file_num_channels and alpha_file_channel
356 // parameters.
357 ////////////////////////////////////////////////////////////////////
358 bool Texture::
359 read(const Filename &fullpath, const Filename &alpha_fullpath,
360  int primary_file_num_channels, int alpha_file_channel,
361  const LoaderOptions &options) {
362  CDWriter cdata(_cycler, true);
363  do_clear(cdata);
364  cdata->inc_properties_modified();
365  cdata->inc_image_modified();
366  return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
367  alpha_file_channel, 0, 0, false, false,
368  options, NULL);
369 }
370 
371 ////////////////////////////////////////////////////////////////////
372 // Function: Texture::read
373 // Access: Published
374 // Description: Reads a single file into a single page or mipmap
375 // level, or automatically reads a series of files into
376 // a series of pages and/or mipmap levels.
377 //
378 // See the description of the full-parameter read()
379 // method for the meaning of the various parameters.
380 ////////////////////////////////////////////////////////////////////
381 bool Texture::
382 read(const Filename &fullpath, int z, int n,
383  bool read_pages, bool read_mipmaps,
384  const LoaderOptions &options) {
385  CDWriter cdata(_cycler, true);
386  cdata->inc_properties_modified();
387  cdata->inc_image_modified();
388  return do_read(cdata, fullpath, Filename(), 0, 0, z, n, read_pages, read_mipmaps,
389  options, NULL);
390 }
391 
392 ////////////////////////////////////////////////////////////////////
393 // Function: Texture::read
394 // Access: Published
395 // Description: Reads the texture from the indicated filename. If
396 // primary_file_num_channels is not 0, it specifies the
397 // number of components to downgrade the image to if it
398 // is greater than this number.
399 //
400 // If the filename has the extension .txo, this
401 // implicitly reads a texture object instead of a
402 // filename (which replaces all of the texture
403 // properties). In this case, all the rest of the
404 // parameters are ignored, and the filename should not
405 // contain any hash marks; just the one named file will
406 // be read, since a single .txo file can contain all
407 // pages and mipmaps necessary to define a texture.
408 //
409 // If alpha_fullpath is not empty, it specifies the name
410 // of a file from which to retrieve the alpha. In this
411 // case, alpha_file_channel represents the numeric
412 // channel of this image file to use as the resulting
413 // texture's alpha channel; usually, this is 0 to
414 // indicate the grayscale combination of r, g, b; or it
415 // may be a one-based channel number, e.g. 1 for the red
416 // channel, 2 for the green channel, and so on.
417 //
418 // If read pages is false, then z indicates the page
419 // number into which this image will be assigned.
420 // Normally this is 0 for the first (or only) page of
421 // the texture. 3-D textures have one page for each
422 // level of depth, and cube map textures always have six
423 // pages.
424 //
425 // If read_pages is true, multiple images will be read
426 // at once, one for each page of a cube map or a 3-D
427 // texture. In this case, the filename should contain a
428 // sequence of one or more hash marks ("#") which will
429 // be filled in with the z value of each page,
430 // zero-based. In this case, the z parameter indicates
431 // the maximum z value that will be loaded, or 0 to load
432 // all filenames that exist.
433 //
434 // If read_mipmaps is false, then n indicates the mipmap
435 // level to which this image will be assigned. Normally
436 // this is 0 for the base texture image, but it is
437 // possible to load custom mipmap levels into the later
438 // images. After the base texture image is loaded (thus
439 // defining the size of the texture), you can call
440 // get_expected_num_mipmap_levels() to determine the
441 // maximum sensible value for n.
442 //
443 // If read_mipmaps is true, multiple images will be read
444 // as above, but this time the images represent the
445 // different mipmap levels of the texture image. In
446 // this case, the n parameter indicates the maximum n
447 // value that will be loaded, or 0 to load all filenames
448 // that exist (up to the expected number of mipmap
449 // levels).
450 //
451 // If both read_pages and read_mipmaps is true, then
452 // both sequences will be read; the filename should
453 // contain two sequences of hash marks, separated by
454 // some character such as a hyphen, underscore, or dot.
455 // The first hash mark sequence will be filled in with
456 // the mipmap level, while the second hash mark sequence
457 // will be the page index.
458 //
459 // This method implicitly sets keep_ram_image to false.
460 ////////////////////////////////////////////////////////////////////
461 bool Texture::
462 read(const Filename &fullpath, const Filename &alpha_fullpath,
463  int primary_file_num_channels, int alpha_file_channel,
464  int z, int n, bool read_pages, bool read_mipmaps,
465  BamCacheRecord *record,
466  const LoaderOptions &options) {
467  CDWriter cdata(_cycler, true);
468  cdata->inc_properties_modified();
469  cdata->inc_image_modified();
470  return do_read(cdata, fullpath, alpha_fullpath, primary_file_num_channels,
471  alpha_file_channel, z, n, read_pages, read_mipmaps,
472  options, record);
473 }
474 
475 ////////////////////////////////////////////////////////////////////
476 // Function: Texture::estimate_texture_memory
477 // Access: Published
478 // Description: Estimates the amount of texture memory that will be
479 // consumed by loading this texture. This returns a
480 // value that is not specific to any particular graphics
481 // card or driver; it tries to make a reasonable
482 // assumption about how a driver will load the texture.
483 // It does not account for texture compression or
484 // anything fancy. This is mainly useful for debugging
485 // and reporting purposes.
486 //
487 // Returns a value in bytes.
488 ////////////////////////////////////////////////////////////////////
489 size_t Texture::
491  CDReader cdata(_cycler);
492  size_t pixels = cdata->_x_size * cdata->_y_size * cdata->_z_size;
493 
494  size_t bpp = 4;
495  switch (cdata->_format) {
496  case Texture::F_rgb332:
497  bpp = 1;
498  break;
499 
500  case Texture::F_alpha:
501  case Texture::F_red:
502  case Texture::F_green:
503  case Texture::F_blue:
504  case Texture::F_luminance:
505  case Texture::F_sluminance:
506  case Texture::F_r8i:
507  bpp = 1;
508  break;
509 
510  case Texture::F_luminance_alpha:
511  case Texture::F_luminance_alphamask:
512  case Texture::F_sluminance_alpha:
513  case Texture::F_rgba4:
514  case Texture::F_rgb5:
515  case Texture::F_rgba5:
516  bpp = 2;
517  break;
518 
519  case Texture::F_rgba:
520  case Texture::F_rgbm:
521  case Texture::F_rgb:
522  case Texture::F_srgb:
523  // Most of the above formats have only 3 bytes, but they are most likely to
524  // get padded by the driver
525  bpp = 4;
526  break;
527 
528  case Texture::F_color_index:
529  case Texture::F_rgb8:
530  case Texture::F_rgba8:
531  case Texture::F_srgb_alpha:
532  case Texture::F_rgb8i:
533  case Texture::F_rgba8i:
534  bpp = 4;
535  break;
536 
537  case Texture::F_depth_stencil:
538  bpp = 4;
539  break;
540 
541  case Texture::F_depth_component:
542  case Texture::F_depth_component16:
543  bpp = 2;
544  break;
545 
546  case Texture::F_depth_component24: // Gets padded
547  case Texture::F_depth_component32:
548  bpp = 4;
549  break;
550 
551  case Texture::F_rgba12:
552  case Texture::F_rgb12:
553  bpp = 8;
554  break;
555 
556  case Texture::F_rgba16:
557  bpp = 8;
558  break;
559  case Texture::F_rgba32:
560  bpp = 16;
561  break;
562 
563  case Texture::F_r16:
564  case Texture::F_rg8i:
565  bpp = 2;
566  break;
567  case Texture::F_rg16:
568  bpp = 4;
569  break;
570  case Texture::F_rgb16:
571  bpp = 8;
572  break;
573 
574  case Texture::F_r32i:
575  case Texture::F_r32:
576  bpp = 4;
577  break;
578 
579  case Texture::F_rg32:
580  bpp = 8;
581  break;
582 
583  case Texture::F_rgb32:
584  bpp = 12;
585  break;
586 
587  default:
588  break;
589  }
590 
591  size_t bytes = pixels * bpp;
592  if (uses_mipmaps()) {
593  bytes = (bytes * 4) / 3;
594  }
595 
596  return bytes;
597 }
598 
599 ////////////////////////////////////////////////////////////////////
600 // Function: Texture::set_aux_data
601 // Access: Published
602 // Description: Records an arbitrary object in the Texture,
603 // associated with a specified key. The object may
604 // later be retrieved by calling get_aux_data() with the
605 // same key.
606 //
607 // These data objects are not recorded to a bam or txo
608 // file.
609 ////////////////////////////////////////////////////////////////////
610 void Texture::
611 set_aux_data(const string &key, TypedReferenceCount *aux_data) {
612  MutexHolder holder(_lock);
613  _aux_data[key] = aux_data;
614 }
615 
616 ////////////////////////////////////////////////////////////////////
617 // Function: Texture::clear_aux_data
618 // Access: Published
619 // Description: Removes a record previously recorded via
620 // set_aux_data().
621 ////////////////////////////////////////////////////////////////////
622 void Texture::
623 clear_aux_data(const string &key) {
624  MutexHolder holder(_lock);
625  _aux_data.erase(key);
626 }
627 
628 ////////////////////////////////////////////////////////////////////
629 // Function: Texture::get_aux_data
630 // Access: Published
631 // Description: Returns a record previously recorded via
632 // set_aux_data(). Returns NULL if there was no record
633 // associated with the indicated key.
634 ////////////////////////////////////////////////////////////////////
636 get_aux_data(const string &key) const {
637  MutexHolder holder(_lock);
638  AuxData::const_iterator di;
639  di = _aux_data.find(key);
640  if (di != _aux_data.end()) {
641  return (*di).second;
642  }
643  return NULL;
644 }
645 
646 ////////////////////////////////////////////////////////////////////
647 // Function: Texture::read_txo
648 // Access: Published
649 // Description: Reads the texture from a Panda texture object. This
650 // defines the complete Texture specification, including
651 // the image data as well as all texture properties.
652 // This only works if the txo file contains a static
653 // Texture image, as opposed to a subclass of Texture
654 // such as a movie texture.
655 //
656 // Pass a real filename if it is available, or empty
657 // string if it is not.
658 ////////////////////////////////////////////////////////////////////
659 bool Texture::
660 read_txo(istream &in, const string &filename) {
661  CDWriter cdata(_cycler, true);
662  cdata->inc_properties_modified();
663  cdata->inc_image_modified();
664  return do_read_txo(cdata, in, filename);
665 }
666 
667 ////////////////////////////////////////////////////////////////////
668 // Function: Texture::make_from_txo
669 // Access: Published, Static
670 // Description: Constructs a new Texture object from the txo file.
671 // This is similar to Texture::read_txo(), but it
672 // constructs and returns a new object, which allows it
673 // to return a subclass of Texture (for instance, a
674 // movie texture).
675 //
676 // Pass a real filename if it is available, or empty
677 // string if it is not.
678 ////////////////////////////////////////////////////////////////////
679 PT(Texture) Texture::
680 make_from_txo(istream &in, const string &filename) {
681  DatagramInputFile din;
682 
683  if (!din.open(in, filename)) {
684  gobj_cat.error()
685  << "Could not read texture object: " << filename << "\n";
686  return NULL;
687  }
688 
689  string head;
690  if (!din.read_header(head, _bam_header.size())) {
691  gobj_cat.error()
692  << filename << " is not a texture object file.\n";
693  return NULL;
694  }
695 
696  if (head != _bam_header) {
697  gobj_cat.error()
698  << filename << " is not a texture object file.\n";
699  return NULL;
700  }
701 
702  BamReader reader(&din);
703  if (!reader.init()) {
704  return NULL;
705  }
706 
707  TypedWritable *object = reader.read_object();
708 
709  if (object != (TypedWritable *)NULL &&
710  object->is_exact_type(BamCacheRecord::get_class_type())) {
711  // Here's a special case: if the first object in the file is a
712  // BamCacheRecord, it's really a cache data file and not a true
713  // txo file; but skip over the cache data record and let the user
714  // treat it like an ordinary txo file.
715  object = reader.read_object();
716  }
717 
718  if (object == (TypedWritable *)NULL) {
719  gobj_cat.error()
720  << "Texture object " << filename << " is empty.\n";
721  return NULL;
722 
723  } else if (!object->is_of_type(Texture::get_class_type())) {
724  gobj_cat.error()
725  << "Texture object " << filename << " contains a "
726  << object->get_type() << ", not a Texture.\n";
727  return NULL;
728  }
729 
730  PT(Texture) other = DCAST(Texture, object);
731  if (!reader.resolve()) {
732  gobj_cat.error()
733  << "Unable to fully resolve texture object file.\n";
734  return NULL;
735  }
736 
737  return other;
738 }
739 
740 ////////////////////////////////////////////////////////////////////
741 // Function: Texture::write_txo
742 // Access: Published
743 // Description: Writes the texture to a Panda texture object. This
744 // defines the complete Texture specification, including
745 // the image data as well as all texture properties.
746 //
747 // The filename is just for reference.
748 ////////////////////////////////////////////////////////////////////
749 bool Texture::
750 write_txo(ostream &out, const string &filename) const {
751  CDReader cdata(_cycler);
752  return do_write_txo(cdata, out, filename);
753 }
754 
755 ////////////////////////////////////////////////////////////////////
756 // Function: Texture::read_dds
757 // Access: Published
758 // Description: Reads the texture from a DDS file object. This is a
759 // Microsoft-defined file format; it is similar in
760 // principle to a txo object, in that it is designed to
761 // contain the texture image in a form as similar as
762 // possible to its runtime image, and it can contain
763 // mipmaps, pre-compressed textures, and so on.
764 //
765 // As with read_txo, the filename is just for reference.
766 ////////////////////////////////////////////////////////////////////
767 bool Texture::
768 read_dds(istream &in, const string &filename, bool header_only) {
769  CDWriter cdata(_cycler, true);
770  cdata->inc_properties_modified();
771  cdata->inc_image_modified();
772  return do_read_dds(cdata, in, filename, header_only);
773 }
774 
775 ////////////////////////////////////////////////////////////////////
776 // Function: Texture::load_related
777 // Access: Published
778 // Description: Loads a texture whose filename is derived by
779 // concatenating a suffix to the filename of this
780 // texture. May return NULL, for example, if this
781 // texture doesn't have a filename.
782 ////////////////////////////////////////////////////////////////////
784 load_related(const InternalName *suffix) const {
785  MutexHolder holder(_lock);
786  CDReader cdata(_cycler);
787 
788  RelatedTextures::const_iterator ti;
789  ti = _related_textures.find(suffix);
790  if (ti != _related_textures.end()) {
791  return (*ti).second;
792  }
793  if (cdata->_fullpath.empty()) {
794  return (Texture*)NULL;
795  }
796  Filename main = cdata->_fullpath;
798  suffix->get_name());
799  PT(Texture) res;
800  if (!cdata->_alpha_fullpath.empty()) {
801  Filename alph = cdata->_alpha_fullpath;
803  suffix->get_name());
805  if (vfs->exists(alph)) {
806  // The alpha variant of the filename, with the suffix, exists.
807  // Use it to load the texture.
808  res = TexturePool::load_texture(main, alph,
809  cdata->_primary_file_num_channels,
810  cdata->_alpha_file_channel, false);
811  } else {
812  // If the alpha variant of the filename doesn't exist, just go
813  // ahead and load the related texture without alpha.
814  res = TexturePool::load_texture(main);
815  }
816 
817  } else {
818  // No alpha filename--just load the single file. It doesn't
819  // necessarily have the same number of channels as this one.
820  res = TexturePool::load_texture(main);
821  }
822 
823  // I'm casting away the const-ness of 'this' because this
824  // field is only a cache.
825  ((Texture *)this)->_related_textures.insert(RelatedTextures::value_type(suffix, res));
826  return res;
827 }
828 
829 ////////////////////////////////////////////////////////////////////
830 // Function: Texture::set_ram_image_as
831 // Access: Published
832 // Description: Replaces the current system-RAM image with the new
833 // data, converting it first if necessary from the
834 // indicated component-order format. See
835 // get_ram_image_as() for specifications about the
836 // format. This method cannot support compressed image
837 // data or sub-pages; use set_ram_image() for that.
838 ////////////////////////////////////////////////////////////////////
839 void Texture::
840 set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
841  CDWriter cdata(_cycler, true);
842 
843  string format = upcase(supplied_format);
844 
845  // Make sure we can grab something that's uncompressed.
846  int imgsize = cdata->_x_size * cdata->_y_size;
847  nassertv(image.size() == (size_t)(cdata->_component_width * format.size() * imgsize));
848 
849  // Check if the format is already what we have internally.
850  if ((cdata->_num_components == 1 && format.size() == 1) ||
851  (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
852  (cdata->_num_components == 3 && format == "BGR") ||
853  (cdata->_num_components == 4 && format == "BGRA")) {
854  // The format string is already our format, so we just need to copy it.
855  do_set_ram_image(cdata, image);
856  return;
857  }
858 
859  // Create a new empty array that can hold our image.
860  PTA_uchar newdata = PTA_uchar::empty_array(imgsize * cdata->_num_components * cdata->_component_width, get_class_type());
861 
862  // These ifs are for optimization of commonly used image types.
863  if (cdata->_component_width == 1) {
864  if (format == "RGBA" && cdata->_num_components == 4) {
865  imgsize *= 4;
866  for (int p = 0; p < imgsize; p += 4) {
867  newdata[p + 2] = image[p ];
868  newdata[p + 1] = image[p + 1];
869  newdata[p ] = image[p + 2];
870  newdata[p + 3] = image[p + 3];
871  }
872  do_set_ram_image(cdata, newdata);
873  return;
874  }
875  if (format == "RGB" && cdata->_num_components == 3) {
876  imgsize *= 3;
877  for (int p = 0; p < imgsize; p += 3) {
878  newdata[p + 2] = image[p ];
879  newdata[p + 1] = image[p + 1];
880  newdata[p ] = image[p + 2];
881  }
882  do_set_ram_image(cdata, newdata);
883  return;
884  }
885  if (format == "A" && cdata->_num_components != 3) {
886  // We can generally rely on alpha to be the last component.
887  int component = cdata->_num_components - 1;
888  for (int p = 0; p < imgsize; ++p) {
889  newdata[component] = image[p];
890  }
891  do_set_ram_image(cdata, newdata);
892  return;
893  }
894  for (int p = 0; p < imgsize; ++p) {
895  for (uchar s = 0; s < format.size(); ++s) {
896  signed char component = -1;
897  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
898  component = 0;
899  } else if (format.at(s) == 'G') {
900  component = 1;
901  } else if (format.at(s) == 'R') {
902  component = 2;
903  } else if (format.at(s) == 'A') {
904  nassertv(cdata->_num_components != 3);
905  component = cdata->_num_components - 1;
906  } else if (format.at(s) == '0') {
907  // Ignore.
908  } else if (format.at(s) == '1') {
909  // Ignore.
910  } else {
911  gobj_cat.error() << "Unexpected component character '"
912  << format.at(s) << "', expected one of RGBA!\n";
913  return;
914  }
915  if (component >= 0) {
916  newdata[p * cdata->_num_components + component] = image[p * format.size() + s];
917  }
918  }
919  }
920  do_set_ram_image(cdata, newdata);
921  return;
922  }
923  for (int p = 0; p < imgsize; ++p) {
924  for (uchar s = 0; s < format.size(); ++s) {
925  signed char component = -1;
926  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
927  component = 0;
928  } else if (format.at(s) == 'G') {
929  component = 1;
930  } else if (format.at(s) == 'R') {
931  component = 2;
932  } else if (format.at(s) == 'A') {
933  nassertv(cdata->_num_components != 3);
934  component = cdata->_num_components - 1;
935  } else if (format.at(s) == '0') {
936  // Ignore.
937  } else if (format.at(s) == '1') {
938  // Ignore.
939  } else {
940  gobj_cat.error() << "Unexpected component character '"
941  << format.at(s) << "', expected one of RGBA!\n";
942  return;
943  }
944  if (component >= 0) {
945  memcpy((void*)(newdata + (p * cdata->_num_components + component) * cdata->_component_width),
946  (void*)(image + (p * format.size() + s) * cdata->_component_width),
947  cdata->_component_width);
948  }
949  }
950  }
951  do_set_ram_image(cdata, newdata);
952  return;
953 }
954 
955 ////////////////////////////////////////////////////////////////////
956 // Function: Texture::get_keep_ram_image
957 // Access: Published, Virtual
958 // Description: Returns the flag that indicates whether this Texture
959 // is eligible to have its main RAM copy of the texture
960 // memory dumped when the texture is prepared for
961 // rendering. See set_keep_ram_image().
962 ////////////////////////////////////////////////////////////////////
963 bool Texture::
965  CDReader cdata(_cycler);
966  return cdata->_keep_ram_image;
967 }
968 
969 ////////////////////////////////////////////////////////////////////
970 // Function: Texture::is_cacheable
971 // Access: Published, Virtual
972 // Description: Returns true if there is enough information in this
973 // Texture object to write it to the bam cache
974 // successfully, false otherwise. For most textures,
975 // this is the same as has_ram_image().
976 ////////////////////////////////////////////////////////////////////
977 bool Texture::
978 is_cacheable() const {
979  CDReader cdata(_cycler);
980  return do_has_bam_rawdata(cdata);
981 }
982 
983 ////////////////////////////////////////////////////////////////////
984 // Function: Texture::get_num_loadable_ram_mipmap_images
985 // Access: Published
986 // Description: Returns the number of contiguous mipmap levels that
987 // exist in RAM, up until the first gap in the sequence.
988 // It is guaranteed that at least mipmap levels [0,
989 // get_num_ram_mipmap_images()) exist.
990 //
991 // The number returned will never exceed the number of
992 // required mipmap images based on the size of the
993 // texture and its filter mode.
994 //
995 // This method is different from
996 // get_num_ram_mipmap_images() in that it returns only
997 // the number of mipmap levels that can actually be
998 // usefully loaded, regardless of the actual number that
999 // may be stored.
1000 ////////////////////////////////////////////////////////////////////
1001 int Texture::
1003  CDReader cdata(_cycler);
1004  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
1005  // If we don't even have a base image, the answer is none.
1006  return 0;
1007  }
1008  if (!uses_mipmaps()) {
1009  // If we have a base image and don't require mipmapping, the
1010  // answer is 1.
1011  return 1;
1012  }
1013 
1014  // Check that we have enough mipmap levels to meet the size
1015  // requirements.
1016  int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
1017  int n = 0;
1018  int x = 1;
1019  while (x < size) {
1020  x = (x << 1);
1021  ++n;
1022  if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
1023  return n;
1024  }
1025  }
1026 
1027  ++n;
1028  return n;
1029 }
1030 
1031 ////////////////////////////////////////////////////////////////////
1032 // Function: Texture::get_ram_mipmap_image
1033 // Access: Published
1034 // Description: Returns the system-RAM image data associated with the
1035 // nth mipmap level, if present. Returns NULL if the
1036 // nth mipmap level is not present.
1037 ////////////////////////////////////////////////////////////////////
1039 get_ram_mipmap_image(int n) const {
1040  CDReader cdata(_cycler);
1041  if (n < (int)cdata->_ram_images.size() && !cdata->_ram_images[n]._image.empty()) {
1042  return cdata->_ram_images[n]._image;
1043  }
1044  return CPTA_uchar(get_class_type());
1045 }
1046 
1047 ////////////////////////////////////////////////////////////////////
1048 // Function: Texture::get_ram_mipmap_pointer
1049 // Access: Published
1050 // Description: Similiar to get_ram_mipmap_image(), however, in this
1051 // case the void pointer for the given ram image is
1052 // returned. This will be NULL unless it has been
1053 // explicitly set.
1054 ////////////////////////////////////////////////////////////////////
1055 void *Texture::
1057  CDReader cdata(_cycler);
1058  if (n < (int)cdata->_ram_images.size()) {
1059  return cdata->_ram_images[n]._pointer_image;
1060  }
1061  return NULL;
1062 }
1063 
1064 ////////////////////////////////////////////////////////////////////
1065 // Function: Texture::set_ram_mipmap_pointer
1066 // Access: Published
1067 // Description: Sets an explicit void pointer as the texture's mipmap
1068 // image for the indicated level. This is a special
1069 // call to direct a texture to reference some external
1070 // image location, for instance from a webcam input.
1071 //
1072 // The texture will henceforth reference this pointer
1073 // directly, instead of its own internal storage; the
1074 // user is responsible for ensuring the data at this
1075 // address remains allocated and valid, and in the
1076 // correct format, during the lifetime of the texture.
1077 ////////////////////////////////////////////////////////////////////
1078 void Texture::
1079 set_ram_mipmap_pointer(int n, void *image, size_t page_size) {
1080  CDWriter cdata(_cycler, true);
1081  nassertv(cdata->_ram_image_compression != CM_off || do_get_expected_ram_mipmap_image_size(cdata, n));
1082 
1083  while (n >= (int)cdata->_ram_images.size()) {
1084  cdata->_ram_images.push_back(RamImage());
1085  }
1086 
1087  cdata->_ram_images[n]._page_size = page_size;
1088  //_ram_images[n]._image.clear(); wtf is going on?!
1089  cdata->_ram_images[n]._pointer_image = image;
1090  cdata->inc_image_modified();
1091 }
1092 
1093 ////////////////////////////////////////////////////////////////////
1094 // Function: Texture::set_ram_mipmap_pointer_from_int
1095 // Access: Published
1096 // Description: Accepts a raw pointer cast as an int, which is then
1097 // passed to set_ram_mipmap_pointer(); see the
1098 // documentation for that method.
1099 //
1100 // This variant is particularly useful to set an
1101 // external pointer from a language like Python, which
1102 // doesn't support void pointers directly.
1103 ////////////////////////////////////////////////////////////////////
1104 void Texture::
1105 set_ram_mipmap_pointer_from_int(long long pointer, int n, int page_size) {
1106  set_ram_mipmap_pointer(n, (void*)pointer, (size_t)page_size);
1107 }
1108 
1109 ////////////////////////////////////////////////////////////////////
1110 // Function: Texture::clear_ram_mipmap_image
1111 // Access: Published
1112 // Description: Discards the current system-RAM image for the nth
1113 // mipmap level.
1114 ////////////////////////////////////////////////////////////////////
1115 void Texture::
1117  CDWriter cdata(_cycler, true);
1118  if (n >= (int)cdata->_ram_images.size()) {
1119  return;
1120  }
1121  cdata->_ram_images[n]._page_size = 0;
1122  cdata->_ram_images[n]._image.clear();
1123  cdata->_ram_images[n]._pointer_image = NULL;
1124 }
1125 
1126 ////////////////////////////////////////////////////////////////////
1127 // Function: Texture::modify_simple_ram_image
1128 // Access: Published
1129 // Description: Returns a modifiable pointer to the internal "simple"
1130 // texture image. See set_simple_ram_image().
1131 ////////////////////////////////////////////////////////////////////
1132 PTA_uchar Texture::
1134  CDWriter cdata(_cycler, true);
1135  cdata->_simple_image_date_generated = (PN_int32)time(NULL);
1136  return cdata->_simple_ram_image._image;
1137 }
1138 
1139 ////////////////////////////////////////////////////////////////////
1140 // Function: Texture::new_simple_ram_image
1141 // Access: Published
1142 // Description: Creates an empty array for the simple ram image of
1143 // the indicated size, and returns a modifiable pointer
1144 // to the new array. See set_simple_ram_image().
1145 ////////////////////////////////////////////////////////////////////
1146 PTA_uchar Texture::
1147 new_simple_ram_image(int x_size, int y_size) {
1148  CDWriter cdata(_cycler, true);
1149  nassertr(cdata->_texture_type == TT_2d_texture, PTA_uchar());
1150  size_t expected_page_size = (size_t)(x_size * y_size * 4);
1151 
1152  cdata->_simple_x_size = x_size;
1153  cdata->_simple_y_size = y_size;
1154  cdata->_simple_ram_image._image = PTA_uchar::empty_array(expected_page_size);
1155  cdata->_simple_ram_image._page_size = expected_page_size;
1156  cdata->_simple_image_date_generated = (PN_int32)time(NULL);
1157  cdata->inc_simple_image_modified();
1158 
1159  return cdata->_simple_ram_image._image;
1160 }
1161 
1162 ////////////////////////////////////////////////////////////////////
1163 // Function: Texture::generate_simple_ram_image
1164 // Access: Published
1165 // Description: Computes the "simple" ram image by loading the main
1166 // RAM image, if it is not already available, and
1167 // reducing it to 16x16 or smaller. This may be an
1168 // expensive operation.
1169 ////////////////////////////////////////////////////////////////////
1170 void Texture::
1172  CDWriter cdata(_cycler, true);
1173 
1174  if (cdata->_texture_type != TT_2d_texture ||
1175  cdata->_ram_image_compression != CM_off) {
1176  return;
1177  }
1178 
1179  PNMImage pnmimage;
1180  if (!do_store_one(cdata, pnmimage, 0, 0)) {
1181  return;
1182  }
1183 
1184  // Start at the suggested size from the config file.
1185  int x_size = simple_image_size.get_word(0);
1186  int y_size = simple_image_size.get_word(1);
1187 
1188  // Limit it to no larger than the source image, and also make it a
1189  // power of two.
1190  x_size = down_to_power_2(min(x_size, cdata->_x_size));
1191  y_size = down_to_power_2(min(y_size, cdata->_y_size));
1192 
1193  // Generate a reduced image of that size.
1194  PNMImage scaled(x_size, y_size, pnmimage.get_num_channels());
1195  scaled.quick_filter_from(pnmimage);
1196 
1197  // Make sure the reduced image has 4 components, by convention.
1198  if (!scaled.has_alpha()) {
1199  scaled.add_alpha();
1200  scaled.alpha_fill(1.0);
1201  }
1202  scaled.set_num_channels(4);
1203 
1204  // Now see if we can go even smaller.
1205  bool did_anything;
1206  do {
1207  did_anything = false;
1208 
1209  // Try to reduce X.
1210  if (x_size > 1) {
1211  int new_x_size = (x_size >> 1);
1212  PNMImage smaller(new_x_size, y_size, 4);
1213  smaller.quick_filter_from(scaled);
1214  PNMImage bigger(x_size, y_size, 4);
1215  bigger.quick_filter_from(smaller);
1216 
1217  if (compare_images(scaled, bigger)) {
1218  scaled.take_from(smaller);
1219  x_size = new_x_size;
1220  did_anything = true;
1221  }
1222  }
1223 
1224  // Try to reduce Y.
1225  if (y_size > 1) {
1226  int new_y_size = (y_size >> 1);
1227  PNMImage smaller(x_size, new_y_size, 4);
1228  smaller.quick_filter_from(scaled);
1229  PNMImage bigger(x_size, y_size, 4);
1230  bigger.quick_filter_from(smaller);
1231 
1232  if (compare_images(scaled, bigger)) {
1233  scaled.take_from(smaller);
1234  y_size = new_y_size;
1235  did_anything = true;
1236  }
1237  }
1238  } while (did_anything);
1239 
1240  size_t expected_page_size = (size_t)(x_size * y_size * 4);
1241  PTA_uchar image = PTA_uchar::empty_array(expected_page_size, get_class_type());
1242  convert_from_pnmimage(image, expected_page_size, x_size, 0, 0, 0, scaled, 4, 1);
1243 
1244  do_set_simple_ram_image(cdata, image, x_size, y_size);
1245  cdata->_simple_image_date_generated = (PN_int32)time(NULL);
1246 }
1247 
1248 ////////////////////////////////////////////////////////////////////
1249 // Function: Texture::peek
1250 // Access: Published
1251 // Description: Returns a TexturePeeker object that can be used to
1252 // examine the individual texels stored within this
1253 // Texture by (u, v) coordinate.
1254 //
1255 // If the texture has a ram image resident, that image
1256 // is used. If it does not have a full ram image but
1257 // does have a simple_ram_image resident, that image is
1258 // used instead. If neither image is resident the full
1259 // image is reloaded.
1260 //
1261 // Returns NULL if the texture cannot find an image to
1262 // load, or the texture format is incompatible.
1263 ////////////////////////////////////////////////////////////////////
1264 PT(TexturePeeker) Texture::
1265 peek() {
1266  CDWriter cdata(_cycler, unlocked_ensure_ram_image(true));
1267 
1268  PT(TexturePeeker) peeker = new TexturePeeker(this, cdata);
1269  if (peeker->is_valid()) {
1270  return peeker;
1271  }
1272 
1273  return NULL;
1274 }
1275 
1276 ////////////////////////////////////////////////////////////////////
1277 // Function: Texture::prepare
1278 // Access: Published
1279 // Description: Indicates that the texture should be enqueued to be
1280 // prepared in the indicated prepared_objects at the
1281 // beginning of the next frame. This will ensure the
1282 // texture is already loaded into texture memory if it
1283 // is expected to be rendered soon.
1284 //
1285 // Use this function instead of prepare_now() to preload
1286 // textures from a user interface standpoint.
1287 ////////////////////////////////////////////////////////////////////
1288 void Texture::
1289 prepare(PreparedGraphicsObjects *prepared_objects) {
1290  prepared_objects->enqueue_texture(this);
1291 }
1292 
1293 ////////////////////////////////////////////////////////////////////
1294 // Function: Texture::is_prepared
1295 // Access: Published
1296 // Description: Returns true if the texture has already been prepared
1297 // or enqueued for preparation on the indicated GSG,
1298 // false otherwise.
1299 ////////////////////////////////////////////////////////////////////
1300 bool Texture::
1301 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
1302  MutexHolder holder(_lock);
1303  PreparedViews::const_iterator pvi;
1304  pvi = _prepared_views.find(prepared_objects);
1305  if (pvi != _prepared_views.end()) {
1306  return true;
1307  }
1308  return prepared_objects->is_texture_queued(this);
1309 }
1310 
1311 ////////////////////////////////////////////////////////////////////
1312 // Function: Texture::was_image_modified
1313 // Access: Published
1314 // Description: Returns true if the texture needs to be re-loaded
1315 // onto the indicated GSG, either because its image data
1316 // is out-of-date, or because it's not fully prepared
1317 // now.
1318 ////////////////////////////////////////////////////////////////////
1319 bool Texture::
1321  MutexHolder holder(_lock);
1322  CDReader cdata(_cycler);
1323 
1324  PreparedViews::const_iterator pvi;
1325  pvi = _prepared_views.find(prepared_objects);
1326  if (pvi != _prepared_views.end()) {
1327  const Contexts &contexts = (*pvi).second;
1328  for (int view = 0; view < cdata->_num_views; ++view) {
1329  Contexts::const_iterator ci;
1330  ci = contexts.find(view);
1331  if (ci == contexts.end()) {
1332  return true;
1333  }
1334  TextureContext *tc = (*ci).second;
1335  if (tc->was_image_modified()) {
1336  return true;
1337  }
1338  }
1339  return false;
1340  }
1341  return true;
1342 }
1343 
1344 ////////////////////////////////////////////////////////////////////
1345 // Function: Texture::get_data_size_bytes
1346 // Access: Public
1347 // Description: Returns the number of bytes which the texture is
1348 // reported to consume within graphics memory, for the
1349 // indicated GSG. This may return a nonzero value even
1350 // if the texture is not currently resident; you should
1351 // also check get_resident() if you want to know how
1352 // much space the texture is actually consuming right
1353 // now.
1354 ////////////////////////////////////////////////////////////////////
1355 size_t Texture::
1357  MutexHolder holder(_lock);
1358  CDReader cdata(_cycler);
1359 
1360  PreparedViews::const_iterator pvi;
1361  size_t total_size = 0;
1362  pvi = _prepared_views.find(prepared_objects);
1363  if (pvi != _prepared_views.end()) {
1364  const Contexts &contexts = (*pvi).second;
1365  for (int view = 0; view < cdata->_num_views; ++view) {
1366  Contexts::const_iterator ci;
1367  ci = contexts.find(view);
1368  if (ci != contexts.end()) {
1369  TextureContext *tc = (*ci).second;
1370  total_size += tc->get_data_size_bytes();
1371  }
1372  }
1373  }
1374 
1375  return total_size;
1376 }
1377 
1378 ////////////////////////////////////////////////////////////////////
1379 // Function: Texture::get_active
1380 // Access: Public
1381 // Description: Returns true if this Texture was rendered in the most
1382 // recent frame within the indicated GSG.
1383 ////////////////////////////////////////////////////////////////////
1384 bool Texture::
1385 get_active(PreparedGraphicsObjects *prepared_objects) const {
1386  MutexHolder holder(_lock);
1387  CDReader cdata(_cycler);
1388 
1389  PreparedViews::const_iterator pvi;
1390  pvi = _prepared_views.find(prepared_objects);
1391  if (pvi != _prepared_views.end()) {
1392  const Contexts &contexts = (*pvi).second;
1393  for (int view = 0; view < cdata->_num_views; ++view) {
1394  Contexts::const_iterator ci;
1395  ci = contexts.find(view);
1396  if (ci != contexts.end()) {
1397  TextureContext *tc = (*ci).second;
1398  if (tc->get_active()) {
1399  return true;
1400  }
1401  }
1402  }
1403  }
1404  return false;
1405 }
1406 
1407 ////////////////////////////////////////////////////////////////////
1408 // Function: Texture::get_resident
1409 // Access: Public
1410 // Description: Returns true if this Texture is reported to be
1411 // resident within graphics memory for the indicated
1412 // GSG.
1413 ////////////////////////////////////////////////////////////////////
1414 bool Texture::
1415 get_resident(PreparedGraphicsObjects *prepared_objects) const {
1416  MutexHolder holder(_lock);
1417  CDReader cdata(_cycler);
1418 
1419  PreparedViews::const_iterator pvi;
1420  pvi = _prepared_views.find(prepared_objects);
1421  if (pvi != _prepared_views.end()) {
1422  const Contexts &contexts = (*pvi).second;
1423  for (int view = 0; view < cdata->_num_views; ++view) {
1424  Contexts::const_iterator ci;
1425  ci = contexts.find(view);
1426  if (ci != contexts.end()) {
1427  TextureContext *tc = (*ci).second;
1428  if (tc->get_resident()) {
1429  return true;
1430  }
1431  }
1432  }
1433  }
1434  return false;
1435 }
1436 
1437 ////////////////////////////////////////////////////////////////////
1438 // Function: Texture::release
1439 // Access: Published
1440 // Description: Frees the texture context only on the indicated object,
1441 // if it exists there. Returns true if it was released,
1442 // false if it had not been prepared.
1443 ////////////////////////////////////////////////////////////////////
1444 bool Texture::
1445 release(PreparedGraphicsObjects *prepared_objects) {
1446  MutexHolder holder(_lock);
1447  PreparedViews::iterator pvi;
1448  pvi = _prepared_views.find(prepared_objects);
1449  if (pvi != _prepared_views.end()) {
1450  Contexts temp;
1451  temp.swap((*pvi).second);
1452  Contexts::iterator ci;
1453  for (ci = temp.begin(); ci != temp.end(); ++ci) {
1454  TextureContext *tc = (*ci).second;
1455  if (tc != (TextureContext *)NULL) {
1456  prepared_objects->release_texture(tc);
1457  }
1458  }
1459  _prepared_views.erase(pvi);
1460  }
1461 
1462  // Maybe it wasn't prepared yet, but it's about to be.
1463  return prepared_objects->dequeue_texture(this);
1464 }
1465 
1466 ////////////////////////////////////////////////////////////////////
1467 // Function: Texture::release_all
1468 // Access: Published
1469 // Description: Frees the context allocated on all objects for which
1470 // the texture has been declared. Returns the number of
1471 // contexts which have been freed.
1472 ////////////////////////////////////////////////////////////////////
1473 int Texture::
1475  MutexHolder holder(_lock);
1476 
1477  // We have to traverse a copy of the _prepared_views list, because the
1478  // PreparedGraphicsObjects object will call clear_prepared() in response
1479  // to each release_texture(), and we don't want to be modifying the
1480  // _prepared_views list while we're traversing it.
1481  PreparedViews temp;
1482  temp.swap(_prepared_views);
1483  int num_freed = (int)temp.size();
1484 
1485  PreparedViews::iterator pvi;
1486  for (pvi = temp.begin(); pvi != temp.end(); ++pvi) {
1487  PreparedGraphicsObjects *prepared_objects = (*pvi).first;
1488  Contexts temp;
1489  temp.swap((*pvi).second);
1490  Contexts::iterator ci;
1491  for (ci = temp.begin(); ci != temp.end(); ++ci) {
1492  TextureContext *tc = (*ci).second;
1493  if (tc != (TextureContext *)NULL) {
1494  prepared_objects->release_texture(tc);
1495  }
1496  }
1497  }
1498 
1499  return num_freed;
1500 }
1501 
1502 ////////////////////////////////////////////////////////////////////
1503 // Function: Texture::write
1504 // Access: Published
1505 // Description: Not to be confused with write(Filename), this method
1506 // simply describes the texture properties.
1507 ////////////////////////////////////////////////////////////////////
1508 void Texture::
1509 write(ostream &out, int indent_level) const {
1510  CDReader cdata(_cycler);
1511  indent(out, indent_level)
1512  << cdata->_texture_type << " " << get_name();
1513  if (!cdata->_filename.empty()) {
1514  out << " (from " << cdata->_filename << ")";
1515  }
1516  out << "\n";
1517 
1518  indent(out, indent_level + 2);
1519 
1520  switch (cdata->_texture_type) {
1521  case TT_1d_texture:
1522  out << "1-d, " << cdata->_x_size;
1523  break;
1524 
1525  case TT_2d_texture:
1526  out << "2-d, " << cdata->_x_size << " x " << cdata->_y_size;
1527  break;
1528 
1529  case TT_3d_texture:
1530  out << "3-d, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
1531  break;
1532 
1533  case TT_2d_texture_array:
1534  out << "2-d array, " << cdata->_x_size << " x " << cdata->_y_size << " x " << cdata->_z_size;
1535  break;
1536 
1537  case TT_cube_map:
1538  out << "cube map, " << cdata->_x_size << " x " << cdata->_y_size;
1539  break;
1540  }
1541 
1542  if (cdata->_num_views > 1) {
1543  out << " (x " << cdata->_num_views << " views)";
1544  }
1545 
1546  out << " pixels, each " << cdata->_num_components;
1547 
1548  switch (cdata->_component_type) {
1549  case T_unsigned_byte:
1550  out << " bytes";
1551  break;
1552 
1553  case T_unsigned_short:
1554  out << " shorts";
1555  break;
1556 
1557  case T_float:
1558  out << " floats";
1559  break;
1560 
1561  case T_unsigned_int_24_8:
1562  case T_int:
1563  out << " ints";
1564  break;
1565 
1566  default:
1567  break;
1568  }
1569 
1570  out << ", ";
1571  switch (cdata->_format) {
1572  case F_color_index:
1573  out << "color_index";
1574  break;
1575  case F_depth_stencil:
1576  out << "depth_stencil";
1577  break;
1578  case F_depth_component:
1579  out << "depth_component";
1580  break;
1581  case F_depth_component16:
1582  out << "depth_component16";
1583  break;
1584  case F_depth_component24:
1585  out << "depth_component24";
1586  break;
1587  case F_depth_component32:
1588  out << "depth_component32";
1589  break;
1590 
1591  case F_rgba:
1592  out << "rgba";
1593  break;
1594  case F_rgbm:
1595  out << "rgbm";
1596  break;
1597  case F_rgba32:
1598  out << "rgba32";
1599  break;
1600  case F_rgba16:
1601  out << "rgba16";
1602  break;
1603  case F_rgba12:
1604  out << "rgba12";
1605  break;
1606  case F_rgba8:
1607  out << "rgba8";
1608  break;
1609  case F_rgba4:
1610  out << "rgba4";
1611  break;
1612 
1613  case F_rgb:
1614  out << "rgb";
1615  break;
1616  case F_rgb12:
1617  out << "rgb12";
1618  break;
1619  case F_rgb8:
1620  out << "rgb8";
1621  break;
1622  case F_rgb5:
1623  out << "rgb5";
1624  break;
1625  case F_rgba5:
1626  out << "rgba5";
1627  break;
1628  case F_rgb332:
1629  out << "rgb332";
1630  break;
1631 
1632  case F_red:
1633  out << "red";
1634  break;
1635  case F_green:
1636  out << "green";
1637  break;
1638  case F_blue:
1639  out << "blue";
1640  break;
1641  case F_alpha:
1642  out << "alpha";
1643  break;
1644  case F_luminance:
1645  out << "luminance";
1646  break;
1647  case F_luminance_alpha:
1648  out << "luminance_alpha";
1649  break;
1650  case F_luminance_alphamask:
1651  out << "luminance_alphamask";
1652  break;
1653 
1654  case F_r16:
1655  out << "r16";
1656  break;
1657  case F_rg16:
1658  out << "rg16";
1659  break;
1660  case F_rgb16:
1661  out << "rgb16";
1662  break;
1663 
1664  case F_srgb:
1665  out << "srgb";
1666  break;
1667  case F_srgb_alpha:
1668  out << "srgb_alpha";
1669  break;
1670  case F_sluminance:
1671  out << "sluminance";
1672  break;
1673  case F_sluminance_alpha:
1674  out << "sluminance_alpha";
1675  break;
1676 
1677  case F_r32i:
1678  out << "r32i";
1679  break;
1680 
1681  case F_r32:
1682  out << "r32";
1683  break;
1684  case F_rg32:
1685  out << "rg32";
1686  break;
1687  case F_rgb32:
1688  out << "rgb32";
1689  break;
1690 
1691  case F_r8i:
1692  out << "r8i";
1693  break;
1694  case F_rg8i:
1695  out << "rg8i";
1696  break;
1697  case F_rgb8i:
1698  out << "rgb8i";
1699  break;
1700  case F_rgba8i:
1701  out << "rgba8i";
1702  break;
1703  }
1704 
1705  if (cdata->_compression != CM_default) {
1706  out << ", compression " << cdata->_compression;
1707  }
1708  out << "\n";
1709 
1710  indent(out, indent_level + 2);
1711 
1712  cdata->_default_sampler.output(out);
1713 
1714  if (do_has_ram_image(cdata)) {
1715  indent(out, indent_level + 2)
1716  << do_get_ram_image_size(cdata) << " bytes in ram, compression "
1717  << cdata->_ram_image_compression << "\n";
1718 
1719  if (cdata->_ram_images.size() > 1) {
1720  int count = 0;
1721  size_t total_size = 0;
1722  for (size_t n = 1; n < cdata->_ram_images.size(); ++n) {
1723  if (!cdata->_ram_images[n]._image.empty()) {
1724  ++count;
1725  total_size += cdata->_ram_images[n]._image.size();
1726  } else {
1727  // Stop at the first gap.
1728  break;
1729  }
1730  }
1731  indent(out, indent_level + 2)
1732  << count
1733  << " mipmap levels also present in ram (" << total_size
1734  << " bytes).\n";
1735  }
1736 
1737  } else {
1738  indent(out, indent_level + 2)
1739  << "no ram image\n";
1740  }
1741 
1742  if (!cdata->_simple_ram_image._image.empty()) {
1743  indent(out, indent_level + 2)
1744  << "simple image: " << cdata->_simple_x_size << " x "
1745  << cdata->_simple_y_size << ", "
1746  << cdata->_simple_ram_image._image.size() << " bytes\n";
1747  }
1748 }
1749 
1750 
1751 ////////////////////////////////////////////////////////////////////
1752 // Function: Texture::set_size_padded
1753 // Access: Published
1754 // Description: Changes the size of the texture, padding
1755 // if necessary, and setting the pad region
1756 // as well.
1757 ////////////////////////////////////////////////////////////////////
1758 void Texture::
1759 set_size_padded(int x, int y, int z) {
1760  CDWriter cdata(_cycler, true);
1761  if (do_get_auto_texture_scale(cdata) != ATS_none) {
1762  do_set_x_size(cdata, up_to_power_2(x));
1763  do_set_y_size(cdata, up_to_power_2(y));
1764 
1765  if (cdata->_texture_type == TT_3d_texture) {
1766  // Only pad 3D textures. It does not make sense
1767  // to do so for cube maps or 2D texture arrays.
1768  do_set_z_size(cdata, up_to_power_2(z));
1769  } else {
1770  do_set_z_size(cdata, z);
1771  }
1772  } else {
1773  do_set_x_size(cdata, x);
1774  do_set_y_size(cdata, y);
1775  do_set_z_size(cdata, z);
1776  }
1777  do_set_pad_size(cdata,
1778  cdata->_x_size - x,
1779  cdata->_y_size - y,
1780  cdata->_z_size - z);
1781 }
1782 
1783 ////////////////////////////////////////////////////////////////////
1784 // Function: Texture::set_orig_file_size
1785 // Access: Published
1786 // Description: Specifies the size of the texture as it exists in its
1787 // original disk file, before any Panda scaling.
1788 ////////////////////////////////////////////////////////////////////
1789 void Texture::
1790 set_orig_file_size(int x, int y, int z) {
1791  CDWriter cdata(_cycler, true);
1792  cdata->_orig_file_x_size = x;
1793  cdata->_orig_file_y_size = y;
1794 
1795  nassertv(z == cdata->_z_size);
1796 }
1797 
1798 ////////////////////////////////////////////////////////////////////
1799 // Function: Texture::prepare_now
1800 // Access: Published
1801 // Description: Creates a context for the texture on the particular
1802 // GSG, if it does not already exist. Returns the new
1803 // (or old) TextureContext. This assumes that the
1804 // GraphicsStateGuardian is the currently active
1805 // rendering context and that it is ready to accept new
1806 // textures. If this is not necessarily the case, you
1807 // should use prepare() instead.
1808 //
1809 // Normally, this is not called directly except by the
1810 // GraphicsStateGuardian; a texture does not need to be
1811 // explicitly prepared by the user before it may be
1812 // rendered.
1813 ////////////////////////////////////////////////////////////////////
1815 prepare_now(int view,
1816  PreparedGraphicsObjects *prepared_objects,
1818  MutexHolder holder(_lock);
1819  CDReader cdata(_cycler);
1820 
1821  // Don't exceed the actual number of views.
1822  view = max(min(view, cdata->_num_views - 1), 0);
1823 
1824  // Get the list of PreparedGraphicsObjects for this view.
1825  Contexts &contexts = _prepared_views[prepared_objects];
1826  Contexts::const_iterator pvi;
1827  pvi = contexts.find(view);
1828  if (pvi != contexts.end()) {
1829  return (*pvi).second;
1830  }
1831 
1832  TextureContext *tc = prepared_objects->prepare_texture_now(this, view, gsg);
1833  contexts[view] = tc;
1834 
1835  return tc;
1836 }
1837 
1838 ////////////////////////////////////////////////////////////////////
1839 // Function: Texture::up_to_power_2
1840 // Access: Published, Static
1841 // Description: Returns the smallest power of 2 greater than or equal
1842 // to value.
1843 ////////////////////////////////////////////////////////////////////
1844 int Texture::
1845 up_to_power_2(int value) {
1846  if (value <= 1) {
1847  return 1;
1848  }
1849  int bit = get_next_higher_bit(((unsigned int)value) - 1);
1850  return (1 << bit);
1851 }
1852 
1853 ////////////////////////////////////////////////////////////////////
1854 // Function: Texture::down_to_power_2
1855 // Access: Published, Static
1856 // Description: Returns the largest power of 2 less than or equal
1857 // to value.
1858 ////////////////////////////////////////////////////////////////////
1859 int Texture::
1860 down_to_power_2(int value) {
1861  if (value <= 1) {
1862  return 1;
1863  }
1864  int bit = get_next_higher_bit(((unsigned int)value) >> 1);
1865  return (1 << bit);
1866 }
1867 
1868 ////////////////////////////////////////////////////////////////////
1869 // Function: Texture::consider_rescale
1870 // Access: Published
1871 // Description: Asks the PNMImage to change its scale when it reads
1872 // the image, according to the whims of the Config.prc
1873 // file.
1874 //
1875 // For most efficient results, this method should be
1876 // called after pnmimage.read_header() has been called,
1877 // but before pnmimage.read(). This method may also be
1878 // called after pnmimage.read(), i.e. when the pnmimage
1879 // is already loaded; in this case it will rescale the
1880 // image on the spot. Also see rescale_texture().
1881 ////////////////////////////////////////////////////////////////////
1882 void Texture::
1884  consider_rescale(pnmimage, get_name(), get_auto_texture_scale());
1885 }
1886 
1887 ////////////////////////////////////////////////////////////////////
1888 // Function: Texture::consider_rescale
1889 // Access: Published, Static
1890 // Description: Asks the PNMImage to change its scale when it reads
1891 // the image, according to the whims of the Config.prc
1892 // file.
1893 //
1894 // For most efficient results, this method should be
1895 // called after pnmimage.read_header() has been called,
1896 // but before pnmimage.read(). This method may also be
1897 // called after pnmimage.read(), i.e. when the pnmimage
1898 // is already loaded; in this case it will rescale the
1899 // image on the spot. Also see rescale_texture().
1900 ////////////////////////////////////////////////////////////////////
1901 void Texture::
1902 consider_rescale(PNMImage &pnmimage, const string &name, AutoTextureScale auto_texture_scale) {
1903  int new_x_size = pnmimage.get_x_size();
1904  int new_y_size = pnmimage.get_y_size();
1905  if (adjust_size(new_x_size, new_y_size, name, false, auto_texture_scale)) {
1906  if (pnmimage.is_valid()) {
1907  // The image is already loaded. Rescale on the spot.
1908  PNMImage new_image(new_x_size, new_y_size, pnmimage.get_num_channels(),
1909  pnmimage.get_maxval(), pnmimage.get_type(),
1910  pnmimage.get_color_space());
1911  new_image.quick_filter_from(pnmimage);
1912  pnmimage.take_from(new_image);
1913  } else {
1914  // Rescale while reading. Some image types (e.g. jpeg) can take
1915  // advantage of this.
1916  pnmimage.set_read_size(new_x_size, new_y_size);
1917  }
1918  }
1919 }
1920 
1921 ////////////////////////////////////////////////////////////////////
1922 // Function: Texture::format_texture_type
1923 // Access: Published, Static
1924 // Description: Returns the indicated TextureType converted to a
1925 // string word.
1926 ////////////////////////////////////////////////////////////////////
1927 string Texture::
1928 format_texture_type(TextureType tt) {
1929  switch (tt) {
1930  case TT_1d_texture:
1931  return "1d_texture";
1932  case TT_2d_texture:
1933  return "2d_texture";
1934  case TT_3d_texture:
1935  return "3d_texture";
1936  case TT_2d_texture_array:
1937  return "2d_texture_array";
1938  case TT_cube_map:
1939  return "cube_map";
1940  }
1941  return "**invalid**";
1942 }
1943 
1944 ////////////////////////////////////////////////////////////////////
1945 // Function: Texture::string_texture_type
1946 // Access: Published, Static
1947 // Description: Returns the TextureType corresponding to the
1948 // indicated string word.
1949 ////////////////////////////////////////////////////////////////////
1950 Texture::TextureType Texture::
1951 string_texture_type(const string &str) {
1952  if (cmp_nocase(str, "1d_texture") == 0) {
1953  return TT_1d_texture;
1954  } else if (cmp_nocase(str, "2d_texture") == 0) {
1955  return TT_2d_texture;
1956  } else if (cmp_nocase(str, "3d_texture") == 0) {
1957  return TT_3d_texture;
1958  } else if (cmp_nocase(str, "2d_texture_array") == 0) {
1959  return TT_2d_texture_array;
1960  } else if (cmp_nocase(str, "cube_map") == 0) {
1961  return TT_cube_map;
1962  }
1963 
1964  gobj_cat->error()
1965  << "Invalid Texture::TextureType value: " << str << "\n";
1966  return TT_2d_texture;
1967 }
1968 
1969 ////////////////////////////////////////////////////////////////////
1970 // Function: Texture::format_component_type
1971 // Access: Published, Static
1972 // Description: Returns the indicated ComponentType converted to a
1973 // string word.
1974 ////////////////////////////////////////////////////////////////////
1975 string Texture::
1976 format_component_type(ComponentType ct) {
1977  switch (ct) {
1978  case T_unsigned_byte:
1979  return "unsigned_byte";
1980  case T_unsigned_short:
1981  return "unsigned_short";
1982  case T_float:
1983  return "float";
1984  case T_unsigned_int_24_8:
1985  return "unsigned_int_24_8";
1986  case T_int:
1987  return "int";
1988  }
1989 
1990  return "**invalid**";
1991 }
1992 
1993 ////////////////////////////////////////////////////////////////////
1994 // Function: Texture::string_component_type
1995 // Access: Published, Static
1996 // Description: Returns the ComponentType corresponding to the
1997 // indicated string word.
1998 ////////////////////////////////////////////////////////////////////
1999 Texture::ComponentType Texture::
2000 string_component_type(const string &str) {
2001  if (cmp_nocase(str, "unsigned_byte") == 0) {
2002  return T_unsigned_byte;
2003  } else if (cmp_nocase(str, "unsigned_short") == 0) {
2004  return T_unsigned_short;
2005  } else if (cmp_nocase(str, "float") == 0) {
2006  return T_float;
2007  } else if (cmp_nocase(str, "unsigned_int_24_8") == 0) {
2008  return T_unsigned_int_24_8;
2009  } else if (cmp_nocase(str, "int") == 0) {
2010  return T_int;
2011  }
2012 
2013  gobj_cat->error()
2014  << "Invalid Texture::ComponentType value: " << str << "\n";
2015  return T_unsigned_byte;
2016 }
2017 
2018 ////////////////////////////////////////////////////////////////////
2019 // Function: Texture::format_format
2020 // Access: Published, Static
2021 // Description: Returns the indicated Format converted to a
2022 // string word.
2023 ////////////////////////////////////////////////////////////////////
2024 string Texture::
2025 format_format(Format format) {
2026  switch (format) {
2027  case F_depth_stencil:
2028  return "depth_stencil";
2029  case F_depth_component:
2030  return "depth_component";
2031  case F_depth_component16:
2032  return "depth_component16";
2033  case F_depth_component24:
2034  return "depth_component24";
2035  case F_depth_component32:
2036  return "depth_component32";
2037  case F_color_index:
2038  return "color_index";
2039  case F_red:
2040  return "red";
2041  case F_green:
2042  return "green";
2043  case F_blue:
2044  return "blue";
2045  case F_alpha:
2046  return "alpha";
2047  case F_rgb:
2048  return "rgb";
2049  case F_rgb5:
2050  return "rgb5";
2051  case F_rgb8:
2052  return "rgb8";
2053  case F_rgb12:
2054  return "rgb12";
2055  case F_rgb332:
2056  return "rgb332";
2057  case F_rgba:
2058  return "rgba";
2059  case F_rgbm:
2060  return "rgbm";
2061  case F_rgba4:
2062  return "rgba4";
2063  case F_rgba5:
2064  return "rgba5";
2065  case F_rgba8:
2066  return "rgba8";
2067  case F_rgba12:
2068  return "rgba12";
2069  case F_luminance:
2070  return "luminance";
2071  case F_luminance_alpha:
2072  return "luminance_alpha";
2073  case F_luminance_alphamask:
2074  return "luminance_alphamask";
2075  case F_rgba16:
2076  return "rgba16";
2077  case F_rgba32:
2078  return "rgba32";
2079  case F_r16:
2080  return "r16";
2081  case F_rg16:
2082  return "rg16";
2083  case F_rgb16:
2084  return "rgb16";
2085  case F_srgb:
2086  return "srgb";
2087  case F_srgb_alpha:
2088  return "srgb_alpha";
2089  case F_sluminance:
2090  return "sluminance";
2091  case F_sluminance_alpha:
2092  return "sluminance_alpha";
2093  case F_r32i:
2094  return "r32i";
2095  case F_r32:
2096  return "r32";
2097  case F_rg32:
2098  return "rg32";
2099  case F_rgb32:
2100  return "rgb32";
2101  case F_r8i:
2102  return "r8i";
2103  case F_rg8i:
2104  return "rg8i";
2105  case F_rgb8i:
2106  return "rgb8i";
2107  case F_rgba8i:
2108  return "rgba8i";
2109  }
2110  return "**invalid**";
2111 }
2112 
2113 ////////////////////////////////////////////////////////////////////
2114 // Function: Texture::string_format
2115 // Access: Published, Static
2116 // Description: Returns the Format corresponding to the
2117 // indicated string word.
2118 ////////////////////////////////////////////////////////////////////
2119 Texture::Format Texture::
2120 string_format(const string &str) {
2121  if (cmp_nocase(str, "depth_stencil") == 0) {
2122  return F_depth_stencil;
2123  } else if (cmp_nocase(str, "depth_component") == 0) {
2124  return F_depth_component;
2125  } else if (cmp_nocase(str, "depth_component16") == 0 || cmp_nocase(str, "d16") == 0) {
2126  return F_depth_component16;
2127  } else if (cmp_nocase(str, "depth_component24") == 0 || cmp_nocase(str, "d24") == 0) {
2128  return F_depth_component24;
2129  } else if (cmp_nocase(str, "depth_component32") == 0 || cmp_nocase(str, "d32") == 0) {
2130  return F_depth_component32;
2131  } else if (cmp_nocase(str, "color_index") == 0) {
2132  return F_color_index;
2133  } else if (cmp_nocase(str, "red") == 0) {
2134  return F_red;
2135  } else if (cmp_nocase(str, "green") == 0) {
2136  return F_green;
2137  } else if (cmp_nocase(str, "blue") == 0) {
2138  return F_blue;
2139  } else if (cmp_nocase(str, "alpha") == 0) {
2140  return F_alpha;
2141  } else if (cmp_nocase(str, "rgb") == 0) {
2142  return F_rgb;
2143  } else if (cmp_nocase(str, "rgb5") == 0) {
2144  return F_rgb5;
2145  } else if (cmp_nocase(str, "rgb8") == 0 || cmp_nocase(str, "r8g8b8") == 0) {
2146  return F_rgb8;
2147  } else if (cmp_nocase(str, "rgb12") == 0) {
2148  return F_rgb12;
2149  } else if (cmp_nocase(str, "rgb332") == 0 || cmp_nocase(str, "r3g3b2") == 0) {
2150  return F_rgb332;
2151  } else if (cmp_nocase(str, "rgba") == 0) {
2152  return F_rgba;
2153  } else if (cmp_nocase(str, "rgbm") == 0) {
2154  return F_rgbm;
2155  } else if (cmp_nocase(str, "rgba4") == 0) {
2156  return F_rgba4;
2157  } else if (cmp_nocase(str, "rgba5") == 0) {
2158  return F_rgba5;
2159  } else if (cmp_nocase(str, "rgba8") == 0 || cmp_nocase(str, "r8g8b8a8") == 0) {
2160  return F_rgba8;
2161  } else if (cmp_nocase(str, "rgba12") == 0) {
2162  return F_rgba12;
2163  } else if (cmp_nocase(str, "luminance") == 0) {
2164  return F_luminance;
2165  } else if (cmp_nocase(str, "luminance_alpha") == 0) {
2166  return F_luminance_alpha;
2167  } else if (cmp_nocase(str, "luminance_alphamask") == 0) {
2168  return F_luminance_alphamask;
2169  } else if (cmp_nocase(str, "rgba16") == 0 || cmp_nocase(str, "r16g16b16a16") == 0) {
2170  return F_rgba16;
2171  } else if (cmp_nocase(str, "rgba32") == 0 || cmp_nocase(str, "r32g32b32a32") == 0) {
2172  return F_rgba32;
2173  } else if (cmp_nocase(str, "r16") == 0 || cmp_nocase(str, "red16") == 0) {
2174  return F_r16;
2175  } else if (cmp_nocase(str, "rg16") == 0 || cmp_nocase(str, "r16g16") == 0) {
2176  return F_rg16;
2177  } else if (cmp_nocase(str, "rgb16") == 0 || cmp_nocase(str, "r16g16b16") == 0) {
2178  return F_rgb16;
2179  } else if (cmp_nocase(str, "srgb") == 0) {
2180  return F_srgb;
2181  } else if (cmp_nocase(str, "srgb_alpha") == 0) {
2182  return F_srgb_alpha;
2183  } else if (cmp_nocase(str, "sluminance") == 0) {
2184  return F_sluminance;
2185  } else if (cmp_nocase(str, "sluminance_alpha") == 0) {
2186  return F_sluminance_alpha;
2187  } else if (cmp_nocase(str, "r32i") == 0) {
2188  return F_r32i;
2189  } else if (cmp_nocase(str, "r32") == 0 || cmp_nocase(str, "red32") == 0) {
2190  return F_r32;
2191  } else if (cmp_nocase(str, "rg32") == 0 || cmp_nocase(str, "r32g32") == 0) {
2192  return F_rg32;
2193  } else if (cmp_nocase(str, "rgb32") == 0 || cmp_nocase(str, "r32g32b32") == 0) {
2194  return F_rgb32;
2195  }
2196 
2197  gobj_cat->error()
2198  << "Invalid Texture::Format value: " << str << "\n";
2199  return F_rgba;
2200 }
2201 
2202 ////////////////////////////////////////////////////////////////////
2203 // Function: Texture::format_compression_mode
2204 // Access: Published, Static
2205 // Description: Returns the indicated CompressionMode converted to a
2206 // string word.
2207 ////////////////////////////////////////////////////////////////////
2208 string Texture::
2209 format_compression_mode(CompressionMode cm) {
2210  switch (cm) {
2211  case CM_default:
2212  return "default";
2213  case CM_off:
2214  return "off";
2215  case CM_on:
2216  return "on";
2217  case CM_fxt1:
2218  return "fxt1";
2219  case CM_dxt1:
2220  return "dxt1";
2221  case CM_dxt2:
2222  return "dxt2";
2223  case CM_dxt3:
2224  return "dxt3";
2225  case CM_dxt4:
2226  return "dxt4";
2227  case CM_dxt5:
2228  return "dxt5";
2229  case CM_pvr1_2bpp:
2230  return "pvr1_2bpp";
2231  case CM_pvr1_4bpp:
2232  return "pvr1_4bpp";
2233  }
2234 
2235  return "**invalid**";
2236 }
2237 
2238 ////////////////////////////////////////////////////////////////////
2239 // Function: Texture::string_compression_mode
2240 // Access: Public
2241 // Description: Returns the CompressionMode value associated with the
2242 // given string representation.
2243 ////////////////////////////////////////////////////////////////////
2244 Texture::CompressionMode Texture::
2245 string_compression_mode(const string &str) {
2246  if (cmp_nocase_uh(str, "default") == 0) {
2247  return CM_default;
2248  } else if (cmp_nocase_uh(str, "off") == 0) {
2249  return CM_off;
2250  } else if (cmp_nocase_uh(str, "on") == 0) {
2251  return CM_on;
2252  } else if (cmp_nocase_uh(str, "fxt1") == 0) {
2253  return CM_fxt1;
2254  } else if (cmp_nocase_uh(str, "dxt1") == 0) {
2255  return CM_dxt1;
2256  } else if (cmp_nocase_uh(str, "dxt2") == 0) {
2257  return CM_dxt2;
2258  } else if (cmp_nocase_uh(str, "dxt3") == 0) {
2259  return CM_dxt3;
2260  } else if (cmp_nocase_uh(str, "dxt4") == 0) {
2261  return CM_dxt4;
2262  } else if (cmp_nocase_uh(str, "dxt5") == 0) {
2263  return CM_dxt5;
2264  } else if (cmp_nocase_uh(str, "pvr1_2bpp") == 0) {
2265  return CM_pvr1_2bpp;
2266  } else if (cmp_nocase_uh(str, "pvr1_4bpp") == 0) {
2267  return CM_pvr1_4bpp;
2268  }
2269 
2270  gobj_cat->error()
2271  << "Invalid Texture::CompressionMode value: " << str << "\n";
2272  return CM_default;
2273 }
2274 
2275 
2276 ////////////////////////////////////////////////////////////////////
2277 // Function: Texture::format_quality_level
2278 // Access: Published, Static
2279 // Description: Returns the indicated QualityLevel converted to a
2280 // string word.
2281 ////////////////////////////////////////////////////////////////////
2282 string Texture::
2283 format_quality_level(QualityLevel ql) {
2284  switch (ql) {
2285  case QL_default:
2286  return "default";
2287  case QL_fastest:
2288  return "fastest";
2289  case QL_normal:
2290  return "normal";
2291  case QL_best:
2292  return "best";
2293  }
2294 
2295  return "**invalid**";
2296 }
2297 
2298 ////////////////////////////////////////////////////////////////////
2299 // Function: Texture::string_quality_level
2300 // Access: Public
2301 // Description: Returns the QualityLevel value associated with the
2302 // given string representation.
2303 ////////////////////////////////////////////////////////////////////
2304 Texture::QualityLevel Texture::
2305 string_quality_level(const string &str) {
2306  if (cmp_nocase(str, "default") == 0) {
2307  return QL_default;
2308  } else if (cmp_nocase(str, "fastest") == 0) {
2309  return QL_fastest;
2310  } else if (cmp_nocase(str, "normal") == 0) {
2311  return QL_normal;
2312  } else if (cmp_nocase(str, "best") == 0) {
2313  return QL_best;
2314  }
2315 
2316  gobj_cat->error()
2317  << "Invalid Texture::QualityLevel value: " << str << "\n";
2318  return QL_default;
2319 }
2320 
2321 ////////////////////////////////////////////////////////////////////
2322 // Function: Texture::texture_uploaded
2323 // Access: Public
2324 // Description: This method is called by the GraphicsEngine at the
2325 // beginning of the frame *after* a texture has been
2326 // successfully uploaded to graphics memory. It is
2327 // intended as a callback so the texture can release its
2328 // RAM image, if _keep_ram_image is false.
2329 //
2330 // This is called indirectly when the GSG calls
2331 // GraphicsEngine::texture_uploaded().
2332 ////////////////////////////////////////////////////////////////////
2333 void Texture::
2335  CDLockedReader cdata(_cycler);
2336 
2337  if (!keep_texture_ram && !cdata->_keep_ram_image) {
2338  // Once we have prepared the texture, we can generally safely
2339  // remove the pixels from main RAM. The GSG is now responsible
2340  // for remembering what it looks like.
2341 
2342  CDWriter cdataw(_cycler, cdata, false);
2343  if (gobj_cat.is_debug()) {
2344  gobj_cat.debug()
2345  << "Dumping RAM for texture " << get_name() << "\n";
2346  }
2347  do_clear_ram_image(cdataw);
2348  }
2349 }
2350 
2351 ////////////////////////////////////////////////////////////////////
2352 // Function: Texture::has_cull_callback
2353 // Access: Public, Virtual
2354 // Description: Should be overridden by derived classes to return
2355 // true if cull_callback() has been defined. Otherwise,
2356 // returns false to indicate cull_callback() does not
2357 // need to be called for this node during the cull
2358 // traversal.
2359 ////////////////////////////////////////////////////////////////////
2360 bool Texture::
2362  return false;
2363 }
2364 
2365 ////////////////////////////////////////////////////////////////////
2366 // Function: Texture::cull_callback
2367 // Access: Public, Virtual
2368 // Description: If has_cull_callback() returns true, this function
2369 // will be called during the cull traversal to perform
2370 // any additional operations that should be performed at
2371 // cull time.
2372 //
2373 // This is called each time the Texture is discovered
2374 // applied to a Geom in the traversal. It should return
2375 // true if the Geom is visible, false if it should be
2376 // omitted.
2377 ////////////////////////////////////////////////////////////////////
2378 bool Texture::
2380  return true;
2381 }
2382 
2383 ////////////////////////////////////////////////////////////////////
2384 // Function: Texture::make_texture
2385 // Access: Public, Static
2386 // Description: A factory function to make a new Texture, used to
2387 // pass to the TexturePool.
2388 ////////////////////////////////////////////////////////////////////
2389 PT(Texture) Texture::
2390 make_texture() {
2391  return new Texture;
2392 }
2393 
2394 ////////////////////////////////////////////////////////////////////
2395 // Function: Texture::is_specific
2396 // Access: Public, Static
2397 // Description: Returns true if the indicated compression mode is one
2398 // of the specific compression types, false otherwise.
2399 ////////////////////////////////////////////////////////////////////
2400 bool Texture::
2401 is_specific(Texture::CompressionMode compression) {
2402  switch (compression) {
2403  case CM_default:
2404  case CM_off:
2405  case CM_on:
2406  return false;
2407 
2408  default:
2409  return true;
2410  }
2411 }
2412 
2413 ////////////////////////////////////////////////////////////////////
2414 // Function: Texture::has_alpha
2415 // Access: Public, Static
2416 // Description: Returns true if the indicated format includes alpha,
2417 // false otherwise.
2418 ////////////////////////////////////////////////////////////////////
2419 bool Texture::
2420 has_alpha(Format format) {
2421  switch (format) {
2422  case F_alpha:
2423  case F_rgba:
2424  case F_rgbm:
2425  case F_rgba4:
2426  case F_rgba5:
2427  case F_rgba8:
2428  case F_rgba12:
2429  case F_rgba16:
2430  case F_rgba32:
2431  case F_luminance_alpha:
2432  case F_luminance_alphamask:
2433  case F_srgb_alpha:
2434  case F_sluminance_alpha:
2435  return true;
2436 
2437  default:
2438  return false;
2439  }
2440 }
2441 
2442 ////////////////////////////////////////////////////////////////////
2443 // Function: Texture::has_binary_alpha
2444 // Access: Public, Static
2445 // Description: Returns true if the indicated format includes a
2446 // binary alpha only, false otherwise.
2447 ////////////////////////////////////////////////////////////////////
2448 bool Texture::
2449 has_binary_alpha(Format format) {
2450  switch (format) {
2451  case F_rgbm:
2452  return true;
2453 
2454  default:
2455  return false;
2456  }
2457 }
2458 
2459 ////////////////////////////////////////////////////////////////////
2460 // Function: Texture::is_srgb
2461 // Access: Public, Static
2462 // Description: Returns true if the indicated format is in the
2463 // sRGB color space, false otherwise.
2464 ////////////////////////////////////////////////////////////////////
2465 bool Texture::
2466 is_srgb(Format format) {
2467  switch (format) {
2468  case F_srgb:
2469  case F_srgb_alpha:
2470  case F_sluminance:
2471  case F_sluminance_alpha:
2472  return true;
2473 
2474  default:
2475  return false;
2476  }
2477 }
2478 
2479 ////////////////////////////////////////////////////////////////////
2480 // Function: Texture::adjust_size
2481 // Access: Public, Static
2482 // Description: Computes the proper size of the texture, based on the
2483 // original size, the filename, and the resizing whims
2484 // of the config file.
2485 //
2486 // x_size and y_size should be loaded with the texture
2487 // image's original size on disk. On return, they will
2488 // be loaded with the texture's in-memory target size.
2489 // The return value is true if the size has been
2490 // adjusted, or false if it is the same.
2491 ////////////////////////////////////////////////////////////////////
2492 bool Texture::
2493 adjust_size(int &x_size, int &y_size, const string &name,
2494  bool for_padding, AutoTextureScale auto_texture_scale) {
2495  bool exclude = false;
2496  int num_excludes = exclude_texture_scale.get_num_unique_values();
2497  for (int i = 0; i < num_excludes && !exclude; ++i) {
2498  GlobPattern pat(exclude_texture_scale.get_unique_value(i));
2499  if (pat.matches(name)) {
2500  exclude = true;
2501  }
2502  }
2503 
2504  int new_x_size = x_size;
2505  int new_y_size = y_size;
2506 
2507  if (!exclude) {
2508  new_x_size = (int)cfloor(new_x_size * texture_scale + 0.5);
2509  new_y_size = (int)cfloor(new_y_size * texture_scale + 0.5);
2510 
2511  // Don't auto-scale below 4 in either dimension. This causes
2512  // problems for DirectX and texture compression.
2513  new_x_size = min(max(new_x_size, (int)texture_scale_limit), x_size);
2514  new_y_size = min(max(new_y_size, (int)texture_scale_limit), y_size);
2515  }
2516 
2517  AutoTextureScale ats = auto_texture_scale;
2518  if (ats == ATS_unspecified) {
2519  ats = get_textures_power_2();
2520  }
2521  if (!for_padding && ats == ATS_pad) {
2522  // If we're not calculating the padding size--that is, we're
2523  // calculating the initial scaling size instead--then ignore
2524  // ATS_pad, and treat it the same as ATS_none.
2525  ats = ATS_none;
2526  }
2527 
2528  switch (ats) {
2529  case ATS_down:
2530  new_x_size = down_to_power_2(new_x_size);
2531  new_y_size = down_to_power_2(new_y_size);
2532  break;
2533 
2534  case ATS_up:
2535  case ATS_pad:
2536  new_x_size = up_to_power_2(new_x_size);
2537  new_y_size = up_to_power_2(new_y_size);
2538  break;
2539 
2540  case ATS_none:
2541  case ATS_unspecified:
2542  break;
2543  }
2544 
2545  ats = textures_square.get_value();
2546  if (!for_padding && ats == ATS_pad) {
2547  ats = ATS_none;
2548  }
2549  switch (ats) {
2550  case ATS_down:
2551  new_x_size = new_y_size = min(new_x_size, new_y_size);
2552  break;
2553 
2554  case ATS_up:
2555  case ATS_pad:
2556  new_x_size = new_y_size = max(new_x_size, new_y_size);
2557  break;
2558 
2559  case ATS_none:
2560  case ATS_unspecified:
2561  break;
2562  }
2563 
2564  if (!exclude) {
2565  int max_dimension = max_texture_dimension;
2566 
2567  if (max_dimension < 0) {
2569  if (gsg != (GraphicsStateGuardianBase *)NULL) {
2570  max_dimension = gsg->get_max_texture_dimension();
2571  }
2572  }
2573 
2574  if (max_dimension > 0) {
2575  new_x_size = min(new_x_size, (int)max_dimension);
2576  new_y_size = min(new_y_size, (int)max_dimension);
2577  }
2578  }
2579 
2580  if (x_size != new_x_size || y_size != new_y_size) {
2581  x_size = new_x_size;
2582  y_size = new_y_size;
2583  return true;
2584  }
2585 
2586  return false;
2587 }
2588 
2589 ////////////////////////////////////////////////////////////////////
2590 // Function: Texture::ensure_loader_type
2591 // Access: Public, Virtual
2592 // Description: May be called prior to calling read_txo() or any
2593 // bam-related Texture-creating callback, to ensure that
2594 // the proper dynamic libraries for a Texture of the
2595 // current class type, and the indicated filename, have
2596 // been already loaded.
2597 //
2598 // This is a low-level function that should not normally
2599 // need to be called directly by the user.
2600 //
2601 // Note that for best results you must first create a
2602 // Texture object of the appropriate class type for your
2603 // filename, for instance with
2604 // TexturePool::make_texture().
2605 ////////////////////////////////////////////////////////////////////
2606 void Texture::
2607 ensure_loader_type(const Filename &filename) {
2608  // For a plain Texture type, this doesn't need to do anything.
2609 }
2610 
2611 ////////////////////////////////////////////////////////////////////
2612 // Function: Texture::reconsider_dirty
2613 // Access: Protected, Virtual
2614 // Description: Called by TextureContext to give the Texture a chance
2615 // to mark itself dirty before rendering, if necessary.
2616 ////////////////////////////////////////////////////////////////////
2617 void Texture::
2618 reconsider_dirty() {
2619 }
2620 
2621 ////////////////////////////////////////////////////////////////////
2622 // Function: Texture::do_adjust_this_size
2623 // Access: Protected, Virtual
2624 // Description: Works like adjust_size, but also considers the
2625 // texture class. Movie textures, for instance, always
2626 // pad outwards, regardless of textures-power-2.
2627 ////////////////////////////////////////////////////////////////////
2628 bool Texture::
2629 do_adjust_this_size(const CData *cdata, int &x_size, int &y_size, const string &name,
2630  bool for_padding) const {
2631  return adjust_size(x_size, y_size, name, for_padding, cdata->_auto_texture_scale);
2632 }
2633 
2634 ////////////////////////////////////////////////////////////////////
2635 // Function: Texture::do_read
2636 // Access: Protected, Virtual
2637 // Description: The internal implementation of the various read()
2638 // methods.
2639 ////////////////////////////////////////////////////////////////////
2640 bool Texture::
2641 do_read(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
2642  int primary_file_num_channels, int alpha_file_channel,
2643  int z, int n, bool read_pages, bool read_mipmaps,
2644  const LoaderOptions &options, BamCacheRecord *record) {
2645  PStatTimer timer(_texture_read_pcollector);
2646 
2647  if (options.get_auto_texture_scale() != ATS_unspecified) {
2648  cdata->_auto_texture_scale = options.get_auto_texture_scale();
2649  }
2650 
2651  bool header_only = ((options.get_texture_flags() & (LoaderOptions::TF_preload | LoaderOptions::TF_preload_simple)) == 0);
2652  if (record != (BamCacheRecord *)NULL) {
2653  header_only = false;
2654  }
2655 
2656  if ((z == 0 || read_pages) && (n == 0 || read_mipmaps)) {
2657  // When we re-read the page 0 of the base image, we clear
2658  // everything and start over.
2659  do_clear_ram_image(cdata);
2660  }
2661 
2662  if (is_txo_filename(fullpath)) {
2663  if (record != (BamCacheRecord *)NULL) {
2664  record->add_dependent_file(fullpath);
2665  }
2666  return do_read_txo_file(cdata, fullpath);
2667  }
2668 
2669  if (is_dds_filename(fullpath)) {
2670  if (record != (BamCacheRecord *)NULL) {
2671  record->add_dependent_file(fullpath);
2672  }
2673  return do_read_dds_file(cdata, fullpath, header_only);
2674  }
2675 
2676  // If read_pages or read_mipmaps is specified, then z and n actually
2677  // indicate z_size and n_size, respectively--the numerical limits on
2678  // which to search for filenames.
2679  int z_size = z;
2680  int n_size = n;
2681 
2682  // Certain texture types have an implicit z_size. If z_size is
2683  // omitted, choose an appropriate default based on the texture
2684  // type.
2685  if (z_size == 0) {
2686  switch (cdata->_texture_type) {
2687  case TT_1d_texture:
2688  case TT_2d_texture:
2689  z_size = 1;
2690  break;
2691 
2692  case TT_cube_map:
2693  z_size = 6;
2694  break;
2695 
2696  default:
2697  break;
2698  }
2699  }
2700 
2701  int num_views = 0;
2702  if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
2703  // We'll be loading a multiview texture.
2704  read_pages = true;
2705  if (options.get_texture_num_views() != 0) {
2706  num_views = options.get_texture_num_views();
2707  do_set_num_views(cdata, num_views);
2708  }
2709  }
2710 
2712 
2713  if (read_pages && read_mipmaps) {
2714  // Read a sequence of pages * mipmap levels.
2715  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2716  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2717  do_set_z_size(cdata, z_size);
2718 
2719  n = 0;
2720  while (true) {
2721  // For mipmap level 0, the total number of pages might be
2722  // determined by the number of files we find. After mipmap
2723  // level 0, though, the number of pages is predetermined.
2724  if (n != 0) {
2725  z_size = do_get_expected_mipmap_z_size(cdata, n);
2726  }
2727 
2728  z = 0;
2729 
2730  Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
2731  Filename alpha_n_pattern = Filename::pattern_filename(alpha_fullpath_pattern.get_filename_index(z));
2732 
2733  if (!n_pattern.has_hash()) {
2734  gobj_cat.error()
2735  << "Filename requires two different hash sequences: " << fullpath
2736  << "\n";
2737  return false;
2738  }
2739 
2740  Filename file = n_pattern.get_filename_index(n);
2741  Filename alpha_file = alpha_n_pattern.get_filename_index(n);
2742 
2743  if ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
2744  (n_size != 0 && n < n_size)) {
2745  // Continue through the loop.
2746  } else {
2747  // We've reached the end of the mipmap sequence.
2748  break;
2749  }
2750 
2751  int num_pages = z_size * num_views;
2752  while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
2753  (num_pages != 0 && z < num_pages)) {
2754  if (!do_read_one(cdata, file, alpha_file, z, n, primary_file_num_channels,
2755  alpha_file_channel, options, header_only, record)) {
2756  return false;
2757  }
2758  ++z;
2759 
2760  n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
2761  file = n_pattern.get_filename_index(n);
2762  alpha_file = alpha_n_pattern.get_filename_index(n);
2763  }
2764 
2765  if (n == 0 && n_size == 0) {
2766  // If n_size is not specified, it gets implicitly set after we
2767  // read the base texture image (which determines the size of
2768  // the texture).
2769  n_size = do_get_expected_num_mipmap_levels(cdata);
2770  }
2771  ++n;
2772  }
2773  cdata->_fullpath = fullpath_pattern;
2774  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2775 
2776  } else if (read_pages) {
2777  // Read a sequence of cube map or 3-D texture pages.
2778  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2779  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2780  if (!fullpath_pattern.has_hash()) {
2781  gobj_cat.error()
2782  << "Filename requires a hash mark: " << fullpath
2783  << "\n";
2784  return false;
2785  }
2786 
2787  do_set_z_size(cdata, z_size);
2788  z = 0;
2789  Filename file = fullpath_pattern.get_filename_index(z);
2790  Filename alpha_file = alpha_fullpath_pattern.get_filename_index(z);
2791 
2792  int num_pages = z_size * num_views;
2793  while ((num_pages == 0 && (vfs->exists(file) || z == 0)) ||
2794  (num_pages != 0 && z < num_pages)) {
2795  if (!do_read_one(cdata, file, alpha_file, z, 0, primary_file_num_channels,
2796  alpha_file_channel, options, header_only, record)) {
2797  return false;
2798  }
2799  ++z;
2800 
2801  file = fullpath_pattern.get_filename_index(z);
2802  alpha_file = alpha_fullpath_pattern.get_filename_index(z);
2803  }
2804  cdata->_fullpath = fullpath_pattern;
2805  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2806 
2807  } else if (read_mipmaps) {
2808  // Read a sequence of mipmap levels.
2809  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
2810  Filename alpha_fullpath_pattern = Filename::pattern_filename(alpha_fullpath);
2811  if (!fullpath_pattern.has_hash()) {
2812  gobj_cat.error()
2813  << "Filename requires a hash mark: " << fullpath
2814  << "\n";
2815  return false;
2816  }
2817 
2818  n = 0;
2819  Filename file = fullpath_pattern.get_filename_index(n);
2820  Filename alpha_file = alpha_fullpath_pattern.get_filename_index(n);
2821 
2822  while ((n_size == 0 && (vfs->exists(file) || n == 0)) ||
2823  (n_size != 0 && n < n_size)) {
2824  if (!do_read_one(cdata, file, alpha_file, z, n,
2825  primary_file_num_channels, alpha_file_channel,
2826  options, header_only, record)) {
2827  return false;
2828  }
2829  ++n;
2830 
2831  if (n_size == 0 && n >= do_get_expected_num_mipmap_levels(cdata)) {
2832  // Don't try to read more than the requisite number of mipmap
2833  // levels (unless the user insisted on it for some reason).
2834  break;
2835  }
2836 
2837  file = fullpath_pattern.get_filename_index(n);
2838  alpha_file = alpha_fullpath_pattern.get_filename_index(n);
2839  }
2840  cdata->_fullpath = fullpath_pattern;
2841  cdata->_alpha_fullpath = alpha_fullpath_pattern;
2842 
2843  } else {
2844  // Just an ordinary read of one file.
2845  if (!do_read_one(cdata, fullpath, alpha_fullpath, z, n,
2846  primary_file_num_channels, alpha_file_channel,
2847  options, header_only, record)) {
2848  return false;
2849  }
2850  }
2851 
2852  cdata->_has_read_pages = read_pages;
2853  cdata->_has_read_mipmaps = read_mipmaps;
2854  cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
2855 
2856  if (header_only) {
2857  // If we were only supposed to be checking the image header
2858  // information, don't let the Texture think that it's got the
2859  // image now.
2860  do_clear_ram_image(cdata);
2861  } else {
2862  if ((options.get_texture_flags() & LoaderOptions::TF_preload) != 0) {
2863  // If we intend to keep the ram image around, consider
2864  // compressing it etc.
2865  bool generate_mipmaps = ((options.get_texture_flags() & LoaderOptions::TF_generate_mipmaps) != 0);
2866  do_consider_auto_process_ram_image(cdata, generate_mipmaps || uses_mipmaps(), true);
2867  }
2868  }
2869 
2870  return true;
2871 }
2872 
2873 ////////////////////////////////////////////////////////////////////
2874 // Function: Texture::do_read_one
2875 // Access: Protected, Virtual
2876 // Description: Called only from do_read(), this method reads a
2877 // single image file, either one page or one mipmap
2878 // level.
2879 ////////////////////////////////////////////////////////////////////
2880 bool Texture::
2881 do_read_one(CData *cdata, const Filename &fullpath, const Filename &alpha_fullpath,
2882  int z, int n, int primary_file_num_channels, int alpha_file_channel,
2883  const LoaderOptions &options, bool header_only, BamCacheRecord *record) {
2884  if (record != (BamCacheRecord *)NULL) {
2885  nassertr(!header_only, false);
2886  record->add_dependent_file(fullpath);
2887  }
2888 
2889  PNMImage image;
2890  PfmFile pfm;
2891  PNMReader *image_reader = image.make_reader(fullpath, NULL, false);
2892  if (image_reader == NULL) {
2893  gobj_cat.error()
2894  << "Texture::read() - couldn't read: " << fullpath << endl;
2895  return false;
2896  }
2897  image.copy_header_from(*image_reader);
2898 
2899  AutoTextureScale auto_texture_scale = do_get_auto_texture_scale(cdata);
2900 
2901  // If it's a floating-point image file, read it by default into a
2902  // floating-point texture.
2903  bool read_floating_point;
2904  int texture_load_type = (options.get_texture_flags() & (LoaderOptions::TF_integer | LoaderOptions::TF_float));
2905  switch (texture_load_type) {
2906  case LoaderOptions::TF_integer:
2907  read_floating_point = false;
2908  break;
2909 
2910  case LoaderOptions::TF_float:
2911  read_floating_point = true;
2912  break;
2913 
2914  default:
2915  // Neither TF_integer nor TF_float was specified; determine which
2916  // way the texture wants to be loaded.
2917  read_floating_point = (image_reader->is_floating_point());
2918  if (!alpha_fullpath.empty()) {
2919  read_floating_point = false;
2920  }
2921  }
2922 
2923  if (header_only || textures_header_only) {
2924  int x_size = image.get_x_size();
2925  int y_size = image.get_y_size();
2926  if (z == 0 && n == 0) {
2927  cdata->_orig_file_x_size = x_size;
2928  cdata->_orig_file_y_size = y_size;
2929  }
2930 
2931  if (textures_header_only) {
2932  // In this mode, we never intend to load the actual texture
2933  // image anyway, so we don't even need to make the size right.
2934  x_size = 1;
2935  y_size = 1;
2936 
2937  } else {
2938  adjust_size(x_size, y_size, fullpath.get_basename(), false, auto_texture_scale);
2939  }
2940 
2941  if (read_floating_point) {
2942  pfm.clear(x_size, y_size, image.get_num_channels());
2943  } else {
2944  image = PNMImage(x_size, y_size, image.get_num_channels(),
2945  image.get_maxval(), image.get_type(),
2946  image.get_color_space());
2947  image.fill(0.2, 0.3, 1.0);
2948  if (image.has_alpha()) {
2949  image.alpha_fill(1.0);
2950  }
2951  }
2952  delete image_reader;
2953 
2954  } else {
2955  if (z == 0 && n == 0) {
2956  int x_size = image.get_x_size();
2957  int y_size = image.get_y_size();
2958 
2959  cdata->_orig_file_x_size = x_size;
2960  cdata->_orig_file_y_size = y_size;
2961 
2962  if (adjust_size(x_size, y_size, fullpath.get_basename(), false, auto_texture_scale)) {
2963  image.set_read_size(x_size, y_size);
2964  }
2965  } else {
2966  image.set_read_size(do_get_expected_mipmap_x_size(cdata, n),
2967  do_get_expected_mipmap_y_size(cdata, n));
2968  }
2969 
2970  if (image.get_x_size() != image.get_read_x_size() ||
2971  image.get_y_size() != image.get_read_y_size()) {
2972  gobj_cat.info()
2973  << "Implicitly rescaling " << fullpath.get_basename() << " from "
2974  << image.get_x_size() << " by " << image.get_y_size() << " to "
2975  << image.get_read_x_size() << " by " << image.get_read_y_size()
2976  << "\n";
2977  }
2978 
2979  bool success;
2980  if (read_floating_point) {
2981  success = pfm.read(image_reader);
2982  } else {
2983  success = image.read(image_reader);
2984  }
2985 
2986  if (!success) {
2987  gobj_cat.error()
2988  << "Texture::read() - couldn't read: " << fullpath << endl;
2989  return false;
2990  }
2992  }
2993 
2994  PNMImage alpha_image;
2995  if (!alpha_fullpath.empty()) {
2996  PNMReader *alpha_image_reader = alpha_image.make_reader(alpha_fullpath, NULL, false);
2997  if (alpha_image_reader == NULL) {
2998  gobj_cat.error()
2999  << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
3000  return false;
3001  }
3002  alpha_image.copy_header_from(*alpha_image_reader);
3003 
3004  if (record != (BamCacheRecord *)NULL) {
3005  record->add_dependent_file(alpha_fullpath);
3006  }
3007 
3008  if (header_only || textures_header_only) {
3009  int x_size = image.get_x_size();
3010  int y_size = image.get_y_size();
3011  alpha_image = PNMImage(x_size, y_size, alpha_image.get_num_channels(),
3012  alpha_image.get_maxval(), alpha_image.get_type(),
3013  alpha_image.get_color_space());
3014  alpha_image.fill(1.0);
3015  if (alpha_image.has_alpha()) {
3016  alpha_image.alpha_fill(1.0);
3017  }
3018  delete alpha_image_reader;
3019 
3020  } else {
3021  if (image.get_x_size() != alpha_image.get_x_size() ||
3022  image.get_y_size() != alpha_image.get_y_size()) {
3023  gobj_cat.info()
3024  << "Implicitly rescaling " << alpha_fullpath.get_basename()
3025  << " from " << alpha_image.get_x_size() << " by "
3026  << alpha_image.get_y_size() << " to " << image.get_x_size()
3027  << " by " << image.get_y_size() << "\n";
3028  alpha_image.set_read_size(image.get_x_size(), image.get_y_size());
3029  }
3030 
3031  if (!alpha_image.read(alpha_image_reader)) {
3032  gobj_cat.error()
3033  << "Texture::read() - couldn't read (alpha): " << alpha_fullpath << endl;
3034  return false;
3035  }
3037  }
3038  }
3039 
3040  if (z == 0 && n == 0) {
3041  if (!has_name()) {
3042  set_name(fullpath.get_basename_wo_extension());
3043  }
3044  if (cdata->_filename.empty()) {
3045  cdata->_filename = fullpath;
3046  cdata->_alpha_filename = alpha_fullpath;
3047 
3048  // The first time we set the filename via a read() operation, we
3049  // clear keep_ram_image. The user can always set it again later
3050  // if he needs to.
3051  cdata->_keep_ram_image = false;
3052  }
3053 
3054  cdata->_fullpath = fullpath;
3055  cdata->_alpha_fullpath = alpha_fullpath;
3056  }
3057 
3058  if (!alpha_fullpath.empty()) {
3059  // The grayscale (alpha channel) image must be the same size as
3060  // the main image. This should really have been already
3061  // guaranteed by the above.
3062  if (image.get_x_size() != alpha_image.get_x_size() ||
3063  image.get_y_size() != alpha_image.get_y_size()) {
3064  gobj_cat.info()
3065  << "Automatically rescaling " << alpha_fullpath.get_basename()
3066  << " from " << alpha_image.get_x_size() << " by "
3067  << alpha_image.get_y_size() << " to " << image.get_x_size()
3068  << " by " << image.get_y_size() << "\n";
3069 
3070  PNMImage scaled(image.get_x_size(), image.get_y_size(),
3071  alpha_image.get_num_channels(),
3072  alpha_image.get_maxval(), alpha_image.get_type(),
3073  alpha_image.get_color_space());
3074  scaled.quick_filter_from(alpha_image);
3076  alpha_image = scaled;
3077  }
3078  }
3079 
3080  if (n == 0) {
3081  consider_downgrade(image, primary_file_num_channels, get_name());
3082  cdata->_primary_file_num_channels = image.get_num_channels();
3083  cdata->_alpha_file_channel = 0;
3084  }
3085 
3086  if (!alpha_fullpath.empty()) {
3087  // Make the original image a 4-component image by taking the
3088  // grayscale value from the second image.
3089  image.add_alpha();
3090 
3091  if (alpha_file_channel == 4 ||
3092  (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
3093 
3094  if (!alpha_image.has_alpha()) {
3095  gobj_cat.error()
3096  << alpha_fullpath.get_basename() << " has no channel " << alpha_file_channel << ".\n";
3097  } else {
3098  // Use the alpha channel.
3099  for (int x = 0; x < image.get_x_size(); x++) {
3100  for (int y = 0; y < image.get_y_size(); y++) {
3101  image.set_alpha(x, y, alpha_image.get_alpha(x, y));
3102  }
3103  }
3104  }
3105  cdata->_alpha_file_channel = alpha_image.get_num_channels();
3106 
3107  } else if (alpha_file_channel >= 1 && alpha_file_channel <= 3 &&
3108  alpha_image.get_num_channels() >= 3) {
3109  // Use the appropriate red, green, or blue channel.
3110  for (int x = 0; x < image.get_x_size(); x++) {
3111  for (int y = 0; y < image.get_y_size(); y++) {
3112  image.set_alpha(x, y, alpha_image.get_channel_val(x, y, alpha_file_channel - 1));
3113  }
3114  }
3115  cdata->_alpha_file_channel = alpha_file_channel;
3116 
3117  } else {
3118  // Use the grayscale channel.
3119  for (int x = 0; x < image.get_x_size(); x++) {
3120  for (int y = 0; y < image.get_y_size(); y++) {
3121  image.set_alpha(x, y, alpha_image.get_gray(x, y));
3122  }
3123  }
3124  cdata->_alpha_file_channel = 0;
3125  }
3126  }
3127 
3128  if (read_floating_point) {
3129  if (!do_load_one(cdata, pfm, fullpath.get_basename(), z, n, options)) {
3130  return false;
3131  }
3132  } else {
3133  // Now see if we want to pad the image within a larger power-of-2
3134  // image.
3135  int pad_x_size = 0;
3136  int pad_y_size = 0;
3137  if (do_get_auto_texture_scale(cdata) == ATS_pad) {
3138  int new_x_size = image.get_x_size();
3139  int new_y_size = image.get_y_size();
3140  if (do_adjust_this_size(cdata, new_x_size, new_y_size, fullpath.get_basename(), true)) {
3141  pad_x_size = new_x_size - image.get_x_size();
3142  pad_y_size = new_y_size - image.get_y_size();
3143  PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
3144  image.get_maxval(), image.get_type(),
3145  image.get_color_space());
3146  new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
3147  image.take_from(new_image);
3148  }
3149  }
3150 
3151  if (!do_load_one(cdata, image, fullpath.get_basename(), z, n, options)) {
3152  return false;
3153  }
3154 
3155  do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
3156  }
3157  return true;
3158 }
3159 
3160 ////////////////////////////////////////////////////////////////////
3161 // Function: Texture::do_load_one
3162 // Access: Protected, Virtual
3163 // Description: Internal method to load a single page or mipmap
3164 // level.
3165 ////////////////////////////////////////////////////////////////////
3166 bool Texture::
3167 do_load_one(CData *cdata, const PNMImage &pnmimage, const string &name, int z, int n,
3168  const LoaderOptions &options) {
3169  if (cdata->_ram_images.size() <= 1 && n == 0) {
3170  // A special case for mipmap level 0. When we load mipmap level
3171  // 0, unless we already have mipmap levels, it determines the
3172  // image properties like size and number of components.
3173  if (!do_reconsider_z_size(cdata, z, options)) {
3174  return false;
3175  }
3176  nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
3177 
3178  if (z == 0) {
3179  ComponentType component_type = T_unsigned_byte;
3180  xelval maxval = pnmimage.get_maxval();
3181  if (maxval > 255) {
3182  component_type = T_unsigned_short;
3183  }
3184 
3185  if (!do_reconsider_image_properties(cdata, pnmimage.get_x_size(), pnmimage.get_y_size(),
3186  pnmimage.get_num_channels(), component_type,
3187  z, options)) {
3188  return false;
3189  }
3190  }
3191 
3192  do_modify_ram_image(cdata);
3193  cdata->_loaded_from_image = true;
3194  }
3195 
3196  do_modify_ram_mipmap_image(cdata, n);
3197 
3198  // Ensure the PNMImage is an appropriate size.
3199  int x_size = do_get_expected_mipmap_x_size(cdata, n);
3200  int y_size = do_get_expected_mipmap_y_size(cdata, n);
3201  if (pnmimage.get_x_size() != x_size ||
3202  pnmimage.get_y_size() != y_size) {
3203  gobj_cat.info()
3204  << "Automatically rescaling " << name;
3205  if (n != 0) {
3206  gobj_cat.info(false)
3207  << " mipmap level " << n;
3208  }
3209  gobj_cat.info(false)
3210  << " from " << pnmimage.get_x_size() << " by "
3211  << pnmimage.get_y_size() << " to " << x_size << " by "
3212  << y_size << "\n";
3213 
3214  PNMImage scaled(x_size, y_size, pnmimage.get_num_channels(),
3215  pnmimage.get_maxval(), pnmimage.get_type(),
3216  pnmimage.get_color_space());
3217  scaled.quick_filter_from(pnmimage);
3219 
3220  convert_from_pnmimage(cdata->_ram_images[n]._image,
3221  do_get_expected_ram_mipmap_page_size(cdata, n),
3222  x_size, 0, 0, z, scaled,
3223  cdata->_num_components, cdata->_component_width);
3224  } else {
3225  // Now copy the pixel data from the PNMImage into our internal
3226  // cdata->_image component.
3227  convert_from_pnmimage(cdata->_ram_images[n]._image,
3228  do_get_expected_ram_mipmap_page_size(cdata, n),
3229  x_size, 0, 0, z, pnmimage,
3230  cdata->_num_components, cdata->_component_width);
3231  }
3233 
3234  return true;
3235 }
3236 
3237 ////////////////////////////////////////////////////////////////////
3238 // Function: Texture::do_load_one
3239 // Access: Protected, Virtual
3240 // Description: Internal method to load a single page or mipmap
3241 // level.
3242 ////////////////////////////////////////////////////////////////////
3243 bool Texture::
3244 do_load_one(CData *cdata, const PfmFile &pfm, const string &name, int z, int n,
3245  const LoaderOptions &options) {
3246  if (cdata->_ram_images.size() <= 1 && n == 0) {
3247  // A special case for mipmap level 0. When we load mipmap level
3248  // 0, unless we already have mipmap levels, it determines the
3249  // image properties like size and number of components.
3250  if (!do_reconsider_z_size(cdata, z, options)) {
3251  return false;
3252  }
3253  nassertr(z >= 0 && z < cdata->_z_size * cdata->_num_views, false);
3254 
3255  if (z == 0) {
3256  ComponentType component_type = T_float;
3257  if (!do_reconsider_image_properties(cdata, pfm.get_x_size(), pfm.get_y_size(),
3258  pfm.get_num_channels(), component_type,
3259  z, options)) {
3260  return false;
3261  }
3262  }
3263 
3264  do_modify_ram_image(cdata);
3265  cdata->_loaded_from_image = true;
3266  }
3267 
3268  do_modify_ram_mipmap_image(cdata, n);
3269 
3270  // Ensure the PfmFile is an appropriate size.
3271  int x_size = do_get_expected_mipmap_x_size(cdata, n);
3272  int y_size = do_get_expected_mipmap_y_size(cdata, n);
3273  if (pfm.get_x_size() != x_size ||
3274  pfm.get_y_size() != y_size) {
3275  gobj_cat.info()
3276  << "Automatically rescaling " << name;
3277  if (n != 0) {
3278  gobj_cat.info(false)
3279  << " mipmap level " << n;
3280  }
3281  gobj_cat.info(false)
3282  << " from " << pfm.get_x_size() << " by "
3283  << pfm.get_y_size() << " to " << x_size << " by "
3284  << y_size << "\n";
3285 
3286  PfmFile scaled(pfm);
3287  scaled.resize(x_size, y_size);
3289 
3290  convert_from_pfm(cdata->_ram_images[n]._image,
3291  do_get_expected_ram_mipmap_page_size(cdata, n), z,
3292  scaled, cdata->_num_components, cdata->_component_width);
3293  } else {
3294  // Now copy the pixel data from the PfmFile into our internal
3295  // cdata->_image component.
3296  convert_from_pfm(cdata->_ram_images[n]._image,
3297  do_get_expected_ram_mipmap_page_size(cdata, n), z,
3298  pfm, cdata->_num_components, cdata->_component_width);
3299  }
3301 
3302  return true;
3303 }
3304 
3305 ////////////////////////////////////////////////////////////////////
3306 // Function: Texture::do_load_sub_image
3307 // Access: Protected, Virtual
3308 // Description: Internal method to load an image into a section of
3309 // a texture page or mipmap level.
3310 ////////////////////////////////////////////////////////////////////
3311 bool Texture::
3312 do_load_sub_image(CData *cdata, const PNMImage &image, int x, int y, int z, int n) {
3313  nassertr(n >= 0 && (size_t)n < cdata->_ram_images.size(), false);
3314 
3315  int tex_x_size = do_get_expected_mipmap_x_size(cdata, n);
3316  int tex_y_size = do_get_expected_mipmap_y_size(cdata, n);
3317  int tex_z_size = do_get_expected_mipmap_z_size(cdata, n);
3318 
3319  nassertr(x >= 0 && x < tex_x_size, false);
3320  nassertr(y >= 0 && y < tex_y_size, false);
3321  nassertr(z >= 0 && z < tex_z_size, false);
3322 
3323  nassertr(image.get_x_size() + x <= tex_x_size, false);
3324  nassertr(image.get_y_size() + y <= tex_y_size, false);
3325 
3326  // Flip y
3327  y = cdata->_y_size - (image.get_y_size() + y);
3328 
3329  cdata->inc_image_modified();
3330  do_modify_ram_mipmap_image(cdata, n);
3331  convert_from_pnmimage(cdata->_ram_images[n]._image,
3332  do_get_expected_ram_mipmap_page_size(cdata, n),
3333  tex_x_size, x, y, z, image,
3334  cdata->_num_components, cdata->_component_width);
3335 
3336  return true;
3337 }
3338 
3339 ////////////////////////////////////////////////////////////////////
3340 // Function: Texture::do_read_txo_file
3341 // Access: Protected
3342 // Description: Called internally when read() detects a txo file.
3343 // Assumes the lock is already held.
3344 ////////////////////////////////////////////////////////////////////
3345 bool Texture::
3346 do_read_txo_file(CData *cdata, const Filename &fullpath) {
3348 
3349  Filename filename = Filename::binary_filename(fullpath);
3350  PT(VirtualFile) file = vfs->get_file(filename);
3351  if (file == (VirtualFile *)NULL) {
3352  // No such file.
3353  gobj_cat.error()
3354  << "Could not find " << fullpath << "\n";
3355  return false;
3356  }
3357 
3358  if (gobj_cat.is_debug()) {
3359  gobj_cat.debug()
3360  << "Reading texture object " << filename << "\n";
3361  }
3362 
3363  istream *in = file->open_read_file(true);
3364  bool success = do_read_txo(cdata, *in, fullpath);
3365  vfs->close_read_file(in);
3366 
3367  cdata->_fullpath = fullpath;
3368  cdata->_alpha_fullpath = Filename();
3369  cdata->_keep_ram_image = false;
3370 
3371  return success;
3372 }
3373 
3374 ////////////////////////////////////////////////////////////////////
3375 // Function: Texture::do_read_txo
3376 // Access: Protected
3377 // Description:
3378 ////////////////////////////////////////////////////////////////////
3379 bool Texture::
3380 do_read_txo(CData *cdata, istream &in, const string &filename) {
3381  PT(Texture) other = make_from_txo(in, filename);
3382  if (other == (Texture *)NULL) {
3383  return false;
3384  }
3385 
3386  CDReader cdata_other(other->_cycler);
3387  Namable::operator = (*other);
3388  do_assign(cdata, other, cdata_other);
3389 
3390  cdata->_loaded_from_image = true;
3391  cdata->_loaded_from_txo = true;
3392  cdata->_has_read_pages = false;
3393  cdata->_has_read_mipmaps = false;
3394  cdata->_num_mipmap_levels_read = 0;
3395  return true;
3396 }
3397 
3398 ////////////////////////////////////////////////////////////////////
3399 // Function: Texture::do_read_dds_file
3400 // Access: Private
3401 // Description: Called internally when read() detects a DDS file.
3402 // Assumes the lock is already held.
3403 ////////////////////////////////////////////////////////////////////
3404 bool Texture::
3405 do_read_dds_file(CData *cdata, const Filename &fullpath, bool header_only) {
3407 
3408  Filename filename = Filename::binary_filename(fullpath);
3409  PT(VirtualFile) file = vfs->get_file(filename);
3410  if (file == (VirtualFile *)NULL) {
3411  // No such file.
3412  gobj_cat.error()
3413  << "Could not find " << fullpath << "\n";
3414  return false;
3415  }
3416 
3417  if (gobj_cat.is_debug()) {
3418  gobj_cat.debug()
3419  << "Reading DDS file " << filename << "\n";
3420  }
3421 
3422  istream *in = file->open_read_file(true);
3423  bool success = do_read_dds(cdata, *in, fullpath, header_only);
3424  vfs->close_read_file(in);
3425 
3426  if (!has_name()) {
3427  set_name(fullpath.get_basename_wo_extension());
3428  }
3429 
3430  cdata->_fullpath = fullpath;
3431  cdata->_alpha_fullpath = Filename();
3432  cdata->_keep_ram_image = false;
3433 
3434  return success;
3435 }
3436 
3437 ////////////////////////////////////////////////////////////////////
3438 // Function: Texture::do_read_dds
3439 // Access: Protected
3440 // Description:
3441 ////////////////////////////////////////////////////////////////////
3442 bool Texture::
3443 do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) {
3444  StreamReader dds(in);
3445 
3446  // DDS header (19 words)
3447  DDSHeader header;
3448  header.dds_magic = dds.get_uint32();
3449  header.dds_size = dds.get_uint32();
3450  header.dds_flags = dds.get_uint32();
3451  header.height = dds.get_uint32();
3452  header.width = dds.get_uint32();
3453  header.pitch = dds.get_uint32();
3454  header.depth = dds.get_uint32();
3455  header.num_levels = dds.get_uint32();
3456  dds.skip_bytes(44);
3457 
3458  // Pixelformat (8 words)
3459  header.pf.pf_size = dds.get_uint32();
3460  header.pf.pf_flags = dds.get_uint32();
3461  header.pf.four_cc = dds.get_uint32();
3462  header.pf.rgb_bitcount = dds.get_uint32();
3463  header.pf.r_mask = dds.get_uint32();
3464  header.pf.g_mask = dds.get_uint32();
3465  header.pf.b_mask = dds.get_uint32();
3466  header.pf.a_mask = dds.get_uint32();
3467 
3468  // Caps (4 words)
3469  header.caps.caps1 = dds.get_uint32();
3470  header.caps.caps2 = dds.get_uint32();
3471  header.caps.ddsx = dds.get_uint32();
3472  dds.skip_bytes(4);
3473 
3474  // Pad out to 32 words
3475  dds.skip_bytes(4);
3476 
3477  if (header.dds_magic != DDS_MAGIC || (in.fail() || in.eof())) {
3478  gobj_cat.error()
3479  << filename << " is not a DDS file.\n";
3480  return false;
3481  }
3482 
3483  if ((header.dds_flags & DDSD_MIPMAPCOUNT) == 0) {
3484  // No bit set means only the base mipmap level.
3485  header.num_levels = 1;
3486 
3487  } else if (header.num_levels == 0) {
3488  // Some files seem to have this set to 0 for some reason--existing
3489  // readers assume 0 means 1.
3490  header.num_levels = 1;
3491  }
3492 
3493  TextureType texture_type;
3494  if (header.caps.caps2 & DDSCAPS2_CUBEMAP) {
3495  static const unsigned int all_faces =
3496  (DDSCAPS2_CUBEMAP_POSITIVEX |
3497  DDSCAPS2_CUBEMAP_POSITIVEY |
3498  DDSCAPS2_CUBEMAP_POSITIVEZ |
3499  DDSCAPS2_CUBEMAP_NEGATIVEX |
3500  DDSCAPS2_CUBEMAP_NEGATIVEY |
3501  DDSCAPS2_CUBEMAP_NEGATIVEZ);
3502  if ((header.caps.caps2 & all_faces) != all_faces) {
3503  gobj_cat.error()
3504  << filename << " is missing some cube map faces; cannot load.\n";
3505  return false;
3506  }
3507  header.depth = 6;
3508  texture_type = TT_cube_map;
3509 
3510  } else if (header.caps.caps2 & DDSCAPS2_VOLUME) {
3511  texture_type = TT_3d_texture;
3512 
3513  } else {
3514  texture_type = TT_2d_texture;
3515  header.depth = 1;
3516  }
3517 
3518  // Determine the function to use to read the DDS image.
3519  typedef PTA_uchar (*ReadDDSLevelFunc)(Texture *tex, Texture::CData *cdata,
3520  const DDSHeader &header, int n, istream &in);
3521  ReadDDSLevelFunc func = NULL;
3522 
3523  Format format = F_rgb;
3524 
3525  do_clear_ram_image(cdata);
3526  CompressionMode compression = CM_off;
3527 
3528  if (header.pf.pf_flags & DDPF_FOURCC) {
3529  // Some compressed texture format.
3530  if (texture_type == TT_3d_texture) {
3531  gobj_cat.error()
3532  << filename << ": unsupported compression on 3-d texture.\n";
3533  return false;
3534  }
3535 
3536  if (header.pf.four_cc == 0x31545844) { // 'DXT1', little-endian.
3537  compression = CM_dxt1;
3538  func = read_dds_level_dxt1;
3539  } else if (header.pf.four_cc == 0x32545844) { // 'DXT2'
3540  compression = CM_dxt2;
3541  func = read_dds_level_dxt23;
3542  } else if (header.pf.four_cc == 0x33545844) { // 'DXT3'
3543  compression = CM_dxt3;
3544  func = read_dds_level_dxt23;
3545  } else if (header.pf.four_cc == 0x34545844) { // 'DXT4'
3546  compression = CM_dxt4;
3547  func = read_dds_level_dxt45;
3548  } else if (header.pf.four_cc == 0x35545844) { // 'DXT5'
3549  compression = CM_dxt5;
3550  func = read_dds_level_dxt45;
3551  } else {
3552  gobj_cat.error()
3553  << filename << ": unsupported texture compression.\n";
3554  return false;
3555  }
3556 
3557  // All of the compressed formats support alpha, even DXT1 (to some
3558  // extent, at least).
3559  format = F_rgba;
3560 
3561  } else {
3562  // An uncompressed texture format.
3563  func = read_dds_level_generic_uncompressed;
3564 
3565  if (header.pf.pf_flags & DDPF_ALPHAPIXELS) {
3566  // An uncompressed format that involves alpha.
3567  format = F_rgba;
3568  if (header.pf.rgb_bitcount == 32 &&
3569  header.pf.r_mask == 0x000000ff &&
3570  header.pf.g_mask == 0x0000ff00 &&
3571  header.pf.b_mask == 0x00ff0000 &&
3572  header.pf.a_mask == 0xff000000U) {
3573  func = read_dds_level_abgr8;
3574  } else if (header.pf.rgb_bitcount == 32 &&
3575  header.pf.r_mask == 0x00ff0000 &&
3576  header.pf.g_mask == 0x0000ff00 &&
3577  header.pf.b_mask == 0x000000ff &&
3578  header.pf.a_mask == 0xff000000U) {
3579  func = read_dds_level_rgba8;
3580 
3581  } else if (header.pf.r_mask != 0 &&
3582  header.pf.g_mask == 0 &&
3583  header.pf.b_mask == 0) {
3584  func = read_dds_level_luminance_uncompressed;
3585  format = F_luminance_alpha;
3586  }
3587  } else {
3588  // An uncompressed format that doesn't involve alpha.
3589  if (header.pf.rgb_bitcount == 24 &&
3590  header.pf.r_mask == 0x00ff0000 &&
3591  header.pf.g_mask == 0x0000ff00 &&
3592  header.pf.b_mask == 0x000000ff) {
3593  func = read_dds_level_bgr8;
3594  } else if (header.pf.rgb_bitcount == 24 &&
3595  header.pf.r_mask == 0x000000ff &&
3596  header.pf.g_mask == 0x0000ff00 &&
3597  header.pf.b_mask == 0x00ff0000) {
3598  func = read_dds_level_rgb8;
3599 
3600  } else if (header.pf.r_mask != 0 &&
3601  header.pf.g_mask == 0 &&
3602  header.pf.b_mask == 0) {
3603  func = read_dds_level_luminance_uncompressed;
3604  format = F_luminance;
3605  }
3606  }
3607  }
3608 
3609  do_setup_texture(cdata, texture_type, header.width, header.height, header.depth,
3610  T_unsigned_byte, format);
3611 
3612  cdata->_orig_file_x_size = cdata->_x_size;
3613  cdata->_orig_file_y_size = cdata->_y_size;
3614  cdata->_compression = compression;
3615  cdata->_ram_image_compression = compression;
3616 
3617  if (!header_only) {
3618  switch (texture_type) {
3619  case TT_3d_texture:
3620  {
3621  // 3-d textures store all the depth slices for mipmap level 0,
3622  // then all the depth slices for mipmap level 1, and so on.
3623  for (int n = 0; n < (int)header.num_levels; ++n) {
3624  int z_size = do_get_expected_mipmap_z_size(cdata, n);
3625  pvector<PTA_uchar> pages;
3626  size_t page_size = 0;
3627  int z;
3628  for (z = 0; z < z_size; ++z) {
3629  PTA_uchar page = func(this, cdata, header, n, in);
3630  if (page.is_null()) {
3631  return false;
3632  }
3633  nassertr(page_size == 0 || page_size == page.size(), false);
3634  page_size = page.size();
3635  pages.push_back(page);
3636  }
3637  // Now reassemble the pages into one big image. Because
3638  // this is a Microsoft format, the images are stacked in
3639  // reverse order; re-reverse them.
3640  PTA_uchar image = PTA_uchar::empty_array(page_size * z_size);
3641  unsigned char *imagep = (unsigned char *)image.p();
3642  for (z = 0; z < z_size; ++z) {
3643  int fz = z_size - 1 - z;
3644  memcpy(imagep + z * page_size, pages[fz].p(), page_size);
3645  }
3646 
3647  do_set_ram_mipmap_image(cdata, n, image, page_size);
3648  }
3649  }
3650  break;
3651 
3652  case TT_cube_map:
3653  {
3654  // Cube maps store all the mipmap levels for face 0, then all
3655  // the mipmap levels for face 1, and so on.
3657  pages.reserve(6);
3658  int z, n;
3659  for (z = 0; z < 6; ++z) {
3660  pages.push_back(pvector<PTA_uchar>());
3661  pvector<PTA_uchar> &levels = pages.back();
3662  levels.reserve(header.num_levels);
3663 
3664  for (n = 0; n < (int)header.num_levels; ++n) {
3665  PTA_uchar image = func(this, cdata, header, n, in);
3666  if (image.is_null()) {
3667  return false;
3668  }
3669  levels.push_back(image);
3670  }
3671  }
3672 
3673  // Now, for each level, reassemble the pages into one big
3674  // image. Because this is a Microsoft format, the levels are
3675  // arranged in a rotated order.
3676  static const int level_remap[6] = {
3677  0, 1, 5, 4, 2, 3
3678  };
3679  for (n = 0; n < (int)header.num_levels; ++n) {
3680  size_t page_size = pages[0][n].size();
3681  PTA_uchar image = PTA_uchar::empty_array(page_size * 6);
3682  unsigned char *imagep = (unsigned char *)image.p();
3683  for (z = 0; z < 6; ++z) {
3684  int fz = level_remap[z];
3685  nassertr(pages[fz][n].size() == page_size, false);
3686  memcpy(imagep + z * page_size, pages[fz][n].p(), page_size);
3687  }
3688 
3689  do_set_ram_mipmap_image(cdata, n, image, page_size);
3690  }
3691  }
3692  break;
3693 
3694  default:
3695  // Normal 2-d textures simply store the mipmap levels.
3696  {
3697  for (int n = 0; n < (int)header.num_levels; ++n) {
3698  PTA_uchar image = func(this, cdata, header, n, in);
3699  if (image.is_null()) {
3700  return false;
3701  }
3702  do_set_ram_mipmap_image(cdata, n, image, 0);
3703  }
3704  }
3705  }
3706  cdata->_has_read_pages = true;
3707  cdata->_has_read_mipmaps = true;
3708  cdata->_num_mipmap_levels_read = cdata->_ram_images.size();
3709  }
3710 
3711  if (in.fail() || in.eof()) {
3712  gobj_cat.error()
3713  << filename << ": truncated DDS file.\n";
3714  return false;
3715  }
3716 
3717  cdata->_loaded_from_image = true;
3718  cdata->_loaded_from_txo = true;
3719 
3720  return true;
3721 }
3722 
3723 ////////////////////////////////////////////////////////////////////
3724 // Function: Texture::do_write
3725 // Access: Protected
3726 // Description: Internal method to write a series of pages and/or
3727 // mipmap levels to disk files.
3728 ////////////////////////////////////////////////////////////////////
3729 bool Texture::
3730 do_write(CData *cdata,
3731  const Filename &fullpath, int z, int n, bool write_pages, bool write_mipmaps) {
3732  if (is_txo_filename(fullpath)) {
3733  if (!do_has_bam_rawdata(cdata)) {
3734  do_get_bam_rawdata(cdata);
3735  }
3736  nassertr(do_has_bam_rawdata(cdata), false);
3737  return do_write_txo_file(cdata, fullpath);
3738  }
3739 
3740  if (!do_has_uncompressed_ram_image(cdata)) {
3741  do_get_uncompressed_ram_image(cdata);
3742  }
3743 
3744  nassertr(do_has_ram_mipmap_image(cdata, n), false);
3745  nassertr(cdata->_ram_image_compression == CM_off, false);
3746 
3747  if (write_pages && write_mipmaps) {
3748  // Write a sequence of pages * mipmap levels.
3749  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
3750  int num_levels = cdata->_ram_images.size();
3751 
3752  for (int n = 0; n < num_levels; ++n) {
3753  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
3754 
3755  for (z = 0; z < num_pages; ++z) {
3756  Filename n_pattern = Filename::pattern_filename(fullpath_pattern.get_filename_index(z));
3757 
3758  if (!n_pattern.has_hash()) {
3759  gobj_cat.error()
3760  << "Filename requires two different hash sequences: " << fullpath
3761  << "\n";
3762  return false;
3763  }
3764 
3765  if (!do_write_one(cdata, n_pattern.get_filename_index(n), z, n)) {
3766  return false;
3767  }
3768  }
3769  }
3770 
3771  } else if (write_pages) {
3772  // Write a sequence of pages.
3773  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
3774  if (!fullpath_pattern.has_hash()) {
3775  gobj_cat.error()
3776  << "Filename requires a hash mark: " << fullpath
3777  << "\n";
3778  return false;
3779  }
3780 
3781  int num_pages = cdata->_z_size * cdata->_num_views;
3782  for (z = 0; z < num_pages; ++z) {
3783  if (!do_write_one(cdata, fullpath_pattern.get_filename_index(z), z, n)) {
3784  return false;
3785  }
3786  }
3787 
3788  } else if (write_mipmaps) {
3789  // Write a sequence of mipmap images.
3790  Filename fullpath_pattern = Filename::pattern_filename(fullpath);
3791  if (!fullpath_pattern.has_hash()) {
3792  gobj_cat.error()
3793  << "Filename requires a hash mark: " << fullpath
3794  << "\n";
3795  return false;
3796  }
3797 
3798  int num_levels = cdata->_ram_images.size();
3799  for (int n = 0; n < num_levels; ++n) {
3800  if (!do_write_one(cdata, fullpath_pattern.get_filename_index(n), z, n)) {
3801  return false;
3802  }
3803  }
3804 
3805  } else {
3806  // Write a single file.
3807  if (!do_write_one(cdata, fullpath, z, n)) {
3808  return false;
3809  }
3810  }
3811 
3812  return true;
3813 }
3814 
3815 ////////////////////////////////////////////////////////////////////
3816 // Function: Texture::do_write_one
3817 // Access: Protected
3818 // Description: Internal method to write the indicated page and
3819 // mipmap level to a disk image file.
3820 ////////////////////////////////////////////////////////////////////
3821 bool Texture::
3822 do_write_one(CData *cdata, const Filename &fullpath, int z, int n) {
3823  if (!do_has_ram_mipmap_image(cdata, n)) {
3824  return false;
3825  }
3826 
3827  nassertr(cdata->_ram_image_compression == CM_off, false);
3828 
3829  bool success;
3830  if (cdata->_component_type == T_float) {
3831  // Writing a floating-point texture.
3832  PfmFile pfm;
3833  if (!do_store_one(cdata, pfm, z, n)) {
3834  return false;
3835  }
3836  success = pfm.write(fullpath);
3837  } else {
3838  // Writing a normal, integer texture.
3839  PNMImage pnmimage;
3840  if (!do_store_one(cdata, pnmimage, z, n)) {
3841  return false;
3842  }
3843  success = pnmimage.write(fullpath);
3844  }
3845 
3846  if (!success) {
3847  gobj_cat.error()
3848  << "Texture::write() - couldn't write: " << fullpath << endl;
3849  return false;
3850  }
3851 
3852  return true;
3853 }
3854 
3855 ////////////////////////////////////////////////////////////////////
3856 // Function: Texture::do_store_one
3857 // Access: Protected
3858 // Description: Internal method to copy a page and/or mipmap level to
3859 // a PNMImage.
3860 ////////////////////////////////////////////////////////////////////
3861 bool Texture::
3862 do_store_one(CData *cdata, PNMImage &pnmimage, int z, int n) {
3863  // First, reload the ram image if necessary.
3864  do_get_uncompressed_ram_image(cdata);
3865 
3866  if (!do_has_ram_mipmap_image(cdata, n)) {
3867  return false;
3868  }
3869 
3870  nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
3871  nassertr(cdata->_ram_image_compression == CM_off, false);
3872 
3873  if (cdata->_component_type == T_float) {
3874  // PNMImage by way of PfmFile.
3875  PfmFile pfm;
3876  bool success = convert_to_pfm(pfm,
3877  do_get_expected_mipmap_x_size(cdata, n),
3878  do_get_expected_mipmap_y_size(cdata, n),
3879  cdata->_num_components, cdata->_component_width,
3880  cdata->_ram_images[n]._image,
3881  do_get_ram_mipmap_page_size(cdata, n), z);
3882  if (!success) {
3883  return false;
3884  }
3885  return pfm.store(pnmimage);
3886  }
3887 
3888  return convert_to_pnmimage(pnmimage,
3889  do_get_expected_mipmap_x_size(cdata, n),
3890  do_get_expected_mipmap_y_size(cdata, n),
3891  cdata->_num_components, cdata->_component_width,
3892  cdata->_ram_images[n]._image,
3893  do_get_ram_mipmap_page_size(cdata, n), z);
3894 }
3895 
3896 ////////////////////////////////////////////////////////////////////
3897 // Function: Texture::do_store_one
3898 // Access: Protected
3899 // Description: Internal method to copy a page and/or mipmap level to
3900 // a PfmFile.
3901 ////////////////////////////////////////////////////////////////////
3902 bool Texture::
3903 do_store_one(CData *cdata, PfmFile &pfm, int z, int n) {
3904  // First, reload the ram image if necessary.
3905  do_get_uncompressed_ram_image(cdata);
3906 
3907  if (!do_has_ram_mipmap_image(cdata, n)) {
3908  return false;
3909  }
3910 
3911  nassertr(z >= 0 && z < do_get_expected_mipmap_num_pages(cdata, n), false);
3912  nassertr(cdata->_ram_image_compression == CM_off, false);
3913 
3914  if (cdata->_component_type != T_float) {
3915  // PfmFile by way of PNMImage.
3916  PNMImage pnmimage;
3917  bool success = convert_to_pnmimage(pnmimage,
3918  do_get_expected_mipmap_x_size(cdata, n),
3919  do_get_expected_mipmap_y_size(cdata, n),
3920  cdata->_num_components, cdata->_component_width,
3921  cdata->_ram_images[n]._image,
3922  do_get_ram_mipmap_page_size(cdata, n), z);
3923  if (!success) {
3924  return false;
3925  }
3926  return pfm.load(pnmimage);
3927  }
3928 
3929  return convert_to_pfm(pfm,
3930  do_get_expected_mipmap_x_size(cdata, n),
3931  do_get_expected_mipmap_y_size(cdata, n),
3932  cdata->_num_components, cdata->_component_width,
3933  cdata->_ram_images[n]._image,
3934  do_get_ram_mipmap_page_size(cdata, n), z);
3935 }
3936 
3937 ////////////////////////////////////////////////////////////////////
3938 // Function: Texture::do_write_txo_file
3939 // Access: Private
3940 // Description: Called internally when write() detects a txo
3941 // filename.
3942 ////////////////////////////////////////////////////////////////////
3943 bool Texture::
3944 do_write_txo_file(const CData *cdata, const Filename &fullpath) const {
3946  Filename filename = Filename::binary_filename(fullpath);
3947  ostream *out = vfs->open_write_file(filename, true, true);
3948  if (out == NULL) {
3949  gobj_cat.error()
3950  << "Unable to open " << filename << "\n";
3951  return false;
3952  }
3953 
3954  bool success = do_write_txo(cdata, *out, fullpath);
3955  vfs->close_write_file(out);
3956  return success;
3957 }
3958 
3959 ////////////////////////////////////////////////////////////////////
3960 // Function: Texture::do_write_txo
3961 // Access: Protected
3962 // Description:
3963 ////////////////////////////////////////////////////////////////////
3964 bool Texture::
3965 do_write_txo(const CData *cdata, ostream &out, const string &filename) const {
3966  DatagramOutputFile dout;
3967 
3968  if (!dout.open(out, filename)) {
3969  gobj_cat.error()
3970  << "Could not write texture object: " << filename << "\n";
3971  return false;
3972  }
3973 
3974  if (!dout.write_header(_bam_header)) {
3975  gobj_cat.error()
3976  << "Unable to write to " << filename << "\n";
3977  return false;
3978  }
3979 
3980  BamWriter writer(&dout);
3981  if (!writer.init()) {
3982  return false;
3983  }
3984 
3985  writer.set_file_texture_mode(BamWriter::BTM_rawdata);
3986 
3987  if (!writer.write_object(this)) {
3988  return false;
3989  }
3990 
3991  if (!do_has_bam_rawdata(cdata)) {
3992  gobj_cat.error()
3993  << get_name() << " does not have ram image\n";
3994  return false;
3995  }
3996 
3997  return true;
3998 }
3999 
4000 ////////////////////////////////////////////////////////////////////
4001 // Function: Texture::unlocked_ensure_ram_image
4002 // Access: Protected, Virtual
4003 // Description: If the texture has a ram image already, this acquires
4004 // the CData write lock and returns it.
4005 //
4006 // If the texture lacks a ram image, this performs
4007 // do_reload_ram_image(), but without holding the lock
4008 // on this particular Texture object, to avoid holding
4009 // the lock across what might be a slow operation.
4010 // Instead, the reload is performed in a copy of the
4011 // texture object, and then the lock is acquired and the
4012 // data is copied in.
4013 //
4014 // In any case, the return value is a locked CData
4015 // object, which must be released with an explicit call
4016 // to release_write(). The CData object will have a ram
4017 // image unless for some reason do_reload_ram_image()
4018 // fails.
4019 ////////////////////////////////////////////////////////////////////
4020 Texture::CData *Texture::
4021 unlocked_ensure_ram_image(bool allow_compression) {
4022  Thread *current_thread = Thread::get_current_thread();
4023 
4024  // First, wait for any other threads that might be simultaneously
4025  // performing the same operation.
4026  MutexHolder holder(_lock);
4027  while (_reloading) {
4028  _cvar.wait();
4029  }
4030 
4031  // Then make sure we still need to reload before continuing.
4032  const CData *cdata = _cycler.read(current_thread);
4033  bool has_ram_image = do_has_ram_image(cdata);
4034  if (has_ram_image && !allow_compression && cdata->_ram_image_compression != Texture::CM_off) {
4035  // If we don't want compression, but the ram image we have is
4036  // pre-compressed, we don't consider it.
4037  has_ram_image = false;
4038  }
4039  if (has_ram_image || !do_can_reload(cdata)) {
4040  // We don't need to reload after all, or maybe we can't reload
4041  // anyway. Return, but elevate the lock first, as we promised.
4042  return _cycler.elevate_read_upstream(cdata, false, current_thread);
4043  }
4044 
4045  // We need to reload.
4046  nassertr(!_reloading, NULL);
4047  _reloading = true;
4048 
4049  PT(Texture) tex = do_make_copy(cdata);
4050  _cycler.release_read(cdata);
4051  _lock.release();
4052 
4053  // Perform the actual reload in a copy of the texture, while our
4054  // own mutex is left unlocked.
4055  CDWriter cdata_tex(tex->_cycler, true);
4056  tex->do_reload_ram_image(cdata_tex, allow_compression);
4057 
4058  _lock.acquire();
4059 
4060  CData *cdataw = _cycler.write_upstream(false, current_thread);
4061 
4062  // Rather than calling do_assign(), which would copy *all* of the
4063  // reloaded texture's properties over, we only copy in the ones
4064  // which are relevant to the ram image. This way, if the
4065  // properties have changed during the reload (for instance,
4066  // because we reloaded a txo), it won't contaminate the original
4067  // texture.
4068  cdataw->_orig_file_x_size = cdata_tex->_orig_file_x_size;
4069  cdataw->_orig_file_y_size = cdata_tex->_orig_file_y_size;
4070 
4071  // If any of *these* properties have changed, the texture has
4072  // changed in some fundamental way. Update it appropriately.
4073  if (cdata_tex->_x_size != cdataw->_x_size ||
4074  cdata_tex->_y_size != cdataw->_y_size ||
4075  cdata_tex->_z_size != cdataw->_z_size ||
4076  cdata_tex->_num_views != cdataw->_num_views ||
4077  cdata_tex->_num_components != cdataw->_num_components ||
4078  cdata_tex->_component_width != cdataw->_component_width ||
4079  cdata_tex->_texture_type != cdataw->_texture_type ||
4080  cdata_tex->_component_type != cdataw->_component_type) {
4081 
4082  cdataw->_x_size = cdata_tex->_x_size;
4083  cdataw->_y_size = cdata_tex->_y_size;
4084  cdataw->_z_size = cdata_tex->_z_size;
4085  cdataw->_num_views = cdata_tex->_num_views;
4086 
4087  cdataw->_num_components = cdata_tex->_num_components;
4088  cdataw->_component_width = cdata_tex->_component_width;
4089  cdataw->_texture_type = cdata_tex->_texture_type;
4090  cdataw->_format = cdata_tex->_format;
4091  cdataw->_component_type = cdata_tex->_component_type;
4092 
4093  cdataw->inc_properties_modified();
4094  cdataw->inc_image_modified();
4095  }
4096 
4097  cdataw->_keep_ram_image = cdata_tex->_keep_ram_image;
4098  cdataw->_ram_image_compression = cdata_tex->_ram_image_compression;
4099  cdataw->_ram_images = cdata_tex->_ram_images;
4100 
4101  nassertr(_reloading, NULL);
4102  _reloading = false;
4103 
4104  // We don't generally increment the cdata->_image_modified semaphore,
4105  // because this is just a reload, and presumably the image hasn't
4106  // changed (unless we hit the if condition above).
4107 
4108  _cvar.notify_all();
4109 
4110  // Return the still-locked cdata.
4111  return cdataw;
4112 }
4113 
4114 ////////////////////////////////////////////////////////////////////
4115 // Function: Texture::do_reload_ram_image
4116 // Access: Protected, Virtual
4117 // Description: Called when the Texture image is required but the ram
4118 // image is not available, this will reload it from disk
4119 // or otherwise do whatever is required to make it
4120 // available, if possible.
4121 //
4122 // Assumes the lock is already held. The lock will be
4123 // held during the duration of this operation.
4124 ////////////////////////////////////////////////////////////////////
4125 void Texture::
4126 do_reload_ram_image(CData *cdata, bool allow_compression) {
4128  PT(BamCacheRecord) record;
4129 
4130  if (!do_has_compression(cdata)) {
4131  allow_compression = false;
4132  }
4133 
4134  if ((cache->get_cache_textures() || (allow_compression && cache->get_cache_compressed_textures())) && !textures_header_only) {
4135  // See if the texture can be found in the on-disk cache, if it is
4136  // active.
4137 
4138  record = cache->lookup(cdata->_fullpath, "txo");
4139  if (record != (BamCacheRecord *)NULL &&
4140  record->has_data()) {
4141  PT(Texture) tex = DCAST(Texture, record->get_data());
4142 
4143  // But don't use the cache record if the config parameters have
4144  // changed, and we want a different-sized texture now.
4145  int x_size = cdata->_orig_file_x_size;
4146  int y_size = cdata->_orig_file_y_size;
4147  do_adjust_this_size(cdata, x_size, y_size, cdata->_filename.get_basename(), true);
4148  if (x_size != tex->get_x_size() || y_size != tex->get_y_size()) {
4149  if (gobj_cat.is_debug()) {
4150  gobj_cat.debug()
4151  << "Cached texture " << *this << " has size "
4152  << tex->get_x_size() << " x " << tex->get_y_size()
4153  << " instead of " << x_size << " x " << y_size
4154  << "; ignoring cache.\n";
4155  }
4156  } else {
4157  // Also don't keep the cached version if it's compressed but
4158  // we want uncompressed.
4159  if (!allow_compression && tex->get_ram_image_compression() != Texture::CM_off) {
4160  if (gobj_cat.is_debug()) {
4161  gobj_cat.debug()
4162  << "Cached texture " << *this
4163  << " is compressed in cache; ignoring cache.\n";
4164  }
4165  } else {
4166  gobj_cat.info()
4167  << "Texture " << get_name() << " reloaded from disk cache\n";
4168  // We don't want to replace all the texture parameters--for
4169  // instance, we don't want to change the filter type or the
4170  // border color or anything--we just want to get the image and
4171  // necessary associated parameters.
4172  CDReader cdata_tex(tex->_cycler);
4173  cdata->_x_size = cdata_tex->_x_size;
4174  cdata->_y_size = cdata_tex->_y_size;
4175  if (cdata->_num_components != cdata_tex->_num_components) {
4176  cdata->_num_components = cdata_tex->_num_components;
4177  cdata->_format = cdata_tex->_format;
4178  }
4179  cdata->_component_type = cdata_tex->_component_type;
4180  cdata->_compression = cdata_tex->_compression;
4181  cdata->_ram_image_compression = cdata_tex->_ram_image_compression;
4182  cdata->_ram_images = cdata_tex->_ram_images;
4183  cdata->_loaded_from_image = true;
4184 
4185  bool was_compressed = (cdata->_ram_image_compression != CM_off);
4186  if (do_consider_auto_process_ram_image(cdata, uses_mipmaps(), allow_compression)) {
4187  bool is_compressed = (cdata->_ram_image_compression != CM_off);
4188  if (!was_compressed && is_compressed &&
4189  cache->get_cache_compressed_textures()) {
4190  // We've re-compressed the image after loading it from the
4191  // cache. To keep the cache current, rewrite it to the
4192  // cache now, in its newly compressed form.
4193  record->set_data(this, this);
4194  cache->store(record);
4195  }
4196  }
4197 
4198  return;
4199  }
4200  }
4201  }
4202  }
4203 
4204  gobj_cat.info()
4205  << "Reloading texture " << get_name() << "\n";
4206 
4207  int z = 0;
4208  int n = 0;
4209 
4210  if (cdata->_has_read_pages) {
4211  z = cdata->_z_size;
4212  }
4213  if (cdata->_has_read_mipmaps) {
4214  n = cdata->_num_mipmap_levels_read;
4215  }
4216 
4217  cdata->_loaded_from_image = false;
4218  Format orig_format = cdata->_format;
4219  int orig_num_components = cdata->_num_components;
4220 
4221  LoaderOptions options;
4222  options.set_texture_flags(LoaderOptions::TF_preload);
4223  do_read(cdata, cdata->_fullpath, cdata->_alpha_fullpath,
4224  cdata->_primary_file_num_channels, cdata->_alpha_file_channel,
4225  z, n, cdata->_has_read_pages, cdata->_has_read_mipmaps, options, NULL);
4226 
4227  if (orig_num_components == cdata->_num_components) {
4228  // Restore the original format, in case it was needlessly changed
4229  // during the reload operation.
4230  cdata->_format = orig_format;
4231  }
4232 
4233  if (do_has_ram_image(cdata) && record != (BamCacheRecord *)NULL) {
4234  if (cache->get_cache_textures() || (cdata->_ram_image_compression != CM_off && cache->get_cache_compressed_textures())) {
4235  // Update the cache.
4236  if (record != (BamCacheRecord *)NULL) {
4237  record->add_dependent_file(cdata->_fullpath);
4238  }
4239  record->set_data(this, this);
4240  cache->store(record);
4241  }
4242  }
4243 }
4244 
4245 ////////////////////////////////////////////////////////////////////
4246 // Function: Texture::do_modify_ram_image
4247 // Access: Protected
4248 // Description: This is called internally to uniquify the ram image
4249 // pointer without updating cdata->_image_modified.
4250 ////////////////////////////////////////////////////////////////////
4251 PTA_uchar Texture::
4252 do_modify_ram_image(CData *cdata) {
4253  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty() ||
4254  cdata->_ram_image_compression != CM_off) {
4255  do_make_ram_image(cdata);
4256  } else {
4257  do_clear_ram_mipmap_images(cdata);
4258  }
4259  return cdata->_ram_images[0]._image;
4260 }
4261 
4262 ////////////////////////////////////////////////////////////////////
4263 // Function: Texture::do_make_ram_image
4264 // Access: Protected
4265 // Description: This is called internally to make a new ram image
4266 // without updating cdata->_image_modified.
4267 ////////////////////////////////////////////////////////////////////
4268 PTA_uchar Texture::
4269 do_make_ram_image(CData *cdata) {
4270  int image_size = do_get_expected_ram_image_size(cdata);
4271  cdata->_ram_images.clear();
4272  cdata->_ram_images.push_back(RamImage());
4273  cdata->_ram_images[0]._page_size = do_get_expected_ram_page_size(cdata);
4274  cdata->_ram_images[0]._image = PTA_uchar::empty_array(image_size, get_class_type());
4275  cdata->_ram_images[0]._pointer_image = NULL;
4276  cdata->_ram_image_compression = CM_off;
4277 
4278  if (cdata->_has_clear_color) {
4279  // Fill the image with the clear color.
4280  unsigned char pixel[16];
4281  const int pixel_size = do_get_clear_data(cdata, pixel);
4282  nassertr(pixel_size > 0, cdata->_ram_images[0]._image);
4283 
4284  unsigned char *image_data = cdata->_ram_images[0]._image;
4285  for (int i = 0; i < image_size; i += pixel_size) {
4286  memcpy(image_data + i, pixel, pixel_size);
4287  }
4288  }
4289 
4290  return cdata->_ram_images[0]._image;
4291 }
4292 
4293 ////////////////////////////////////////////////////////////////////
4294 // Function: Texture::do_set_ram_image
4295 // Access: Protected
4296 // Description: Replaces the current system-RAM image with the new
4297 // data. If compression is not CM_off, it indicates
4298 // that the new data is already pre-compressed in the
4299 // indicated format.
4300 //
4301 // This does *not* affect keep_ram_image.
4302 ////////////////////////////////////////////////////////////////////
4303 void Texture::
4304 do_set_ram_image(CData *cdata, CPTA_uchar image, Texture::CompressionMode compression,
4305  size_t page_size) {
4306  nassertv(compression != CM_default);
4307  nassertv(compression != CM_off || image.size() == do_get_expected_ram_image_size(cdata));
4308  if (cdata->_ram_images.empty()) {
4309  cdata->_ram_images.push_back(RamImage());
4310  } else {
4311  do_clear_ram_mipmap_images(cdata);
4312  }
4313  if (page_size == 0) {
4314  page_size = image.size();
4315  }
4316  if (cdata->_ram_images[0]._image != image ||
4317  cdata->_ram_images[0]._page_size != page_size ||
4318  cdata->_ram_image_compression != compression) {
4319  cdata->_ram_images[0]._image = image.cast_non_const();
4320  cdata->_ram_images[0]._page_size = page_size;
4321  cdata->_ram_images[0]._pointer_image = NULL;
4322  cdata->_ram_image_compression = compression;
4323  cdata->inc_image_modified();
4324  }
4325 }
4326 
4327 ////////////////////////////////////////////////////////////////////
4328 // Function: Texture::do_modify_ram_mipmap_image
4329 // Access: Protected
4330 // Description: This is called internally to uniquify the nth mipmap
4331 // image pointer without updating cdata->_image_modified.
4332 ////////////////////////////////////////////////////////////////////
4333 PTA_uchar Texture::
4334 do_modify_ram_mipmap_image(CData *cdata, int n) {
4335  nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar());
4336 
4337  if (n >= (int)cdata->_ram_images.size() ||
4338  cdata->_ram_images[n]._image.empty()) {
4339  do_make_ram_mipmap_image(cdata, n);
4340  }
4341  return cdata->_ram_images[n]._image;
4342 }
4343 
4344 ////////////////////////////////////////////////////////////////////
4345 // Function: Texture::do_make_ram_mipmap_image
4346 // Access: Protected
4347 // Description:
4348 ////////////////////////////////////////////////////////////////////
4349 PTA_uchar Texture::
4350 do_make_ram_mipmap_image(CData *cdata, int n) {
4351  nassertr(cdata->_ram_image_compression == CM_off, PTA_uchar(get_class_type()));
4352 
4353  while (n >= (int)cdata->_ram_images.size()) {
4354  cdata->_ram_images.push_back(RamImage());
4355  }
4356 
4357  size_t image_size = do_get_expected_ram_mipmap_image_size(cdata, n);
4358  cdata->_ram_images[n]._image = PTA_uchar::empty_array(image_size, get_class_type());
4359  cdata->_ram_images[n]._pointer_image = NULL;
4360  cdata->_ram_images[n]._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
4361 
4362  if (cdata->_has_clear_color) {
4363  // Fill the image with the clear color.
4364  unsigned char pixel[16];
4365  const size_t pixel_size = (size_t)do_get_clear_data(cdata, pixel);
4366  nassertr(pixel_size > 0, cdata->_ram_images[n]._image);
4367 
4368  unsigned char *image_data = cdata->_ram_images[n]._image;
4369  for (size_t i = 0; i < image_size; i += pixel_size) {
4370  memcpy(image_data + i, pixel, pixel_size);
4371  }
4372  }
4373 
4374  return cdata->_ram_images[n]._image;
4375 }
4376 
4377 ////////////////////////////////////////////////////////////////////
4378 // Function: Texture::do_set_ram_mipmap_image
4379 // Access: Published
4380 // Description:
4381 ////////////////////////////////////////////////////////////////////
4382 void Texture::
4383 do_set_ram_mipmap_image(CData *cdata, int n, CPTA_uchar image, size_t page_size) {
4384  nassertv(cdata->_ram_image_compression != CM_off || image.size() == do_get_expected_ram_mipmap_image_size(cdata, n));
4385 
4386  while (n >= (int)cdata->_ram_images.size()) {
4387  cdata->_ram_images.push_back(RamImage());
4388  }
4389  if (page_size == 0) {
4390  page_size = image.size();
4391  }
4392 
4393  if (cdata->_ram_images[n]._image != image ||
4394  cdata->_ram_images[n]._page_size != page_size) {
4395  cdata->_ram_images[n]._image = image.cast_non_const();
4396  cdata->_ram_images[n]._pointer_image = NULL;
4397  cdata->_ram_images[n]._page_size = page_size;
4398  cdata->inc_image_modified();
4399  }
4400 }
4401 
4402 ////////////////////////////////////////////////////////////////////
4403 // Function: Texture::do_get_clear_color
4404 // Access: Published
4405 // Description: Returns a string with a single pixel representing
4406 // the clear color of the texture in the format of
4407 // this texture.
4408 //
4409 // In other words, to create an uncompressed RAM
4410 // texture filled with the clear color, it should
4411 // be initialized with this string repeated for
4412 // every pixel.
4413 ////////////////////////////////////////////////////////////////////
4414 int Texture::
4415 do_get_clear_data(const CData *cdata, unsigned char *into) const {
4416  nassertr(cdata->_has_clear_color, 0);
4417  nassertr(cdata->_num_components <= 4, 0);
4418 
4419  //TODO: encode the color into the sRGB color space if used
4420  switch (cdata->_component_type) {
4421  case T_unsigned_byte:
4422  {
4423  LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero());
4424  scaled *= 255;
4425  switch (cdata->_num_components) {
4426  case 2:
4427  into[1] = (unsigned char)scaled[1];
4428  case 1:
4429  into[0] = (unsigned char)scaled[0];
4430  break;
4431  case 4:
4432  into[3] = (unsigned char)scaled[3];
4433  case 3: // BGR <-> RGB
4434  into[0] = (unsigned char)scaled[2];
4435  into[1] = (unsigned char)scaled[1];
4436  into[2] = (unsigned char)scaled[0];
4437  break;
4438  }
4439  break;
4440  }
4441 
4442  case T_unsigned_short:
4443  {
4444  LColor scaled = cdata->_clear_color.fmin(LColor(1)).fmax(LColor::zero());
4445  scaled *= 65535;
4446  switch (cdata->_num_components) {
4447  case 2:
4448  ((unsigned short *)into)[1] = (unsigned short)scaled[1];
4449  case 1:
4450  ((unsigned short *)into)[0] = (unsigned short)scaled[0];
4451  break;
4452  case 4:
4453  ((unsigned short *)into)[3] = (unsigned short)scaled[3];
4454  case 3: // BGR <-> RGB
4455  ((unsigned short *)into)[0] = (unsigned short)scaled[2];
4456  ((unsigned short *)into)[1] = (unsigned short)scaled[1];
4457  ((unsigned short *)into)[2] = (unsigned short)scaled[0];
4458  break;
4459  }
4460  break;
4461  }
4462 
4463  case T_float:
4464  switch (cdata->_num_components) {
4465  case 2:
4466  ((float *)into)[1] = cdata->_clear_color[1];
4467  case 1:
4468  ((float *)into)[0] = cdata->_clear_color[0];
4469  break;
4470  case 4:
4471  ((float *)into)[3] = cdata->_clear_color[3];
4472  case 3: // BGR <-> RGB
4473  ((float *)into)[0] = cdata->_clear_color[2];
4474  ((float *)into)[1] = cdata->_clear_color[1];
4475  ((float *)into)[2] = cdata->_clear_color[0];
4476  break;
4477  }
4478  break;
4479 
4480  case T_unsigned_int_24_8:
4481  nassertr(cdata->_num_components == 1, 0);
4482  *((unsigned int *)into) =
4483  ((unsigned int)(cdata->_clear_color[0] * 16777215) << 8) +
4484  (unsigned int)max(min(cdata->_clear_color[1], (PN_stdfloat)255), (PN_stdfloat)0);
4485  break;
4486 
4487  case T_int:
4488  {
4489  // Note: there are no 32-bit UNORM textures. Therefore, we don't
4490  // do any normalization here, either.
4491  switch (cdata->_num_components) {
4492  case 2:
4493  ((int *)into)[1] = (int)cdata->_clear_color[1];
4494  case 1:
4495  ((int *)into)[0] = (int)cdata->_clear_color[0];
4496  break;
4497  case 4:
4498  ((int *)into)[3] = (int)cdata->_clear_color[3];
4499  case 3: // BGR <-> RGB
4500  ((int *)into)[0] = (int)cdata->_clear_color[2];
4501  ((int *)into)[1] = (int)cdata->_clear_color[1];
4502  ((int *)into)[2] = (int)cdata->_clear_color[0];
4503  break;
4504  }
4505  break;
4506  }
4507  }
4508 
4509  return cdata->_num_components * cdata->_component_width;
4510 }
4511 
4512 ////////////////////////////////////////////////////////////////////
4513 // Function: Texture::consider_auto_process_ram_image
4514 // Access: Protected
4515 // Description: Should be called after a texture has been loaded into
4516 // RAM, this considers generating mipmaps and/or
4517 // compressing the RAM image.
4518 //
4519 // Returns true if the image was modified by this
4520 // operation, false if it wasn't.
4521 ////////////////////////////////////////////////////////////////////
4522 bool Texture::
4523 consider_auto_process_ram_image(bool generate_mipmaps, bool allow_compression) {
4524  CDWriter cdata(_cycler, false);
4525  return do_consider_auto_process_ram_image(cdata, generate_mipmaps, allow_compression);
4526 }
4527 
4528 ////////////////////////////////////////////////////////////////////
4529 // Function: Texture::do_consider_auto_process_ram_image
4530 // Access: Protected
4531 // Description: Should be called after a texture has been loaded into
4532 // RAM, this considers generating mipmaps and/or
4533 // compressing the RAM image.
4534 //
4535 // Returns true if the image was modified by this
4536 // operation, false if it wasn't.
4537 ////////////////////////////////////////////////////////////////////
4538 bool Texture::
4539 do_consider_auto_process_ram_image(CData *cdata, bool generate_mipmaps,
4540  bool allow_compression) {
4541  bool modified = false;
4542 
4543  if (generate_mipmaps && !driver_generate_mipmaps &&
4544  cdata->_ram_images.size() == 1) {
4545  do_generate_ram_mipmap_images(cdata);
4546  modified = true;
4547  }
4548 
4549  if (allow_compression && !driver_compress_textures) {
4550  CompressionMode compression = cdata->_compression;
4551  if (compression == CM_default && compressed_textures) {
4552  compression = CM_on;
4553  }
4554  if (compression != CM_off && cdata->_ram_image_compression == CM_off) {
4556  if (do_compress_ram_image(cdata, compression, QL_default, gsg)) {
4557  if (gobj_cat.is_debug()) {
4558  gobj_cat.debug()
4559  << "Compressed " << get_name() << " with "
4560  << cdata->_ram_image_compression << "\n";
4561  }
4562  modified = true;
4563  }
4564  }
4565  }
4566 
4567  return modified;
4568 }
4569 
4570 ////////////////////////////////////////////////////////////////////
4571 // Function: Texture::do_compress_ram_image
4572 // Access: Protected
4573 // Description:
4574 ////////////////////////////////////////////////////////////////////
4575 bool Texture::
4576 do_compress_ram_image(CData *cdata, Texture::CompressionMode compression,
4577  Texture::QualityLevel quality_level,
4579  nassertr(compression != CM_off, false);
4580 
4581  if (compression == CM_on) {
4582  // Select an appropriate compression mode automatically.
4583  switch (cdata->_format) {
4584  case Texture::F_rgbm:
4585  case Texture::F_rgb:
4586  case Texture::F_rgb5:
4587  case Texture::F_rgba5:
4588  case Texture::F_rgb8:
4589  case Texture::F_rgb12:
4590  case Texture::F_rgb332:
4591  case Texture::F_rgb16:
4592  case Texture::F_rgb32:
4593  if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt1)) {
4594  compression = CM_dxt1;
4595  } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
4596  compression = CM_dxt3;
4597  } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
4598  compression = CM_dxt5;
4599  }
4600  break;
4601 
4602  case Texture::F_rgba4:
4603  if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt3)) {
4604  compression = CM_dxt3;
4605  } else if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
4606  compression = CM_dxt5;
4607  }
4608  break;
4609 
4610  case Texture::F_rgba:
4611  case Texture::F_rgba8:
4612  case Texture::F_rgba12:
4613  case Texture::F_rgba16:
4614  case Texture::F_rgba32:
4615  if (gsg == NULL || gsg->get_supports_compressed_texture_format(CM_dxt5)) {
4616  compression = CM_dxt5;
4617  }
4618  break;
4619 
4620  default:
4621  break;
4622  }
4623  }
4624 
4625  // Choose an appropriate quality level.
4626  if (quality_level == Texture::QL_default) {
4627  quality_level = cdata->_quality_level;
4628  }
4629  if (quality_level == Texture::QL_default) {
4630  quality_level = texture_quality_level;
4631  }
4632 
4633 #ifdef HAVE_SQUISH
4634  if (cdata->_texture_type != TT_3d_texture &&
4635  cdata->_texture_type != TT_2d_texture_array &&
4636  cdata->_component_type == T_unsigned_byte) {
4637  int squish_flags = 0;
4638  switch (compression) {
4639  case CM_dxt1:
4640  squish_flags |= squish::kDxt1;
4641  break;
4642 
4643  case CM_dxt3:
4644  squish_flags |= squish::kDxt3;
4645  break;
4646 
4647  case CM_dxt5:
4648  squish_flags |= squish::kDxt5;
4649  break;
4650 
4651  default:
4652  break;
4653  }
4654 
4655  if (squish_flags != 0) {
4656  // This compression mode is supported by squish; use it.
4657  switch (quality_level) {
4658  case QL_fastest:
4659  squish_flags |= squish::kColourRangeFit;
4660  break;
4661 
4662  case QL_normal:
4663  // ColourClusterFit is just too slow for everyday use.
4664  squish_flags |= squish::kColourRangeFit;
4665  // squish_flags |= squish::kColourClusterFit;
4666  break;
4667 
4668  case QL_best:
4669  squish_flags |= squish::kColourIterativeClusterFit;
4670  break;
4671 
4672  default:
4673  break;
4674  }
4675 
4676  if (do_squish(cdata, compression, squish_flags)) {
4677  return true;
4678  }
4679  }
4680  }
4681 #endif // HAVE_SQUISH
4682 
4683  return false;
4684 }
4685 
4686 ////////////////////////////////////////////////////////////////////
4687 // Function: Texture::do_uncompress_ram_image
4688 // Access: Protected
4689 // Description:
4690 ////////////////////////////////////////////////////////////////////
4691 bool Texture::
4692 do_uncompress_ram_image(CData *cdata) {
4693 
4694 #ifdef HAVE_SQUISH
4695  if (cdata->_texture_type != TT_3d_texture &&
4696  cdata->_texture_type != TT_2d_texture_array &&
4697  cdata->_component_type == T_unsigned_byte) {
4698  int squish_flags = 0;
4699  switch (cdata->_ram_image_compression) {
4700  case CM_dxt1:
4701  squish_flags |= squish::kDxt1;
4702  break;
4703 
4704  case CM_dxt3:
4705  squish_flags |= squish::kDxt3;
4706  break;
4707 
4708  case CM_dxt5:
4709  squish_flags |= squish::kDxt5;
4710  break;
4711 
4712  default:
4713  break;
4714  }
4715 
4716  if (squish_flags != 0) {
4717  // This compression mode is supported by squish; use it.
4718  if (do_unsquish(cdata, squish_flags)) {
4719  return true;
4720  }
4721  }
4722  }
4723 #endif // HAVE_SQUISH
4724  return false;
4725 }
4726 
4727 ////////////////////////////////////////////////////////////////////
4728 // Function: Texture::do_has_all_ram_mipmap_images
4729 // Access: Protected
4730 // Description:
4731 ////////////////////////////////////////////////////////////////////
4732 bool Texture::
4733 do_has_all_ram_mipmap_images(const CData *cdata) const {
4734  if (cdata->_ram_images.empty() || cdata->_ram_images[0]._image.empty()) {
4735  // If we don't even have a base image, the answer is no.
4736  return false;
4737  }
4738  if (!uses_mipmaps()) {
4739  // If we have a base image and don't require mipmapping, the
4740  // answer is yes.
4741  return true;
4742  }
4743 
4744  // Check that we have enough mipmap levels to meet the size
4745  // requirements.
4746  int size = max(cdata->_x_size, max(cdata->_y_size, cdata->_z_size));
4747  int n = 0;
4748  int x = 1;
4749  while (x < size) {
4750  x = (x << 1);
4751  ++n;
4752  if (n >= (int)cdata->_ram_images.size() || cdata->_ram_images[n]._image.empty()) {
4753  return false;
4754  }
4755  }
4756 
4757  return true;
4758 }
4759 
4760 ////////////////////////////////////////////////////////////////////
4761 // Function: Texture::do_reconsider_z_size
4762 // Access: Protected
4763 // Description: Considers whether the z_size (or num_views) should
4764 // automatically be adjusted when the user loads a new
4765 // page. Returns true if the z size is valid, false
4766 // otherwise.
4767 //
4768 // Assumes the lock is already held.
4769 ////////////////////////////////////////////////////////////////////
4770 bool Texture::
4771 do_reconsider_z_size(CData *cdata, int z, const LoaderOptions &options) {
4772  if (z >= cdata->_z_size * cdata->_num_views) {
4773  bool num_views_specified = true;
4774  if (options.get_texture_flags() & LoaderOptions::TF_multiview) {
4775  // This flag is false if is a multiview texture with a specified
4776  // number of views. It is true if it is not a multiview
4777  // texture, or if it is but the number of views is explicitly
4778  // specified.
4779  num_views_specified = (options.get_texture_num_views() != 0);
4780  }
4781 
4782  if (num_views_specified &&
4783  (cdata->_texture_type == Texture::TT_3d_texture ||
4784  cdata->_texture_type == Texture::TT_2d_texture_array)) {
4785  // If we're loading a page past _z_size, treat it as an implicit
4786  // request to enlarge _z_size. However, this is only legal if
4787  // this is, in fact, a 3-d texture or a 2d texture array (cube maps
4788  // always have z_size 6, and other types have z_size 1).
4789  nassertr(cdata->_num_views != 0, false);
4790  cdata->_z_size = (z / cdata->_num_views) + 1;
4791 
4792  } else if (cdata->_z_size != 0) {
4793  // In the case of a 2-d texture or cube map, or a 3-d texture
4794  // with an unspecified _num_views, assume we're loading views of
4795  // a multiview texture.
4796  cdata->_num_views = (z / cdata->_z_size) + 1;
4797 
4798  } else {
4799  // The first image loaded sets an implicit z-size.
4800  cdata->_z_size = 1;
4801  }
4802 
4803  // Increase the size of the data buffer to make room for the new
4804  // texture level.
4805  do_allocate_pages(cdata);
4806  }
4807 
4808  return true;
4809 }
4810 
4811 ////////////////////////////////////////////////////////////////////
4812 // Function: Texture::do_allocate_pages
4813 // Access: Protected, Virtual
4814 // Description: Called internally by do_reconsider_z_size() to
4815 // allocate new memory in _ram_images[0] for the new
4816 // number of pages.
4817 //
4818 // Assumes the lock is already held.
4819 ////////////////////////////////////////////////////////////////////
4820 void Texture::
4821 do_allocate_pages(CData *cdata) {
4822  size_t new_size = do_get_expected_ram_image_size(cdata);
4823  if (!cdata->_ram_images.empty() &&
4824  !cdata->_ram_images[0]._image.empty() &&
4825  new_size > cdata->_ram_images[0]._image.size()) {
4826  cdata->_ram_images[0]._image.insert(cdata->_ram_images[0]._image.end(), new_size - cdata->_ram_images[0]._image.size(), 0);
4827  nassertv(cdata->_ram_images[0]._image.size() == new_size);
4828  }
4829 }
4830 
4831 ////////////////////////////////////////////////////////////////////
4832 // Function: Texture::do_reconsider_image_properties
4833 // Access: Protected
4834 // Description: Resets the internal Texture properties when a new
4835 // image file is loaded. Returns true if the new image
4836 // is valid, false otherwise.
4837 //
4838 // Assumes the lock is already held.
4839 ////////////////////////////////////////////////////////////////////
4840 bool Texture::
4841 do_reconsider_image_properties(CData *cdata, int x_size, int y_size, int num_components,
4842  Texture::ComponentType component_type, int z,
4843  const LoaderOptions &options) {
4844  if (!cdata->_loaded_from_image || num_components != cdata->_num_components || component_type != cdata->_component_type) {
4845  // Come up with a default format based on the number of channels.
4846  // But only do this the first time the file is loaded, or if the
4847  // number of channels in the image changes on subsequent loads.
4848 
4849  //TODO: handle sRGB properly
4850  switch (num_components) {
4851  case 1:
4852  cdata->_format = F_luminance;
4853  break;
4854 
4855  case 2:
4856  cdata->_format = F_luminance_alpha;
4857  break;
4858 
4859  case 3:
4860  cdata->_format = F_rgb;
4861  break;
4862 
4863  case 4:
4864  cdata->_format = F_rgba;
4865  break;
4866 
4867  default:
4868  // Eh?
4869  nassertr(false, false);
4870  cdata->_format = F_rgb;
4871  }
4872  }
4873 
4874  if (!cdata->_loaded_from_image) {
4875  if ((options.get_texture_flags() & LoaderOptions::TF_allow_1d) &&
4876  cdata->_texture_type == TT_2d_texture && x_size != 1 && y_size == 1) {
4877  // If we're loading an Nx1 size texture, infer a 1-d texture type.
4878  cdata->_texture_type = TT_1d_texture;
4879  }
4880 
4881 #ifndef NDEBUG
4882  if (cdata->_texture_type == TT_1d_texture) {
4883  nassertr(y_size == 1, false);
4884  } else if (cdata->_texture_type == TT_cube_map) {
4885  nassertr(x_size == y_size, false);
4886  }
4887 #endif
4888  if ((cdata->_x_size != x_size)||(cdata->_y_size != y_size)) {
4889  do_set_pad_size(cdata, 0, 0, 0);
4890  }
4891  cdata->_x_size = x_size;
4892  cdata->_y_size = y_size;
4893  cdata->_num_components = num_components;
4894  do_set_component_type(cdata, component_type);
4895 
4896  } else {
4897  if (cdata->_x_size != x_size ||
4898  cdata->_y_size != y_size ||
4899  cdata->_num_components != num_components ||
4900  cdata->_component_type != component_type) {
4901  gobj_cat.error()
4902  << "Texture properties have changed for texture " << get_name()
4903  << " page " << z << ".\n";
4904  return false;
4905  }
4906  }
4907 
4908  return true;
4909 }
4910 
4911 ////////////////////////////////////////////////////////////////////
4912 // Function: Texture::do_rescale_texture
4913 // Access: Private
4914 // Description:
4915 ////////////////////////////////////////////////////////////////////
4916 bool Texture::
4917 do_rescale_texture(CData *cdata) {
4918  int new_x_size = cdata->_x_size;
4919  int new_y_size = cdata->_y_size;
4920  if (cdata->_z_size * cdata->_num_views != 1) {
4921  nassert_raise("rescale_texture() doesn't support 3-d or multiview textures.");
4922  return false;
4923  }
4924 
4925  if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), false)) {
4926  // OK, we have to scale the image.
4927  PNMImage orig_image;
4928  if (!do_store_one(cdata, orig_image, 0, 0)) {
4929  gobj_cat.warning()
4930  << "Couldn't get image in rescale_texture()\n";
4931  return false;
4932  }
4933 
4934  gobj_cat.info()
4935  << "Resizing " << get_name() << " to " << new_x_size << " x "
4936  << new_y_size << "\n";
4937  PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
4938  orig_image.get_maxval(), orig_image.get_type(),
4939  orig_image.get_color_space());
4940  new_image.quick_filter_from(orig_image);
4941 
4942  do_clear_ram_image(cdata);
4943  cdata->inc_image_modified();
4944  cdata->_x_size = new_x_size;
4945  cdata->_y_size = new_y_size;
4946  if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
4947  return false;
4948  }
4949 
4950  return true;
4951  }
4952 
4953  // Maybe we should pad the image.
4954  int pad_x_size = 0;
4955  int pad_y_size = 0;
4956  if (do_get_auto_texture_scale(cdata) == ATS_pad) {
4957  new_x_size = cdata->_x_size;
4958  new_y_size = cdata->_y_size;
4959  if (do_adjust_this_size(cdata, new_x_size, new_y_size, get_name(), true)) {
4960  pad_x_size = new_x_size - cdata->_x_size;
4961  pad_y_size = new_y_size - cdata->_y_size;
4962 
4963  PNMImage orig_image;
4964  if (!do_store_one(cdata, orig_image, 0, 0)) {
4965  gobj_cat.warning()
4966  << "Couldn't get image in rescale_texture()\n";
4967  return false;
4968  }
4969  PNMImage new_image(new_x_size, new_y_size, orig_image.get_num_channels(),
4970  orig_image.get_maxval(), orig_image.get_type(),
4971  orig_image.get_color_space());
4972  new_image.copy_sub_image(orig_image, 0, new_y_size - orig_image.get_y_size());
4973 
4974  do_clear_ram_image(cdata);
4975  cdata->_loaded_from_image = false;
4976  cdata->inc_image_modified();
4977  if (!do_load_one(cdata, new_image, get_name(), 0, 0, LoaderOptions())) {
4978  return false;
4979  }
4980 
4981  do_set_pad_size(cdata, pad_x_size, pad_y_size, 0);
4982  return true;
4983  }
4984  }
4985 
4986  // No changes needed.
4987  return false;
4988 }
4989 
4990 ////////////////////////////////////////////////////////////////////
4991 // Function: Texture::make_copy_impl
4992 // Access: Protected, Virtual
4993 // Description:
4994 ////////////////////////////////////////////////////////////////////
4995 PT(Texture) Texture::
4996 make_copy_impl() const {
4997  CDReader cdata(_cycler);
4998  return do_make_copy(cdata);
4999 }
5000 
5001 ////////////////////////////////////////////////////////////////////
5002 // Function: Texture::do_make_copy
5003 // Access: Protected
5004 // Description:
5005 ////////////////////////////////////////////////////////////////////
5006 PT(Texture) Texture::
5007 do_make_copy(const CData *cdata) const {
5008  PT(Texture) tex = new Texture(get_name());
5009  CDWriter cdata_tex(tex->_cycler, true);
5010  tex->do_assign(cdata_tex, this, cdata);
5011  return tex;
5012 }
5013 
5014 ////////////////////////////////////////////////////////////////////
5015 // Function: Texture::do_assign
5016 // Access: Protected
5017 // Description: The internal implementation of operator =(). Assumes
5018 // the lock is already held on both Textures.
5019 ////////////////////////////////////////////////////////////////////
5020 void Texture::
5021 do_assign(CData *cdata, const Texture *copy, const CData *cdata_copy) {
5022  cdata->do_assign(cdata_copy);
5023 }
5024 
5025 ////////////////////////////////////////////////////////////////////
5026 // Function: Texture::do_clear
5027 // Access: Protected, Virtual
5028 // Description: The protected implementation of clear(). Assumes the
5029 // lock is already held.
5030 ////////////////////////////////////////////////////////////////////
5031 void Texture::
5032 do_clear(CData *cdata) {
5033  Texture tex;
5034  tex.local_object();
5035  CDReader cdata_tex(tex._cycler);
5036  do_assign(cdata, &tex, cdata_tex);
5037 
5038  cdata->inc_properties_modified();
5039  cdata->inc_image_modified();
5040  cdata->inc_simple_image_modified();
5041 }
5042 
5043 ////////////////////////////////////////////////////////////////////
5044 // Function: Texture::do_setup_texture
5045 // Access: Protected
5046 // Description:
5047 ////////////////////////////////////////////////////////////////////
5048 void Texture::
5049 do_setup_texture(CData *cdata, Texture::TextureType texture_type,
5050  int x_size, int y_size, int z_size,
5051  Texture::ComponentType component_type,
5052  Texture::Format format) {
5053  switch (texture_type) {
5054  case TT_1d_texture:
5055  nassertv(y_size == 1 && z_size == 1);
5056  break;
5057 
5058  case TT_2d_texture:
5059  nassertv(z_size == 1);
5060  break;
5061 
5062  case TT_3d_texture:
5063  break;
5064 
5065  case TT_2d_texture_array:
5066  break;
5067 
5068  case TT_cube_map:
5069  // Cube maps must always consist of six square images.
5070  nassertv(x_size == y_size && z_size == 6);
5071 
5072  // In principle the wrap mode shouldn't mean anything to a cube
5073  // map, but some drivers seem to misbehave if it's other than
5074  // SamplerState::WM_clamp.
5075  cdata->_default_sampler.set_wrap_u(SamplerState::WM_clamp);
5076  cdata->_default_sampler.set_wrap_v(SamplerState::WM_clamp);
5077  cdata->_default_sampler.set_wrap_w(SamplerState::WM_clamp);
5078  break;
5079  }
5080 
5081  if (texture_type != TT_2d_texture) {
5082  do_clear_simple_ram_image(cdata);
5083  }
5084 
5085  cdata->_texture_type = texture_type;
5086  cdata->_x_size = x_size;
5087  cdata->_y_size = y_size;
5088  cdata->_z_size = z_size;
5089  cdata->_num_views = 1;
5090  do_set_component_type(cdata, component_type);
5091  do_set_format(cdata, format);
5092 
5093  do_clear_ram_image(cdata);
5094  do_set_pad_size(cdata, 0, 0, 0);
5095  cdata->_orig_file_x_size = 0;
5096  cdata->_orig_file_y_size = 0;
5097  cdata->_loaded_from_image = false;
5098  cdata->_loaded_from_txo = false;
5099  cdata->_has_read_pages = false;
5100  cdata->_has_read_mipmaps = false;
5101 }
5102 
5103 ////////////////////////////////////////////////////////////////////
5104 // Function: Texture::do_set_format
5105 // Access: Protected
5106 // Description:
5107 ////////////////////////////////////////////////////////////////////
5108 void Texture::
5109 do_set_format(CData *cdata, Texture::Format format) {
5110  if (format == cdata->_format) {
5111  return;
5112  }
5113  cdata->_format = format;
5114  cdata->inc_properties_modified();
5115 
5116  switch (cdata->_format) {
5117  case F_color_index:
5118  case F_depth_stencil:
5119  case F_depth_component:
5120  case F_depth_component16:
5121  case F_depth_component24:
5122  case F_depth_component32:
5123  case F_red:
5124  case F_green:
5125  case F_blue:
5126  case F_alpha:
5127  case F_luminance:
5128  case F_r16:
5129  case F_sluminance:
5130  case F_r32i:
5131  case F_r32:
5132  case F_r8i:
5133  cdata->_num_components = 1;
5134  break;
5135 
5136  case F_luminance_alpha:
5137  case F_luminance_alphamask:
5138  case F_rg16:
5139  case F_sluminance_alpha:
5140  case F_rg32:
5141  case F_rg8i:
5142  cdata->_num_components = 2;
5143  break;
5144 
5145  case F_rgb:
5146  case F_rgb5:
5147  case F_rgb8:
5148  case F_rgb12:
5149  case F_rgb332:
5150  case F_rgb16:
5151  case F_srgb:
5152  case F_rgb32:
5153  case F_rgb8i:
5154  cdata->_num_components = 3;
5155  break;
5156 
5157  case F_rgba:
5158  case F_rgbm:
5159  case F_rgba4:
5160  case F_rgba5:
5161  case F_rgba8:
5162  case F_rgba12:
5163  case F_rgba16:
5164  case F_rgba32:
5165  case F_srgb_alpha:
5166  case F_rgba8i:
5167  cdata->_num_components = 4;
5168  break;
5169  }
5170 }
5171 
5172 ////////////////////////////////////////////////////////////////////
5173 // Function: Texture::do_set_component_type
5174 // Access: Protected
5175 // Description:
5176 ////////////////////////////////////////////////////////////////////
5177 void Texture::
5178 do_set_component_type(CData *cdata, Texture::ComponentType component_type) {
5179  cdata->_component_type = component_type;
5180 
5181  switch (component_type) {
5182  case T_unsigned_byte:
5183  cdata->_component_width = 1;
5184  break;
5185 
5186  case T_unsigned_short:
5187  cdata->_component_width = 2;
5188  break;
5189 
5190  case T_float:
5191  cdata->_component_width = 4;
5192  break;
5193 
5194  case T_unsigned_int_24_8:
5195  cdata->_component_width = 4;
5196  break;
5197 
5198  case T_int:
5199  cdata->_component_width = 4;
5200  break;
5201  }
5202 }
5203 
5204 ////////////////////////////////////////////////////////////////////
5205 // Function: Texture::do_set_x_size
5206 // Access: Protected
5207 // Description:
5208 ////////////////////////////////////////////////////////////////////
5209 void Texture::
5210 do_set_x_size(CData *cdata, int x_size) {
5211  if (cdata->_x_size != x_size) {
5212  cdata->_x_size = x_size;
5213  cdata->inc_image_modified();
5214  do_clear_ram_image(cdata);
5215  do_set_pad_size(cdata, 0, 0, 0);
5216  }
5217 }
5218 
5219 ////////////////////////////////////////////////////////////////////
5220 // Function: Texture::do_set_y_size
5221 // Access: Protected
5222 // Description:
5223 ////////////////////////////////////////////////////////////////////
5224 void Texture::
5225 do_set_y_size(CData *cdata, int y_size) {
5226  if (cdata->_y_size != y_size) {
5227  nassertv(cdata->_texture_type != Texture::TT_1d_texture || y_size == 1);
5228  cdata->_y_size = y_size;
5229  cdata->inc_image_modified();
5230  do_clear_ram_image(cdata);
5231  do_set_pad_size(cdata, 0, 0, 0);
5232  }
5233 }
5234 
5235 ////////////////////////////////////////////////////////////////////
5236 // Function: Texture::do_set_z_size
5237 // Access: Protected
5238 // Description: Changes the z size indicated for the texture. This
5239 // also implicitly unloads the texture if it has already
5240 // been loaded.
5241 ////////////////////////////////////////////////////////////////////
5242 void Texture::
5243 do_set_z_size(CData *cdata, int z_size) {
5244  if (cdata->_z_size != z_size) {
5245  nassertv((cdata->_texture_type == Texture::TT_3d_texture) ||
5246  (cdata->_texture_type == Texture::TT_cube_map && z_size == 6) ||
5247  (cdata->_texture_type == Texture::TT_2d_texture_array) || (z_size == 1));
5248  cdata->_z_size = z_size;
5249  cdata->inc_image_modified();
5250  do_clear_ram_image(cdata);
5251  do_set_pad_size(cdata, 0, 0, 0);
5252  }
5253 }
5254 
5255 ////////////////////////////////////////////////////////////////////
5256 // Function: Texture::do_set_num_views
5257 // Access: Protected
5258 // Description:
5259 ////////////////////////////////////////////////////////////////////
5260 void Texture::
5261 do_set_num_views(CData *cdata, int num_views) {
5262  nassertv(num_views >= 1);
5263  if (cdata->_num_views != num_views) {
5264  cdata->_num_views = num_views;
5265  if (do_has_ram_image(cdata)) {
5266  cdata->inc_image_modified();
5267  do_clear_ram_image(cdata);
5268  }
5269  do_set_pad_size(cdata, 0, 0, 0);
5270  }
5271 }
5272 
5273 ////////////////////////////////////////////////////////////////////
5274 // Function: Texture::do_set_wrap_u
5275 // Access: Protected
5276 // Description:
5277 ////////////////////////////////////////////////////////////////////
5278 void Texture::
5279 do_set_wrap_u(CData *cdata, SamplerState::WrapMode wrap) {
5280  if (cdata->_default_sampler.get_wrap_u() != wrap) {
5281  cdata->inc_properties_modified();
5282  cdata->_default_sampler.set_wrap_u(wrap);
5283  }
5284 }
5285 
5286 ////////////////////////////////////////////////////////////////////
5287 // Function: Texture::do_set_wrap_v
5288 // Access: Protected
5289 // Description:
5290 ////////////////////////////////////////////////////////////////////
5291 void Texture::
5292 do_set_wrap_v(CData *cdata, SamplerState::WrapMode wrap) {
5293  if (cdata->_default_sampler.get_wrap_v() != wrap) {
5294  cdata->inc_properties_modified();
5295  cdata->_default_sampler.set_wrap_v(wrap);
5296  }
5297 }
5298 
5299 ////////////////////////////////////////////////////////////////////
5300 // Function: Texture::do_set_wrap_w
5301 // Access: Protected
5302 // Description:
5303 ////////////////////////////////////////////////////////////////////
5304 void Texture::
5305 do_set_wrap_w(CData *cdata, SamplerState::WrapMode wrap) {
5306  if (cdata->_default_sampler.get_wrap_w() != wrap) {
5307  cdata->inc_properties_modified();
5308  cdata->_default_sampler.set_wrap_w(wrap);
5309  }
5310 }
5311 
5312 ////////////////////////////////////////////////////////////////////
5313 // Function: Texture::do_set_minfilter
5314 // Access: Protected
5315 // Description:
5316 ////////////////////////////////////////////////////////////////////
5317 void Texture::
5318 do_set_minfilter(CData *cdata, SamplerState::FilterType filter) {
5319  if (cdata->_default_sampler.get_minfilter() != filter) {
5320  cdata->inc_properties_modified();
5321  cdata->_default_sampler.set_minfilter(filter);
5322  }
5323 }
5324 
5325 ////////////////////////////////////////////////////////////////////
5326 // Function: Texture::do_set_magfilter
5327 // Access: Protected
5328 // Description:
5329 ////////////////////////////////////////////////////////////////////
5330 void Texture::
5331 do_set_magfilter(CData *cdata, SamplerState::FilterType filter) {
5332  if (cdata->_default_sampler.get_magfilter() != filter) {
5333  cdata->inc_properties_modified();
5334  cdata->_default_sampler.set_magfilter(filter);
5335  }
5336 }
5337 
5338 ////////////////////////////////////////////////////////////////////
5339 // Function: Texture::do_set_anisotropic_degree
5340 // Access: Protected
5341 // Description:
5342 ////////////////////////////////////////////////////////////////////
5343 void Texture::
5344 do_set_anisotropic_degree(CData *cdata, int anisotropic_degree) {
5345  if (cdata->_default_sampler.get_anisotropic_degree() != anisotropic_degree) {
5346  cdata->inc_properties_modified();
5347  cdata->_default_sampler.set_anisotropic_degree(anisotropic_degree);
5348  }
5349 }
5350 
5351 ////////////////////////////////////////////////////////////////////
5352 // Function: Texture::do_set_border_color
5353 // Access: Protected
5354 // Description:
5355 ////////////////////////////////////////////////////////////////////
5356 void Texture::
5357 do_set_border_color(CData *cdata, const LColor &color) {
5358  if (cdata->_default_sampler.get_border_color() != color) {
5359  cdata->inc_properties_modified();
5360  cdata->_default_sampler.set_border_color(color);
5361  }
5362 }
5363 
5364 ////////////////////////////////////////////////////////////////////
5365 // Function: Texture::do_set_compression
5366 // Access: Protected
5367 // Description:
5368 ////////////////////////////////////////////////////////////////////
5369 void Texture::
5370 do_set_compression(CData *cdata, Texture::CompressionMode compression) {
5371  if (cdata->_compression != compression) {
5372  cdata->inc_properties_modified();
5373  cdata->_compression = compression;
5374 
5375  if (do_has_ram_image(cdata)) {
5376  bool has_compression = do_has_compression(cdata);
5377  bool has_ram_image_compression = (cdata->_ram_image_compression != CM_off);
5378  if (has_compression != has_ram_image_compression ||
5379  has_compression) {
5380  // Reload if we're turning compression on or off, or if we're
5381  // changing the compression mode to a different kind of
5382  // compression.
5383  do_reload(cdata);
5384  }
5385  }
5386  }
5387 }
5388 
5389 ////////////////////////////////////////////////////////////////////
5390 // Function: Texture::do_set_quality_level
5391 // Access: Public
5392 // Description:
5393 ////////////////////////////////////////////////////////////////////
5394 void Texture::
5395 do_set_quality_level(CData *cdata, Texture::QualityLevel quality_level) {
5396  if (cdata->_quality_level != quality_level) {
5397  cdata->inc_properties_modified();
5398  cdata->_quality_level = quality_level;
5399  }
5400 }
5401 
5402 ////////////////////////////////////////////////////////////////////
5403 // Function: Texture::do_has_compression
5404 // Access: Protected
5405 // Description:
5406 ////////////////////////////////////////////////////////////////////
5407 bool Texture::
5408 do_has_compression(const CData *cdata) const {
5409  if (cdata->_compression == CM_default) {
5410  return compressed_textures;
5411  } else {
5412  return (cdata->_compression != CM_off);
5413  }
5414 }
5415 
5416 ////////////////////////////////////////////////////////////////////
5417 // Function: Texture::do_has_ram_image
5418 // Access: Protected, Virtual
5419 // Description: The protected implementation of has_ram_image().
5420 // Assumes the lock is already held.
5421 ////////////////////////////////////////////////////////////////////
5422 bool Texture::
5423 do_has_ram_image(const CData *cdata) const {
5424  return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty();
5425 }
5426 
5427 ////////////////////////////////////////////////////////////////////
5428 // Function: Texture::do_has_uncompressed_ram_image
5429 // Access: Protected, Virtual
5430 // Description: The protected implementation of
5431 // has_uncompressed_ram_image(). Assumes the lock is
5432 // already held.
5433 ////////////////////////////////////////////////////////////////////
5434 bool Texture::
5435 do_has_uncompressed_ram_image(const CData *cdata) const {
5436  return !cdata->_ram_images.empty() && !cdata->_ram_images[0]._image.empty() && cdata->_ram_image_compression == CM_off;
5437 }
5438 
5439 ////////////////////////////////////////////////////////////////////
5440 // Function: Texture::do_get_ram_image
5441 // Access: Protected
5442 // Description:
5443 ////////////////////////////////////////////////////////////////////
5444 CPTA_uchar Texture::
5445 do_get_ram_image(CData *cdata) {
5446  if (!do_has_ram_image(cdata) && do_can_reload(cdata)) {
5447  do_reload_ram_image(cdata, true);
5448 
5449  if (do_has_ram_image(cdata)) {
5450  // Normally, we don't update the cdata->_modified semaphores in a do_blah
5451  // method, but we'll make an exception in this case, because it's
5452  // easiest to modify these here, and only when we know it's
5453  // needed.
5454  cdata->inc_image_modified();
5455  cdata->inc_properties_modified();
5456  }
5457  }
5458 
5459  if (cdata->_ram_images.empty()) {
5460  return CPTA_uchar(get_class_type());
5461  }
5462 
5463  return cdata->_ram_images[0]._image;
5464 }
5465 
5466 ////////////////////////////////////////////////////////////////////
5467 // Function: Texture::do_get_uncompressed_ram_image
5468 // Access: Protected
5469 // Description:
5470 ////////////////////////////////////////////////////////////////////
5471 CPTA_uchar Texture::
5472 do_get_uncompressed_ram_image(CData *cdata) {
5473  if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
5474  // We have an image in-ram, but it's compressed. Try to
5475  // uncompress it first.
5476  if (do_uncompress_ram_image(cdata)) {
5477  if (gobj_cat.is_debug()) {
5478  gobj_cat.debug()
5479  << "Uncompressed " << get_name() << "\n";
5480  }
5481  return cdata->_ram_images[0]._image;
5482  }
5483  }
5484 
5485  // Couldn't uncompress the existing image. Try to reload it.
5486  if ((!do_has_ram_image(cdata) || cdata->_ram_image_compression != CM_off) && do_can_reload(cdata)) {
5487  do_reload_ram_image(cdata, false);
5488  }
5489 
5490  if (!cdata->_ram_images.empty() && cdata->_ram_image_compression != CM_off) {
5491  // Great, now we have an image.
5492  if (do_uncompress_ram_image(cdata)) {
5493  gobj_cat.info()
5494  << "Uncompressed " << get_name() << "\n";
5495  return cdata->_ram_images[0]._image;
5496  }
5497  }
5498 
5499  if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
5500  return CPTA_uchar(get_class_type());
5501  }
5502 
5503  return cdata->_ram_images[0]._image;
5504 }
5505 
5506 ////////////////////////////////////////////////////////////////////
5507 // Function: Texture::get_ram_image_as
5508 // Access: Published
5509 // Description: Returns the uncompressed system-RAM image data
5510 // associated with the texture. Rather than
5511 // just returning a pointer to the data, like
5512 // get_uncompressed_ram_image, this function first
5513 // processes the data and reorders the components
5514 // using the specified format string, and places these
5515 // into a new char array. The 'format' argument should
5516 // specify in which order the components of the texture
5517 // must be. For example, valid format strings are
5518 // "RGBA", "GA", "ABRG" or "AAA". A component can
5519 // also be written as "0" or "1", which means an
5520 // empty/black or a full/white channel, respectively.
5521 // This function is particularly useful to
5522 // copy an image in-memory to a different library
5523 // (for example, PIL or wxWidgets) that require
5524 // a different component order than Panda's internal
5525 // format, BGRA. Note, however, that this conversion
5526 // can still be too slow if you want to do it every
5527 // frame, and should thus be avoided for that purpose.
5528 // The only requirement for the reordering is that
5529 // an uncompressed image must be available. If the
5530 // RAM image is compressed, it will attempt to re-load
5531 // the texture from disk, if it doesn't find an
5532 // uncompressed image there, it will return NULL.
5533 ////////////////////////////////////////////////////////////////////
5535 get_ram_image_as(const string &requested_format) {
5536  CDWriter cdata(_cycler, false);
5537  string format = upcase(requested_format);
5538 
5539  // Make sure we can grab something that's uncompressed.
5540  CPTA_uchar data = do_get_uncompressed_ram_image(cdata);
5541  if (data == NULL) {
5542  gobj_cat.error() << "Couldn't find an uncompressed RAM image!\n";
5543  return CPTA_uchar(get_class_type());
5544  }
5545  int imgsize = cdata->_x_size * cdata->_y_size;
5546  nassertr(cdata->_num_components > 0 && cdata->_num_components <= 4, CPTA_uchar(get_class_type()));
5547  nassertr(data.size() == (size_t)(cdata->_component_width * cdata->_num_components * imgsize), CPTA_uchar(get_class_type()));
5548 
5549  // Check if the format is already what we have internally.
5550  if ((cdata->_num_components == 1 && format.size() == 1) ||
5551  (cdata->_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
5552  (cdata->_num_components == 3 && format == "BGR") ||
5553  (cdata->_num_components == 4 && format == "BGRA")) {
5554  // The format string is already our format, so we just need to copy it.
5555  return CPTA_uchar(data);
5556  }
5557 
5558  // Create a new empty array that can hold our image.
5559  PTA_uchar newdata = PTA_uchar::empty_array(imgsize * format.size() * cdata->_component_width, get_class_type());
5560 
5561  // These ifs are for optimization of commonly used image types.
5562  if (format == "RGBA" && cdata->_num_components == 4 && cdata->_component_width == 1) {
5563  imgsize *= 4;
5564  for (int p = 0; p < imgsize; p += 4) {
5565  newdata[p ] = data[p + 2];
5566  newdata[p + 1] = data[p + 1];
5567  newdata[p + 2] = data[p ];
5568  newdata[p + 3] = data[p + 3];
5569  }
5570  return newdata;
5571  }
5572  if (format == "RGB" && cdata->_num_components == 3 && cdata->_component_width == 1) {
5573  imgsize *= 3;
5574  for (int p = 0; p < imgsize; p += 3) {
5575  newdata[p ] = data[p + 2];
5576  newdata[p + 1] = data[p + 1];
5577  newdata[p + 2] = data[p ];
5578  }
5579  return newdata;
5580  }
5581  if (format == "A" && cdata->_component_width == 1 && cdata->_num_components != 3) {
5582  // We can generally rely on alpha to be the last component.
5583  int component = cdata->_num_components - 1;
5584  for (int p = 0; p < imgsize; ++p) {
5585  newdata[p] = data[component];
5586  }
5587  return newdata;
5588  }
5589  if (cdata->_component_width == 1) {
5590  for (int p = 0; p < imgsize; ++p) {
5591  for (uchar s = 0; s < format.size(); ++s) {
5592  signed char component = -1;
5593  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
5594  component = 0;
5595  } else if (format.at(s) == 'G') {
5596  component = 1;
5597  } else if (format.at(s) == 'R') {
5598  component = 2;
5599  } else if (format.at(s) == 'A') {
5600  nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
5601  component = cdata->_num_components - 1;
5602  } else if (format.at(s) == '0') {
5603  newdata[p * format.size() + s] = 0x00;
5604  } else if (format.at(s) == '1') {
5605  newdata[p * format.size() + s] = 0xff;
5606  } else {
5607  gobj_cat.error() << "Unexpected component character '"
5608  << format.at(s) << "', expected one of RGBA!\n";
5609  return CPTA_uchar(get_class_type());
5610  }
5611  if (component >= 0) {
5612  newdata[p * format.size() + s] = data[p * cdata->_num_components + component];
5613  }
5614  }
5615  }
5616  return newdata;
5617  }
5618  for (int p = 0; p < imgsize; ++p) {
5619  for (uchar s = 0; s < format.size(); ++s) {
5620  signed char component = -1;
5621  if (format.at(s) == 'B' || (cdata->_num_components <= 2 && format.at(s) != 'A')) {
5622  component = 0;
5623  } else if (format.at(s) == 'G') {
5624  component = 1;
5625  } else if (format.at(s) == 'R') {
5626  component = 2;
5627  } else if (format.at(s) == 'A') {
5628  nassertr(cdata->_num_components != 3, CPTA_uchar(get_class_type()));
5629  component = cdata->_num_components - 1;
5630  } else if (format.at(s) == '0') {
5631  memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width), 0, cdata->_component_width);
5632  } else if (format.at(s) == '1') {
5633  memset((void*)(newdata + (p * format.size() + s) * cdata->_component_width), -1, cdata->_component_width);
5634  } else {
5635  gobj_cat.error() << "Unexpected component character '"
5636  << format.at(s) << "', expected one of RGBA!\n";
5637  return CPTA_uchar(get_class_type());
5638  }
5639  if (component >= 0) {
5640  memcpy((void*)(newdata + (p * format.size() + s) * cdata->_component_width),
5641  (void*)(data + (p * cdata->_num_components + component) * cdata->_component_width),
5642  cdata->_component_width);
5643  }
5644  }
5645  }
5646  return newdata;
5647 }
5648 
5649 ////////////////////////////////////////////////////////////////////
5650 // Function: Texture::do_set_simple_ram_image
5651 // Access: Protected
5652 // Description:
5653 ////////////////////////////////////////////////////////////////////
5654 void Texture::
5655 do_set_simple_ram_image(CData *cdata, CPTA_uchar image, int x_size, int y_size) {
5656  nassertv(cdata->_texture_type == TT_2d_texture);
5657  size_t expected_page_size = (size_t)(x_size * y_size * 4);
5658  nassertv(image.size() == expected_page_size);
5659 
5660  cdata->_simple_x_size = x_size;
5661  cdata->_simple_y_size = y_size;
5662  cdata->_simple_ram_image._image = image.cast_non_const();
5663  cdata->_simple_ram_image._page_size = image.size();
5664  cdata->_simple_image_date_generated = (PN_int32)time(NULL);
5665  cdata->inc_simple_image_modified();
5666 }
5667 
5668 ////////////////////////////////////////////////////////////////////
5669 // Function: Texture::do_get_expected_num_mipmap_levels
5670 // Access: Protected
5671 // Description:
5672 ////////////////////////////////////////////////////////////////////
5673 int Texture::
5674 do_get_expected_num_mipmap_levels(const CData *cdata) const {
5675  int size = max(cdata->_x_size, cdata->_y_size);
5676  if (cdata->_texture_type == Texture::TT_3d_texture) {
5677  size = max(size, cdata->_z_size);
5678  }
5679  int count = 1;
5680  while (size > 1) {
5681  size >>= 1;
5682  ++count;
5683  }
5684  return count;
5685 }
5686 
5687 ////////////////////////////////////////////////////////////////////
5688 // Function: Texture::do_get_ram_mipmap_page_size
5689 // Access: Protected
5690 // Description:
5691 ////////////////////////////////////////////////////////////////////
5692 size_t Texture::
5693 do_get_ram_mipmap_page_size(const CData *cdata, int n) const {
5694  if (cdata->_ram_image_compression != CM_off) {
5695  if (n >= 0 && n < (int)cdata->_ram_images.size()) {
5696  return cdata->_ram_images[n]._page_size;
5697  }
5698  return 0;
5699  } else {
5700  return do_get_expected_ram_mipmap_page_size(cdata, n);
5701  }
5702 }
5703 
5704 ////////////////////////////////////////////////////////////////////
5705 // Function: Texture::do_get_expected_mipmap_x_size
5706 // Access: Protected
5707 // Description:
5708 ////////////////////////////////////////////////////////////////////
5709 int Texture::
5710 do_get_expected_mipmap_x_size(const CData *cdata, int n) const {
5711  int size = max(cdata->_x_size, 1);
5712  while (n > 0 && size > 1) {
5713  size >>= 1;
5714  --n;
5715  }
5716  return size;
5717 }
5718 
5719 ////////////////////////////////////////////////////////////////////
5720 // Function: Texture::do_get_expected_mipmap_y_size
5721 // Access: Protected
5722 // Description:
5723 ////////////////////////////////////////////////////////////////////
5724 int Texture::
5725 do_get_expected_mipmap_y_size(const CData *cdata, int n) const {
5726  int size = max(cdata->_y_size, 1);
5727  while (n > 0 && size > 1) {
5728  size >>= 1;
5729  --n;
5730  }
5731  return size;
5732 }
5733 
5734 ////////////////////////////////////////////////////////////////////
5735 // Function: Texture::do_get_expected_mipmap_z_size
5736 // Access: Protected
5737 // Description:
5738 ////////////////////////////////////////////////////////////////////
5739 int Texture::
5740 do_get_expected_mipmap_z_size(const CData *cdata, int n) const {
5741  // 3-D textures have a different number of pages per each mipmap
5742  // level. Other kinds of textures--especially, cube map
5743  // textures--always have the same.
5744  if (cdata->_texture_type == Texture::TT_3d_texture) {
5745  int size = max(cdata->_z_size, 1);
5746  while (n > 0 && size > 1) {
5747  size >>= 1;
5748  --n;
5749  }
5750  return size;
5751 
5752  } else {
5753  return cdata->_z_size;
5754  }
5755 }
5756 
5757 ////////////////////////////////////////////////////////////////////
5758 // Function: Texture::do_clear_simple_ram_image
5759 // Access: Protected
5760 // Description:
5761 ////////////////////////////////////////////////////////////////////
5762 void Texture::
5763 do_clear_simple_ram_image(CData *cdata) {
5764  cdata->_simple_x_size = 0;
5765  cdata->_simple_y_size = 0;
5766  cdata->_simple_ram_image._image.clear();
5767  cdata->_simple_ram_image._page_size = 0;
5768  cdata->_simple_image_date_generated = 0;
5769 
5770  // We allow this exception: we update the _simple_image_modified
5771  // here, since no one really cares much about that anyway, and it's
5772  // convenient to do it here.
5773  cdata->inc_simple_image_modified();
5774 }
5775 
5776 ////////////////////////////////////////////////////////////////////
5777 // Function: Texture::do_clear_ram_mipmap_images
5778 // Access: Protected
5779 // Description:
5780 ////////////////////////////////////////////////////////////////////
5781 void Texture::
5782 do_clear_ram_mipmap_images(CData *cdata) {
5783  if (!cdata->_ram_images.empty()) {
5784  cdata->_ram_images.erase(cdata->_ram_images.begin() + 1, cdata->_ram_images.end());
5785  }
5786 }
5787 
5788 ////////////////////////////////////////////////////////////////////
5789 // Function: Texture::do_generate_ram_mipmap_images
5790 // Access: Protected
5791 // Description:
5792 ////////////////////////////////////////////////////////////////////
5793 void Texture::
5794 do_generate_ram_mipmap_images(CData *cdata) {
5795  nassertv(do_has_ram_image(cdata));
5796 
5797  if (do_get_expected_num_mipmap_levels(cdata) == 1) {
5798  // Don't bother.
5799  return;
5800  }
5801 
5802  RamImage orig_compressed_image;
5803  CompressionMode orig_compression_mode = CM_off;
5804 
5805  if (cdata->_ram_image_compression != CM_off) {
5806  // The RAM image is compressed. This means we need to uncompress
5807  // it in order to generate mipmap images. Save the original
5808  // first, to avoid lossy recompression.
5809  orig_compressed_image = cdata->_ram_images[0];
5810  orig_compression_mode = cdata->_ram_image_compression;
5811 
5812  // Now try to get the uncompressed source image.
5813  do_get_uncompressed_ram_image(cdata);
5814 
5815  nassertv(cdata->_ram_image_compression == CM_off);
5816  }
5817 
5818  do_clear_ram_mipmap_images(cdata);
5819 
5820  if (gobj_cat.is_debug()) {
5821  gobj_cat.debug()
5822  << "Generating mipmap levels for " << *this << "\n";
5823  }
5824 
5825  if (cdata->_texture_type == Texture::TT_3d_texture && cdata->_z_size != 1) {
5826  // Eek, a 3-D texture.
5827  int x_size = cdata->_x_size;
5828  int y_size = cdata->_y_size;
5829  int z_size = cdata->_z_size;
5830  int n = 0;
5831  while (x_size > 1 || y_size > 1 || z_size > 1) {
5832  cdata->_ram_images.push_back(RamImage());
5833  do_filter_3d_mipmap_level(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
5834  x_size, y_size, z_size);
5835  x_size = max(x_size >> 1, 1);
5836  y_size = max(y_size >> 1, 1);
5837  z_size = max(z_size >> 1, 1);
5838  ++n;
5839  }
5840 
5841  } else {
5842  // A 1-D, 2-D, or cube map texture.
5843  int x_size = cdata->_x_size;
5844  int y_size = cdata->_y_size;
5845  int n = 0;
5846  while (x_size > 1 || y_size > 1) {
5847  cdata->_ram_images.push_back(RamImage());
5848  do_filter_2d_mipmap_pages(cdata, cdata->_ram_images[n + 1], cdata->_ram_images[n],
5849  x_size, y_size);
5850  x_size = max(x_size >> 1, 1);
5851  y_size = max(y_size >> 1, 1);
5852  ++n;
5853  }
5854  }
5855 
5856  if (orig_compression_mode != CM_off) {
5857  // Now attempt to recompress the mipmap images according to the
5858  // original compression mode. We don't need to bother compressing
5859  // the first image (it was already compressed, after all), so
5860  // temporarily remove it from the top of the mipmap stack, and
5861  // compress all of the rest of them instead.
5862  nassertv(cdata->_ram_images.size() > 1);
5863  int l0_x_size = cdata->_x_size;
5864  int l0_y_size = cdata->_y_size;
5865  int l0_z_size = cdata->_z_size;
5866  cdata->_x_size = do_get_expected_mipmap_x_size(cdata, 1);
5867  cdata->_y_size = do_get_expected_mipmap_y_size(cdata, 1);
5868  cdata->_z_size = do_get_expected_mipmap_z_size(cdata, 1);
5869  RamImage uncompressed_image = cdata->_ram_images[0];
5870  cdata->_ram_images.erase(cdata->_ram_images.begin());
5871 
5872  bool success = do_compress_ram_image(cdata, orig_compression_mode, QL_default, NULL);
5873  // Now restore the toplevel image.
5874  if (success) {
5875  cdata->_ram_images.insert(cdata->_ram_images.begin(), orig_compressed_image);
5876  } else {
5877  cdata->_ram_images.insert(cdata->_ram_images.begin(), uncompressed_image);
5878  }
5879  cdata->_x_size = l0_x_size;
5880  cdata->_y_size = l0_y_size;
5881  cdata->_z_size = l0_z_size;
5882  }
5883 }
5884 
5885 ////////////////////////////////////////////////////////////////////
5886 // Function: Texture::do_set_pad_size
5887 // Access: Protected
5888 // Description:
5889 ////////////////////////////////////////////////////////////////////
5890 void Texture::
5891 do_set_pad_size(CData *cdata, int x, int y, int z) {
5892  if (x > cdata->_x_size) {
5893  x = cdata->_x_size;
5894  }
5895  if (y > cdata->_y_size) {
5896  y = cdata->_y_size;
5897  }
5898  if (z > cdata->_z_size) {
5899  z = cdata->_z_size;
5900  }
5901 
5902  cdata->_pad_x_size = x;
5903  cdata->_pad_y_size = y;
5904  cdata->_pad_z_size = z;
5905 }
5906 
5907 ////////////////////////////////////////////////////////////////////
5908 // Function: Texture::do_can_reload
5909 // Access: Protected, Virtual
5910 // Description: Returns true if we can safely call
5911 // do_reload_ram_image() in order to make the image
5912 // available, or false if we shouldn't do this (because
5913 // we know from a priori knowledge that it wouldn't work
5914 // anyway).
5915 ////////////////////////////////////////////////////////////////////
5916 bool Texture::
5917 do_can_reload(const CData *cdata) const {
5918  return (cdata->_loaded_from_image && !cdata->_fullpath.empty());
5919 }
5920 
5921 ////////////////////////////////////////////////////////////////////
5922 // Function: Texture::do_reload
5923 // Access: Protected
5924 // Description:
5925 ////////////////////////////////////////////////////////////////////
5926 bool Texture::
5927 do_reload(CData *cdata) {
5928  if (do_can_reload(cdata)) {
5929  do_clear_ram_image(cdata);
5930  do_reload_ram_image(cdata, true);
5931  if (do_has_ram_image(cdata)) {
5932  // An explicit call to reload() should increment image_modified.
5933  cdata->inc_image_modified();
5934  return true;
5935  }
5936  return false;
5937  }
5938 
5939  // We don't have a filename to load from.
5940  return false;
5941 }
5942 
5943 ////////////////////////////////////////////////////////////////////
5944 // Function: Texture::do_has_bam_rawdata
5945 // Access: Protected, Virtual
5946 // Description: Returns true if there is a rawdata image that we have
5947 // available to write to the bam stream. For a normal
5948 // Texture, this is the same thing as
5949 // do_has_ram_image(), but a movie texture might define
5950 // it differently.
5951 ////////////////////////////////////////////////////////////////////
5952 bool Texture::
5953 do_has_bam_rawdata(const CData *cdata) const {
5954  return do_has_ram_image(cdata);
5955 }
5956 
5957 ////////////////////////////////////////////////////////////////////
5958 // Function: Texture::do_get_bam_rawdata
5959 // Access: Protected, Virtual
5960 // Description: If do_has_bam_rawdata() returned false, this attempts
5961 // to reload the rawdata image if possible.
5962 ////////////////////////////////////////////////////////////////////
5963 void Texture::
5964 do_get_bam_rawdata(CData *cdata) {
5965  do_get_ram_image(cdata);
5966 }
5967 
5968 ////////////////////////////////////////////////////////////////////
5969 // Function: Texture::convert_from_pnmimage
5970 // Access: Private, Static
5971 // Description: Internal method to convert pixel data from the
5972 // indicated PNMImage into the given ram_image.
5973 ////////////////////////////////////////////////////////////////////
5974 void Texture::
5975 convert_from_pnmimage(PTA_uchar &image, size_t page_size,
5976  int row_stride, int x, int y, int z,
5977  const PNMImage &pnmimage, int num_components,
5978  int component_width) {
5979  int x_size = pnmimage.get_x_size();
5980  int y_size = pnmimage.get_y_size();
5981  xelval maxval = pnmimage.get_maxval();
5982  int pixel_size = num_components * component_width;
5983 
5984  int row_skip = 0;
5985  if (row_stride == 0) {
5986  row_stride = x_size;
5987  } else {
5988  row_skip = (row_stride - x_size) * pixel_size;
5989  nassertv(row_skip >= 0);
5990  }
5991 
5992  bool is_grayscale = (num_components == 1 || num_components == 2);
5993  bool has_alpha = (num_components == 2 || num_components == 4);
5994  bool img_has_alpha = pnmimage.has_alpha();
5995 
5996  int idx = page_size * z;
5997  nassertv(idx + page_size <= image.size());
5998  unsigned char *p = &image[idx];
5999 
6000  if (x != 0 || y != 0) {
6001  p += (row_stride * y + x) * pixel_size;
6002  }
6003 
6004  if (maxval == 255 && component_width == 1) {
6005  // Most common case: one byte per pixel, and the source image
6006  // maxval of 255. No scaling is necessary. Because this is such a common
6007  // case, we break it out per component for best performance.
6008  const xel *array = pnmimage.get_array();
6009  switch (num_components) {
6010  case 1:
6011  for (int j = y_size-1; j >= 0; j--) {
6012  const xel *row = array + j * x_size;
6013  for (int i = 0; i < x_size; i++) {
6014  *p++ = (uchar)PPM_GETB(row[i]);
6015  }
6016  p += row_skip;
6017  }
6018  break;
6019 
6020  case 2:
6021  if (img_has_alpha) {
6022  const xelval *alpha = pnmimage.get_alpha_array();
6023  for (int j = y_size-1; j >= 0; j--) {
6024  const xel *row = array + j * x_size;
6025  const xelval *alpha_row = alpha + j * x_size;
6026  for (int i = 0; i < x_size; i++) {
6027  *p++ = (uchar)PPM_GETB(row[i]);
6028  *p++ = (uchar)alpha_row[i];
6029  }
6030  p += row_skip;
6031  }
6032  } else {
6033  for (int j = y_size-1; j >= 0; j--) {
6034  const xel *row = array + j * x_size;
6035  for (int i = 0; i < x_size; i++) {
6036  *p++ = (uchar)PPM_GETB(row[i]);
6037  *p++ = (uchar)255;
6038  }
6039  p += row_skip;
6040  }
6041  }
6042  break;
6043 
6044  case 3:
6045  for (int j = y_size-1; j >= 0; j--) {
6046  const xel *row = array + j * x_size;
6047  for (int i = 0; i < x_size; i++) {
6048  *p++ = (uchar)PPM_GETB(row[i]);
6049  *p++ = (uchar)PPM_GETG(row[i]);
6050  *p++ = (uchar)PPM_GETR(row[i]);
6051  }
6052  p += row_skip;
6053  }
6054  break;
6055 
6056  case 4:
6057  if (img_has_alpha) {
6058  const xelval *alpha = pnmimage.get_alpha_array();
6059  for (int j = y_size-1; j >= 0; j--) {
6060  const xel *row = array + j * x_size;
6061  const xelval *alpha_row = alpha + j * x_size;
6062  for (int i = 0; i < x_size; i++) {
6063  *p++ = (uchar)PPM_GETB(row[i]);
6064  *p++ = (uchar)PPM_GETG(row[i]);
6065  *p++ = (uchar)PPM_GETR(row[i]);
6066  *p++ = (uchar)alpha_row[i];
6067  }
6068  p += row_skip;
6069  }
6070  } else {
6071  for (int j = y_size-1; j >= 0; j--) {
6072  const xel *row = array + j * x_size;
6073  for (int i = 0; i < x_size; i++) {
6074  *p++ = (uchar)PPM_GETB(row[i]);
6075  *p++ = (uchar)PPM_GETG(row[i]);
6076  *p++ = (uchar)PPM_GETR(row[i]);
6077  *p++ = (uchar)255;
6078  }
6079  p += row_skip;
6080  }
6081  }
6082  break;
6083 
6084  default:
6085  nassertv(num_components >= 1 && num_components <= 4);
6086  break;
6087  }
6088 
6089  } else if (maxval == 65535 && component_width == 2) {
6090  // Another possible case: two bytes per pixel, and the source image shows
6091  // a maxval of 65535. Again, no scaling is necessary.
6092  for (int j = y_size-1; j >= 0; j--) {
6093  for (int i = 0; i < x_size; i++) {
6094  if (is_grayscale) {
6095  store_unscaled_short(p, pnmimage.get_gray_val(i, j));
6096  } else {
6097  store_unscaled_short(p, pnmimage.get_blue_val(i, j));
6098  store_unscaled_short(p, pnmimage.get_green_val(i, j));
6099  store_unscaled_short(p, pnmimage.get_red_val(i, j));
6100  }
6101  if (has_alpha) {
6102  if (img_has_alpha) {
6103  store_unscaled_short(p, pnmimage.get_alpha_val(i, j));
6104  } else {
6105  store_unscaled_short(p, 65535);
6106  }
6107  }
6108  }
6109  p += row_skip;
6110  }
6111 
6112  } else if (component_width == 1) {
6113  // A less common case: one byte per pixel, but the maxval is
6114  // something other than 255. In this case, we should scale the
6115  // pixel values up to the appropriate amount.
6116  double scale = 255.0 / (double)maxval;
6117 
6118  for (int j = y_size-1; j >= 0; j--) {
6119  for (int i = 0; i < x_size; i++) {
6120  if (is_grayscale) {
6121  store_scaled_byte(p, pnmimage.get_gray_val(i, j), scale);
6122  } else {
6123  store_scaled_byte(p, pnmimage.get_blue_val(i, j), scale);
6124  store_scaled_byte(p, pnmimage.get_green_val(i, j), scale);
6125  store_scaled_byte(p, pnmimage.get_red_val(i, j), scale);
6126  }
6127  if (has_alpha) {
6128  if (img_has_alpha) {
6129  store_scaled_byte(p, pnmimage.get_alpha_val(i, j), scale);
6130  } else {
6131  store_unscaled_byte(p, 255);
6132  }
6133  }
6134  }
6135  p += row_skip;
6136  }
6137 
6138  } else { // component_width == 2
6139  // Another uncommon case: two bytes per pixel, and the maxval is
6140  // something other than 65535. Again, we must scale the pixel
6141  // values.
6142  double scale = 65535.0 / (double)maxval;
6143 
6144  for (int j = y_size-1; j >= 0; j--) {
6145  for (int i = 0; i < x_size; i++) {
6146  if (is_grayscale) {
6147  store_scaled_short(p, pnmimage.get_gray_val(i, j), scale);
6148  } else {
6149  store_scaled_short(p, pnmimage.get_blue_val(i, j), scale);
6150  store_scaled_short(p, pnmimage.get_green_val(i, j), scale);
6151  store_scaled_short(p, pnmimage.get_red_val(i, j), scale);
6152  }
6153  if (has_alpha) {
6154  if (img_has_alpha) {
6155  store_scaled_short(p, pnmimage.get_alpha_val(i, j), 1.0);
6156  } else {
6157  store_unscaled_short(p, 65535);
6158  }
6159  }
6160  }
6161  p += row_skip;
6162  }
6163  }
6164 }
6165 
6166 ////////////////////////////////////////////////////////////////////
6167 // Function: Texture::convert_from_pfm
6168 // Access: Private, Static
6169 // Description: Internal method to convert pixel data from the
6170 // indicated PfmFile into the given ram_image.
6171 ////////////////////////////////////////////////////////////////////
6172 void Texture::
6173 convert_from_pfm(PTA_uchar &image, size_t page_size, int z,
6174  const PfmFile &pfm, int num_components, int component_width) {
6175  nassertv(component_width == 4); // Currently only PN_float32 is expected.
6176  int x_size = pfm.get_x_size();
6177  int y_size = pfm.get_y_size();
6178 
6179  int idx = page_size * z;
6180  nassertv(idx + page_size <= image.size());
6181  PN_float32 *p = (PN_float32 *)&image[idx];
6182 
6183  switch (num_components) {
6184  case 1:
6185  {
6186  for (int j = y_size-1; j >= 0; j--) {
6187  for (int i = 0; i < x_size; i++) {
6188  p[0] = pfm.get_channel(i, j, 0);
6189  ++p;
6190  }
6191  }
6192  }
6193  break;
6194 
6195  case 2:
6196  {
6197  for (int j = y_size-1; j >= 0; j--) {
6198  for (int i = 0; i < x_size; i++) {
6199  p[0] = pfm.get_channel(i, j, 0);
6200  p[1] = pfm.get_channel(i, j, 1);
6201  p += 2;
6202  }
6203  }
6204  }
6205  break;
6206 
6207  case 3:
6208  {
6209  // RGB -> BGR
6210  for (int j = y_size-1; j >= 0; j--) {
6211  for (int i = 0; i < x_size; i++) {
6212  p[0] = pfm.get_channel(i, j, 2);
6213  p[1] = pfm.get_channel(i, j, 1);
6214  p[2] = pfm.get_channel(i, j, 0);
6215  p += 3;
6216  }
6217  }
6218  }
6219  break;
6220 
6221  case 4:
6222  {
6223  // RGBA -> BGRA
6224  for (int j = y_size-1; j >= 0; j--) {
6225  for (int i = 0; i < x_size; i++) {
6226  p[0] = pfm.get_channel(i, j, 2);
6227  p[1] = pfm.get_channel(i, j, 1);
6228  p[2] = pfm.get_channel(i, j, 0);
6229  p[3] = pfm.get_channel(i, j, 3);
6230  p += 4;
6231  }
6232  }
6233  }
6234  break;
6235 
6236  default:
6237  nassertv(false);
6238  }
6239 
6240  nassertv((unsigned char *)p == &image[idx] + page_size);
6241 }
6242 
6243 ////////////////////////////////////////////////////////////////////
6244 // Function: Texture::convert_to_pnmimage
6245 // Access: Private, Static
6246 // Description: Internal method to convert pixel data to the
6247 // indicated PNMImage from the given ram_image.
6248 ////////////////////////////////////////////////////////////////////
6249 bool Texture::
6250 convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size,
6251  int num_components, int component_width,
6252  CPTA_uchar image, size_t page_size, int z) {
6253  xelval maxval = 0xff;
6254  if (component_width > 1) {
6255  maxval = 0xffff;
6256  }
6257  pnmimage.clear(x_size, y_size, num_components, maxval);
6258  bool has_alpha = pnmimage.has_alpha();
6259  bool is_grayscale = pnmimage.is_grayscale();
6260 
6261  int idx = page_size * z;
6262  nassertr(idx + page_size <= image.size(), false);
6263  const unsigned char *p = &image[idx];
6264 
6265  if (component_width == 1) {
6266  xel *array = pnmimage.get_array();
6267  if (is_grayscale) {
6268  if (has_alpha) {
6269  xelval *alpha = pnmimage.get_alpha_array();
6270  for (int j = y_size-1; j >= 0; j--) {
6271  xel *row = array + j * x_size;
6272  xelval *alpha_row = alpha + j * x_size;
6273  for (int i = 0; i < x_size; i++) {
6274  PPM_PUTB(row[i], *p++);
6275  alpha_row[i] = *p++;
6276  }
6277  }
6278  } else {
6279  for (int j = y_size-1; j >= 0; j--) {
6280  xel *row = array + j * x_size;
6281  for (int i = 0; i < x_size; i++) {
6282  PPM_PUTB(row[i], *p++);
6283  }
6284  }
6285  }
6286  } else {
6287  if (has_alpha) {
6288  xelval *alpha = pnmimage.get_alpha_array();
6289  for (int j = y_size-1; j >= 0; j--) {
6290  xel *row = array + j * x_size;
6291  xelval *alpha_row = alpha + j * x_size;
6292  for (int i = 0; i < x_size; i++) {
6293  PPM_PUTB(row[i], *p++);
6294  PPM_PUTG(row[i], *p++);
6295  PPM_PUTR(row[i], *p++);
6296  alpha_row[i] = *p++;
6297  }
6298  }
6299  } else {
6300  for (int j = y_size-1; j >= 0; j--) {
6301  xel *row = array + j * x_size;
6302  for (int i = 0; i < x_size; i++) {
6303  PPM_PUTB(row[i], *p++);
6304  PPM_PUTG(row[i], *p++);
6305  PPM_PUTR(row[i], *p++);
6306  }
6307  }
6308  }
6309  }
6310 
6311  } else if (component_width == 2) {
6312  for (int j = y_size-1; j >= 0; j--) {
6313  for (int i = 0; i < x_size; i++) {
6314  if (is_grayscale) {
6315  pnmimage.set_gray(i, j, get_unsigned_short(p));
6316  } else {
6317  pnmimage.set_blue(i, j, get_unsigned_short(p));
6318  pnmimage.set_green(i, j, get_unsigned_short(p));
6319  pnmimage.set_red(i, j, get_unsigned_short(p));
6320  }
6321  if (has_alpha) {
6322  pnmimage.set_alpha(i, j, get_unsigned_short(p));
6323  }
6324  }
6325  }
6326 
6327  } else {
6328  return false;
6329  }
6330 
6331  nassertr(p == &image[idx] + page_size, false);
6332  return true;
6333 }
6334 
6335 ////////////////////////////////////////////////////////////////////
6336 // Function: Texture::convert_to_pfm
6337 // Access: Private, Static
6338 // Description: Internal method to convert pixel data to the
6339 // indicated PfmFile from the given ram_image.
6340 ////////////////////////////////////////////////////////////////////
6341 bool Texture::
6342 convert_to_pfm(PfmFile &pfm, int x_size, int y_size,
6343  int num_components, int component_width,
6344  CPTA_uchar image, size_t page_size, int z) {
6345  nassertr(component_width == 4, false); // Currently only PN_float32 is expected.
6346  pfm.clear(x_size, y_size, num_components);
6347 
6348  int idx = page_size * z;
6349  nassertr(idx + page_size <= image.size(), false);
6350  const PN_float32 *p = (const PN_float32 *)&image[idx];
6351 
6352  switch (num_components) {
6353  case 1:
6354  for (int j = y_size-1; j >= 0; j--) {
6355  for (int i = 0; i < x_size; i++) {
6356  pfm.set_channel(i, j, 0, p[0]);
6357  ++p;
6358  }
6359  }
6360  break;
6361 
6362  case 2:
6363  for (int j = y_size-1; j >= 0; j--) {
6364  for (int i = 0; i < x_size; i++) {
6365  pfm.set_channel(i, j, 0, p[0]);
6366  pfm.set_channel(i, j, 1, p[1]);
6367  p += 2;
6368  }
6369  }
6370  break;
6371 
6372  case 3:
6373  // BGR -> RGB
6374  for (int j = y_size-1; j >= 0; j--) {
6375  for (int i = 0; i < x_size; i++) {
6376  pfm.set_channel(i, j, 2, p[0]);
6377  pfm.set_channel(i, j, 1, p[1]);
6378  pfm.set_channel(i, j, 0, p[2]);
6379  p += 3;
6380  }
6381  }
6382  break;
6383 
6384  case 4:
6385  // BGRA -> RGBA
6386  for (int j = y_size-1; j >= 0; j--) {
6387  for (int i = 0; i < x_size; i++) {
6388  pfm.set_channel(i, j, 2, p[0]);
6389  pfm.set_channel(i, j, 1, p[1]);
6390  pfm.set_channel(i, j, 0, p[2]);
6391  pfm.set_channel(i, j, 3, p[3]);
6392  p += 4;
6393  }
6394  }
6395  break;
6396 
6397  default:
6398  nassertr(false, false);
6399  }
6400 
6401  nassertr((unsigned char *)p == &image[idx] + page_size, false);
6402  return true;
6403 }
6404 
6405 ////////////////////////////////////////////////////////////////////
6406 // Function: Texture::read_dds_level_bgr8
6407 // Access: Private, Static
6408 // Description: Called by read_dds for a DDS file in BGR8 format.
6409 ////////////////////////////////////////////////////////////////////
6410 PTA_uchar Texture::
6411 read_dds_level_bgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6412  // This is in order B, G, R.
6413  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6414  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6415 
6416  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6417  size_t row_bytes = x_size * 3;
6418  PTA_uchar image = PTA_uchar::empty_array(size);
6419  for (int y = y_size - 1; y >= 0; --y) {
6420  unsigned char *p = image.p() + y * row_bytes;
6421  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
6422  in.read((char *)p, row_bytes);
6423  }
6424 
6425  return image;
6426 }
6427 
6428 ////////////////////////////////////////////////////////////////////
6429 // Function: Texture::read_dds_level_rgb8
6430 // Access: Private, Static
6431 // Description: Called by read_dds for a DDS file in RGB8 format.
6432 ////////////////////////////////////////////////////////////////////
6433 PTA_uchar Texture::
6434 read_dds_level_rgb8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6435  // This is in order R, G, B.
6436  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6437  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6438 
6439  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6440  size_t row_bytes = x_size * 3;
6441  PTA_uchar image = PTA_uchar::empty_array(size);
6442  for (int y = y_size - 1; y >= 0; --y) {
6443  unsigned char *p = image.p() + y * row_bytes;
6444  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
6445  in.read((char *)p, row_bytes);
6446 
6447  // Now reverse the r, g, b triples.
6448  for (int x = 0; x < x_size; ++x) {
6449  unsigned char r = p[0];
6450  p[0] = p[2];
6451  p[2] = r;
6452  p += 3;
6453  }
6454  nassertr(p <= image.p() + size, PTA_uchar());
6455  }
6456 
6457  return image;
6458 }
6459 
6460 ////////////////////////////////////////////////////////////////////
6461 // Function: Texture::read_dds_level_abgr8
6462 // Access: Private, Static
6463 // Description: Called by read_dds for a DDS file in ABGR8 format.
6464 ////////////////////////////////////////////////////////////////////
6465 PTA_uchar Texture::
6466 read_dds_level_abgr8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6467  // This is laid out in order R, G, B, A.
6468  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6469  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6470 
6471  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6472  size_t row_bytes = x_size * 4;
6473  PTA_uchar image = PTA_uchar::empty_array(size);
6474  for (int y = y_size - 1; y >= 0; --y) {
6475  unsigned char *p = image.p() + y * row_bytes;
6476  in.read((char *)p, row_bytes);
6477 
6478  PN_uint32 *pw = (PN_uint32 *)p;
6479  for (int x = 0; x < x_size; ++x) {
6480  PN_uint32 w = *pw;
6481 #ifdef WORDS_BIGENDIAN
6482  // bigendian: convert R, G, B, A to B, G, R, A.
6483  w = ((w & 0xff00) << 16) | ((w & 0xff000000U) >> 16) | (w & 0xff00ff);
6484 #else
6485  // littendian: convert A, B, G, R to to A, R, G, B.
6486  w = ((w & 0xff) << 16) | ((w & 0xff0000) >> 16) | (w & 0xff00ff00U);
6487 #endif
6488  *pw = w;
6489  ++pw;
6490  }
6491  nassertr((unsigned char *)pw <= image.p() + size, PTA_uchar());
6492  }
6493 
6494  return image;
6495 }
6496 
6497 ////////////////////////////////////////////////////////////////////
6498 // Function: Texture::read_dds_level_rgba8
6499 // Access: Private, Static
6500 // Description: Called by read_dds for a DDS file in RGBA8 format.
6501 ////////////////////////////////////////////////////////////////////
6502 PTA_uchar Texture::
6503 read_dds_level_rgba8(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6504  // This is actually laid out in order B, G, R, A.
6505  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6506  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6507 
6508  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6509  size_t row_bytes = x_size * 4;
6510  PTA_uchar image = PTA_uchar::empty_array(size);
6511  for (int y = y_size - 1; y >= 0; --y) {
6512  unsigned char *p = image.p() + y * row_bytes;
6513  nassertr(p + row_bytes <= image.p() + size, PTA_uchar());
6514  in.read((char *)p, row_bytes);
6515  }
6516 
6517  return image;
6518 }
6519 
6520 ////////////////////////////////////////////////////////////////////
6521 // Function: Texture::read_dds_level_generic_uncompressed
6522 // Access: Private, Static
6523 // Description: Called by read_dds for a DDS file whose format isn't
6524 // one we've specifically optimized.
6525 ////////////////////////////////////////////////////////////////////
6526 PTA_uchar Texture::
6527 read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
6528  int n, istream &in) {
6529  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6530  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6531 
6532  int pitch = (x_size * header.pf.rgb_bitcount) / 8;
6533 
6534  // MS says the pitch can be supplied in the header file and must be
6535  // DWORD aligned, but this appears to apply to level 0 mipmaps only
6536  // (where it almost always will be anyway). Other mipmap levels
6537  // seem to be tightly packed, but there isn't a separate pitch for
6538  // each mipmap level. Weird.
6539  if (n == 0) {
6540  pitch = ((pitch + 3) / 4) * 4;
6541  if (header.dds_flags & DDSD_PITCH) {
6542  pitch = header.pitch;
6543  }
6544  }
6545 
6546  int bpp = header.pf.rgb_bitcount / 8;
6547  int skip_bytes = pitch - (bpp * x_size);
6548  nassertr(skip_bytes >= 0, PTA_uchar());
6549 
6550  unsigned int r_mask = header.pf.r_mask;
6551  unsigned int g_mask = header.pf.g_mask;
6552  unsigned int b_mask = header.pf.b_mask;
6553  unsigned int a_mask = header.pf.a_mask;
6554 
6555  // Determine the number of bits to shift each mask to the right so
6556  // that the lowest on bit is at bit 0.
6557  int r_shift = get_lowest_on_bit(r_mask);
6558  int g_shift = get_lowest_on_bit(g_mask);
6559  int b_shift = get_lowest_on_bit(b_mask);
6560  int a_shift = get_lowest_on_bit(a_mask);
6561 
6562  // Then determine the scale factor required to raise the highest
6563  // color value to 0xff000000.
6564  unsigned int r_scale = 0;
6565  if (r_mask != 0) {
6566  r_scale = 0xff000000 / (r_mask >> r_shift);
6567  }
6568  unsigned int g_scale = 0;
6569  if (g_mask != 0) {
6570  g_scale = 0xff000000 / (g_mask >> g_shift);
6571  }
6572  unsigned int b_scale = 0;
6573  if (b_mask != 0) {
6574  b_scale = 0xff000000 / (b_mask >> b_shift);
6575  }
6576  unsigned int a_scale = 0;
6577  if (a_mask != 0) {
6578  a_scale = 0xff000000 / (a_mask >> a_shift);
6579  }
6580 
6581  bool add_alpha = has_alpha(cdata->_format);
6582 
6583  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6584  size_t row_bytes = x_size * cdata->_num_components;
6585  PTA_uchar image = PTA_uchar::empty_array(size);
6586  for (int y = y_size - 1; y >= 0; --y) {
6587  unsigned char *p = image.p() + y * row_bytes;
6588  for (int x = 0; x < x_size; ++x) {
6589 
6590  // Read a little-endian numeric value of bpp bytes.
6591  unsigned int pixel = 0;
6592  int shift = 0;
6593  for (int bi = 0; bi < bpp; ++bi) {
6594  unsigned int ch = (unsigned char)in.get();
6595  pixel |= (ch << shift);
6596  shift += 8;
6597  }
6598 
6599  // Then break apart that value into its R, G, B, and maybe A
6600  // components.
6601  unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
6602  unsigned int g = (((pixel & g_mask) >> g_shift) * g_scale) >> 24;
6603  unsigned int b = (((pixel & b_mask) >> b_shift) * b_scale) >> 24;
6604 
6605  // Store the components in the Texture's image data.
6606  store_unscaled_byte(p, b);
6607  store_unscaled_byte(p, g);
6608  store_unscaled_byte(p, r);
6609  if (add_alpha) {
6610  unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
6611  store_unscaled_byte(p, a);
6612  }
6613  }
6614  nassertr(p <= image.p() + size, PTA_uchar());
6615  for (int bi = 0; bi < skip_bytes; ++bi) {
6616  in.get();
6617  }
6618  }
6619 
6620  return image;
6621 }
6622 
6623 ////////////////////////////////////////////////////////////////////
6624 // Function: Texture::read_dds_level_luminance_uncompressed
6625 // Access: Private, Static
6626 // Description: Called by read_dds for a DDS file in uncompressed
6627 // luminance or luminance-alpha format.
6628 ////////////////////////////////////////////////////////////////////
6629 PTA_uchar Texture::
6630 read_dds_level_luminance_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header,
6631  int n, istream &in) {
6632  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6633  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6634 
6635  int pitch = (x_size * header.pf.rgb_bitcount) / 8;
6636 
6637  // MS says the pitch can be supplied in the header file and must be
6638  // DWORD aligned, but this appears to apply to level 0 mipmaps only
6639  // (where it almost always will be anyway). Other mipmap levels
6640  // seem to be tightly packed, but there isn't a separate pitch for
6641  // each mipmap level. Weird.
6642  if (n == 0) {
6643  pitch = ((pitch + 3) / 4) * 4;
6644  if (header.dds_flags & DDSD_PITCH) {
6645  pitch = header.pitch;
6646  }
6647  }
6648 
6649  int bpp = header.pf.rgb_bitcount / 8;
6650  int skip_bytes = pitch - (bpp * x_size);
6651  nassertr(skip_bytes >= 0, PTA_uchar());
6652 
6653  unsigned int r_mask = header.pf.r_mask;
6654  unsigned int a_mask = header.pf.a_mask;
6655 
6656  // Determine the number of bits to shift each mask to the right so
6657  // that the lowest on bit is at bit 0.
6658  int r_shift = get_lowest_on_bit(r_mask);
6659  int a_shift = get_lowest_on_bit(a_mask);
6660 
6661  // Then determine the scale factor required to raise the highest
6662  // color value to 0xff000000.
6663  unsigned int r_scale = 0;
6664  if (r_mask != 0) {
6665  r_scale = 0xff000000 / (r_mask >> r_shift);
6666  }
6667  unsigned int a_scale = 0;
6668  if (a_mask != 0) {
6669  a_scale = 0xff000000 / (a_mask >> a_shift);
6670  }
6671 
6672  bool add_alpha = has_alpha(cdata->_format);
6673 
6674  size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n);
6675  size_t row_bytes = x_size * cdata->_num_components;
6676  PTA_uchar image = PTA_uchar::empty_array(size);
6677  for (int y = y_size - 1; y >= 0; --y) {
6678  unsigned char *p = image.p() + y * row_bytes;
6679  for (int x = 0; x < x_size; ++x) {
6680 
6681  // Read a little-endian numeric value of bpp bytes.
6682  unsigned int pixel = 0;
6683  int shift = 0;
6684  for (int bi = 0; bi < bpp; ++bi) {
6685  unsigned int ch = (unsigned char)in.get();
6686  pixel |= (ch << shift);
6687  shift += 8;
6688  }
6689 
6690  unsigned int r = (((pixel & r_mask) >> r_shift) * r_scale) >> 24;
6691 
6692  // Store the components in the Texture's image data.
6693  store_unscaled_byte(p, r);
6694  if (add_alpha) {
6695  unsigned int a = (((pixel & a_mask) >> a_shift) * a_scale) >> 24;
6696  store_unscaled_byte(p, a);
6697  }
6698  }
6699  nassertr(p <= image.p() + size, PTA_uchar());
6700  for (int bi = 0; bi < skip_bytes; ++bi) {
6701  in.get();
6702  }
6703  }
6704 
6705  return image;
6706 }
6707 
6708 ////////////////////////////////////////////////////////////////////
6709 // Function: Texture::read_dds_level_dxt1
6710 // Access: Private, Static
6711 // Description: Called by read_dds for DXT1 file format.
6712 ////////////////////////////////////////////////////////////////////
6713 PTA_uchar Texture::
6714 read_dds_level_dxt1(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6715  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6716  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6717 
6718  static const int div = 4;
6719  static const int block_bytes = 8;
6720 
6721  // The DXT1 image is divided into num_rows x num_cols blocks, where
6722  // each block represents 4x4 pixels.
6723  int num_cols = max(div, x_size) / div;
6724  int num_rows = max(div, y_size) / div;
6725  int row_length = num_cols * block_bytes;
6726  int linear_size = row_length * num_rows;
6727 
6728  if (n == 0) {
6729  if (header.dds_flags & DDSD_LINEARSIZE) {
6730  nassertr(linear_size == (int)header.pitch, PTA_uchar());
6731  }
6732  }
6733 
6734  PTA_uchar image = PTA_uchar::empty_array(linear_size);
6735 
6736  if (y_size >= 4) {
6737  // We have to flip the image as we read it, because of DirectX's
6738  // inverted sense of up. That means we (a) reverse the order of the
6739  // rows of blocks . . .
6740  for (int ri = num_rows - 1; ri >= 0; --ri) {
6741  unsigned char *p = image.p() + row_length * ri;
6742  in.read((char *)p, row_length);
6743 
6744  for (int ci = 0; ci < num_cols; ++ci) {
6745  // . . . and (b) within each block, we reverse the 4 individual
6746  // rows of 4 pixels.
6747  PN_uint32 *cells = (PN_uint32 *)p;
6748  PN_uint32 w = cells[1];
6749  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
6750  cells[1] = w;
6751 
6752  p += block_bytes;
6753  }
6754  }
6755 
6756  } else if (y_size >= 2) {
6757  // To invert a two-pixel high image, we just flip two rows within a cell.
6758  unsigned char *p = image.p();
6759  in.read((char *)p, row_length);
6760 
6761  for (int ci = 0; ci < num_cols; ++ci) {
6762  PN_uint32 *cells = (PN_uint32 *)p;
6763  PN_uint32 w = cells[1];
6764  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
6765  cells[1] = w;
6766 
6767  p += block_bytes;
6768  }
6769 
6770  } else if (y_size >= 1) {
6771  // No need to invert a one-pixel-high image.
6772  unsigned char *p = image.p();
6773  in.read((char *)p, row_length);
6774  }
6775 
6776  return image;
6777 }
6778 
6779 ////////////////////////////////////////////////////////////////////
6780 // Function: Texture::read_dds_level_dxt23
6781 // Access: Private, Static
6782 // Description: Called by read_dds for DXT2 or DXT3 file format.
6783 ////////////////////////////////////////////////////////////////////
6784 PTA_uchar Texture::
6785 read_dds_level_dxt23(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6786  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6787  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6788 
6789  static const int div = 4;
6790  static const int block_bytes = 16;
6791 
6792  // The DXT3 image is divided into num_rows x num_cols blocks, where
6793  // each block represents 4x4 pixels. Unlike DXT1, each block
6794  // consists of two 8-byte chunks, representing the alpha and color
6795  // separately.
6796  int num_cols = max(div, x_size) / div;
6797  int num_rows = max(div, y_size) / div;
6798  int row_length = num_cols * block_bytes;
6799  int linear_size = row_length * num_rows;
6800 
6801  if (n == 0) {
6802  if (header.dds_flags & DDSD_LINEARSIZE) {
6803  nassertr(linear_size == (int)header.pitch, PTA_uchar());
6804  }
6805  }
6806 
6807  PTA_uchar image = PTA_uchar::empty_array(linear_size);
6808 
6809  if (y_size >= 4) {
6810  // We have to flip the image as we read it, because of DirectX's
6811  // inverted sense of up. That means we (a) reverse the order of the
6812  // rows of blocks . . .
6813  for (int ri = num_rows - 1; ri >= 0; --ri) {
6814  unsigned char *p = image.p() + row_length * ri;
6815  in.read((char *)p, row_length);
6816 
6817  for (int ci = 0; ci < num_cols; ++ci) {
6818  // . . . and (b) within each block, we reverse the 4 individual
6819  // rows of 4 pixels.
6820  PN_uint32 *cells = (PN_uint32 *)p;
6821 
6822  // Alpha. The block is four 16-bit words of pixel data.
6823  PN_uint32 w0 = cells[0];
6824  PN_uint32 w1 = cells[1];
6825  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
6826  w1 = ((w1 & 0xffff) << 16) | ((w1 & 0xffff0000U) >> 16);
6827  cells[0] = w1;
6828  cells[1] = w0;
6829 
6830  // Color. Only the second 32-bit dword of the color block
6831  // represents the pixel data.
6832  PN_uint32 w = cells[3];
6833  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
6834  cells[3] = w;
6835 
6836  p += block_bytes;
6837  }
6838  }
6839 
6840  } else if (y_size >= 2) {
6841  // To invert a two-pixel high image, we just flip two rows within a cell.
6842  unsigned char *p = image.p();
6843  in.read((char *)p, row_length);
6844 
6845  for (int ci = 0; ci < num_cols; ++ci) {
6846  PN_uint32 *cells = (PN_uint32 *)p;
6847 
6848  PN_uint32 w0 = cells[0];
6849  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
6850  cells[0] = w0;
6851 
6852  PN_uint32 w = cells[3];
6853  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
6854  cells[3] = w;
6855 
6856  p += block_bytes;
6857  }
6858 
6859  } else if (y_size >= 1) {
6860  // No need to invert a one-pixel-high image.
6861  unsigned char *p = image.p();
6862  in.read((char *)p, row_length);
6863  }
6864 
6865  return image;
6866 }
6867 
6868 ////////////////////////////////////////////////////////////////////
6869 // Function: Texture::read_dds_level_dxt45
6870 // Access: Private, Static
6871 // Description: Called by read_dds for DXT4 or DXT5 file format.
6872 ////////////////////////////////////////////////////////////////////
6873 PTA_uchar Texture::
6874 read_dds_level_dxt45(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) {
6875  int x_size = tex->do_get_expected_mipmap_x_size(cdata, n);
6876  int y_size = tex->do_get_expected_mipmap_y_size(cdata, n);
6877 
6878  static const int div = 4;
6879  static const int block_bytes = 16;
6880 
6881  // The DXT5 image is similar to DXT3, in that there each 4x4 block
6882  // of pixels consists of an alpha block and a color block, but the
6883  // layout of the alpha block is different.
6884  int num_cols = max(div, x_size) / div;
6885  int num_rows = max(div, y_size) / div;
6886  int row_length = num_cols * block_bytes;
6887  int linear_size = row_length * num_rows;
6888 
6889  if (n == 0) {
6890  if (header.dds_flags & DDSD_LINEARSIZE) {
6891  nassertr(linear_size == (int)header.pitch, PTA_uchar());
6892  }
6893  }
6894 
6895  PTA_uchar image = PTA_uchar::empty_array(linear_size);
6896 
6897  if (y_size >= 4) {
6898  // We have to flip the image as we read it, because of DirectX's
6899  // inverted sense of up. That means we (a) reverse the order of the
6900  // rows of blocks . . .
6901  for (int ri = num_rows - 1; ri >= 0; --ri) {
6902  unsigned char *p = image.p() + row_length * ri;
6903  in.read((char *)p, row_length);
6904 
6905  for (int ci = 0; ci < num_cols; ++ci) {
6906  // . . . and (b) within each block, we reverse the 4 individual
6907  // rows of 4 pixels.
6908  PN_uint32 *cells = (PN_uint32 *)p;
6909 
6910  // Alpha. The block is one 16-bit word of reference values,
6911  // followed by six words of pixel values, in 12-bit rows.
6912  // Tricky to invert.
6913  unsigned char p2 = p[2];
6914  unsigned char p3 = p[3];
6915  unsigned char p4 = p[4];
6916  unsigned char p5 = p[5];
6917  unsigned char p6 = p[6];
6918  unsigned char p7 = p[7];
6919 
6920  p[2] = ((p7 & 0xf) << 4) | ((p6 & 0xf0) >> 4);
6921  p[3] = ((p5 & 0xf) << 4) | ((p7 & 0xf0) >> 4);
6922  p[4] = ((p6 & 0xf) << 4) | ((p5 & 0xf0) >> 4);
6923  p[5] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
6924  p[6] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
6925  p[7] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
6926 
6927  // Color. Only the second 32-bit dword of the color block
6928  // represents the pixel data.
6929  PN_uint32 w = cells[3];
6930  w = ((w & 0xff) << 24) | ((w & 0xff00) << 8) | ((w & 0xff0000) >> 8) | ((w & 0xff000000U) >> 24);
6931  cells[3] = w;
6932 
6933  p += block_bytes;
6934  }
6935  }
6936 
6937  } else if (y_size >= 2) {
6938  // To invert a two-pixel high image, we just flip two rows within a cell.
6939  unsigned char *p = image.p();
6940  in.read((char *)p, row_length);
6941 
6942  for (int ci = 0; ci < num_cols; ++ci) {
6943  PN_uint32 *cells = (PN_uint32 *)p;
6944 
6945  unsigned char p2 = p[2];
6946  unsigned char p3 = p[3];
6947  unsigned char p4 = p[4];
6948 
6949  p[2] = ((p4 & 0xf) << 4) | ((p3 & 0xf0) >> 4);
6950  p[3] = ((p2 & 0xf) << 4) | ((p4 & 0xf0) >> 4);
6951  p[4] = ((p3 & 0xf) << 4) | ((p2 & 0xf0) >> 4);
6952 
6953  PN_uint32 w0 = cells[0];
6954  w0 = ((w0 & 0xffff) << 16) | ((w0 & 0xffff0000U) >> 16);
6955  cells[0] = w0;
6956 
6957  PN_uint32 w = cells[3];
6958  w = ((w & 0xff) << 8) | ((w & 0xff00) >> 8);
6959  cells[3] = w;
6960 
6961  p += block_bytes;
6962  }
6963 
6964  } else if (y_size >= 1) {
6965  // No need to invert a one-pixel-high image.
6966  unsigned char *p = image.p();
6967  in.read((char *)p, row_length);
6968  }
6969 
6970  return image;
6971 }
6972 
6973 ////////////////////////////////////////////////////////////////////
6974 // Function: Texture::clear_prepared
6975 // Access: Private
6976 // Description: Removes the indicated PreparedGraphicsObjects table
6977 // from the Texture's table, without actually releasing
6978 // the texture. This is intended to be called only from
6979 // PreparedGraphicsObjects::release_texture(); it should
6980 // never be called by user code.
6981 ////////////////////////////////////////////////////////////////////
6982 void Texture::
6983 clear_prepared(int view, PreparedGraphicsObjects *prepared_objects) {
6984  PreparedViews::iterator pvi;
6985  pvi = _prepared_views.find(prepared_objects);
6986  if (pvi != _prepared_views.end()) {
6987  Contexts &contexts = (*pvi).second;
6988  Contexts::iterator ci;
6989  ci = contexts.find(view);
6990  if (ci != contexts.end()) {
6991  contexts.erase(ci);
6992  }
6993 
6994  if (contexts.empty()) {
6995  _prepared_views.erase(pvi);
6996  }
6997  }
6998 }
6999 
7000 ////////////////////////////////////////////////////////////////////
7001 // Function: Texture::consider_downgrade
7002 // Access: Private, Static
7003 // Description: Reduces the number of channels in the texture, if
7004 // necessary, according to num_channels.
7005 ////////////////////////////////////////////////////////////////////
7006 void Texture::
7007 consider_downgrade(PNMImage &pnmimage, int num_channels, const string &name) {
7008  if (num_channels != 0 && num_channels < pnmimage.get_num_channels()) {
7009  // One special case: we can't reduce from 3 to 2 components, since
7010  // that would require adding an alpha channel.
7011  if (pnmimage.get_num_channels() == 3 && num_channels == 2) {
7012  return;
7013  }
7014 
7015  gobj_cat.info()
7016  << "Downgrading " << name << " from "
7017  << pnmimage.get_num_channels() << " components to "
7018  << num_channels << ".\n";
7019  pnmimage.set_num_channels(num_channels);
7020  }
7021 }
7022 
7023 ////////////////////////////////////////////////////////////////////
7024 // Function: Texture::compare_images
7025 // Access: Private, Static
7026 // Description: Called by generate_simple_ram_image(), this compares
7027 // the two PNMImages pixel-by-pixel. If they're similar
7028 // enough (within a given threshold), returns true.
7029 ////////////////////////////////////////////////////////////////////
7030 bool Texture::
7031 compare_images(const PNMImage &a, const PNMImage &b) {
7032  nassertr(a.get_maxval() == 255 && b.get_maxval() == 255, false);
7033  nassertr(a.get_num_channels() == 4 && b.get_num_channels() == 4, false);
7034  nassertr(a.get_x_size() == b.get_x_size() &&
7035  a.get_y_size() == b.get_y_size(), false);
7036 
7037  const xel *a_array = a.get_array();
7038  const xel *b_array = b.get_array();
7039  const xelval *a_alpha = a.get_alpha_array();
7040  const xelval *b_alpha = b.get_alpha_array();
7041 
7042  int x_size = a.get_x_size();
7043 
7044  int delta = 0;
7045  for (int yi = 0; yi < a.get_y_size(); ++yi) {
7046  const xel *a_row = a_array + yi * x_size;
7047  const xel *b_row = b_array + yi * x_size;
7048  const xelval *a_alpha_row = a_alpha + yi * x_size;
7049  const xelval *b_alpha_row = b_alpha + yi * x_size;
7050  for (int xi = 0; xi < x_size; ++xi) {
7051  delta += abs(PPM_GETR(a_row[xi]) - PPM_GETR(b_row[xi]));
7052  delta += abs(PPM_GETG(a_row[xi]) - PPM_GETG(b_row[xi]));
7053  delta += abs(PPM_GETB(a_row[xi]) - PPM_GETB(b_row[xi]));
7054  delta += abs(a_alpha_row[xi] - b_alpha_row[xi]);
7055  }
7056  }
7057 
7058  double average_delta = (double)delta / ((double)a.get_x_size() * (double)b.get_y_size() * (double)a.get_maxval());
7059  return (average_delta <= simple_image_threshold);
7060 }
7061 
7062 ////////////////////////////////////////////////////////////////////
7063 // Function: Texture::do_filter_2d_mipmap_pages
7064 // Access: Private
7065 // Description: Generates the next mipmap level from the previous
7066 // one. If there are multiple pages (e.g. a cube map),
7067 // generates each page independently.
7068 //
7069 // x_size and y_size are the size of the previous level.
7070 // They need not be a power of 2, or even a multiple of
7071 // 2.
7072 //
7073 // Assumes the lock is already held.
7074 ////////////////////////////////////////////////////////////////////
7075 void Texture::
7076 do_filter_2d_mipmap_pages(const CData *cdata,
7077  Texture::RamImage &to, const Texture::RamImage &from,
7078  int x_size, int y_size) const {
7079  Filter2DComponent *filter_component;
7080  Filter2DComponent *filter_alpha;
7081 
7082  if (is_srgb(cdata->_format)) {
7083  // We currently only support sRGB mipmap generation for
7084  // unsigned byte textures, due to our use of a lookup table.
7085  nassertv(cdata->_component_type == T_unsigned_byte);
7086 
7087  if (has_sse2_sRGB_encode()) {
7088  filter_component = &filter_2d_unsigned_byte_srgb_sse2;
7089  } else {
7090  filter_component = &filter_2d_unsigned_byte_srgb;
7091  }
7092 
7093  // Alpha is always linear.
7094  filter_alpha = &filter_2d_unsigned_byte;
7095 
7096  } else {
7097  switch (cdata->_component_type) {
7098  case T_unsigned_byte:
7099  filter_component = &filter_2d_unsigned_byte;
7100  break;
7101 
7102  case T_unsigned_short:
7103  filter_component = &filter_2d_unsigned_short;
7104  break;
7105 
7106  case T_float:
7107  filter_component = &filter_2d_float;
7108  break;
7109 
7110  default:
7111  gobj_cat.error()
7112  << "Unable to generate mipmaps for 2D texture with component type "
7113  << cdata->_component_type << "!";
7114  return;
7115  }
7116  filter_alpha = filter_component;
7117  }
7118 
7119  size_t pixel_size = cdata->_num_components * cdata->_component_width;
7120  size_t row_size = (size_t)x_size * pixel_size;
7121 
7122  int to_x_size = max(x_size >> 1, 1);
7123  int to_y_size = max(y_size >> 1, 1);
7124 
7125  size_t to_row_size = (size_t)to_x_size * pixel_size;
7126  to._page_size = (size_t)to_y_size * to_row_size;
7127  to._image = PTA_uchar::empty_array(to._page_size * cdata->_z_size * cdata->_num_views, get_class_type());
7128 
7129  bool alpha = has_alpha(cdata->_format);
7130  int num_color_components = cdata->_num_components;
7131  if (alpha) {
7132  --num_color_components;
7133  }
7134 
7135  int num_pages = cdata->_z_size * cdata->_num_views;
7136  for (int z = 0; z < num_pages; ++z) {
7137  // For each level.
7138  unsigned char *p = to._image.p() + z * to._page_size;
7139  nassertv(p <= to._image.p() + to._image.size() + to._page_size);
7140  const unsigned char *q = from._image.p() + z * from._page_size;
7141  nassertv(q <= from._image.p() + from._image.size() + from._page_size);
7142  if (y_size != 1) {
7143  int y;
7144  for (y = 0; y < y_size - 1; y += 2) {
7145  // For each row.
7146  nassertv(p == to._image.p() + z * to._page_size + (y / 2) * to_row_size);
7147  nassertv(q == from._image.p() + z * from._page_size + y * row_size);
7148  if (x_size != 1) {
7149  int x;
7150  for (x = 0; x < x_size - 1; x += 2) {
7151  // For each pixel.
7152  for (int c = 0; c < num_color_components; ++c) {
7153  // For each component.
7154  filter_component(p, q, pixel_size, row_size);
7155  }
7156  if (alpha) {
7157  filter_alpha(p, q, pixel_size, row_size);
7158  }
7159  q += pixel_size;
7160  }
7161  if (x < x_size) {
7162  // Skip the last odd pixel.
7163  q += pixel_size;
7164  }
7165  } else {
7166  // Just one pixel.
7167  for (int c = 0; c < num_color_components; ++c) {
7168  // For each component.
7169  filter_component(p, q, 0, row_size);
7170  }
7171  if (alpha) {
7172  filter_alpha(p, q, 0, row_size);
7173  }
7174  }
7175  q += row_size;
7177  }
7178  if (y < y_size) {
7179  // Skip the last odd row.
7180  q += row_size;
7181  }
7182  } else {
7183  // Just one row.
7184  if (x_size != 1) {
7185  int x;
7186  for (x = 0; x < x_size - 1; x += 2) {
7187  // For each pixel.
7188  for (int c = 0; c < num_color_components; ++c) {
7189  // For each component.
7190  filter_component(p, q, pixel_size, 0);
7191  }
7192  if (alpha) {
7193  filter_alpha(p, q, pixel_size, 0);
7194  }
7195  q += pixel_size;
7196  }
7197  if (x < x_size) {
7198  // Skip the last odd pixel.
7199  q += pixel_size;
7200  }
7201  } else {
7202  // Just one pixel.
7203  for (int c = 0; c < num_color_components; ++c) {
7204  // For each component.
7205  filter_component(p, q, 0, 0);
7206  }
7207  if (alpha) {
7208  filter_alpha(p, q, pixel_size, 0);
7209  }
7210  }
7211  }
7212 
7213  nassertv(p == to._image.p() + (z + 1) * to._page_size);
7214  nassertv(q == from._image.p() + (z + 1) * from._page_size);
7215  }
7216 }
7217 
7218 ////////////////////////////////////////////////////////////////////
7219 // Function: Texture::do_filter_3d_mipmap_level
7220 // Access: Private
7221 // Description: Generates the next mipmap level from the previous
7222 // one, treating all the pages of the level as a single
7223 // 3-d block of pixels.
7224 //
7225 // x_size, y_size, and z_size are the size of the
7226 // previous level. They need not be a power of 2, or
7227 // even a multiple of 2.
7228 //
7229 // Assumes the lock is already held.
7230 ////////////////////////////////////////////////////////////////////
7231 void Texture::
7232 do_filter_3d_mipmap_level(const CData *cdata,
7233  Texture::RamImage &to, const Texture::RamImage &from,
7234  int x_size, int y_size, int z_size) const {
7235  Filter3DComponent *filter_component;
7236  Filter3DComponent *filter_alpha;
7237 
7238  if (is_srgb(cdata->_format)) {
7239  // We currently only support sRGB mipmap generation for
7240  // unsigned byte textures, due to our use of a lookup table.
7241  nassertv(cdata->_component_type == T_unsigned_byte);
7242 
7243  if (has_sse2_sRGB_encode()) {
7244  filter_component = &filter_3d_unsigned_byte_srgb_sse2;
7245  } else {
7246  filter_component = &filter_3d_unsigned_byte_srgb;
7247  }
7248 
7249  // Alpha is always linear.
7250  filter_alpha = &filter_3d_unsigned_byte;
7251 
7252  } else {
7253  switch (cdata->_component_type) {
7254  case T_unsigned_byte:
7255  filter_component = &filter_3d_unsigned_byte;
7256  break;
7257 
7258  case T_unsigned_short:
7259  filter_component = &filter_3d_unsigned_short;
7260  break;
7261 
7262  case T_float:
7263  filter_component = &filter_3d_float;
7264  break;
7265 
7266  default:
7267  gobj_cat.error()
7268  << "Unable to generate mipmaps for 3D texture with component type "
7269  << cdata->_component_type << "!";
7270  return;
7271  }
7272  filter_alpha = filter_component;
7273  }
7274 
7275  size_t pixel_size = cdata->_num_components * cdata->_component_width;
7276  size_t row_size = (size_t)x_size * pixel_size;
7277  size_t page_size = (size_t)y_size * row_size;
7278  size_t view_size = (size_t)z_size * page_size;
7279 
7280  int to_x_size = max(x_size >> 1, 1);
7281  int to_y_size = max(y_size >> 1, 1);
7282  int to_z_size = max(z_size >> 1, 1);
7283 
7284  size_t to_row_size = (size_t)to_x_size * pixel_size;
7285  size_t to_page_size = (size_t)to_y_size * to_row_size;
7286  size_t to_view_size = (size_t)to_z_size * to_page_size;
7287  to._page_size = to_page_size;
7288  to._image = PTA_uchar::empty_array(to_page_size * to_z_size * cdata->_num_views, get_class_type());
7289 
7290  bool alpha = has_alpha(cdata->_format);
7291  int num_color_components = cdata->_num_components;
7292  if (alpha) {
7293  --num_color_components;
7294  }
7295 
7296  for (int view = 0; view < cdata->_num_views; ++view) {
7297  unsigned char *start_to = to._image.p() + view * to_view_size;
7298  const unsigned char *start_from = from._image.p() + view * view_size;
7299  nassertv(start_to + to_view_size <= to._image.p() + to._image.size());
7300  nassertv(start_from + view_size <= from._image.p() + from._image.size());
7301  unsigned char *p = start_to;
7302  const unsigned char *q = start_from;
7303  if (z_size != 1) {
7304  int z;
7305  for (z = 0; z < z_size - 1; z += 2) {
7306  // For each level.
7307  nassertv(p == start_to + (z / 2) * to_page_size);
7308  nassertv(q == start_from + z * page_size);
7309  if (y_size != 1) {
7310  int y;
7311  for (y = 0; y < y_size - 1; y += 2) {
7312  // For each row.
7313  nassertv(p == start_to + (z / 2) * to_page_size + (y / 2) * to_row_size);
7314  nassertv(q == start_from + z * page_size + y * row_size);
7315  if (x_size != 1) {
7316  int x;
7317  for (x = 0; x < x_size - 1; x += 2) {
7318  // For each pixel.
7319  for (int c = 0; c < num_color_components; ++c) {
7320  // For each component.
7321  filter_component(p, q, pixel_size, row_size, page_size);
7322  }
7323  if (alpha) {
7324  filter_alpha(p, q, pixel_size, row_size, page_size);
7325  }
7326  q += pixel_size;
7327  }
7328  if (x < x_size) {
7329  // Skip the last odd pixel.
7330  q += pixel_size;
7331  }
7332  } else {
7333  // Just one pixel.
7334  for (int c = 0; c < num_color_components; ++c) {
7335  // For each component.
7336  filter_component(p, q, 0, row_size, page_size);
7337  }
7338  if (alpha) {
7339  filter_alpha(p, q, 0, row_size, page_size);
7340  }
7341  }
7342  q += row_size;
7344  }
7345  if (y < y_size) {
7346  // Skip the last odd row.
7347  q += row_size;
7348  }
7349  } else {
7350  // Just one row.
7351  if (x_size != 1) {
7352  int x;
7353  for (x = 0; x < x_size - 1; x += 2) {
7354  // For each pixel.
7355  for (int c = 0; c < num_color_components; ++c) {
7356  // For each component.
7357  filter_component(p, q, pixel_size, 0, page_size);
7358  }
7359  if (alpha) {
7360  filter_alpha(p, q, pixel_size, 0, page_size);
7361  }
7362  q += pixel_size;
7363  }
7364  if (x < x_size) {
7365  // Skip the last odd pixel.
7366  q += pixel_size;
7367  }
7368  } else {
7369  // Just one pixel.
7370  for (int c = 0; c < num_color_components; ++c) {
7371  // For each component.
7372  filter_component(p, q, 0, 0, page_size);
7373  }
7374  if (alpha) {
7375  filter_alpha(p, q, 0, 0, page_size);
7376  }
7377  }
7378  }
7379  q += page_size;
7380  }
7381  if (z < z_size) {
7382  // Skip the last odd page.
7383  q += page_size;
7384  }
7385  } else {
7386  // Just one page.
7387  if (y_size != 1) {
7388  int y;
7389  for (y = 0; y < y_size - 1; y += 2) {
7390  // For each row.
7391  nassertv(p == start_to + (y / 2) * to_row_size);
7392  nassertv(q == start_from + y * row_size);
7393  if (x_size != 1) {
7394  int x;
7395  for (x = 0; x < x_size - 1; x += 2) {
7396  // For each pixel.
7397  for (int c = 0; c < num_color_components; ++c) {
7398  // For each component.
7399  filter_component(p, q, pixel_size, row_size, 0);
7400  }
7401  if (alpha) {
7402  filter_alpha(p, q, pixel_size, row_size, 0);
7403  }
7404  q += pixel_size;
7405  }
7406  if (x < x_size) {
7407  // Skip the last odd pixel.
7408  q += pixel_size;
7409  }
7410  } else {
7411  // Just one pixel.
7412  for (int c = 0; c < num_color_components; ++c) {
7413  // For each component.
7414  filter_component(p, q, 0, row_size, 0);
7415  }
7416  if (alpha) {
7417  filter_alpha(p, q, 0, row_size, 0);
7418  }
7419  }
7420  q += row_size;
7422  }
7423  if (y < y_size) {
7424  // Skip the last odd row.
7425  q += row_size;
7426  }
7427  } else {
7428  // Just one row.
7429  if (x_size != 1) {
7430  int x;
7431  for (x = 0; x < x_size - 1; x += 2) {
7432  // For each pixel.
7433  for (int c = 0; c < num_color_components; ++c) {
7434  // For each component.
7435  filter_component(p, q, pixel_size, 0, 0);
7436  }
7437  if (alpha) {
7438  filter_alpha(p, q, pixel_size, 0, 0);
7439  }
7440  q += pixel_size;
7441  }
7442  if (x < x_size) {
7443  // Skip the last odd pixel.
7444  q += pixel_size;
7445  }
7446  } else {
7447  // Just one pixel.
7448  for (int c = 0; c < num_color_components; ++c) {
7449  // For each component.
7450  filter_component(p, q, 0, 0, 0);
7451  }
7452  if (alpha) {
7453  filter_alpha(p, q, 0, 0, 0);
7454  }
7455  }
7456  }
7457  }
7458 
7459  nassertv(p == start_to + to_z_size * to_page_size);
7460  nassertv(q == start_from + z_size * page_size);
7461  }
7462 }
7463 
7464 ////////////////////////////////////////////////////////////////////
7465 // Function: Texture::filter_2d_unsigned_byte
7466 // Access: Public, Static
7467 // Description: Averages a 2x2 block of pixel components into a
7468 // single pixel component, for producing the next mipmap
7469 // level. Increments p and q to the next component.
7470 ////////////////////////////////////////////////////////////////////
7471 void Texture::
7472 filter_2d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
7473  size_t pixel_size, size_t row_size) {
7474  unsigned int result = ((unsigned int)q[0] +
7475  (unsigned int)q[pixel_size] +
7476  (unsigned int)q[row_size] +
7477  (unsigned int)q[pixel_size + row_size]) >> 2;
7478  *p = (unsigned char)result;
7479  ++p;
7480  ++q;
7481 }
7482 
7483 ////////////////////////////////////////////////////////////////////
7484 // Function: Texture::filter_2d_unsigned_byte_srgb
7485 // Access: Public, Static
7486 // Description: Averages a 2x2 block of pixel components into a
7487 // single pixel component, for producing the next mipmap
7488 // level. Increments p and q to the next component.
7489 ////////////////////////////////////////////////////////////////////
7490 void Texture::
7491 filter_2d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
7492  size_t pixel_size, size_t row_size) {
7493  float result = (decode_sRGB_float(q[0]) +
7494  decode_sRGB_float(q[pixel_size]) +
7495  decode_sRGB_float(q[row_size]) +
7496  decode_sRGB_float(q[pixel_size + row_size]));
7497 
7498  *p = encode_sRGB_uchar(result * 0.25f);
7499  ++p;
7500  ++q;
7501 }
7502 
7503 ////////////////////////////////////////////////////////////////////
7504 // Function: Texture::filter_2d_unsigned_byte_srgb_sse2
7505 // Access: Public, Static
7506 // Description: Averages a 2x2 block of pixel components into a
7507 // single pixel component, for producing the next mipmap
7508 // level. Increments p and q to the next component.
7509 ////////////////////////////////////////////////////////////////////
7510 void Texture::
7511 filter_2d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
7512  size_t pixel_size, size_t row_size) {
7513  float result = (decode_sRGB_float(q[0]) +
7514  decode_sRGB_float(q[pixel_size]) +
7515  decode_sRGB_float(q[row_size]) +
7516  decode_sRGB_float(q[pixel_size + row_size]));
7517 
7518  *p = encode_sRGB_uchar_sse2(result * 0.25f);
7519  ++p;
7520  ++q;
7521 }
7522 
7523 ////////////////////////////////////////////////////////////////////
7524 // Function: Texture::filter_2d_unsigned_short
7525 // Access: Public, Static
7526 // Description: Averages a 2x2 block of pixel components into a
7527 // single pixel component, for producing the next mipmap
7528 // level. Increments p and q to the next component.
7529 ////////////////////////////////////////////////////////////////////
7530 void Texture::
7531 filter_2d_unsigned_short(unsigned char *&p, const unsigned char *&q,
7532  size_t pixel_size, size_t row_size) {
7533  unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
7534  (unsigned int)*(unsigned short *)&q[pixel_size] +
7535  (unsigned int)*(unsigned short *)&q[row_size] +
7536  (unsigned int)*(unsigned short *)&q[pixel_size + row_size]) >> 2;
7537  store_unscaled_short(p, result);
7538  q += 2;
7539 }
7540 
7541 ////////////////////////////////////////////////////////////////////
7542 // Function: Texture::filter_2d_float
7543 // Access: Public, Static
7544 // Description: Averages a 2x2 block of pixel components into a
7545 // single pixel component, for producing the next mipmap
7546 // level. Increments p and q to the next component.
7547 ////////////////////////////////////////////////////////////////////
7548 void Texture::
7549 filter_2d_float(unsigned char *&p, const unsigned char *&q,
7550  size_t pixel_size, size_t row_size) {
7551  *(float *)p = (*(float *)&q[0] +
7552  *(float *)&q[pixel_size] +
7553  *(float *)&q[row_size] +
7554  *(float *)&q[pixel_size + row_size]) / 4.0f;
7555  p += 4;
7556  q += 4;
7557 }
7558 
7559 ////////////////////////////////////////////////////////////////////
7560 // Function: Texture::filter_3d_unsigned_byte
7561 // Access: Public, Static
7562 // Description: Averages a 2x2x2 block of pixel components into a
7563 // single pixel component, for producing the next mipmap
7564 // level. Increments p and q to the next component.
7565 ////////////////////////////////////////////////////////////////////
7566 void Texture::
7567 filter_3d_unsigned_byte(unsigned char *&p, const unsigned char *&q,
7568  size_t pixel_size, size_t row_size, size_t page_size) {
7569  unsigned int result = ((unsigned int)q[0] +
7570  (unsigned int)q[pixel_size] +
7571  (unsigned int)q[row_size] +
7572  (unsigned int)q[pixel_size + row_size] +
7573  (unsigned int)q[page_size] +
7574  (unsigned int)q[pixel_size + page_size] +
7575  (unsigned int)q[row_size + page_size] +
7576  (unsigned int)q[pixel_size + row_size + page_size]) >> 3;
7577  *p = (unsigned char)result;
7578  ++p;
7579  ++q;
7580 }
7581 
7582 ////////////////////////////////////////////////////////////////////
7583 // Function: Texture::filter_3d_unsigned_byte_srgb
7584 // Access: Public, Static
7585 // Description: Averages a 2x2x2 block of pixel components into a
7586 // single pixel component, for producing the next mipmap
7587 // level. Increments p and q to the next component.
7588 ////////////////////////////////////////////////////////////////////
7589 void Texture::
7590 filter_3d_unsigned_byte_srgb(unsigned char *&p, const unsigned char *&q,
7591  size_t pixel_size, size_t row_size, size_t page_size) {
7592  float result = (decode_sRGB_float(q[0]) +
7593  decode_sRGB_float(q[pixel_size]) +
7594  decode_sRGB_float(q[row_size]) +
7595  decode_sRGB_float(q[pixel_size + row_size]) +
7596  decode_sRGB_float(q[page_size]) +
7597  decode_sRGB_float(q[pixel_size + page_size]) +
7598  decode_sRGB_float(q[row_size + page_size]) +
7599  decode_sRGB_float(q[pixel_size + row_size + page_size]));
7600 
7601  *p = encode_sRGB_uchar(result * 0.125f);
7602  ++p;
7603  ++q;
7604 }
7605 
7606 ////////////////////////////////////////////////////////////////////
7607 // Function: Texture::filter_3d_unsigned_byte_srgb_sse2
7608 // Access: Public, Static
7609 // Description: Averages a 2x2x2 block of pixel components into a
7610 // single pixel component, for producing the next mipmap
7611 // level. Increments p and q to the next component.
7612 ////////////////////////////////////////////////////////////////////
7613 void Texture::
7614 filter_3d_unsigned_byte_srgb_sse2(unsigned char *&p, const unsigned char *&q,
7615  size_t pixel_size, size_t row_size, size_t page_size) {
7616  float result = (decode_sRGB_float(q[0]) +
7617  decode_sRGB_float(q[pixel_size]) +
7618  decode_sRGB_float(q[row_size]) +
7619  decode_sRGB_float(q[pixel_size + row_size]) +
7620  decode_sRGB_float(q[page_size]) +
7621  decode_sRGB_float(q[pixel_size + page_size]) +
7622  decode_sRGB_float(q[row_size + page_size]) +
7623  decode_sRGB_float(q[pixel_size + row_size + page_size]));
7624 
7625  *p = encode_sRGB_uchar_sse2(result * 0.125f);
7626  ++p;
7627  ++q;
7628 }
7629 
7630 ////////////////////////////////////////////////////////////////////
7631 // Function: Texture::filter_3d_unsigned_short
7632 // Access: Public, Static
7633 // Description: Averages a 2x2x2 block of pixel components into a
7634 // single pixel component, for producing the next mipmap
7635 // level. Increments p and q to the next component.
7636 ////////////////////////////////////////////////////////////////////
7637 void Texture::
7638 filter_3d_unsigned_short(unsigned char *&p, const unsigned char *&q,
7639  size_t pixel_size, size_t row_size,
7640  size_t page_size) {
7641  unsigned int result = ((unsigned int)*(unsigned short *)&q[0] +
7642  (unsigned int)*(unsigned short *)&q[pixel_size] +
7643  (unsigned int)*(unsigned short *)&q[row_size] +
7644  (unsigned int)*(unsigned short *)&q[pixel_size + row_size] +
7645  (unsigned int)*(unsigned short *)&q[page_size] +
7646  (unsigned int)*(unsigned short *)&q[pixel_size + page_size] +
7647  (unsigned int)*(unsigned short *)&q[row_size + page_size] +
7648  (unsigned int)*(unsigned short *)&q[pixel_size + row_size + page_size]) >> 3;
7649  store_unscaled_short(p, result);
7650  q += 2;
7651 }
7652 
7653 ////////////////////////////////////////////////////////////////////
7654 // Function: Texture::filter_3d_float
7655 // Access: Public, Static
7656 // Description: Averages a 2x2x2 block of pixel components into a
7657 // single pixel component, for producing the next mipmap
7658 // level. Increments p and q to the next component.
7659 ////////////////////////////////////////////////////////////////////
7660 void Texture::
7661 filter_3d_float(unsigned char *&p, const unsigned char *&q,
7662  size_t pixel_size, size_t row_size, size_t page_size) {
7663  *(float *)p = (*(float *)&q[0] +
7664  *(float *)&q[pixel_size] +
7665  *(float *)&q[row_size] +
7666  *(float *)&q[pixel_size + row_size] +
7667  *(float *)&q[page_size] +
7668  *(float *)&q[pixel_size + page_size] +
7669  *(float *)&q[row_size + page_size] +
7670  *(float *)&q[pixel_size + row_size + page_size]) / 8.0f;
7671  p += 4;
7672  q += 4;
7673 }
7674 
7675 ////////////////////////////////////////////////////////////////////
7676 // Function: Texture::do_squish
7677 // Access: Private
7678 // Description: Invokes the squish library to compress the RAM
7679 // image(s).
7680 ////////////////////////////////////////////////////////////////////
7681 bool Texture::
7682 do_squish(CData *cdata, Texture::CompressionMode compression, int squish_flags) {
7683 #ifdef HAVE_SQUISH
7684  if (cdata->_ram_images.empty() || cdata->_ram_image_compression != CM_off) {
7685  return false;
7686  }
7687 
7688  if (!do_has_all_ram_mipmap_images(cdata)) {
7689  // If we're about to compress the RAM image, we should ensure that
7690  // we have all of the mipmap levels first.
7691  do_generate_ram_mipmap_images(cdata);
7692  }
7693 
7694  RamImages compressed_ram_images;
7695  compressed_ram_images.reserve(cdata->_ram_images.size());
7696  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
7697  RamImage compressed_image;
7698  int x_size = do_get_expected_mipmap_x_size(cdata, n);
7699  int y_size = do_get_expected_mipmap_y_size(cdata, n);
7700  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
7701  int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
7702  int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
7703 
7704  compressed_image._page_size = page_size;
7705  compressed_image._image = PTA_uchar::empty_array(page_size * num_pages);
7706  for (int z = 0; z < num_pages; ++z) {
7707  unsigned char *dest_page = compressed_image._image.p() + z * page_size;
7708  unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * cdata->_ram_images[n]._page_size;
7709  unsigned const char *source_page_end = source_page + cdata->_ram_images[n]._page_size;
7710  // Convert one 4 x 4 cell at a time.
7711  unsigned char *d = dest_page;
7712  for (int y = 0; y < y_size; y += 4) {
7713  for (int x = 0; x < x_size; x += 4) {
7714  unsigned char tb[16 * 4];
7715  int mask = 0;
7716  unsigned char *t = tb;
7717  for (int i = 0; i < 16; ++i) {
7718  int xi = x + i % 4;
7719  int yi = y + i / 4;
7720  unsigned const char *s = source_page + (yi * x_size + xi) * cdata->_num_components;
7721  if (s < source_page_end) {
7722  switch (cdata->_num_components) {
7723  case 1:
7724  t[0] = s[0]; // r
7725  t[1] = s[0]; // g
7726  t[2] = s[0]; // b
7727  t[3] = 255; // a
7728  break;
7729 
7730  case 2:
7731  t[0] = s[0]; // r
7732  t[1] = s[0]; // g
7733  t[2] = s[0]; // b
7734  t[3] = s[1]; // a
7735  break;
7736 
7737  case 3:
7738  t[0] = s[2]; // r
7739  t[1] = s[1]; // g
7740  t[2] = s[0]; // b
7741  t[3] = 255; // a
7742  break;
7743 
7744  case 4:
7745  t[0] = s[2]; // r
7746  t[1] = s[1]; // g
7747  t[2] = s[0]; // b
7748  t[3] = s[3]; // a
7749  break;
7750  }
7751  mask |= (1 << i);
7752  }
7753  t += 4;
7754  }
7755  squish::CompressMasked(tb, mask, d, squish_flags);
7756  d += cell_size;
7758  }
7759  }
7760  }
7761  compressed_ram_images.push_back(compressed_image);
7762  }
7763  cdata->_ram_images.swap(compressed_ram_images);
7764  cdata->_ram_image_compression = compression;
7765  return true;
7766 
7767 #else // HAVE_SQUISH
7768  return false;
7769 
7770 #endif // HAVE_SQUISH
7771 }
7772 
7773 ////////////////////////////////////////////////////////////////////
7774 // Function: Texture::do_unsquish
7775 // Access: Private
7776 // Description: Invokes the squish library to uncompress the RAM
7777 // image(s).
7778 ////////////////////////////////////////////////////////////////////
7779 bool Texture::
7780 do_unsquish(CData *cdata, int squish_flags) {
7781 #ifdef HAVE_SQUISH
7782  if (cdata->_ram_images.empty()) {
7783  return false;
7784  }
7785  RamImages uncompressed_ram_images;
7786  uncompressed_ram_images.reserve(cdata->_ram_images.size());
7787  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
7788  RamImage uncompressed_image;
7789  int x_size = do_get_expected_mipmap_x_size(cdata, n);
7790  int y_size = do_get_expected_mipmap_y_size(cdata, n);
7791  int num_pages = do_get_expected_mipmap_num_pages(cdata, n);
7792  int page_size = squish::GetStorageRequirements(x_size, y_size, squish_flags);
7793  int cell_size = squish::GetStorageRequirements(4, 4, squish_flags);
7794 
7795  uncompressed_image._page_size = do_get_expected_ram_mipmap_page_size(cdata, n);
7796  uncompressed_image._image = PTA_uchar::empty_array(uncompressed_image._page_size * num_pages);
7797  for (int z = 0; z < num_pages; ++z) {
7798  unsigned char *dest_page = uncompressed_image._image.p() + z * uncompressed_image._page_size;
7799  unsigned char *dest_page_end = dest_page + uncompressed_image._page_size;
7800  unsigned const char *source_page = cdata->_ram_images[n]._image.p() + z * page_size;
7801  // Unconvert one 4 x 4 cell at a time.
7802  unsigned const char *s = source_page;
7803  for (int y = 0; y < y_size; y += 4) {
7804  for (int x = 0; x < x_size; x += 4) {
7805  unsigned char tb[16 * 4];
7806  squish::Decompress(tb, s, squish_flags);
7807  s += cell_size;
7808 
7809  unsigned char *t = tb;
7810  for (int i = 0; i < 16; ++i) {
7811  int xi = x + i % 4;
7812  int yi = y + i / 4;
7813  unsigned char *d = dest_page + (yi * x_size + xi) * cdata->_num_components;
7814  if (d < dest_page_end) {
7815  switch (cdata->_num_components) {
7816  case 1:
7817  d[0] = t[1]; // g
7818  break;
7819 
7820  case 2:
7821  d[0] = t[1]; // g
7822  d[1] = t[3]; // a
7823  break;
7824 
7825  case 3:
7826  d[2] = t[0]; // r
7827  d[1] = t[1]; // g
7828  d[0] = t[2]; // b
7829  break;
7830 
7831  case 4:
7832  d[2] = t[0]; // r
7833  d[1] = t[1]; // g
7834  d[0] = t[2]; // b
7835  d[3] = t[3]; // a
7836  break;
7837  }
7838  }
7839  t += 4;
7840  }
7841  }
7843  }
7844  }
7845  uncompressed_ram_images.push_back(uncompressed_image);
7846  }
7847  cdata->_ram_images.swap(uncompressed_ram_images);
7848  cdata->_ram_image_compression = CM_off;
7849  return true;
7850 
7851 #else // HAVE_SQUISH
7852  return false;
7853 
7854 #endif // HAVE_SQUISH
7855 }
7856 
7857 ////////////////////////////////////////////////////////////////////
7858 // Function: Texture::register_with_read_factory
7859 // Access: Public, Static
7860 // Description: Factory method to generate a Texture object
7861 ////////////////////////////////////////////////////////////////////
7862 void Texture::
7864  BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
7865 }
7866 
7867 ////////////////////////////////////////////////////////////////////
7868 // Function: Texture::write_datagram
7869 // Access: Public, Virtual
7870 // Description: Function to write the important information in
7871 // the particular object to a Datagram
7872 ////////////////////////////////////////////////////////////////////
7873 void Texture::
7875  CDWriter cdata(_cycler, false);
7876 
7877  bool has_rawdata = false;
7878  do_write_datagram_header(cdata, manager, me, has_rawdata);
7879  do_write_datagram_body(cdata, manager, me);
7880 
7881  // If we are also including the texture's image data, then stuff it
7882  // in here.
7883  if (has_rawdata) {
7884  do_write_datagram_rawdata(cdata, manager, me);
7885  }
7886 }
7887 
7888 ////////////////////////////////////////////////////////////////////
7889 // Function: Texture::finalize
7890 // Access: Public, Virtual
7891 // Description: Called by the BamReader to perform any final actions
7892 // needed for setting up the object after all objects
7893 // have been read and all pointers have been completed.
7894 ////////////////////////////////////////////////////////////////////
7895 void Texture::
7897  // Unref the pointer that we explicitly reffed in make_from_bam().
7898  unref();
7899 
7900  // We should never get back to zero after unreffing our own count,
7901  // because we expect to have been stored in a pointer somewhere. If
7902  // we do get to zero, it's a memory leak; the way to avoid this is
7903  // to call unref_delete() above instead of unref(), but this is
7904  // dangerous to do from within a virtual function.
7905  nassertv(get_ref_count() != 0);
7906 }
7907 
7908 
7909 ////////////////////////////////////////////////////////////////////
7910 // Function: Texture::do_write_datagram_header
7911 // Access: Protected
7912 // Description: Writes the header part of the texture to the
7913 // Datagram. This is the common part that is shared by
7914 // all Texture subclasses, and contains the filename and
7915 // rawdata flags. This method is not virtual because
7916 // all Texture subclasses must write the same data at
7917 // this step.
7918 //
7919 // This part must be read first before calling
7920 // do_fillin_body() to determine whether to load the
7921 // Texture from the TexturePool or directly from the bam
7922 // stream.
7923 //
7924 // After this call, has_rawdata will be filled with
7925 // either true or false, according to whether we expect
7926 // to write the texture rawdata to the bam stream
7927 // following the texture body.
7928 ////////////////////////////////////////////////////////////////////
7929 void Texture::
7930 do_write_datagram_header(CData *cdata, BamWriter *manager, Datagram &me, bool &has_rawdata) {
7931  // Write out the texture's raw pixel data if (a) the current Bam
7932  // Texture Mode requires that, or (b) there's no filename, so the
7933  // file can't be loaded up from disk, but the raw pixel data is
7934  // currently available in RAM.
7935 
7936  // Otherwise, we just write out the filename, and assume whoever
7937  // loads the bam file later will have access to the image file on
7938  // disk.
7939  BamWriter::BamTextureMode file_texture_mode = manager->get_file_texture_mode();
7940  has_rawdata = (file_texture_mode == BamWriter::BTM_rawdata ||
7941  (cdata->_filename.empty() && do_has_bam_rawdata(cdata)));
7942  if (has_rawdata && !do_has_bam_rawdata(cdata)) {
7943  do_get_bam_rawdata(cdata);
7944  if (!do_has_bam_rawdata(cdata)) {
7945  // No image data after all.
7946  has_rawdata = false;
7947  }
7948  }
7949 
7950  bool has_bam_dir = !manager->get_filename().empty();
7951  Filename bam_dir = manager->get_filename().get_dirname();
7952  Filename filename = cdata->_filename;
7953  Filename alpha_filename = cdata->_alpha_filename;
7954 
7956 
7957  switch (file_texture_mode) {
7958  case BamWriter::BTM_unchanged:
7959  case BamWriter::BTM_rawdata:
7960  break;
7961 
7962  case BamWriter::BTM_fullpath:
7963  filename = cdata->_fullpath;
7964  alpha_filename = cdata->_alpha_fullpath;
7965  break;
7966 
7967  case BamWriter::BTM_relative:
7968  filename = cdata->_fullpath;
7969  alpha_filename = cdata->_alpha_fullpath;
7970  bam_dir.make_absolute(vfs->get_cwd());
7971  if (!has_bam_dir || !filename.make_relative_to(bam_dir, true)) {
7972  filename.find_on_searchpath(get_model_path());
7973  }
7974  if (gobj_cat.is_debug()) {
7975  gobj_cat.debug()
7976  << "Texture file " << cdata->_fullpath
7977  << " found as " << filename << "\n";
7978  }
7979  if (!has_bam_dir || !alpha_filename.make_relative_to(bam_dir, true)) {
7980  alpha_filename.find_on_searchpath(get_model_path());
7981  }
7982  if (gobj_cat.is_debug()) {
7983  gobj_cat.debug()
7984  << "Alpha image " << cdata->_alpha_fullpath
7985  << " found as " << alpha_filename << "\n";
7986  }
7987  break;
7988 
7989  case BamWriter::BTM_basename:
7990  filename = cdata->_fullpath.get_basename();
7991  alpha_filename = cdata->_alpha_fullpath.get_basename();
7992  break;
7993 
7994  default:
7995  gobj_cat.error()
7996  << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
7997  }
7998 
7999  if (filename.empty() && do_has_bam_rawdata(cdata)) {
8000  // If we don't have a filename, we have to store rawdata anyway.
8001  has_rawdata = true;
8002  }
8003 
8004  me.add_string(get_name());
8005  me.add_string(filename);
8006  me.add_string(alpha_filename);
8007  me.add_uint8(cdata->_primary_file_num_channels);
8008  me.add_uint8(cdata->_alpha_file_channel);
8009  me.add_bool(has_rawdata);
8010  me.add_uint8(cdata->_texture_type);
8011  me.add_bool(cdata->_has_read_mipmaps);
8012 }
8013 
8014 ////////////////////////////////////////////////////////////////////
8015 // Function: Texture::do_write_datagram_body
8016 // Access: Protected, Virtual
8017 // Description: Writes the body part of the texture to the
8018 // Datagram. This is generally all of the texture
8019 // parameters except for the header and the rawdata.
8020 ////////////////////////////////////////////////////////////////////
8021 void Texture::
8022 do_write_datagram_body(CData *cdata, BamWriter *manager, Datagram &me) {
8023  cdata->_default_sampler.write_datagram(me);
8024 
8025  me.add_uint8(cdata->_compression);
8026  me.add_uint8(cdata->_quality_level);
8027 
8028  me.add_uint8(cdata->_format);
8029  me.add_uint8(cdata->_num_components);
8030 
8031  me.add_uint8(cdata->_auto_texture_scale);
8032  me.add_uint32(cdata->_orig_file_x_size);
8033  me.add_uint32(cdata->_orig_file_y_size);
8034 
8035  bool has_simple_ram_image = !cdata->_simple_ram_image._image.empty();
8036  me.add_bool(has_simple_ram_image);
8037 
8038  // Write out the simple image too, so it will be available later.
8039  if (has_simple_ram_image) {
8040  me.add_uint32(cdata->_simple_x_size);
8041  me.add_uint32(cdata->_simple_y_size);
8042  me.add_int32(cdata->_simple_image_date_generated);
8043  me.add_uint32(cdata->_simple_ram_image._image.size());
8044  me.append_data(cdata->_simple_ram_image._image, cdata->_simple_ram_image._image.size());
8045  }
8046 }
8047 
8048 ////////////////////////////////////////////////////////////////////
8049 // Function: Texture::do_write_datagram_rawdata
8050 // Access: Protected, Virtual
8051 // Description: Writes the rawdata part of the texture to the
8052 // Datagram.
8053 ////////////////////////////////////////////////////////////////////
8054 void Texture::
8055 do_write_datagram_rawdata(CData *cdata, BamWriter *manager, Datagram &me) {
8056  me.add_uint32(cdata->_x_size);
8057  me.add_uint32(cdata->_y_size);
8058  me.add_uint32(cdata->_z_size);
8059 
8060  me.add_uint32(cdata->_pad_x_size);
8061  me.add_uint32(cdata->_pad_y_size);
8062  me.add_uint32(cdata->_pad_z_size);
8063 
8064  me.add_uint32(cdata->_num_views);
8065  me.add_uint8(cdata->_component_type);
8066  me.add_uint8(cdata->_component_width);
8067  me.add_uint8(cdata->_ram_image_compression);
8068  me.add_uint8(cdata->_ram_images.size());
8069  for (size_t n = 0; n < cdata->_ram_images.size(); ++n) {
8070  me.add_uint32(cdata->_ram_images[n]._page_size);
8071  me.add_uint32(cdata->_ram_images[n]._image.size());
8072  me.append_data(cdata->_ram_images[n]._image, cdata->_ram_images[n]._image.size());
8073  }
8074 }
8075 
8076 ////////////////////////////////////////////////////////////////////
8077 // Function: Texture::make_from_bam
8078 // Access: Protected, Static
8079 // Description: Factory method to generate a Texture object
8080 ////////////////////////////////////////////////////////////////////
8081 TypedWritable *Texture::
8082 make_from_bam(const FactoryParams &params) {
8083  PT(Texture) dummy = new Texture;
8084  return dummy->make_this_from_bam(params);
8085 }
8086 
8087 ////////////////////////////////////////////////////////////////////
8088 // Function: Texture::make_this_from_bam
8089 // Access: Protected, Virtual
8090 // Description: Called by make_from_bam() once the particular
8091 // subclass of Texture is known. This is called on a
8092 // newly-constructed Texture object of the appropriate
8093 // subclass. It will return either the same Texture
8094 // object (e.g. this), or a different Texture object
8095 // loaded via the TexturePool, as appropriate.
8096 ////////////////////////////////////////////////////////////////////
8097 TypedWritable *Texture::
8098 make_this_from_bam(const FactoryParams &params) {
8099  // The process of making a texture is slightly different than making
8100  // other TypedWritable objects. That is because all creation of
8101  // Textures should be done through calls to TexturePool, which
8102  // ensures that any loads of the same filename refer to the same
8103  // memory.
8104 
8105  DatagramIterator scan;
8106  BamReader *manager;
8107 
8108  parse_params(params, scan, manager);
8109 
8110  // Get the header information--the filenames and texture type--so we
8111  // can look up the file on disk first.
8112  string name = scan.get_string();
8113  Filename filename = scan.get_string();
8114  Filename alpha_filename = scan.get_string();
8115 
8116  int primary_file_num_channels = scan.get_uint8();
8117  int alpha_file_channel = scan.get_uint8();
8118  bool has_rawdata = scan.get_bool();
8119  TextureType texture_type = (TextureType)scan.get_uint8();
8120  if (manager->get_file_minor_ver() < 25) {
8121  // Between Panda3D releases 1.7.2 and 1.8.0 (bam versions 6.24 and
8122  // 6.25), we added TT_2d_texture_array, shifting the definition
8123  // for TT_cube_map.
8124  if (texture_type == TT_2d_texture_array) {
8125  texture_type = TT_cube_map;
8126  }
8127  }
8128  bool has_read_mipmaps = false;
8129  if (manager->get_file_minor_ver() >= 32) {
8130  has_read_mipmaps = scan.get_bool();
8131  }
8132 
8133  Texture *me = NULL;
8134  if (has_rawdata) {
8135  // If the raw image data is included, then just load the texture
8136  // directly from the stream, and return it. In this case we
8137  // return the "this" pointer, since it's a newly-created Texture
8138  // object of the appropriate type.
8139  me = this;
8140  me->set_name(name);
8141  CDWriter cdata_me(me->_cycler, true);
8142  cdata_me->_filename = filename;
8143  cdata_me->_alpha_filename = alpha_filename;
8144  cdata_me->_primary_file_num_channels = primary_file_num_channels;
8145  cdata_me->_alpha_file_channel = alpha_file_channel;
8146  cdata_me->_texture_type = texture_type;
8147  cdata_me->_has_read_mipmaps = has_read_mipmaps;
8148 
8149  // Read the texture attributes directly from the bam stream.
8150  me->do_fillin_body(cdata_me, scan, manager);
8151  me->do_fillin_rawdata(cdata_me, scan, manager);
8152 
8153  // To manage the reference count, explicitly ref it now, then
8154  // unref it in the finalize callback.
8155  me->ref();
8156  manager->register_finalize(me);
8157 
8158  } else {
8159  // The raw image data isn't included, so we'll be loading the
8160  // Texture via the TexturePool. In this case we use the "this"
8161  // pointer as a temporary object to read all of the attributes
8162  // from the bam stream.
8163  Texture *dummy = this;
8164  AutoTextureScale auto_texture_scale = ATS_unspecified;
8165  {
8166  CDWriter cdata_dummy(dummy->_cycler, true);
8167  dummy->do_fillin_body(cdata_dummy, scan, manager);
8168  auto_texture_scale = cdata_dummy->_auto_texture_scale;
8169  }
8170 
8171  if (filename.empty()) {
8172  // This texture has no filename; since we don't have an image to
8173  // load, we can't actually create the texture.
8174  gobj_cat.info()
8175  << "Cannot create texture '" << name << "' with no filename.\n";
8176 
8177  } else {
8178  // This texture does have a filename, so try to load it from disk.
8180  if (!manager->get_filename().empty()) {
8181  // If texture filename was given relative to the bam filename,
8182  // expand it now.
8183  Filename bam_dir = manager->get_filename().get_dirname();
8184  vfs->resolve_filename(filename, bam_dir);
8185  if (!alpha_filename.empty()) {
8186  vfs->resolve_filename(alpha_filename, bam_dir);
8187  }
8188  }
8189 
8190  LoaderOptions options = manager->get_loader_options();
8191  if (dummy->uses_mipmaps()) {
8192  options.set_texture_flags(options.get_texture_flags() | LoaderOptions::TF_generate_mipmaps);
8193  }
8194  options.set_auto_texture_scale(auto_texture_scale);
8195 
8196  switch (texture_type) {
8197  case TT_1d_texture:
8198  case TT_2d_texture:
8199  if (alpha_filename.empty()) {
8200  me = TexturePool::load_texture(filename, primary_file_num_channels,
8201  has_read_mipmaps, options);
8202  } else {
8203  me = TexturePool::load_texture(filename, alpha_filename,
8204  primary_file_num_channels,
8205  alpha_file_channel,
8206  has_read_mipmaps, options);
8207  }
8208  break;
8209 
8210  case TT_3d_texture:
8211  me = TexturePool::load_3d_texture(filename, has_read_mipmaps, options);
8212  break;
8213 
8214  case TT_2d_texture_array:
8215  me = TexturePool::load_2d_texture_array(filename, has_read_mipmaps, options);
8216  break;
8217 
8218  case TT_cube_map:
8219  me = TexturePool::load_cube_map(filename, has_read_mipmaps, options);
8220  break;
8221  }
8222  }
8223 
8224  if (me != (Texture *)NULL) {
8225  me->set_name(name);
8226  CDWriter cdata_me(me->_cycler, true);
8227  me->do_fillin_from(cdata_me, dummy);
8228 
8229  // Since in this case me was loaded from the TexturePool,
8230  // there's no need to explicitly manage the reference count.
8231  // TexturePool will hold it safely.
8232  }
8233  }
8234 
8235  return me;
8236 }
8237 
8238 ////////////////////////////////////////////////////////////////////
8239 // Function: Texture::do_fillin_body
8240 // Access: Protected, Virtual
8241 // Description: Reads in the part of the Texture that was written
8242 // with do_write_datagram_body().
8243 ////////////////////////////////////////////////////////////////////
8244 void Texture::
8245 do_fillin_body(CData *cdata, DatagramIterator &scan, BamReader *manager) {
8246  cdata->_default_sampler.read_datagram(scan, manager);
8247 
8248  if (manager->get_file_minor_ver() >= 1) {
8249  cdata->_compression = (CompressionMode)scan.get_uint8();
8250  }
8251  if (manager->get_file_minor_ver() >= 16) {
8252  cdata->_quality_level = (QualityLevel)scan.get_uint8();
8253  }
8254 
8255  cdata->_format = (Format)scan.get_uint8();
8256  cdata->_num_components = scan.get_uint8();
8257 
8258  if ((int)cdata->_texture_type == 5) {
8259  // As a kind little gesture to the people who try to load a Panda 1.10
8260  // bam in Panda 1.9.
8261  scan.get_uint8();
8262  gobj_cat.error()
8263  << "Buffer textures are not supported in this version of Panda3D.\n";
8264  }
8265 
8266  cdata->inc_properties_modified();
8267 
8268  cdata->_auto_texture_scale = ATS_unspecified;
8269  if (manager->get_file_minor_ver() >= 28) {
8270  cdata->_auto_texture_scale = (AutoTextureScale)scan.get_uint8();
8271  }
8272 
8273  bool has_simple_ram_image = false;
8274  if (manager->get_file_minor_ver() >= 18) {
8275  cdata->_orig_file_x_size = scan.get_uint32();
8276  cdata->_orig_file_y_size = scan.get_uint32();
8277 
8278  has_simple_ram_image = scan.get_bool();
8279  }
8280 
8281  if (has_simple_ram_image) {
8282  cdata->_simple_x_size = scan.get_uint32();
8283  cdata->_simple_y_size = scan.get_uint32();
8284  cdata->_simple_image_date_generated = scan.get_int32();
8285 
8286  size_t u_size = scan.get_uint32();
8287 
8288  // Protect against large allocation.
8289  if (u_size > scan.get_remaining_size()) {
8290  gobj_cat.error()
8291  << "simple RAM image extends past end of datagram, is texture corrupt?\n";
8292  return;
8293  }
8294 
8295  PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
8296  scan.extract_bytes(image.p(), u_size);
8297 
8298  cdata->_simple_ram_image._image = image;
8299  cdata->_simple_ram_image._page_size = u_size;
8300  cdata->inc_simple_image_modified();
8301  }
8302 }
8303 
8304 ////////////////////////////////////////////////////////////////////
8305 // Function: Texture::do_fillin_rawdata
8306 // Access: Protected, Virtual
8307 // Description: Reads in the part of the Texture that was written
8308 // with do_write_datagram_rawdata().
8309 ////////////////////////////////////////////////////////////////////
8310 void Texture::
8311 do_fillin_rawdata(CData *cdata, DatagramIterator &scan, BamReader *manager) {
8312  cdata->_x_size = scan.get_uint32();
8313  cdata->_y_size = scan.get_uint32();
8314  cdata->_z_size = scan.get_uint32();
8315 
8316  if (manager->get_file_minor_ver() >= 30) {
8317  cdata->_pad_x_size = scan.get_uint32();
8318  cdata->_pad_y_size = scan.get_uint32();
8319  cdata->_pad_z_size = scan.get_uint32();
8320  } else {
8321  do_set_pad_size(cdata, 0, 0, 0);
8322  }
8323 
8324  cdata->_num_views = 1;
8325  if (manager->get_file_minor_ver() >= 26) {
8326  cdata->_num_views = scan.get_uint32();
8327  }
8328  cdata->_component_type = (ComponentType)scan.get_uint8();
8329  cdata->_component_width = scan.get_uint8();
8330  cdata->_ram_image_compression = CM_off;
8331  if (manager->get_file_minor_ver() >= 1) {
8332  cdata->_ram_image_compression = (CompressionMode)scan.get_uint8();
8333  }
8334 
8335  int num_ram_images = 1;
8336  if (manager->get_file_minor_ver() >= 3) {
8337  num_ram_images = scan.get_uint8();
8338  }
8339 
8340  cdata->_ram_images.clear();
8341  cdata->_ram_images.reserve(num_ram_images);
8342  for (int n = 0; n < num_ram_images; ++n) {
8343  cdata->_ram_images.push_back(RamImage());
8344  cdata->_ram_images[n]._page_size = get_expected_ram_page_size();
8345  if (manager->get_file_minor_ver() >= 1) {
8346  cdata->_ram_images[n]._page_size = scan.get_uint32();
8347  }
8348 
8349  // fill the cdata->_image buffer with image data
8350  size_t u_size = scan.get_uint32();
8351 
8352  // Protect against large allocation.
8353  if (u_size > scan.get_remaining_size()) {
8354  gobj_cat.error()
8355  << "RAM image " << n << " extends past end of datagram, is texture corrupt?\n";
8356  return;
8357  }
8358 
8359  PTA_uchar image = PTA_uchar::empty_array(u_size, get_class_type());
8360  scan.extract_bytes(image.p(), u_size);
8361 
8362  cdata->_ram_images[n]._image = image;
8363  }
8364  cdata->_loaded_from_image = true;
8365  cdata->inc_image_modified();
8366 }
8367 
8368 ////////////////////////////////////////////////////////////////////
8369 // Function: Texture::do_fillin_from
8370 // Access: Protected, Virtual
8371 // Description: Called in make_from_bam(), this method properly
8372 // copies the attributes from the bam stream (as stored
8373 // in dummy) into this texture, updating the modified
8374 // flags appropriately.
8375 ////////////////////////////////////////////////////////////////////
8376 void Texture::
8377 do_fillin_from(CData *cdata, const Texture *dummy) {
8378  // Use the setters instead of setting these directly, so we can
8379  // correctly avoid incrementing cdata->_properties_modified if none of
8380  // these actually change. (Otherwise, we'd have to reload the
8381  // texture to the GSG every time we loaded a new bam file that
8382  // reference the texture, since each bam file reference passes
8383  // through this function.)
8384 
8385  CDReader cdata_dummy(dummy->_cycler);
8386 
8387  do_set_wrap_u(cdata, cdata_dummy->_default_sampler.get_wrap_u());
8388  do_set_wrap_v(cdata, cdata_dummy->_default_sampler.get_wrap_v());
8389  do_set_wrap_w(cdata, cdata_dummy->_default_sampler.get_wrap_w());
8390  do_set_border_color(cdata, cdata_dummy->_default_sampler.get_border_color());
8391 
8392  if (cdata_dummy->_default_sampler.get_minfilter() != SamplerState::FT_default) {
8393  do_set_minfilter(cdata, cdata_dummy->_default_sampler.get_minfilter());
8394  }
8395  if (cdata_dummy->_default_sampler.get_magfilter() != SamplerState::FT_default) {
8396  do_set_magfilter(cdata, cdata_dummy->_default_sampler.get_magfilter());
8397  }
8398  if (cdata_dummy->_default_sampler.get_anisotropic_degree() != 0) {
8399  do_set_anisotropic_degree(cdata, cdata_dummy->_default_sampler.get_anisotropic_degree());
8400  }
8401  if (cdata_dummy->_compression != CM_default) {
8402  do_set_compression(cdata, cdata_dummy->_compression);
8403  }
8404  if (cdata_dummy->_quality_level != QL_default) {
8405  do_set_quality_level(cdata, cdata_dummy->_quality_level);
8406  }
8407 
8408  Format format = cdata_dummy->_format;
8409  int num_components = cdata_dummy->_num_components;
8410 
8411  if (num_components == cdata->_num_components) {
8412  // Only reset the format if the number of components hasn't
8413  // changed, since if the number of components has changed our
8414  // texture no longer matches what it was when the bam was
8415  // written.
8416  do_set_format(cdata, format);
8417  }
8418 
8419  if (!cdata_dummy->_simple_ram_image._image.empty()) {
8420  // Only replace the simple ram image if it was generated more
8421  // recently than the one we already have.
8422  if (cdata->_simple_ram_image._image.empty() ||
8423  cdata_dummy->_simple_image_date_generated > cdata->_simple_image_date_generated) {
8424  do_set_simple_ram_image(cdata,
8425  cdata_dummy->_simple_ram_image._image,
8426  cdata_dummy->_simple_x_size,
8427  cdata_dummy->_simple_y_size);
8428  cdata->_simple_image_date_generated = cdata_dummy->_simple_image_date_generated;
8429  }
8430  }
8431 }
8432 
8433 ////////////////////////////////////////////////////////////////////
8434 // Function: Texture::CData::Constructor
8435 // Access: Public
8436 // Description:
8437 ////////////////////////////////////////////////////////////////////
8438 Texture::CData::
8439 CData() {
8440  _primary_file_num_channels = 0;
8441  _alpha_file_channel = 0;
8442  _keep_ram_image = true;
8443  _compression = CM_default;
8444  _auto_texture_scale = ATS_unspecified;
8445  _ram_image_compression = CM_off;
8446  _render_to_texture = false;
8447  _match_framebuffer_format = false;
8448  _post_load_store_cache = false;
8449  _quality_level = QL_default;
8450 
8451  _texture_type = TT_2d_texture;
8452  _x_size = 0;
8453  _y_size = 1;
8454  _z_size = 1;
8455  _num_views = 1;
8456 
8457  // We will override the format in a moment (in the Texture
8458  // constructor), but set it to something else first to avoid the
8459  // check in do_set_format depending on an uninitialized value.
8460  _format = F_rgba;
8461 
8462  _pad_x_size = 0;
8463  _pad_y_size = 0;
8464  _pad_z_size = 0;
8465 
8466  _orig_file_x_size = 0;
8467  _orig_file_y_size = 0;
8468 
8469  _loaded_from_image = false;
8470  _loaded_from_txo = false;
8471  _has_read_pages = false;
8472  _has_read_mipmaps = false;
8473  _num_mipmap_levels_read = 0;
8474 
8475  _simple_x_size = 0;
8476  _simple_y_size = 0;
8477  _simple_ram_image._page_size = 0;
8478 
8479  _has_clear_color = false;
8480 }
8481 
8482 ////////////////////////////////////////////////////////////////////
8483 // Function: Texture::CData::Copy Constructor
8484 // Access: Public
8485 // Description:
8486 ////////////////////////////////////////////////////////////////////
8487 Texture::CData::
8488 CData(const Texture::CData &copy) {
8489  _num_mipmap_levels_read = 0;
8490 
8491  do_assign(&copy);
8492 
8493  _properties_modified = copy._properties_modified;
8494  _image_modified = copy._image_modified;
8495  _simple_image_modified = copy._simple_image_modified;
8496 }
8497 
8498 ////////////////////////////////////////////////////////////////////
8499 // Function: Texture::CData::make_copy
8500 // Access: Public, Virtual
8501 // Description:
8502 ////////////////////////////////////////////////////////////////////
8503 CycleData *Texture::CData::
8504 make_copy() const {
8505  return new CData(*this);
8506 }
8507 
8508 ////////////////////////////////////////////////////////////////////
8509 // Function: Texture::CData::do_assign
8510 // Access: Public
8511 // Description:
8512 ////////////////////////////////////////////////////////////////////
8513 void Texture::CData::
8514 do_assign(const Texture::CData *copy) {
8515  _filename = copy->_filename;
8516  _alpha_filename = copy->_alpha_filename;
8517  if (!copy->_fullpath.empty()) {
8518  // Since the fullpath is often empty on a file loaded directly
8519  // from a txo, we only assign the fullpath if it is not empty.
8520  _fullpath = copy->_fullpath;
8521  _alpha_fullpath = copy->_alpha_fullpath;
8522  }
8523  _primary_file_num_channels = copy->_primary_file_num_channels;
8524  _alpha_file_channel = copy->_alpha_file_channel;
8525  _x_size = copy->_x_size;
8526  _y_size = copy->_y_size;
8527  _z_size = copy->_z_size;
8528  _num_views = copy->_num_views;
8529  _pad_x_size = copy->_pad_x_size;
8530  _pad_y_size = copy->_pad_y_size;
8531  _pad_z_size = copy->_pad_z_size;
8532  _orig_file_x_size = copy->_orig_file_x_size;
8533  _orig_file_y_size = copy->_orig_file_y_size;
8534  _num_components = copy->_num_components;
8535  _component_width = copy->_component_width;
8536  _texture_type = copy->_texture_type;
8537  _format = copy->_format;
8538  _component_type = copy->_component_type;
8539  _loaded_from_image = copy->_loaded_from_image;
8540  _loaded_from_txo = copy->_loaded_from_txo;
8541  _has_read_pages = copy->_has_read_pages;
8542  _has_read_mipmaps = copy->_has_read_mipmaps;
8543  _num_mipmap_levels_read = copy->_num_mipmap_levels_read;
8544  _default_sampler = copy->_default_sampler;
8545  _keep_ram_image = copy->_keep_ram_image;
8546  _compression = copy->_compression;
8547  _match_framebuffer_format = copy->_match_framebuffer_format;
8548  _quality_level = copy->_quality_level;
8549  _auto_texture_scale = copy->_auto_texture_scale;
8550  _ram_image_compression = copy->_ram_image_compression;
8551  _ram_images = copy->_ram_images;
8552  _simple_x_size = copy->_simple_x_size;
8553  _simple_y_size = copy->_simple_y_size;
8554  _simple_ram_image = copy->_simple_ram_image;
8555 }
8556 
8557 ////////////////////////////////////////////////////////////////////
8558 // Function: Texture::CData::write_datagram
8559 // Access: Public, Virtual
8560 // Description: Writes the contents of this object to the datagram
8561 // for shipping out to a Bam file.
8562 ////////////////////////////////////////////////////////////////////
8563 void Texture::CData::
8564 write_datagram(BamWriter *manager, Datagram &dg) const {
8565 }
8566 
8567 ////////////////////////////////////////////////////////////////////
8568 // Function: Texture::CData::complete_pointers
8569 // Access: Public, Virtual
8570 // Description: Receives an array of pointers, one for each time
8571 // manager->read_pointer() was called in fillin().
8572 // Returns the number of pointers processed.
8573 ////////////////////////////////////////////////////////////////////
8574 int Texture::CData::
8575 complete_pointers(TypedWritable **p_list, BamReader *manager) {
8576  return 0;
8577 }
8578 
8579 ////////////////////////////////////////////////////////////////////
8580 // Function: Texture::CData::fillin
8581 // Access: Public, Virtual
8582 // Description: This internal function is called by make_from_bam to
8583 // read in all of the relevant data from the BamFile for
8584 // the new Geom.
8585 ////////////////////////////////////////////////////////////////////
8586 void Texture::CData::
8587 fillin(DatagramIterator &scan, BamReader *manager) {
8588 }
8589 
8590 ////////////////////////////////////////////////////////////////////
8591 // Function: Texture::TextureType output operator
8592 // Description:
8593 ////////////////////////////////////////////////////////////////////
8594 ostream &
8595 operator << (ostream &out, Texture::TextureType tt) {
8596  return out << Texture::format_texture_type(tt);
8597 }
8598 
8599 ////////////////////////////////////////////////////////////////////
8600 // Function: Texture::ComponentType output operator
8601 // Description:
8602 ////////////////////////////////////////////////////////////////////
8603 ostream &
8604 operator << (ostream &out, Texture::ComponentType ct) {
8605  return out << Texture::format_component_type(ct);
8606 }
8607 
8608 ////////////////////////////////////////////////////////////////////
8609 // Function: Texture::Format output operator
8610 // Description:
8611 ////////////////////////////////////////////////////////////////////
8612 ostream &
8613 operator << (ostream &out, Texture::Format f) {
8614  return out << Texture::format_format(f);
8615 }
8616 
8617 ////////////////////////////////////////////////////////////////////
8618 // Function: Texture::CompressionMode output operator
8619 // Description:
8620 ////////////////////////////////////////////////////////////////////
8621 ostream &
8622 operator << (ostream &out, Texture::CompressionMode cm) {
8623  return out << Texture::format_compression_mode(cm);
8624 }
8625 
8626 ////////////////////////////////////////////////////////////////////
8627 // Function: Texture::QualityLevel output operator
8628 // Description:
8629 ////////////////////////////////////////////////////////////////////
8630 ostream &
8631 operator << (ostream &out, Texture::QualityLevel tql) {
8632  return out << Texture::format_quality_level(tql);
8633 }
8634 
8635 ////////////////////////////////////////////////////////////////////
8636 // Function: Texture::QualityLevel input operator
8637 // Description:
8638 ////////////////////////////////////////////////////////////////////
8639 istream &
8640 operator >> (istream &in, Texture::QualityLevel &tql) {
8641  string word;
8642  in >> word;
8643 
8644  tql = Texture::string_quality_level(word);
8645  return in;
8646 }
Filename get_filename_index(int index) const
If the pattern flag is set for this Filename and the filename string actually includes a sequence of ...
Definition: filename.cxx:873
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
void set_data(TypedWritable *ptr, ReferenceCount *ref_ptr)
Stores a new data object on the record.
virtual void ensure_loader_type(const Filename &filename)
May be called prior to calling read_txo() or any bam-related Texture-creating callback, to ensure that the proper dynamic libraries for a Texture of the current class type, and the indicated filename, have been already loaded.
Definition: texture.cxx:2607
static bool is_srgb(Format format)
Returns true if the indicated format is in the sRGB color space, false otherwise. ...
Definition: texture.cxx:2466
string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:436
static string format_quality_level(QualityLevel tql)
Returns the indicated QualityLevel converted to a string word.
Definition: texture.cxx:2283
bool was_image_modified() const
Returns true if the texture image has been modified since the last time mark_loaded() was called...
const Filename & get_filename() const
If a BAM is a file, then the BamWriter should contain the name of the file.
Definition: bamWriter.I:36
void add_uint8(PN_uint8 value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:138
void set_green(int x, int y, float g)
Sets the green component color only at the indicated pixel.
Definition: pnmImage.I:965
void enqueue_texture(Texture *tex)
Indicates that a texture would like to be put on the list to be prepared when the GSG is next ready t...
bool is_exact_type(TypeHandle handle) const
Returns true if the current object is the indicated type exactly.
Definition: typedObject.I:74
This is our own Panda specialization on the default STL map.
Definition: pmap.h:52
static string format_component_type(ComponentType ct)
Returns the indicated ComponentType converted to a string word.
Definition: texture.cxx:1976
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const string &default_extension=string()) const
Searches the given search path for the filename.
void copy_sub_image(const PNMImage &copy, int xto, int yto, int xfrom=0, int yfrom=0, int x_size=-1, int y_size=-1)
Copies a rectangular area of another image into a rectangular area of this image. ...
Definition: pnmImage.cxx:1119
xelval get_channel_val(int x, int y, int channel) const
Returns the nth component color at the indicated pixel.
Definition: pnmImage.cxx:850
bool get_bool()
Extracts a boolean value.
void add_string(const string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:351
The name of this class derives from the fact that we originally implemented it as a layer on top of t...
Definition: pnmImage.h:68
static Texture * load_cube_map(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a cube map texture that is specified with a series of 6 pages, numbered 0 through 5...
Definition: texturePool.I:146
ColorSpace get_color_space() const
Returns the color space in which the image is encoded.
Definition: pnmImage.I:296
void release_texture(TextureContext *tc)
Indicates that a texture context, created by a previous call to prepare_texture(), is no longer needed.
TypedWritable * get_data() const
Returns a pointer to the data stored in the record, or NULL if there is no data.
static bool adjust_size(int &x_size, int &y_size, const string &name, bool for_padding, AutoTextureScale auto_texture_scale=ATS_unspecified)
Computes the proper size of the texture, based on the original size, the filename, and the resizing whims of the config file.
Definition: texture.cxx:2493
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
Definition: texture.cxx:7896
virtual bool is_cacheable() const
Returns true if there is enough information in this Texture object to write it to the bam cache succe...
Definition: texture.cxx:978
xelval get_gray_val(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:490
void wait()
Waits on the condition.
static string format_format(Format f)
Returns the indicated Format converted to a string word.
Definition: texture.cxx:2025
void append_data(const void *data, size_t size)
Appends some more raw data to the end of the datagram.
Definition: datagram.cxx:144
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
bool get_resident(PreparedGraphicsObjects *prepared_objects) const
Returns true if this Texture is reported to be resident within graphics memory for the indicated GSG...
Definition: texture.cxx:1415
bool resolve()
This may be called at any time during processing of the Bam file to resolve all the known pointers so...
Definition: bamReader.cxx:359
void alpha_fill(float alpha=0.0)
Sets the entire alpha channel to the given level.
Definition: pnmImage.I:218
void set_ram_mipmap_pointer_from_int(long long pointer, int n, int page_size)
Accepts a raw pointer cast as an int, which is then passed to set_ram_mipmap_pointer(); see the docum...
Definition: texture.cxx:1105
AutoTextureScale get_auto_texture_scale() const
Returns the power-of-2 texture-scaling mode that will be applied to this particular texture when it i...
Definition: texture.I:2190
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:26
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:47
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
bool dequeue_texture(Texture *tex)
Removes a texture from the queued list of textures to be prepared.
static Filename pattern_filename(const string &filename)
Constructs a filename that represents a sequence of numbered files.
Definition: filename.I:179
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:249
This class can be used to read a binary file that consists of an arbitrary header followed by a numbe...
int release_all()
Frees the context allocated on all objects for which the texture has been declared.
Definition: texture.cxx:1474
float get_gray(int x, int y) const
Returns the gray component color at the indicated pixel.
Definition: pnmImage.I:928
static void register_with_read_factory()
Factory method to generate a Texture object.
Definition: texture.cxx:7863
bool read(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true)
Reads the indicated image filename.
Definition: pnmImage.cxx:245
A single page of data maintained by a PipelineCycler.
Definition: cycleData.h:50
static Format string_format(const string &str)
Returns the Format corresponding to the indicated string word.
Definition: texture.cxx:2120
bool was_image_modified(PreparedGraphicsObjects *prepared_objects) const
Returns true if the texture needs to be re-loaded onto the indicated GSG, either because its image da...
Definition: texture.cxx:1320
virtual bool is_floating_point()
Returns true if this PNMFileType represents a floating-point image type, false if it is a normal...
Definition: pnmReader.cxx:82
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:75
void set_ram_image_as(CPTA_uchar image, const string &provided_format)
Replaces the current system-RAM image with the new data, converting it first if necessary from the in...
Definition: texture.cxx:840
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:183
bool write_header(const string &header)
Writes a sequence of bytes to the beginning of the datagram file.
size_t estimate_texture_memory() const
Estimates the amount of texture memory that will be consumed by loading this texture.
Definition: texture.cxx:490
void set_orig_file_size(int x, int y, int z=1)
Specifies the size of the texture as it exists in its original disk file, before any Panda scaling...
Definition: texture.cxx:1790
static bool is_specific(CompressionMode compression)
Returns true if the indicated compression mode is one of the specific compression types...
Definition: texture.cxx:2401
bool get_active(PreparedGraphicsObjects *prepared_objects) const
Returns true if this Texture was rendered in the most recent frame within the indicated GSG...
Definition: texture.cxx:1385
This is a special class object that holds all the information returned by a particular GSG to indicat...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
bool read_txo(istream &in, const string &filename="")
Reads the texture from a Panda texture object.
Definition: texture.cxx:660
A base class for things which need to inherit from both TypedObject and from ReferenceCount.
virtual void write_datagram(BamWriter *manager, Datagram &me)
Function to write the important information in the particular object to a Datagram.
Definition: texture.cxx:7874
PTA_uchar modify_simple_ram_image()
Returns a modifiable pointer to the internal "simple" texture image.
Definition: texture.cxx:1133
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:100
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
xel * get_array()
Directly access the underlying PNMImage array.
Definition: pnmImage.I:1312
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool get_cache_textures() const
Returns whether texture files (e.g.
Definition: bamCache.I:100
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:29
void set_basename_wo_extension(const string &s)
Replaces the basename part of the filename, without the file extension.
Definition: filename.cxx:813
PN_int32 get_int32()
Extracts a signed 32-bit integer.
void set_red(int x, int y, float r)
Sets the red component color only at the indicated pixel.
Definition: pnmImage.I:953
PN_uint8 get_uint8()
Extracts an unsigned 8-bit integer.
bool open(const FileReference *file)
Opens the indicated filename for reading.
int get_texture_num_views() const
See set_texture_num_views().
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
CPTA_uchar get_ram_image_as(const string &requested_format)
Returns the uncompressed system-RAM image data associated with the texture.
Definition: texture.cxx:5535
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
A table of objects that are saved within the graphics context for reference by handle later...
bool has_ram_image() const
Returns true if the Texture has its image contents available in main RAM, false if it exists only in ...
Definition: texture.I:1465
bool read_header(string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
Texture(const string &name=string())
Constructs an empty texture.
Definition: texture.cxx:144
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
string get_string()
Extracts a variable-length string.
int get_file_minor_ver() const
Returns the minor version number of the Bam file currently being read.
Definition: bamReader.I:105
int size() const
Returns the number of unique words in the variable.
static CompressionMode string_compression_mode(const string &str)
Returns the CompressionMode value associated with the given string representation.
Definition: texture.cxx:2245
static Thread * get_current_thread()
Returns a pointer to the currently-executing Thread object.
Definition: thread.I:145
int get_y_size() const
Returns the number of pixels in the Y direction.
void quick_filter_from(const PNMImage &copy, int xborder=0, int yborder=0)
Resizes from the given image, with a fixed radius of 0.5.
TypedReferenceCount * get_aux_data(const string &key) const
Returns a record previously recorded via set_aux_data().
Definition: texture.cxx:636
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
bool open(const FileReference *file)
Opens the indicated filename for writing.
int get_x_size() const
Returns the number of pixels in the X direction.
static QualityLevel string_quality_level(const string &str)
Returns the QualityLevel value associated with the given string representation.
Definition: texture.cxx:2305
void resize(int new_x_size, int new_y_size)
Applies a simple filter to resample the pfm file in-place to the indicated size.
Definition: pfmFile.cxx:996
bool is_valid() const
Returns true if the image has been read in or correctly initialized with a height and width...
Definition: pnmImage.I:309
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
void notify_all()
Informs all of the other threads who are currently blocked on wait() that the relevant condition has ...
void clear_aux_data(const string &key)
Removes a record previously recorded via set_aux_data().
Definition: texture.cxx:623
CPTA_uchar get_ram_mipmap_image(int n) const
Returns the system-RAM image data associated with the nth mipmap level, if present.
Definition: texture.cxx:1039
void set_file_texture_mode(BamTextureMode file_texture_mode)
Changes the BamTextureMode preference for the Bam file currently being written.
Definition: bamWriter.I:94
bool release(PreparedGraphicsObjects *prepared_objects)
Frees the texture context only on the indicated object, if it exists there.
Definition: texture.cxx:1445
void consider_rescale(PNMImage &pnmimage)
Asks the PNMImage to change its scale when it reads the image, according to the whims of the Config...
Definition: texture.cxx:1883
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well...
Definition: texture.cxx:1759
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:194
bool read(const Filename &fullpath)
Reads the PFM data from the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:130
A lightweight class that represents a single element that may be timed and/or counted via stats...
static AutoTextureScale get_textures_power_2()
This flag returns ATS_none, ATS_up, or ATS_down and controls the scaling of textures in general...
Definition: texture.I:2232
bool write(const Filename &fullpath)
Writes the PFM data to the indicated file, returning true on success, false on failure.
Definition: pfmFile.cxx:222
virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const
If has_cull_callback() returns true, this function will be called during the cull traversal to perfor...
Definition: texture.cxx:2379
string extract_bytes(size_t size)
Extracts the indicated number of bytes in the datagram and returns them as a string.
void generate_alpha_scale_map()
Generates a special 256x1 1-d texture that can be used to apply an arbitrary alpha scale to objects b...
Definition: texture.cxx:311
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:118
A base class for all things which can have a name.
Definition: namable.h:29
void acquire() const
Grabs the mutex if it is available.
Definition: mutexDirect.I:70
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping, false otherwise.
Definition: texture.I:1319
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:94
int get_read_y_size() const
Returns the requested y_size of the image if set_read_size() has been called, or the image y_size oth...
Definition: pnmImage.I:285
bool get_resident() const
Returns the resident flag associated with this object.
Definition: bufferContext.I:61
void set_auto_texture_scale(AutoTextureScale scale)
Set this flag to ATS_none, ATS_up, ATS_down, or ATS_pad to control how a texture is scaled from disk ...
bool get_cache_compressed_textures() const
Returns whether compressed texture files will be stored in the cache, as compressed txo files...
Definition: bamCache.I:140
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
PointerToArray< Element > cast_non_const() const
Casts away the constness of the CPTA(Element), and returns an equivalent PTA(Element).
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component, or with a special extension, 2- or 4-component.
Definition: pfmFile.h:34
LVecBase3f xform_point(const LVecBase3f &v) const
The matrix transforms a 3-component point (including translation component) and returns the result...
Definition: lmatrix.h:1667
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_num_loadable_ram_mipmap_images() const
Returns the number of contiguous mipmap levels that exist in RAM, up until the first gap in the seque...
Definition: texture.cxx:1002
int get_remaining_size() const
Return the bytes left in the datagram.
const LoaderOptions & get_loader_options() const
Returns the LoaderOptions passed to the loader when the model was requested, if any.
Definition: bamReader.I:60
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
xelval get_maxval() const
Returns the maximum channel value allowable for any pixel in this image; for instance, 255 for a typical 8-bit-per-channel image.
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
static bool has_alpha(Format format)
Returns true if the indicated format includes alpha, false otherwise.
Definition: texture.cxx:2420
void set_gray(int x, int y, float gray)
Sets the gray component color at the indicated pixel.
Definition: pnmImage.I:994
void copy_header_from(const PNMImageHeader &header)
Copies just the header information into this image.
Definition: pnmImage.cxx:164
string get_unique_value(int n) const
Returns the nth unique value of the variable.
xelval get_blue_val(int x, int y) const
Returns the blue component color at the indicated pixel.
Definition: pnmImage.I:474
bool get_active() const
Returns the active flag associated with this object.
Definition: bufferContext.I:49
void set_blue(int x, int y, float b)
Sets the blue component color only at the indicated pixel.
Definition: pnmImage.I:977
bool has_simple_ram_image() const
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.I:2053
This template class calls PipelineCycler::read() in the constructor and PipelineCycler::release_read(...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
static GraphicsStateGuardianBase * get_default_gsg()
Returns a pointer to the "default" GSG.
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:40
PN_float32 get_channel(int x, int y, int c) const
Returns the cth channel of the point value at the indicated point.
Definition: pfmFile.I:68
PNMFileType * get_type() const
If the file type is known (e.g.
Texture * load_related(const InternalName *suffix) const
Loads a texture whose filename is derived by concatenating a suffix to the filename of this texture...
Definition: texture.cxx:784
void set_aux_data(const string &key, TypedReferenceCount *aux_data)
Records an arbitrary object in the Texture, associated with a specified key.
Definition: texture.cxx:611
This class specializes ConfigVariable as an enumerated type.
int get_word(int n) const
Returns the variable&#39;s nth value.
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:152
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
bool read_dds(istream &in, const string &filename="", bool header_only=false)
Reads the texture from a DDS file object.
Definition: texture.cxx:768
size_t get_data_size_bytes() const
Returns the number of bytes previously reported for the data object.
Definition: bufferContext.I:26
bool write(const Filename &filename, PNMFileType *type=NULL) const
Writes the image to the indicated filename.
Definition: pnmImage.cxx:362
void register_finalize(TypedWritable *whom)
Should be called by an object reading itself from the Bam file to indicate that this particular objec...
Definition: bamReader.cxx:886
static Texture * load_texture(const Filename &filename, int primary_file_num_channels=0, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads the given filename up into a texture, if it has not already been loaded, and returns the new te...
Definition: texturePool.I:58
This template class calls PipelineCycler::write() in the constructor and PipelineCycler::release_writ...
xelval * get_alpha_array()
Directly access the underlying PNMImage array of alpha values.
Definition: pnmImage.I:1334
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
size_t get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const
Returns the number of bytes which the texture is reported to consume within graphics memory...
Definition: texture.cxx:1356
This is an abstract base class that defines the interface for reading image files of various types...
Definition: pnmReader.h:31
void ref() const
Explicitly increments the reference count.
bool make_relative_to(Filename directory, bool allow_backups=true)
Adjusts this filename, which must be a fully-specified pathname beginning with a slash, to make it a relative filename, relative to the fully-specified directory indicated (which must also begin with, and may or may not end with, a slash–a terminating slash is ignored).
Definition: filename.cxx:1766
void register_factory(TypeHandle handle, CreateFunc *func)
Registers a new kind of thing the Factory will be able to create.
Definition: factory.I:90
bool is_texture_queued(const Texture *tex) const
Returns true if the texture has been queued on this GSG, false otherwise.
int get_ref_count() const
Returns the current reference count.
An instance of this object is returned by Texture::peek().
Definition: texturePeeker.h:30
void set_channel(int x, int y, int c, PN_float32 value)
Replaces the cth channel of the point value at the indicated point.
Definition: pfmFile.I:82
static TextureType string_texture_type(const string &str)
Returns the TextureType corresponding to the indicated string word.
Definition: texture.cxx:1951
void local_object()
This function should be called, once, immediately after creating a new instance of some ReferenceCoun...
TextureContext * prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the texture on the particular GSG, if it does not already exist.
Definition: texture.cxx:1815
bool load(const PNMImage &pnmimage)
Fills the PfmFile with the data from the indicated PNMImage, converted to floating-point values...
Definition: pfmFile.cxx:312
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This class can be used to write a binary file that consists of an arbitrary header followed by a numb...
CompressionMode get_ram_image_compression() const
Returns the compression mode in which the ram image is already stored pre-compressed.
Definition: texture.I:1624
void add_alpha()
Adds an alpha channel to the image, if it does not already have one.
Definition: pnmImage.I:336
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname)...
Definition: filename.cxx:1019
void generate_simple_ram_image()
Computes the "simple" ram image by loading the main RAM image, if it is not already available...
Definition: texture.cxx:1171
ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written...
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
CycleDataType * elevate_read_upstream(const CycleDataType *pointer, bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::elevate_read_upstream().
void clear()
Frees all memory allocated for the image, and clears all its parameters (size, color, type, etc).
Definition: pnmImage.cxx:50
static int up_to_power_2(int value)
Returns the smallest power of 2 greater than or equal to value.
Definition: texture.cxx:1845
void set_alpha(int x, int y, float a)
Sets the alpha component color only at the indicated pixel.
Definition: pnmImage.I:1007
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:213
AutoTextureScale get_auto_texture_scale() const
See set_auto_texture_scale().
PTA_uchar new_simple_ram_image(int x_size, int y_size)
Creates an empty array for the simple ram image of the indicated size, and returns a modifiable point...
Definition: texture.cxx:1147
A thread; that is, a lightweight process.
Definition: thread.h:51
void take_from(PNMImage &orig)
Move the contents of the other image into this one, and empty the other image.
Definition: pnmImage.cxx:184
TextureContext * prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg)
Immediately creates a new TextureContext for the indicated texture and returns it.
bool write_txo(ostream &out, const string &filename="") const
Writes the texture to a Panda texture object.
Definition: texture.cxx:750
void prepare(PreparedGraphicsObjects *prepared_objects)
Indicates that the texture should be enqueued to be prepared in the indicated prepared_objects at the...
Definition: texture.cxx:1289
int find_on_searchpath(const DSearchPath &searchpath)
Performs the reverse of the resolve_filename() operation: assuming that the current filename is fully...
Definition: filename.cxx:1820
void add_uint32(PN_uint32 value)
Adds an unsigned 32-bit integer to the datagram.
Definition: datagram.I:192
bool is_prepared(PreparedGraphicsObjects *prepared_objects) const
Returns true if the texture has already been prepared or enqueued for preparation on the indicated GS...
Definition: texture.cxx:1301
int get_y_size() const
Returns the height of the texture image in texels.
Definition: texture.I:650
int get_num_channels() const
Returns the number of channels in the image.
CycleDataType * write_upstream(bool force_to_0, Thread *current_thread)
See PipelineCyclerBase::write_upstream().
static string format_texture_type(TextureType tt)
Returns the indicated TextureType converted to a string word.
Definition: texture.cxx:1928
static int down_to_power_2(int value)
Returns the largest power of 2 less than or equal to value.
Definition: texture.cxx:1860
int get_num_unique_values() const
Returns the number of unique values in the variable.
void generate_normalization_cube_map(int size)
Generates a special cube map image in the texture that can be used to apply bump mapping effects: for...
Definition: texture.cxx:206
bool read(const Filename &fullpath, const LoaderOptions &options=LoaderOptions())
Reads the named filename into the texture.
Definition: texture.cxx:338
static bool is_grayscale(ColorType color_type)
This static variant of is_grayscale() returns true if the indicated image type represents a grayscale...
BamTextureMode get_file_texture_mode() const
Returns the BamTextureMode preference indicated by the Bam file currently being written.
Definition: bamWriter.I:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
Filename get_cwd() const
Returns the current directory name.
void clear_ram_mipmap_image(int n)
Discards the current system-RAM image for the nth mipmap level.
Definition: texture.cxx:1116
void add_dependent_file(const Filename &pathname)
Adds the indicated file to the list of files that will be loaded to generate the data in this record...
void add_int32(PN_int32 value)
Adds a signed 32-bit integer to the datagram.
Definition: datagram.I:159
A class to retrieve the individual data elements previously stored in a Datagram. ...
void texture_uploaded()
This method is called by the GraphicsEngine at the beginning of the frame *after* a texture has been ...
Definition: texture.cxx:2334
bool has_name() const
Returns true if the Namable has a nonempty name set, false if the name is empty.
Definition: namable.I:75
void release() const
Releases the mutex.
Definition: mutexDirect.I:99
static Texture * load_3d_texture(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a 3-D texture that is specified with a series of n pages, all numbered in sequence, and beginning with index 0.
Definition: texturePool.I:104
static Texture * load_2d_texture_array(const Filename &filename_pattern, bool read_mipmaps=false, const LoaderOptions &options=LoaderOptions())
Loads a 2-D texture array that is specified with a series of n pages, all numbered in sequence...
Definition: texturePool.I:125
xelval get_alpha_val(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:503
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
static void close_write_file(ostream *stream)
Closes a file opened by a previous call to open_write_file().
void fill(float red, float green, float blue)
Sets the entire image (except the alpha channel) to the given color.
Definition: pnmImage.I:186
bool store(PNMImage &pnmimage) const
Copies the data to the indicated PNMImage, converting to RGB values.
Definition: pfmFile.cxx:387
bool has_compression() const
Returns true if the texture indicates it wants to be compressed, either with CM_on or higher...
Definition: texture.I:1287
void set_num_channels(int num_channels)
Changes the number of channels associated with the image.
Definition: pnmImage.I:323
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:30
virtual bool get_keep_ram_image() const
Returns the flag that indicates whether this Texture is eligible to have its main RAM copy of the tex...
Definition: texture.cxx:964
void set_read_size(int x_size, int y_size)
Specifies the size to we&#39;d like to scale the image upon reading it.
Definition: pnmImage.I:238
bool has_hash() const
Returns true if the filename is indicated to be a filename pattern (that is, set_pattern(true) was ca...
Definition: filename.I:640
const CycleDataType * read(Thread *current_thread) const
See PipelineCyclerBase::read().
bool normalize()
Normalizes the vector in place.
Definition: lvecBase3.h:783
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
static bool has_binary_alpha(Format format)
Returns true if the indicated format includes a binary alpha only, false otherwise.
Definition: texture.cxx:2449
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:98
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
const Filename & get_filename() const
If a BAM is a file, then the BamReader should contain the name of the file.
Definition: bamReader.I:45
static string format_compression_mode(CompressionMode cm)
Returns the indicated CompressionMode converted to a string word.
Definition: texture.cxx:2209
static ComponentType string_component_type(const string &str)
Returns the ComponentType corresponding to the indicated string word.
Definition: texture.cxx:2000
xelval get_green_val(int x, int y) const
Returns the green component color at the indicated pixel.
Definition: pnmImage.I:462
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
xelval get_red_val(int x, int y) const
Returns the red component color at the indicated pixel.
Definition: pnmImage.I:450
static BamCache * get_global_ptr()
Returns a pointer to the global BamCache object, which is used automatically by the ModelPool and Tex...
Definition: bamCache.I:253
void clear()
Eliminates all data in the file.
Definition: pfmFile.cxx:81
virtual bool has_cull_callback() const
Should be overridden by derived classes to return true if cull_callback() has been defined...
Definition: texture.cxx:2361
size_t get_expected_ram_page_size() const
Returns the number of bytes that should be used per each Z page of the 3-d texture.
Definition: texture.I:1574
bool write(const Filename &fullpath)
Writes the texture to the named filename.
Definition: texture.I:305
This class can be used to test for string matches against standard Unix-shell filename globbing conve...
Definition: globPattern.h:37
int get_x_size() const
Returns the width of the texture image in texels.
Definition: texture.I:638
virtual bool unref() const
Explicitly decrements the reference count.
void skip_bytes(size_t size)
Skips over the indicated number of bytes in the stream.
static const LVecBase4f & zero()
Returns a zero-length vector.
Definition: lvecBase4.h:493
float get_alpha(int x, int y) const
Returns the alpha component color at the indicated pixel.
Definition: pnmImage.I:941
int get_read_x_size() const
Returns the requested x_size of the image if set_read_size() has been called, or the image x_size oth...
Definition: pnmImage.I:273
void set_ram_mipmap_pointer(int n, void *image, size_t page_size=0)
Sets an explicit void pointer as the texture&#39;s mipmap image for the indicated level.
Definition: texture.cxx:1079
PNMReader * make_reader(const Filename &filename, PNMFileType *type=NULL, bool report_unknown_type=true) const
Returns a newly-allocated PNMReader of the suitable type for reading from the indicated image filenam...
void * get_ram_mipmap_pointer(int n) const
Similiar to get_ram_mipmap_image(), however, in this case the void pointer for the given ram image is...
Definition: texture.cxx:1056